{"record":{"id":"2c0ab4e88b8e0f1d","repo":"gitbutlerapp/gitbutler","slug":"line-line-number-invalid-merge-commit-err","errorCode":null,"errorMessage":"line {line_number}: invalid merge commit: {err}","messagePattern":"line (.+?): invalid merge commit: (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-workspace/src/branch/integrate_branch_upstream/parsing.rs","lineNumber":73,"sourceCode":"                    commit_id: resolve_commit(commit, &allowed_commits).map_err(|err| {\n                        anyhow::anyhow!(\"line {line_number}: invalid pick commit: {err}\")\n                    })?,\n                }\n            }\n            \"merge\" => {\n                if message_part.is_some() {\n                    bail!(\"line {line_number}: merge does not accept a message clause\");\n                }\n                if arguments.len() != 1 {\n                    bail!(\"line {line_number}: merge requires exactly one commit\");\n                }\n                let commit = arguments\n                    .first()\n                    .copied()\n                    .expect(\"validated merge arity above\");\n                InteractiveIntegrationStep::Merge {\n                    commit_id: resolve_commit(commit, &allowed_commits).map_err(|err| {\n                        anyhow::anyhow!(\"line {line_number}: invalid merge commit: {err}\")\n                    })?,\n                }\n            }\n            \"squash\" => {\n                if arguments.len() < 2 {\n                    bail!(\"line {line_number}: squash requires at least two commits\");\n                }\n                let commits = arguments\n                    .iter()\n                    .map(|commit| {\n                        resolve_commit(commit, &allowed_commits).map_err(|err| {\n                            anyhow::anyhow!(\n                                \"line {line_number}: invalid squash commit '{commit}': {err}\"\n                            )\n                        })\n                    })\n                    .collect::<Result<Vec<_>>>()?;\n                InteractiveIntegrationStep::Squash {","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-workspace/src/branch/integrate_branch_upstream/parsing.rs#L55-L91","documentation":"Same validation as for pick, but for a `merge <commit>` line in the interactive integration script: the referenced commit is not resolvable within the editable divergence set (not a not-integrated local commit or upstream-only commit), or the short prefix is ambiguous across candidates.","triggerScenarios":"A script line `merge <hash>` where hash is absent from editable_candidate_commits(divergence) or its prefix matches several candidates; merge also requires exactly one argument and no message clause (separate errors).","commonSituations":"Hand-edited integration scripts with typo'd hashes; referencing integrated local commits which are deliberately excluded from the editable set.","solutions":["Replace the hash with one listed in the generated merge lines of the current script","Use a longer prefix if the inner error says 'ambiguous'","Regenerate the plan after the branch/upstream state changed"],"exampleFix":"# before\nmerge 1111111         # integrated local commit → rejected\n# after\nmerge 4444444         # an upstream-only editable commit","handlingStrategy":"validation","validationCode":"// pre-check each merge token against editable candidates (see error 674 helper)\nfor line in script.lines().filter(|l| l.trim_start().starts_with(\"merge\")) {\n    let spec = line.split_whitespace().nth(1).expect(\"arity checked separately\");\n    ensure!(commit_resolves(&candidate_strings, spec), \"merge token {spec} not resolvable\");\n}","typeGuard":"fn merge_line_valid(line: &str, candidates: &HashSet<String>) -> bool {\n    let toks: Vec<_> = line.split_whitespace().collect();\n    toks.len() == 2 && !line.contains('|') && commit_resolves(candidates, toks[1])\n}","tryCatchPattern":"if let Err(e) = parse_integration_steps_script(&script, &divergence) {\n    if e.to_string().contains(\"invalid merge commit\") { /* surface line + hash to user */ }\n}","preventionTips":["Keep merge lines to exactly one argument and no message clause","Refresh candidate hashes after any upstream change before editing","Provide UI validation of edited scripts before parse/execute round-trips"],"tags":["rust","git","rebase-plan","script-parsing","integration","but-workspace"],"backgroundTag":"unknown-commit-in-plan","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}