gitbutlerapp/gitbutler · error

Cannot unapply branch by checking out conflicted commit {new

Error message

Cannot unapply branch by checking out conflicted commit {new_head_id}

What it means

Before checking out a new HEAD during unapply, safe_checkout verifies the target commit is not conflicted (carrying GitButler conflict markers per but_core::Commit::is_conflicted()). Checking out a conflicted commit would materialize conflict markers into the worktree, so the operation is refused. Resolve the conflicts (or pick a different target) before unapplying.

Source

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

            if selected
                .as_ref()
                .is_none_or(|(best_generation, _)| generation < *best_generation)
            {
                selected = Some((generation, ref_to_checkout));
            }
        }
        Ok(selected.map(|(_, ref_to_checkout)| ref_to_checkout))
    }

    /// Run `safe_checkout`, but provide better error messages if the commit to checkout
    /// is conflicted.
    fn safe_checkout(
        repo: &gix::Repository,
        new_head_id: gix::ObjectId,
        options: but_core::worktree::checkout::Options,
    ) -> anyhow::Result<but_core::worktree::checkout::Outcome> {
        if but_core::Commit::from_id(new_head_id.attach(repo))?.is_conflicted() {
            bail!("Cannot unapply branch by checking out conflicted commit {new_head_id}");
        }
        but_core::worktree::safe_checkout_from_head(new_head_id, repo, options)
    }

    /// Check out `ref_to_checkout` using the workspace traversal entrypoint as the
    /// current worktree/index source.
    ///
    /// `ws` must be the workspace projection for the currently checked-out `HEAD`.
    /// Its graph entrypoint is therefore the commit the index and worktree are
    /// expected to match before checkout. This matters when `HEAD` points at a
    /// stack segment inside the workspace rather than the workspace tip.
    ///
    /// The helper only updates the index/worktree according to `options`; callers
    /// remain responsible for any subsequent `HEAD`, reference, metadata, and
    /// projection updates.
    fn safe_checkout_ref_to_checkout(
        repo: &gix::Repository,
        ref_to_checkout: &RefToCheckout,

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Resolve outstanding conflicts in the workspace (or undo the conflicting operation) before unapplying.
  2. Use GitButler undo to return to a pre-conflict state, then retry the unapply.
  3. If automating, pre-check target commits with Commit::is_conflicted() and route the user to conflict resolution.

Example fix

// before
let outcome = unapply(repo, meta, &ws, branch, opts)?;

// after
use but_core::Commit;
for (_, seg) in ws.segments_with_commits() {
    if Commit::from_id(seg.tip()?.id.attach(repo))?.is_conflicted() {
        bail_user!("resolve conflicts before unapplying");
    }
}
let outcome = unapply(repo, meta, &ws, branch, opts)?;
Defensive patterns

Strategy: validation

Validate before calling

use but_core::Commit;
let target = Commit::from_id(new_head_id.attach(repo))?;
if target.is_conflicted() {
    // route the user to conflict resolution instead of unapplying
    return prompt_conflict_resolution();
}

Type guard

fn commit_is_clean(repo: &gix::Repository, id: gix::ObjectId) -> bool {
    but_core::Commit::from_id(id.attach(repo))
        .map(|c| !c.is_conflicted())
        .unwrap_or(false)
}

Try / catch

match unapply(repo, meta, &ws, branch, opts) {
    Err(err) if err.to_string().contains("conflicted commit") => {
        ui::error("Resolve workspace conflicts before unapplying");
        Ok(default_outcome())
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling unapply paths that invoke safe_checkout with a new_head_id whose commit tree is in a conflicted state — typically a workspace that currently contains unresolved merge conflicts.

Common situations: Unapplying branches while the workspace still shows unresolved conflicts from an integration/sync; commits created by GitButler's conflict materialization left in place; retrying an unapply right after a conflicted upstream merge.

Related errors


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