GitoxideLabs/gitoxide · error
a pick is outside the editable history
Error message
a pick is outside the editable history
What it means
Validation inside `edit::todo::parse`. Each `pick` line's commit ID is checked against the set of commits that belong to the editable history; if the picked commit resolves to a commit outside that history (or an ID that cannot be resolved into it), parsing bails. This prevents the resulting rebase plan from silently grafting foreign commits into the edited branch. Fired by hand-edited todo files referencing hashes not part of the displayed history; fix by picking only commits listed in the editable range or removing the line.
Solutions
- Reference a commit ID inside the editable history
- Widen the plan scope to include the desired commit
- Delete the pick line if the commit should not be replayed
Example fix
// before pick 9999999 # outside the edited range // after pick abc1234 # inside the edited range
Defensive patterns
Strategy: validation
Validate before calling
let id = repo.rev_parse_single(value)?;
if !scope.contains(&id) {
anyhow::bail!("pick target {} is outside the editable history", id.shorten_or_id());
} Type guard
fn pick_in_scope(id: gix::Id<'_>, scope: &HashSet<gix::oid::ObjectId>) -> bool {
scope.contains(id.object_id())
} Try / catch
match parse_plan(...) {
Err(e) if e.to_string().contains("a pick is outside the editable history") => { /* correct the pick line */ }
other => other?,
} Prevention
- Verify each picked commit belongs to the edited range before writing the todo
- Avoid copying pick lines from unrelated rebase sessions
- Beware abbreviated IDs that may resolve to out-of-scope commits
When it happens
Trigger: `parse_plan` resolves a `pick <oid>` (or an abbreviated ID via `resolve_commit`) whose commit is not in the scope set.
Common situations: Picking a commit from another branch or from outside the forked range; typos or stale IDs copied from a previous rebase; abbreviated IDs that resolve to an unintended out-of-scope commit.
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
- a squash is outside the editable history
- 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…
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/aeff57fe1c19b5f3.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/todo.rs:974
}
let parent = cursor.context("the first todo command must follow a fork heading")?;
let commit = match verb {
"pick" => {
let value = value.split_whitespace().next().context("a pick needs a commit ID")?;
let resolved_id = state.resolved;
let full_null = resolved_id.is_some_and(|id| {
value.len() == id.kind().len_in_bytes() * 2 && value.bytes().all(|byte| byte == b'0')
});
let (id, resolved) = if full_null {
(
resolved_id.context("a null pick has no materialized conflict state")?,
true,
)
} else {
(resolve_commit(repo, value)?, false)
};
if !scope.contains(&id) {
anyhow::bail!("a pick is outside the editable history");
}
if picked.contains_key(&id) {
anyhow::bail!("a commit is picked more than once");
}
if resolved {
rebase::PlanCommit::Resolved(id)
} else {
rebase::PlanCommit::Pick(id)
}
}
"empty" => {
let title = if value.trim().is_empty() { tail } else { value.trim() };
if title.is_empty() {
anyhow::bail!("an empty commit needs a title");
}
rebase::PlanCommit::Empty(BString::from(title))
}
_ => anyhow::bail!("unsupported rebase todo command {verb:?}"),View on GitHub (pinned to e73179060b)