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

  1. Pick a target commit outside the base..head stack, e.g. a commit the stack currently does not contain.
  2. Check membership before calling: ensure `target` is not among the commits from base to head.
  3. 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

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


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)