{"record":{"id":"4cd4a5ee0b1eacc3","repo":"gitbutlerapp/gitbutler","slug":"line-line-number-invalid-squash-commit-commit","errorCode":null,"errorMessage":"line {line_number}: invalid squash commit '{commit}': {err}","messagePattern":"line (.+?): invalid squash commit '(.+?)': (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-workspace/src/branch/integrate_branch_upstream/parsing.rs","lineNumber":85,"sourceCode":"                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 {\n                    commits,\n                    message: message_part\n                        .map(parse_message_clause)\n                        .transpose()\n                        .map_err(|err| {\n                            anyhow::anyhow!(\"line {line_number}: invalid squash message: {err}\")\n                        })?,\n                }\n            }\n            other => bail!(\"line {line_number}: unknown command '{other}'\"),\n        };\n        steps.push(step);","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-workspace/src/branch/integrate_branch_upstream/parsing.rs#L67-L103","documentation":"A commit inside a `squash <c1> <c2> [...]` line failed the same resolve_commit check against the editable divergence set. The message names the exact offending commit string, so multi-commit squash lines with one bad hash are easy to localize.","triggerScenarios":"A squash line listing ≥2 commits where at least one hash is not a not-integrated local commit or upstream-only commit, or one short prefix is ambiguous. Inner error text distinguishes 'not part of the editable divergence' from 'ambiguous'.","commonSituations":"Hand-editing squash lines and pasting hashes from another branch window; stale scripts reused after upstream force-push changed the candidate set.","solutions":["Fix the specific commit token named in the error to a hash from the generated script","Lengthen ambiguous prefixes","Regenerate the integration script and re-apply the squash edits"],"exampleFix":"# before\nsquash 3333333 deadbee 4444444   # deadbee not in divergence\n# after\nsquash 3333333 4444444","handlingStrategy":"validation","validationCode":"// validate every token of each squash line (≥2 tokens, all resolvable)\nfor line in script.lines().filter(|l| l.trim_start().starts_with(\"squash\")) {\n    let toks: Vec<_> = line.split_whitespace().collect();\n    ensure!(toks.len() >= 2, \"squash needs ≥2 commits\");\n    for t in &toks[1..] { ensure!(commit_resolves(&candidate_strings, t), \"squash token {t} invalid\"); }\n}","typeGuard":"fn squash_line_valid(line: &str, candidates: &HashSet<String>) -> bool {\n    let toks: Vec<_> = line.split('|').next().unwrap_or(\"\").split_whitespace().collect();\n    toks.len() >= 2 && toks[1..].iter().all(|t| commit_resolves(candidates, t))\n}","tryCatchPattern":"if let Err(e) = parse_integration_steps_script(&script, &divergence) {\n    if let Some(rest) = e.to_string().split(\"invalid squash commit '\").nth(1) { /* rest starts with the offending token — show it */ }\n}","preventionTips":["Squash only commits that appear in the generated script's pick/merge lines","Use longer prefixes when several candidates share a short prefix","Re-validate the whole script after each manual edit, not only at submit time"],"tags":["rust","git","rebase-plan","script-parsing","squash","integration","but-workspace"],"backgroundTag":"unknown-commit-in-plan","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}