{"record":{"id":"146fc4fa8b15a6c2","repo":"gitbutlerapp/gitbutler","slug":"cannot-squash-commits-that-would-result-in-merge-conflicts","errorCode":null,"errorMessage":"Cannot squash commits that would result in merge conflicts","messagePattern":"Cannot squash commits that would result in merge conflicts","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-workspace/src/commit/squash_commits.rs","lineNumber":177,"sourceCode":"            continue;\n        }\n        subject_selectors.push(subject_commit_selector);\n    }\n\n    let subject_commit_ids = subject_selectors\n        .iter()\n        .map(|commit_selector| {\n            let (_, commit) = editor.find_selectable_commit(*commit_selector)?;\n            Ok(commit.id)\n        })\n        .collect::<Result<Vec<_>>>()?;\n    let squashed_tree = editor.merge_commit_changes_to_tree(\n        target_commit_obj.id,\n        subject_commit_ids,\n        editor.repo().merge_options_force_ours()?,\n    )?;\n    if squashed_tree.conflict.is_some() {\n        bail!(\"Cannot squash commits that would result in merge conflicts\");\n    }\n\n    let mut combined_message = Vec::new();\n    match how_to_combine_messages {\n        MessageCombinationStrategy::KeepSubject => {\n            for source_id in subject_selectors.iter().copied() {\n                let (_, source_commit) = editor.find_selectable_commit(source_id)?;\n                push_message_with_spacing(&mut combined_message, source_commit.message.as_ref());\n            }\n        }\n        MessageCombinationStrategy::KeepTarget => {\n            push_message_with_spacing(&mut combined_message, target_commit_obj.message.as_ref());\n        }\n        MessageCombinationStrategy::KeepBoth => {\n            push_message_with_spacing(&mut combined_message, target_commit_obj.message.as_ref());\n            for source_id in subject_selectors.iter().copied() {\n                let (_, source_commit) = editor.find_selectable_commit(source_id)?;\n                push_message_with_spacing(&mut combined_message, source_commit.message.as_ref());","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-workspace/src/commit/squash_commits.rs#L159-L195","documentation":"Squashing commits in a GitButler workspace merges the subject commits' changes into the target commit via a tree-level three-way merge (using a force-ours merge strategy). If the resulting tree still contains conflict markers, the library refuses to produce a squash, because the squash would silently embed conflicted content in the commit.","triggerScenarios":"Calling squash_commits where the target commit's tree and any subject commit's tree touch the same lines/regions so that even merge_options_force_ours cannot cleanly merge them, leaving squashed_tree.conflict set to Some.","commonSituations":"Users try to squash two workspace commits that edited the same file regions; stacked changes where a later commit rewrites lines an earlier commit rewrote; uncommitted-hunk-level edits overlapping the commit's content.","solutions":["First resolve the overlap: uncommit one of the conflicting changes, resolve it in the working directory, and re-commit before squashing","Squash the commits one at a time instead of all at once, resolving between steps","Verify the involved commits don't both modify identical hunks (e.g. via git diff between them)","If appropriate for the semantics, apply the changes manually and create a single new commit rather than using squash"],"exampleFix":"// before\nbut.squash_commits([commit_a, commit_b]); // fails: same lines edited in both\n// after\nlet outcome = but.uncommit_changes_from_commits([commit_b]);\n// re-apply conflict-free changes, then:\nbut.squash_commits([commit_a]);","handlingStrategy":"validation","validationCode":"let overlaps = detect_overlapping_hunks(&subject_commits, &target_commit)?;\nif !overlaps.is_empty() { /* resolve or split before squashing */ }","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"merge conflicts\") => {\n        // uncommit one side, resolve, retry squash\n    }\n    other => other?,\n}","preventionTips":["Check for overlapping hunks between commits before squashing","Squash commits incrementally rather than in large batches","Keep workspace commits focused so they rarely touch identical lines"],"tags":["git","merge-conflict","squash"],"backgroundTag":"git-command-failed","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}