{"record":{"id":"76b2d84b9168ef94","repo":"gitbutlerapp/gitbutler","slug":"merge-conflict-when-computing-workspace-tree","errorCode":null,"errorMessage":"merge conflict when computing workspace tree","messagePattern":"merge conflict when computing workspace tree","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/gitbutler-workspace/src/branch_trees.rs","lineNumber":177,"sourceCode":"        return Ok(*workspace.heads.first().expect(\"List is length 1\"));\n    }\n\n    let mut output = workspace.base;\n    let base = workspace.base;\n\n    let (merge_options, conflict_kind) = repo.merge_options_fail_fast()?;\n\n    for head in &workspace.heads {\n        let mut merge = repo.merge_trees(\n            base,\n            output,\n            *head,\n            repo.default_merge_labels(),\n            merge_options.clone(),\n        )?;\n\n        if merge.has_unresolved_conflicts(conflict_kind) {\n            anyhow::bail!(\"merge conflict when computing workspace tree\");\n        }\n        output = merge.tree.write()?.detach();\n    }\n\n    Ok(output)\n}\n\n#[cfg(test)]\nmod tests {\n    use gix::object::tree::EntryKind;\n\n    use super::*;\n\n    fn tree(repo: &gix::Repository, files: &[(&str, &str)]) -> Result<gix::ObjectId> {\n        let mut tree = repo.empty_tree().edit()?;\n        for (path, contents) in files {\n            tree.upsert(\n                *path,","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/gitbutler-workspace/src/branch_trees.rs#L159-L195","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Identify the overlapping edits: compare each stack's diff against the base for the same files and lines","Assign the conflicting hunks to a single stack (hunk assignment) so only one side owns those lines","Commit a manual resolution in one of the stacks so the trees no longer conflict","As a last resort, move one conflicting stack's changes to a branch outside the workspace and reapply later"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// cheap pre-check: stacks should not edit the same paths\nlet files_per_stack: Vec<std::collections::BTreeSet<String>> = stacks.iter()\n    .map(|s| changed_paths_vs_base(repo, s))\n    .collect();\nfor (i, a) in files_per_stack.iter().enumerate() {\n    for b in files_per_stack.iter().skip(i + 1) {\n        if a.intersection(b).next().is_some() {\n            // overlapping files: risk of 'merge conflict when computing workspace tree'\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"match compute_workspace_tree(repo, &ws) {\n    Err(e) if e.to_string().contains(\"merge conflict when computing workspace tree\") => {\n        // surface which stacks overlap; have the user/agent reassign hunks, then retry\n    }\n    r => r,\n}","preventionTips":["Keep one concern per stack - avoid two stacks editing the same file","Use explicit hunk assignment instead of leaving changes unassigned","Resolve stack-vs-stack conflicts as soon as the UI flags them"],"tags":["git","merge","conflict","workspace","virtual-branches"],"backgroundTag":"merge-conflict","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}