gitbutlerapp/gitbutler · error

Cannot unapply branch '{branch}' from an ad-hoc workspace be

Error message

Cannot unapply branch '{branch}' from an ad-hoc workspace because non-tip branches can only disappear if their now removed metadata disambiguated them

What it means

After removing the branch's metadata, unapply re-runs the traversal with an empty overlay to see whether the branch really disappears from the workspace. If it still resolves as a segment, the branch is not the tip of its own history — other refs make it visible — and removing only its metadata cannot make it vanish. The operation aborts rather than leave a half-removed state.

Source

Thrown at crates/but-workspace/src/branch/unapply.rs:270

            bail!("Cannot unapply a branch from an ad-hoc detached workspace");
        };
        let mut ws_md = meta.workspace(workspace_ref_name.as_ref())?;
        if ws.kind.has_managed_ref() || ws.has_metadata() {
            ws.reconcile_metadata(&mut ws_md)?;
        }
        let branch_removed_from_ws_meta = ws_md.unapply_branch(branch);
        if !branch_removed_from_ws_meta {
            // The branch wasn't in workspace metadata, yet it was present, so also delete its branch metadata
            // as it could be used to disambiguate the segment.
            // TODO: this will actually be observable even if it doens't work, unless it's run in a transaction, which right now it's not!
            //       Should be able to redo the traversal with an overlay that hides branch metadata, but I'd say it's not important enough.
            meta.remove(branch)?;
            let graph = ws
                .graph
                .redo_traversal_with_overlay(repo, meta, Overlay::default())?;
            let workspace = graph.into_workspace()?;
            if workspace.refname_is_segment(branch) {
                bail!(
                    "Cannot unapply branch '{branch}' from an ad-hoc workspace because non-tip branches can only disappear if their now removed metadata disambiguated them",
                    branch = branch.shorten()
                );
            }
            return Ok(Outcome::new(Cow::Owned(workspace)));
        }

        // Everything past this point is stricly in non-dry-run mode and we may totally end up in intermediate states
        // if something fails.
        // Redo the traversal with the changed workspace metadata so code below can rely on the reconciled version.
        let ws = ws
            .graph
            .redo_traversal_with_overlay(
                repo,
                meta,
                Overlay::default()
                    .with_dropped_references([branch.to_owned()])
                    .with_workspace_metadata_override(Some((

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Unapply branches top-down (tip first) so each removal actually detaches the segment.
  2. Delete or move the other refs that keep the branch visible (e.g. the branch's own git ref) before unapplying.
  3. Use the workspace unapply flow, which handles ref and metadata disposal together.
Defensive patterns

Strategy: validation

Validate before calling

// before unapplying, verify the branch would disappear once its metadata is dropped
let graph = ws.graph.redo_traversal_with_overlay(repo, meta, Overlay::default())?;
let check = graph.into_workspace()?;
if !check.refname_is_segment(branch) || check.refname_is_segment(branch) {
    // cheap pre-check: only attempt when the branch owns its tip
}
// simplest reliable guard: unapply stacks top-down (tip branches first)

Try / catch

match unapply(repo, meta, &ws, branch, opts) {
    Err(err) if err.to_string().contains("non-tip branches can only disappear") => {
        ui::info("Unapply the branch above this one first");
        Ok(default_outcome())
    }
    other => other,
}

Prevention

When it happens

Trigger: Unapplying a branch in an ad-hoc workspace where the branch is non-tip: it is still reachable/visible through other refs or segments after its disambiguating metadata is gone.

Common situations: Stacked branches sharing history where the lower branch ref still points into the stack; workspaces where the branch was only projected thanks to metadata that has now been removed.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/8ac40657f282e981. Report an issue: GitHub.