gitbutlerapp/gitbutler · error

merge conflict when computing workspace tree

Error message

merge conflict when computing workspace tree

What it means

Thrown while materializing the workspace tree when merging the workspace's stack trees hits unresolved conflicts. merge_workspace octopus-merges every head (stack) onto the base sequentially with fail-fast options; if any merge reports conflicts - two stacks changed the same lines - the merge is abandoned and this error returned instead of writing a conflict-marker tree. It protects the workspace from silently dropping either side's changes.

Source

Thrown at crates/gitbutler-workspace/src/branch_trees.rs:177

        return Ok(*workspace.heads.first().expect("List is length 1"));
    }

    let mut output = workspace.base;
    let base = workspace.base;

    let (merge_options, conflict_kind) = repo.merge_options_fail_fast()?;

    for head in &workspace.heads {
        let mut merge = repo.merge_trees(
            base,
            output,
            *head,
            repo.default_merge_labels(),
            merge_options.clone(),
        )?;

        if merge.has_unresolved_conflicts(conflict_kind) {
            anyhow::bail!("merge conflict when computing workspace tree");
        }
        output = merge.tree.write()?.detach();
    }

    Ok(output)
}

#[cfg(test)]
mod tests {
    use gix::object::tree::EntryKind;

    use super::*;

    fn tree(repo: &gix::Repository, files: &[(&str, &str)]) -> Result<gix::ObjectId> {
        let mut tree = repo.empty_tree().edit()?;
        for (path, contents) in files {
            tree.upsert(
                *path,

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Identify the overlapping edits: compare each stack's diff against the base for the same files and lines
  2. Assign the conflicting hunks to a single stack (hunk assignment) so only one side owns those lines
  3. Commit a manual resolution in one of the stacks so the trees no longer conflict
  4. As a last resort, move one conflicting stack's changes to a branch outside the workspace and reapply later
Defensive patterns

Strategy: try-catch

Validate before calling

// cheap pre-check: stacks should not edit the same paths
let files_per_stack: Vec<std::collections::BTreeSet<String>> = stacks.iter()
    .map(|s| changed_paths_vs_base(repo, s))
    .collect();
for (i, a) in files_per_stack.iter().enumerate() {
    for b in files_per_stack.iter().skip(i + 1) {
        if a.intersection(b).next().is_some() {
            // overlapping files: risk of 'merge conflict when computing workspace tree'
        }
    }
}

Try / catch

match compute_workspace_tree(repo, &ws) {
    Err(e) if e.to_string().contains("merge conflict when computing workspace tree") => {
        // surface which stacks overlap; have the user/agent reassign hunks, then retry
    }
    r => r,
}

Prevention

When it happens

Trigger: Any flow computing the workspace tree (branch_trees::merge_workspace) for a workspace whose heads contain overlapping edits: at some loop iteration repo.merge_trees(base, output, head) reports unresolved conflicts for conflict_kind and the bail fires.

Common situations: Two virtual branches/stacks editing the same function or file; an AI agent and the user working in different stacks on one file; rebases that landed one stack's changes on top of another stack's edits to the same regions.

Related errors


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