{"record":{"id":"fbccdec4383bb62f","repo":"gitbutlerapp/gitbutler","slug":"cherry-picks-can-only-be-done-for-single-parent-co","errorCode":null,"errorMessage":"Cherry picks can only be done for single-parent commits. Merge-commits need to be re-merged","messagePattern":"Cherry picks can only be done for single-parent commits\\. Merge-commits need to be re-merged","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-rebase/src/cherry_pick.rs","lineNumber":81,"sourceCode":"        && commit_to_rebase.parents.contains(&base.id.detach())\n    {\n        return Ok(commit_to_rebase.id);\n    };\n\n    let mut cherry_pick = cherry_pick_tree(&base, &commit_to_rebase)?;\n    let tree_id = cherry_pick.tree.write()?;\n\n    let conflict_kind = gix::merge::tree::TreatAsUnresolved::forced_resolution();\n    if cherry_pick.has_unresolved_conflicts(conflict_kind) {\n        commit_from_conflicted_tree(base, commit_to_rebase, tree_id, cherry_pick, conflict_kind)\n    } else {\n        commit_from_unconflicted_tree(base, commit_to_rebase, tree_id, empty_commit)\n    }\n}\n\nfn set_parent(to_rebase: &mut gix::objs::Commit, new_parent: gix::ObjectId) -> anyhow::Result<()> {\n    if to_rebase.parents.len() > 1 {\n        bail!(\n            \"Cherry picks can only be done for single-parent commits. Merge-commits need to be re-merged\"\n        )\n    }\n    to_rebase.parents.clear();\n    to_rebase.parents.push(new_parent);\n    Ok(())\n}\n\n/// Rebase `to_rebase` onto `new_base`, dealing with the intricacies of conflicted trees, and return the newly\n/// merged tree.\n/// Note that all merges are made to succeed, possibly recording the original trees in a special tree.\nfn cherry_pick_tree<'repo>(\n    new_base: &but_core::Commit<'repo>,\n    to_rebase: &but_core::Commit<'repo>,\n) -> anyhow::Result<gix::merge::tree::Outcome<'repo>> {\n    let repo = to_rebase.id.repo;\n    let (base, ours, theirs) = find_cherry_pick_trees(new_base, to_rebase)?;\n    use but_core::RepositoryExt;","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-rebase/src/cherry_pick.rs#L63-L99","documentation":"During graph rebase, cherry-picking rewrites a commit's parent to its new base. `set_parent` refuses commits with more than one parent: a merge commit cannot be recreated by substituting a single parent — its multiple parents must be re-merged relative to the new base.","triggerScenarios":"A Pick step in the rebase plan whose commit is a merge commit (parents.len() > 1) reaching `set_parent` — usually a plan built without merge handling or with merge steps misclassified as picks.","commonSituations":"Histories containing merge commits (PR merges, criss-cross merges) fed into a pick-only plan; custom graph tooling that forgets to special-case merges.","solutions":["Model merge commits as re-merge steps in the graph plan/editor instead of picks","Skip or drop merge commits from the pick sequence where the semantics allow","Fall back to git rebase strategies that preserve merges, or re-merge the commits manually"],"exampleFix":"// before: merge commit scheduled as a plain pick\nplan.push(Step::Pick { id: merge_commit, ..Default::default() }); // bails in set_parent\n\n// after: detect merges when building the plan and handle them separately\nif parents_of(repo, commit_id)?.len() > 1 {\n    plan.push(Step::ReMerge { id: commit_id, .. }); // or skip\n} else {\n    plan.push(Step::Pick { id: commit_id, ..Default::default() });\n}","handlingStrategy":"validation","validationCode":"// Rust: classify merge commits while building the plan\nlet commit = repo.find_object(commit_id)?.try_into_commit()?;\nlet is_merge = commit.parent_ids().count() > 1;\nif is_merge {\n    plan.push_re_merge(commit_id); // or skip\n} else {\n    plan.push_pick(commit_id);\n}","typeGuard":null,"tryCatchPattern":"Catch the bail, identify the merge commit from the plan, and either drop it from the pick sequence or switch it to a re-merge step before rebuilding.","preventionTips":["Check parent counts when generating rebase plans; never schedule multi-parent commits as plain picks","Test plan builders against merge-heavy fixture repositories"],"tags":["rebase","cherry-pick","merge-commit","graph-rebase","git"],"backgroundTag":"cherry-pick-merge-commit","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}