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
- Unapply the conflicting branches together (the error names the conflicting stacks) instead of one at a time
- Resolve the conflict between the remaining branches first (edit one branch so it merges cleanly), then unapply
- 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
- Unapply interdependent branches in one operation, not singly
- Before unapplying, check whether remaining branches touch the same files
- Keep branches that will survive an unapply independent of the removed branch's reconciliation
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
- Cannot unapply anonymous segments yet even if they have a st
- No uncommitted changes assigned to branch: {branch_name}
- Failed to determine target commit for hunk absorption due to
- Unable to determine target commit for unassigned change: {}
- Integration steps cannot be empty
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/e4fd181e042be1e3.
Report an issue: GitHub.