GitoxideLabs/gitoxide · error

the parent of a repeated rebase must not be pending

Error message

the parent of a repeated rebase must not be pending

What it means

During rebase edit validation, when `repeat` is set, the parent of the first affected commit (`affected.first()`) must NOT itself be pending. If the parent commit is part of an unfinished rebase, rebasing a descendant on top of it would corrupt the in-progress rebase ordering, so the edit bails with this message.

Solutions

  1. Rebase the pending parent commit first (or complete it) before repeating a rebase whose base hangs under it.
  2. Choose a base whose parent is not pending — start the repeated range higher or lower in the history.
  3. Reorder the edit operations so the rebase proceeds root-to-tip and never attaches to a pending parent.
  4. Abort the pending rebase involving the parent, then retry the edit.

Example fix

// before: base parent is still pending
let parent = graph.parents_of(base).unwrap()[0];
assert!(!is_pending(&repo.find_commit(parent)?.decode()?)); // fails
edit.rebase(base, true, ...)?;
// after: resolve the pending parent first
finish_or_abort_pending(parent)?;
let parent = graph.parents_of(base).unwrap()[0];
edit.rebase(base, true, ...)?;
Defensive patterns

Strategy: validation

Validate before calling

let base = *affected.first().context("empty range")?;
if let Some(parent) = graph.parents_of(base).and_then(|p| p.first().copied()) {
    let c = repo.find_commit(parent)?.decode()?.into_owned()?;
    debug_assert!(!gix_tix::edit::rebase::is_pending(&c));
}

Type guard

fn parent_is_settled(repo: &gix::Repository, graph: &Graph, base: ObjectId) -> anyhow::Result<bool> {
    Ok(graph.parents_of(base)
        .and_then(|p| p.first().copied())
        .map(|parent| !gix_tix::edit::rebase::is_pending(&repo.find_commit(parent)?.decode()?.into_owned()?))
        .unwrap_or(true))
}

Prevention

When it happens

Trigger: Calling a repeated rebase edit where `graph.parents_of(*base)` yields a first parent whose decoded commit satisfies `is_pending()`. I.e. the base of the repeated rebase hangs directly under another pending commit.

Common situations: Chaining repeated rebase edits across overlapping ranges; editing a range whose base commit was itself just rewritten but left pending; rebase scripts that operate on adjacent segments of the same pending stack in the wrong order.

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


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/4f6e82acd7145577. Report an issue: GitHub.

Appendix: source

Thrown at gix-tix/src/edit/rebase.rs:2561

) -> Result<()> {
    for (position, id) in affected.iter().enumerate() {
        let parents = graph.parents_of(*id).context("an affected commit is incomplete")?;
        if parents.len() > 1 && (position > 0 || removed || tree == Tree::CherryPick) {
            anyhow::bail!("descendant merge commits cannot be rebased");
        }
        if repeat && position == 0 {
            let commit = repo.find_commit(*id)?.decode()?.into_owned()?;
            if !is_pending(&commit) {
                anyhow::bail!("the root of a repeated rebase must be pending");
            }
        }
    }
    if repeat
        && let Some(base) = affected.first()
        && let Some(parent) = graph.parents_of(*base).and_then(|parents| parents.first().copied())
        && is_pending(&repo.find_commit(parent)?.decode()?.into_owned()?)
    {
        anyhow::bail!("the parent of a repeated rebase must not be pending");
    }
    Ok(())
}

fn reject_pending_checkout_path(repo: &gix::Repository, mut id: ObjectId) -> Result<()> {
    let mut seen = HashSet::new();
    while seen.insert(id) {
        let commit = repo.find_commit(id)?.decode()?.into_owned()?;
        if is_pending(&commit) {
            anyhow::bail!("the current checkout has a pending rebase; time-travel to HEAD before editing it");
        }
        let Some(parent) = commit.parents.first().copied() else {
            break;
        };
        id = parent;
    }
    Ok(())
}

View on GitHub (pinned to e73179060b)