GitoxideLabs/gitoxide · warning
the stack is already directly above the move target
Error message
the stack is already directly above the move target
What it means
The stack is moved to sit directly on top of `target`. If `base`'s current parent is already `target`, the stack already occupies exactly that position and the move is a no-op; the plan builder bails instead of producing an empty rewrite.
Solutions
- Skip the operation if the stack is already in the requested position.
- Choose a different target commit.
- Make the operation idempotent in caller code by checking `parent_of(base) == target` before invoking.
Example fix
// before
stack_insert_plan(&repo, &graph, base, head, target)?
// after
if graph.parents_of(base)?[0] != target {
stack_insert_plan(&repo, &graph, base, head, target)?;
} Defensive patterns
Strategy: validation
Validate before calling
if graph.parents_of(base)?[0] == target {
eprintln!("stack is already directly above target; nothing to do");
return Ok(());
} Type guard
fn move_changes_position(graph: &HistoryGraph, base: ObjectId, target: ObjectId) -> bool {
graph.parents_of(base).map(|p| p.first() != Some(&target)).unwrap_or(false)
} Try / catch
match stack_insert_plan(&repo, &graph, base, head, target) {
Err(e) if e.to_string().contains("already directly above") => { /* treat as success/no-op */ }
other => other?,
} Prevention
- Make repeated operations idempotent by checking current position first
- Track already-applied moves in scripts to avoid replaying them
- Default the destination picker away from the stack's current parent
When it happens
Trigger: Calling `move_insert_plan`/`stack_insert_plan(repo, graph, base, head, target)` where the parent of `base` equals `target` — i.e. repeating an already-applied move, or passing the natural current parent as target.
Common situations: Re-running the same move command twice (e.g. after a retried script); user selecting the commit the stack already sits on; automation replaying recorded operations.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- rebase todo requires at least one -x/--hide revision when…
- the hidden and visible revisions have no editable fork point
- the revisions have multiple editable fork points
- aborted without changes: conflict while applying ; pass…
- an edit unexpectedly produced a merge conflict
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/3b4ef31dc84dbfe2.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/rebase.rs:680
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")?,
)
{
if (id != target || target_rewritten) && scope_set.insert(id) {
scope.push(id);
}View on GitHub (pinned to e73179060b)