GitoxideLabs/gitoxide · error

an editable commit is not connected to the selected base

Error message

an editable commit is not connected to the selected base

What it means

While building the rebase todo, `prepare` validates that every editable commit's parent is either the selected base itself or another commit already in the processing scope. A commit whose parent lies outside both sets is disconnected from the base — its position in the todo cannot be derived — so preparation aborts.

Solutions

  1. Include the missing parent commit in the scope so the chain from base to each editable commit is complete.
  2. Select a different base that is the actual (first-parent) ancestor of all editable commits.
  3. Recompute the scope from `base..tip` (e.g. `git rev-list base..tip`) instead of a hand-built list.
  4. Exclude the disconnected commit from the editable set.

Example fix

// before: scope misses an intermediate commit
let scope = vec![c3]; // c3's parent c2 not included, not base
prepare(repo, base, scope)?; // bails

// after
let scope = vec![c2, c3]; // contiguous chain from base
prepare(repo, base, scope)?;
Defensive patterns

Strategy: validation

Validate before calling

// every editable commit's parent must be base or in scope
for id in &scope {
    let commit = repo.find_commit(id)?.decode()?;
    let parent = commit.parents.first().expect("commit has parent");
    if *parent != base && !scope.contains(parent) {
        anyhow::bail!("commit {id} is disconnected from base");
    }
}

Try / catch

match prepare(repo, base, scope) {
    Ok(todo) => /* proceed */,
    Err(e) if e.to_string().contains("not connected to the selected base") => {
        // rebuild scope from `base..tip` and retry
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `prepare` (or `prepare_test`) with a commit range/scope where one of the editable commits has a parent commit that is neither `base` nor a member of the scope set — e.g. the scope includes a commit whose ancestry skips over the base or contains a merge with an out-of-scope parent.

Common situations: Hand-picking a set of commits that isn't a contiguous chain from the base; scope computed from a stale object list after history changed; including a merge commit's second parent's side without including it.

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/0c1919f277748825. Report an issue: GitHub.

Appendix: source

Thrown at gix-tix/src/edit/todo.rs:142

            break;
        }
        cursor = commit
            .parents
            .first()
            .copied()
            .filter(|parent| scope_set.contains(parent));
    }
    let apply_unchanged = base != onto || has_pending;

    let mut children = HashMap::<ObjectId, Vec<ObjectId>>::new();
    for commit in commits {
        let parent = commit
            .parents
            .first()
            .copied()
            .context("an editable commit has no parent")?;
        if parent != base && !scope_set.contains(&parent) {
            anyhow::bail!("an editable commit is not connected to the selected base");
        }
        children.entry(parent).or_default().push(commit.id);
    }
    let mut sections = Vec::new();
    for child in children.get(&base).into_iter().flatten().copied() {
        let mut section = Section {
            parent: onto,
            commits: Vec::new(),
        };
        let mut branches = Vec::new();
        walk(child, &children, &mut section, &mut branches);
        sections.push(section);
        sections.extend(branches);
    }
    let mut ref_points = scope.clone();
    ref_points.push(onto);
    ref_points.extend(sections.iter().map(|section| section.parent));
    ref_points.sort_unstable();

View on GitHub (pinned to e73179060b)