gitbutlerapp/gitbutler · error · anyhow::Error

Failed to unapply branch due to conflicts while rebuilding t

Error message

Failed to unapply branch due to conflicts while rebuilding the workspace merge commit: {}

What it means

After removing a branch from workspace metadata, unapply rebuilds the workspace merge commit from the remaining stack tips (WorkspaceCommit::from_new_merge_with_tips). If those remaining stacks now conflict with each other in the merged workspace commit, this ensure! aborts the unapply and lists the conflicting stacks by name.

Source

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

    ) -> anyhow::Result<WorkspaceMergeAfterUnapply> {
        let mut tips = future_workspace_tips.to_vec();
        let report_workspace_merge = !tips.is_empty();
        if tips.is_empty() {
            tips.push(base_tip_after_unapply(ws, future_workspace_tips)?);
        }
        let outcome = WorkspaceCommit::from_new_merge_with_tips(tips, &ws.graph, repo, None)?;
        ensure_workspace_merge_has_no_conflicts(&outcome)?;
        let workspace_commit_id = outcome.workspace_commit_id;
        Ok(WorkspaceMergeAfterUnapply {
            workspace_commit_id,
            workspace_merge: report_workspace_merge.then_some(outcome),
        })
    }

    fn ensure_workspace_merge_has_no_conflicts(
        outcome: &crate::commit::merge::Outcome,
    ) -> anyhow::Result<()> {
        ensure!(
            !outcome.has_conflicts(),
            "Failed to unapply branch due to conflicts while rebuilding the workspace merge commit: {}",
            describe_conflicting_stacks(&outcome.conflicting_stacks)
        );
        Ok(())
    }

    fn describe_conflicting_stacks(
        conflicting_stacks: &[crate::commit::merge::ConflictingStack],
    ) -> String {
        let ref_names = conflicting_stacks
            .iter()
            .filter_map(|stack| stack.ref_name.as_ref())
            .map(|ref_name| ref_name.shorten().to_string())
            .collect::<Vec<_>>();
        if ref_names.is_empty() {
            format!("{} stack(s)", conflicting_stacks.len())
        } else {

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Unapply the conflicting branches together (the error names the conflicting stacks) instead of one at a time
  2. Resolve the conflict between the remaining branches first (edit one branch so it merges cleanly), then unapply
  3. As a last resort, unapply the entire workspace or use the non-workspace-merge path that skips rebuilding the managed merge commit
Defensive patterns

Strategy: try-catch

Validate before calling

// preview the post-unapply merge before committing to it: compute tips of remaining stacks
// and check for conflicts (dry-run the same merge the unapply will rebuild)
let preview = WorkspaceCommit::from_new_merge_with_tips(remaining_tips, &ws.graph, repo, None)?;
if preview.has_conflicts() { /* warn user: unapply these stacks together or resolve first */ }

Try / catch

match unapply_branch(...) {
    Err(e) if e.to_string().contains("conflicts while rebuilding the workspace merge commit") => {
        // parse the named conflicting stacks and offer: unapply them all at once
    }
    r => r,
}

Prevention

When it happens

Trigger: Unapplying branch A while branches B and C remain, where B and C only merged cleanly because A's changes reconciled them — the rebuild merge then reports conflicting_stacks and the whole unapply is refused (metadata rollback).

Common situations: Two branches touching the same lines that were both derived from a common reconciling branch; unapplying one of three interdependent stacked branches.

Related errors


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