GitoxideLabs/gitoxide · error
a recorded scope commit is disconnected from the rebase base
Error message
a recorded scope commit is disconnected from the rebase base
What it means
Every commit in the recorded rebase scope must connect back to the rebase base: its first parent must be the base, another scope commit, or the commit itself must be a continuation source (a conflict resolution point). Otherwise the captured scope is disconnected from the history being rewritten, indicating corrupt or tampered state.
Solutions
- Remove the stale rebase state and restart the rebase
- Re-record state so all scope commits are captured together with their connecting history
- If the commit legitimately roots a new history, register it as a continuation-source in the state
Defensive patterns
Strategy: validation
Validate before calling
// ensure each scope commit's first parent connects to base or scope,
// or the commit is a continuation source
for id in &state.scope {
let parent = repo.find_commit(*id)?.parent_ids().next().unwrap();
assert!(parent == state.base || scope.contains(&parent)
|| state.continuation_sources.contains(id));
} Try / catch
match result {
Err(e) if e.to_string().contains("disconnected from the rebase base") => {
// state graph is stale: discard and restart the rebase
}
r => r?,
} Prevention
- Never rewrite history with plain git while tix rebase state exists
- Copy rebase state only between identical repository states
- Register legitimate new roots as continuation-source entries
When it happens
Trigger: validate_state walks each scope commit via `parent_ids().next()`; the bail fires when a scope commit's first parent is not `state.base`, not in `scope`, and the commit is not listed in `continuation_sources`. Happens with hand-edited state, merged-in unrelated commits, or state persisted before external history modification.
Common situations: Amending or rebasing history with plain git while a tix rebase state exists; copying state between repos where the DAG differs; incomplete state files after a crash.
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
- the root of a repeated rebase must be pending
- the parent of a repeated rebase must not be pending
- the current checkout has a pending rebase; time-travel to…
- has a pending rebase
- the rebase state contains duplicate scope commits
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/c23cb641dda2043c.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/todo.rs:817
.iter()
.any(|reference| reference.editable && reference.name == *name)
{
anyhow::bail!("the recorded HEAD ref is not editable");
}
for tip in &state.tips {
repo.find_commit(*tip).context("could not find a recorded rebase tip")?;
}
for id in &state.scope {
let commit = repo
.find_commit(*id)
.context("could not find a recorded scope commit")?;
let parent = commit
.parent_ids()
.next()
.map(gix::Id::detach)
.context("a recorded scope commit has no parent")?;
if parent != state.base && !scope.contains(&parent) && !continuation_sources.contains(id) {
anyhow::bail!("a recorded scope commit is disconnected from the rebase base");
}
}
if state.resolved.is_some_and(|id| !scope.contains(&id)) {
anyhow::bail!("the resolved conflict is outside the rebase scope");
}
if !continuation_sources.is_subset(&scope) {
anyhow::bail!("a continuation source is outside the rebase scope");
}
Ok(())
}
pub(crate) fn parse(repo: &gix::Repository, edited: &[u8]) -> Result<Option<Parsed>> {
repo.head()?.id().context("rebase todos require a born HEAD")?;
let input = std::str::from_utf8(edited).context("the rebase todo is not UTF-8")?;
let Some(mut state) = parse_state(repo, input)? else {
return Ok(None);
};
let scope: HashSet<_> = state.scope.iter().copied().collect();View on GitHub (pinned to e73179060b)