GitoxideLabs/gitoxide · error
the move target must not be part of the moved stack
Error message
the move target must not be part of the moved stack
What it means
The move target is the commit the stack will be re-parented onto. If the target itself is inside the stack being moved (base..head), the move would be circular — a commit cannot be both moved and its own new parent — so the plan builder rejects it once the stack has been enumerated.
Solutions
- Pick a target commit outside the base..head stack, e.g. a commit the stack currently does not contain.
- Check membership before calling: ensure `target` is not among the commits from base to head.
- If the intent is to reorder within the stack, use a reorder/edit operation rather than stack insert.
Example fix
// before stack_insert_plan(&repo, &graph, base, head, some_commit_inside_stack)? // after anyhow::ensure!(!stack_ids(base, head).contains(&target), "target must be outside the stack"); stack_insert_plan(&repo, &graph, base, head, target)?
Defensive patterns
Strategy: validation
Validate before calling
// enumerate stack ids from base..head and reject overlap with target
let mut stack_ids = std::collections::HashSet::new();
let mut id = head;
loop {
stack_ids.insert(id);
if id == base { break; }
id = graph.parents_of(id)?[0];
}
if stack_ids.contains(&target) {
eprintln!("target is inside the moved stack");
return Ok(());
} Type guard
fn target_outside_stack(graph: &HistoryGraph, base: ObjectId, head: ObjectId, target: ObjectId) -> bool {
let mut id = head;
loop {
if id == target { return false; }
if id == base { return true; }
match graph.parents_of(id) { Ok(p) if !p.is_empty() => id = p[0], _ => return true }
}
} Try / catch
match stack_insert_plan(&repo, &graph, base, head, target) {
Err(e) if e.to_string().contains("part of the moved stack") => eprintln!("pick a destination outside the stack"),
other => other?,
} Prevention
- Exclude stack members from destination pickers in UIs
- Compute the stack range once and validate all three ids together
- Treat base and head as implicitly invalid targets
When it happens
Trigger: Calling `move_insert_plan`/`stack_insert_plan(repo, graph, base, head, target)` where `target` equals any commit id in the base..head stack (including base or head).
Common situations: UI selection where the user picks a commit within the highlighted stack as the destination; scripts computing the target from the same range as the stack.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- the squash target must be a strict ancestor of the source
- the copy source and target must differ
- the stack base must be an ancestor of HEAD
- Cannot derive commit or tree from blob at
- rebase todo requires at least one -x/--hide revision when…
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/7e79364d77c7727b.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/rebase.rs:676
let mut stack = vec![head];
let mut stack_parent = HashMap::new();
loop {
let id = *stack.last().expect("a stack always contains HEAD");
let parents = graph.parents_of(id).context("a moved stack commit is incomplete")?;
let [parent] = parents.as_slice() else {
anyhow::bail!("moving a stack requires every commit to have exactly one parent");
};
stack_parent.insert(id, *parent);
if id == base {
break;
}
stack.push(*parent);
}
stack.reverse();
let stack_set: HashSet<_> = stack.iter().copied().collect();
if stack_set.contains(&target) {
anyhow::bail!("the move target must not be part of the moved stack");
}
let base_parent = stack_parent[&base];
if base_parent == target {
anyhow::bail!("the stack is already directly above the move target");
}
let target_rewritten = graph.is_ancestor(base, target);
let mut scope = Vec::new();
let mut scope_set = HashSet::new();
for id in graph
.descendants_in_parent_order(base)
.context("the stack base is not in the loaded history")?
.into_iter()
.chain(
graph
.descendants_in_parent_order(target)
.context("the move target is not in the loaded history")?,
)View on GitHub (pinned to e73179060b)