{"record":{"id":"8c9f1ff1a380e530","repo":"GitoxideLabs/gitoxide","slug":"moving-the-stack-would-create-a-commit-cycle","errorCode":null,"errorMessage":"moving the stack would create a commit cycle","messagePattern":"moving the stack would create a commit cycle","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-tix/src/edit/rebase.rs","lineNumber":747,"sourceCode":"                continue;\n            }\n            let parent = new_parent[id];\n            if scope_set.contains(&parent) && !step_by_id.contains_key(&parent) {\n                continue;\n            }\n            let parent = step_by_id\n                .get(&parent)\n                .copied()\n                .map_or(PlanParent::Existing(parent), PlanParent::Step);\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        if steps.len() == before {\n            anyhow::bail!(\"moving the stack would create a commit cycle\");\n        }\n    }\n\n    let head_step = PlanParent::Step(step_by_id[&head]);\n    let mut ref_scope = scope.clone();\n    if !scope_set.contains(&target) {\n        ref_scope.push(target);\n    }\n    let ref_scope_set: HashSet<_> = ref_scope.iter().copied().collect();\n    let mut non_leaves = HashSet::new();\n    for id in &ref_scope {\n        non_leaves.extend(\n            graph\n                .parents_of(*id)\n                .context(\"an affected move commit is incomplete\")?\n                .into_iter()\n                .filter(|parent| ref_scope_set.contains(parent)),\n        );","sourceCodeStart":729,"sourceCodeEnd":765,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-tix/src/edit/rebase.rs#L729-L765","documentation":"During rebase-plan construction in `gix-tix/src/edit/rebase.rs`, moving a stack of commits is rejected if none of the proposed steps advanced the plan (steps.len() == before after an iteration). That means re-parenting the stack would point a step at one of its own descendants, i.e. a commit cycle. The library refuses because a cycle could never be serialized into a linear rebase.","triggerScenarios":"Calling the stack-move/reorder edit when the requested new position places a picked commit underneath (an ancestor of) itself, e.g. moving commit A to be a child of its own descendant B, or moving a stack to position 0 when the stack's target parent chain loops back to the stack.","commonSituations":"Interactive tooling that lets users drag commits in a history view and constructs an order that loops; scripted rebases computed from parent/child arrays with an off-by-one that makes the destination index fall inside the moved range's own ancestry.","solutions":["Verify the destination position is not inside the ancestry of any moved commit before invoking the edit","Recompute the requested ordering so moved steps always come after their new parent step","If using a UI, disable moves that would drop a commit onto its own subtree"],"exampleFix":"// before: destination index inside the moved stack's ancestry\nedit.move_stack(&[c3, c4], Position::after(c4)); // cycle\n// after: place the stack after a commit outside its own chain\nedit.move_stack(&[c3, c4], Position::after(c1));","handlingStrategy":"validation","validationCode":"fn is_valid_move(steps: &[usize], dest_parent: usize) -> bool {\n    // destination parent must not be inside the moved stack's own ancestry\n    !steps.contains(&dest_parent)\n}","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"commit cycle\") => {\n        eprintln!(\"requested order loops onto itself; pick a destination outside the moved stack\");\n    }\n    r => r?,\n}","preventionTips":["Reject UI moves where the drop target is a descendant of any moved commit","Validate plan parent chains terminate before submitting","Keep moved stacks strictly after their new parent step"],"tags":["rebase","commit-cycle","plan-validation"],"backgroundTag":"invalid-state-transition","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"}