{"record":{"id":"4aa21e107d4e0e9a","repo":"GitoxideLabs/gitoxide","slug":"copying-a-commit-requires-it-to-have-exactly-one-p","errorCode":null,"errorMessage":"copying a commit requires it to have exactly one parent","messagePattern":"copying a commit requires it to have exactly one parent","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-tix/src/edit/rebase.rs","lineNumber":552,"sourceCode":"        base,\n        scope,\n        steps,\n        checkout,\n        expected_refs,\n    })\n}\n\npub(crate) fn copy_insert_plan(\n    repo: &gix::Repository,\n    graph: &HistoryGraph,\n    source: ObjectId,\n    target: ObjectId,\n) -> Result<Plan> {\n    let source_parents = graph\n        .parents_of(source)\n        .context(\"the copy source is not in the loaded history\")?;\n    let [_source_parent] = source_parents.as_slice() else {\n        anyhow::bail!(\"copying a commit requires it to have exactly one parent\");\n    };\n    let source_commit = repo\n        .find_commit(source)\n        .context(\"could not find the copy source\")?\n        .decode()\n        .context(\"could not decode the copy source\")?\n        .into_owned()\n        .context(\"could not own the copy source\")?;\n    if super::review::reference(&source_commit)?.is_some() {\n        anyhow::bail!(\"review commits cannot be copied\");\n    }\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\")?;","sourceCodeStart":534,"sourceCodeEnd":570,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-tix/src/edit/rebase.rs#L534-L570","documentation":"The `copy_insert_plan` operation copies a commit onto another commit and rewrites the affected history, but the rebase plan machinery only knows how to handle linear history (one-parent commits). The copy SOURCE commit must have exactly one parent; root commits (no parent) and merge commits (multiple parents) cannot be copied. The library raises this before planning any rewrite when the source commit's parent count differs from 1.","triggerScenarios":"Calling `copy_insert_plan(repo, graph, source, target)` where `source` resolves in the loaded history graph but its `parents_of(source)` list is empty (root commit) or has 2+ entries (merge commit).","commonSituations":"Attempting to copy the repository's initial (root) commit; attempting to copy a merge commit produced by `git merge`; automation that passes commit IDs gathered from a log walk without filtering non-linear commits.","solutions":["Choose a non-merge, non-root commit as the copy source.","Pre-check `graph.parents_of(source)` for length 1 before calling the copy operation.","To duplicate a merge or root commit, cherry-pick its diff manually and commit the result instead of using the copy operation."],"exampleFix":"// before\ntix copy <root-or-merge-commit-id> <target>\n\n// after\nif graph.parents_of(source)?.len() != 1 {\n    eprintln!(\"cannot copy root or merge commit\");\n    return Ok(());\n}\ntix copy(source, target)","handlingStrategy":"validation","validationCode":"let parents = graph.parents_of(source).context(\"source not in history\")?;\nif parents.len() != 1 {\n    eprintln!(\"cannot copy: source must have exactly one parent\");\n    return Ok(());\n}","typeGuard":"fn is_linear_commit(graph: &HistoryGraph, id: ObjectId) -> bool {\n    graph.parents_of(id).map(|p| p.len() == 1).unwrap_or(false)\n}","tryCatchPattern":"match copy_insert_plan(&repo, &graph, source, target) {\n    Err(e) if e.to_string().contains(\"exactly one parent\") => eprintln!(\"source is a root or merge commit\"),\n    other => other?,\n}","preventionTips":["Filter candidate commits to single-parent commits before offering copy operations","Visually mark root and merge commits in tooling so users avoid selecting them","Reuse one `is_copyable` predicate across copy/stack operations"],"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"}