{"record":{"id":"2b6bef764038b535","repo":"GitoxideLabs/gitoxide","slug":"copying-a-commit-cannot-rewrite-root-or-merge-comm","errorCode":null,"errorMessage":"copying a commit cannot rewrite root or merge commits","messagePattern":"copying a commit cannot rewrite root or merge commits","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-tix/src/edit/rebase.rs","lineNumber":581,"sourceCode":"    }\n    if source == target {\n        anyhow::bail!(\"the copy source and target must differ\");\n    }\n\n    let mut scope = graph\n        .descendants_in_parent_order(target)\n        .context(\"the copy target is not in the loaded history\")?;\n    scope.retain(|id| *id != target);\n    let mut steps = vec![PlanStep {\n        parent: PlanParent::Existing(target),\n        commit: PlanCommit::Copy(source),\n        squash: Vec::new(),\n    }];\n    let mut step_by_id = HashMap::with_capacity(scope.len());\n    for id in &scope {\n        let parents = graph.parents_of(*id).context(\"an affected copy commit is incomplete\")?;\n        let [parent] = parents.as_slice() else {\n            anyhow::bail!(\"copying a commit cannot rewrite root or merge commits\");\n        };\n        let parent = if *parent == target {\n            PlanParent::Step(0)\n        } else {\n            step_by_id\n                .get(parent)\n                .copied()\n                .map_or(PlanParent::Existing(*parent), PlanParent::Step)\n        };\n        step_by_id.insert(*id, steps.len());\n        steps.push(PlanStep {\n            parent,\n            commit: PlanCommit::Pick(*id),\n            squash: Vec::new(),\n        });\n    }\n\n    let mut ref_scope = Vec::with_capacity(scope.len() + 1);","sourceCodeStart":563,"sourceCodeEnd":599,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-tix/src/edit/rebase.rs#L563-L599","documentation":"After inserting the copied commit under `target`, the plan rewrites all descendants of the target so they sit on top of the copy. Each rewritten commit must have exactly one parent: the rebase plan can only re-parent linear commits. If any affected descendant is a root or merge commit, the plan cannot represent the rewrite and this error is raised.","triggerScenarios":"Calling `copy_insert_plan(repo, graph, source, target)` where any commit in `graph.descendants_in_parent_order(target)` (excluding target itself) has zero or multiple parents — e.g. the target's descendants contain a merge commit that spans the target.","commonSituations":"Copying a commit into a branch of history where a later merge brings those changes together; repositories with frequent merges where the insertion point has merge descendants.","solutions":["Choose a copy target whose descendants are all linear (single-parent) commits.","Rebase the merge-heavy section linearly first, then perform the copy.","Manually cherry-pick the source commit onto the target with git tooling instead of using tix copy."],"exampleFix":"// before\ntix copy <source> <target-with-merge-descendants>\n\n// after\nlet scope = graph.descendants_in_parent_order(target)?;\nif scope.iter().any(|id| graph.parents_of(*id)?.len() != 1) {\n    eprintln!(\"target has merge descendants; cannot copy here\");\n    return Ok(());\n}\ntix copy(source, target)","handlingStrategy":"validation","validationCode":"for id in graph.descendants_in_parent_order(target)? {\n    if graph.parents_of(id)?.len() != 1 {\n        eprintln!(\"target has non-linear descendants; copy would rewrite a merge/root commit\");\n        return Ok(());\n    }\n}","typeGuard":"fn target_scope_is_linear(graph: &HistoryGraph, target: ObjectId) -> bool {\n    graph.descendants_in_parent_order(target)\n        .map(|ds| ds.iter().all(|id| graph.parents_of(*id).map(|p| p.len() == 1).unwrap_or(false)))\n        .unwrap_or(false)\n}","tryCatchPattern":"match copy_insert_plan(&repo, &graph, source, target) {\n    Err(e) if e.to_string().contains(\"root or merge commits\") => eprintln!(\"choose a target without merge descendants\"),\n    other => other?,\n}","preventionTips":["Prefer insertion points in linear segments of history","Show merge topology when choosing a copy target","Check descendants' parent counts before initiating copy operations in merge-heavy repos"],"tags":["git","rebase","merge-commit","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"e73179060badf27222d790981fac3f84c1830a7e","analyzedAt":"2026-09-08T11:26:50.865Z","contentChangedAt":"2026-09-08T11:26:50.865Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}