{"record":{"id":"cfc9100d8e01fae2","repo":"gitbutlerapp/gitbutler","slug":"line-line-number-invalid-pick-commit-err","errorCode":null,"errorMessage":"line {line_number}: invalid pick commit: {err}","messagePattern":"line (.+?): invalid pick commit: (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-workspace/src/branch/integrate_branch_upstream/parsing.rs","lineNumber":56,"sourceCode":"            continue;\n        };\n        let arguments = tokens.get(1..).unwrap_or_default();\n\n        let step = match command {\n            \"pick\" => {\n                if message_part.is_some() {\n                    bail!(\"line {line_number}: pick does not accept a message clause\");\n                }\n                if arguments.len() != 1 {\n                    bail!(\"line {line_number}: pick requires exactly one commit\");\n                }\n                let commit = arguments\n                    .first()\n                    .copied()\n                    .expect(\"validated pick arity above\");\n                InteractiveIntegrationStep::Pick {\n                    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                    })?,","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-workspace/src/branch/integrate_branch_upstream/parsing.rs#L38-L74","documentation":"While parsing an edited interactive integration script, a `pick <commit>` line referenced a commit that resolve_commit could not match: the hash/prefix is not in the editable divergence set (not-integrated local-only commits plus all upstream-only commits), or the prefix matched multiple commits and is ambiguous. The inner error states which case.","triggerScenarios":"parse_integration_steps_script receiving a script where a pick line references a commit outside the editable candidates — e.g. an already-integrated local commit, a random hash like deadbee, or a short prefix that matches two candidate hashes (ambiguity).","commonSituations":"User hand-edits the rebase-plan-style script and introduces/keeps a stale hash from an older divergence; long scripts edited after the divergence changed underneath.","solutions":["Use only hashes printed in the generated script (the current divergence candidates)","Lengthen an ambiguous short hash until it matches exactly one candidate","Remove pick lines for commits already integrated upstream","Regenerate the script from the current divergence and re-apply edits"],"exampleFix":"# before\npick deadbee          # not part of the editable divergence\n# after — copy the exact hash from the generated script\npick 3333333","handlingStrategy":"validation","validationCode":"// validate the edited script against the same divergence before executing\nlet candidates: HashSet<String> = divergence.local_only.iter()\n    .filter(|c| matches!(c.target_relation, IntegrationDivergenceTargetRelation::NotIntegrated))\n    .map(|c| c.id.to_string()).chain(divergence.upstream_only.iter().map(|c| c.id.to_string())).collect();\nfor token in extract_commit_tokens(&script) {\n    let matches = candidates.iter().filter(|c| c.starts_with(token)).count();\n    assert_eq!(matches, 1, \"commit {token} unknown or ambiguous\");\n}","typeGuard":"fn commit_resolves(candidates: &HashSet<String>, spec: &str) -> bool {\n    candidates.iter().filter(|c| c.starts_with(spec)).count() == 1\n}","tryCatchPattern":"match parse_integration_steps_script(&script, &divergence) {\n    Err(e) if e.to_string().contains(\"invalid pick commit\") => { /* highlight the line for the user to fix the hash */ }\n    r => r,\n}","preventionTips":["Only edit hashes the generated script already contains; never paste hashes from elsewhere","Use full or sufficiently long hashes to avoid ambiguity errors","Re-generate the script whenever the branch or upstream moves"],"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"}