{"record":{"id":"6f8b02363ad12f84","repo":"gitbutlerapp/gitbutler","slug":"picked-commit-already-exists-in-a-previous-step","errorCode":null,"errorMessage":"Picked commit already exists in a previous step","messagePattern":"Picked commit already exists in a previous step","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-rebase/src/lib.rs","lineNumber":215,"sourceCode":"                        \"Reference step must have a non-empty virtual branch name\"\n                    ));\n                }\n            }\n        }\n        Ok(())\n    }\n\n    fn assure_unique_step_and_existing_non_base(\n        &self,\n        commit_id: &gix::oid,\n        kind: &str,\n    ) -> Result<()> {\n        self.repo.find_commit(commit_id)?;\n        if Some(commit_id) == self.base.as_deref() {\n            bail!(\"{kind} commit cannot be the base commit\");\n        }\n        if self.steps.iter().any(|s| s.commit_id() == Some(commit_id)) {\n            bail!(\"Picked commit already exists in a previous step\");\n        }\n        Ok(())\n    }\n}\n\n#[instrument(level = \"debug\", skip(repo))]\nfn rebase(\n    repo: &gix::Repository,\n    base: Option<gix::ObjectId>,\n    base_substitute: Option<gix::ObjectId>,\n    steps: Vec<RebaseStep>,\n    pick_mode: PickMode,\n) -> Result<RebaseOutput> {\n    let (mut references, mut commit_mapping) = (\n        vec![],\n        Vec::<(Option<gix::ObjectId>, gix::ObjectId, gix::ObjectId)>::new(),\n    );\n    let (mut cursor, mut last_seen_commit) = (base, base);","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-rebase/src/lib.rs#L197-L233","documentation":"The same validator rejects a rebase plan in which the same commit appears in more than one step. Since `assure_unique_step_and_existing_non_base` runs for both Pick and Fixup/Squash steps, adding any commit twice makes the second occurrence fail with \"Picked commit already exists in a previous step\". Rebasing the same commit twice is not meaningful, so the plan is rejected up front.","triggerScenarios":"Calling `validate_step` (through plan building) with a `RebaseStep::Pick` or `RebaseStep::SquashIntoPreceding` whose `commit_id` already appears in an earlier step in `self.steps` (`steps.iter().any(|s| s.commit_id() == Some(commit_id))`).","commonSituations":"UI drag-and-drop that duplicates a commit entry when reordering; merging two step lists that both reference the same commit; appending a fixup for a commit that is also being picked; loading a stale plan and re-adding its commits.","solutions":["Deduplicate the step list by commit_id before constructing/validating the rebase plan.","When a commit is both picked and fixup'd into another, use one step with `new_message` instead of two steps.","Reset or rebuild the plan from scratch when re-submitting after an edit, rather than appending to an existing steps vector."],"exampleFix":"// before\nsteps.push(pick(commit_a));\nsteps.push(fixup(commit_a)); // duplicate -> error\n// after\nsteps.push(pick_with_message(commit_a, \"new message\"));","handlingStrategy":"validation","validationCode":"fn has_unique_commits(steps: &[RebaseStep]) -> bool {\n    let mut ids: Vec<_> = steps.iter().filter_map(|s| s.commit_id().map(|c| c.to_string())).collect();\n    ids.sort();\n    ids.dedup();\n    // dedup shrank the vec only if duplicates existed\n    ids.len() == steps.len()\n}","typeGuard":"fn is_first_occurrence(step: &RebaseStep, prior: &[RebaseStep]) -> bool {\n    !prior.iter().any(|s| s.commit_id() == step.commit_id())\n}","tryCatchPattern":"match request.validate_step(&step) {\n    Err(e) if e.to_string().contains(\"already exists in a previous step\") => {\n        // remove the duplicate step, optionally merge message changes into the existing one\n    }\n    other => other?,\n}","preventionTips":["Deduplicate steps by commit_id when assembling or merging plans.","Prefer representing reword + pick as a single step with new_message instead of two steps.","Run validate_step on the full plan before execution."],"tags":["rebase","duplicate","validation","git"],"backgroundTag":"invalid-argument-value","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"}