{"record":{"id":"4025b452e5b21b37","repo":"GitoxideLabs/gitoxide","slug":"commit-belongs-to-multiple-unrelated-review-trees","errorCode":null,"errorMessage":"commit belongs to multiple unrelated review trees","messagePattern":"commit belongs to multiple unrelated review trees","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-tix/src/edit/time_travel.rs","lineNumber":924,"sourceCode":"#[derive(Clone, Debug, Eq, PartialEq)]\nstruct ReviewTree {\n    root: ObjectId,\n    reference: gix::refs::FullName,\n}\n\nfn review_tree(\n    repo: &gix::Repository,\n    graph: &history::HistoryGraph,\n    roots: &[ObjectId],\n    commit: ObjectId,\n) -> Result<Option<ReviewTree>> {\n    let mut nearest = None;\n    for root in roots.iter().copied().filter(|root| graph.is_ancestor(*root, commit)) {\n        nearest = match nearest {\n            None => Some(root),\n            Some(current) if graph.is_ancestor(current, root) => Some(root),\n            Some(current) if graph.is_ancestor(root, current) => Some(current),\n            Some(_) => anyhow::bail!(\"commit belongs to multiple unrelated review trees\"),\n        };\n    }\n    let Some(root) = nearest else { return Ok(None) };\n    let commit = repo.find_commit(root)?.decode()?.into_owned()?;\n    let reference = super::review::reference(&commit)?.context(\"review root lost its review identity\")?;\n    Ok(Some(ReviewTree { root, reference }))\n}\n\n#[tracing::instrument(skip_all, fields(review = %review.reference))]\nfn save_review_stash(\n    repository_path: &Path,\n    bare: bool,\n    workdir: &Path,\n    review: &ReviewTree,\n) -> Result<Option<super::stash::SavedStash>> {\n    if !super::review::is_dirty(workdir)? {\n        return Ok(None);\n    }","sourceCodeStart":906,"sourceCodeEnd":942,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-tix/src/edit/time_travel.rs#L906-L942","documentation":"`review_tree` locates the single nearest review-tree root that is an ancestor of the given commit. If the commit is a descendant of two or more review roots where neither is an ancestor of the other, the roots are unrelated and there is no unique review tree to attribute the commit to. The library throws this instead of picking an arbitrary root.","triggerScenarios":"Calling `perform_reporting_rebased` (time-travel) targeting a commit whose ancestry includes two review-tree roots that don't nest (e.g. a merge commit joining two independent review branches, each with its own review root).","commonSituations":"Merging two independently-created review branches into one; a review root created on a branch that was later merged with another review line; accidental duplicate review roots on divergent histories.","solutions":["Rebase or linearize the branch so the target commit descends from only one review root.","Remove or re-create one of the conflicting review roots so the roots nest (one ancestor of the other).","Time-travel to a commit that lies within a single review tree instead of a merge point.","Restructure the history (e.g. squash the merge) so review roots form a chain."],"exampleFix":"// before: commit is a merge of two unrelated review roots\nlet dest = repo.find_commit(merge_id)?;\nreview_tree(&repo, &graph, &roots, dest)?; // bails\n\n// after: target a commit on one review line\nlet dest = repo.find_commit(side_a_tip)?;\nreview_tree(&repo, &graph, &roots, dest)?; // unique nearest root","handlingStrategy":"validation","validationCode":"let review_roots: Vec<_> = roots.iter().copied()\n    .filter(|r| graph.is_ancestor(*r, target_commit)).collect();\n// must be 0 or 1, or a nesting chain of exactly one root\nif review_roots.len() > 1 { /* restructure history first */ }","typeGuard":null,"tryCatchPattern":"match review_tree(&repo, &graph, &roots, commit) {\n    Ok(Some(tree)) => /* use tree */,\n    Ok(None) => /* commit is outside any review tree */,\n    Err(e) if e.to_string().contains(\"multiple unrelated review trees\") => {\n        // rebase/merge restructure, then retry\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Keep review roots on a single line of history; avoid merging independent review branches","Deduplicate review roots when creating them (check ancestry first)","Time-travel only to commits inside a single review tree"],"tags":["git","history","review","ambiguity"],"backgroundTag":"invalid-argument-value","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"}