GitoxideLabs/gitoxide · error

a squash is outside the editable history

Error message

a squash is outside the editable history

What it means

Validation inside `edit::todo::parse` of gix-tix. Commands prefixed with `@` (e.g. `@squash`) mark a position in the editable history, but a `squash` verb is only legal when it sits within the history that the todo is allowed to edit. When a squash command's marked position resolves outside that editable history, parsing bails rather than producing a plan that would rewrite commits the tool doesn't own. Triggered by hand-edited todos that move a squash to a foreign segment of history; fix by removing the squash or placing it inside the editable range.

Solutions

  1. Use a commit ID that is part of the editable history range
  2. Extend the plan's scope if the commit legitimately belongs to the edit
  3. Remove the squash line if the commit should not be folded

Example fix

// before
squash 9999999  # commit outside the edited range
// after
squash abc1234  # commit inside the edited range
Defensive patterns

Strategy: validation

Validate before calling

let id = repo.rev_parse_single(value)?;
if !scope.contains(&id) {
    anyhow::bail!("squash target {} is outside the editable history", id.shorten_or_id());
}

Type guard

fn squash_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 squash is outside the editable history") => { /* fix the squash line */ }
    other => other?,
}

Prevention

When it happens

Trigger: `parse_plan` reads a `squash <oid>` line whose resolved commit is not in the plan's scope set.

Common situations: Typing a wrong or truncated commit ID; squashing a commit that lives outside the range forked for editing; copying a squash line from another rebase session.

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/1a6af13172ae2089. Report an issue: GitHub.

Appendix: source

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

        let (verb, value) = command.split_once(char::is_whitespace).unwrap_or((command, ""));
        let marked = verb.starts_with('@');
        let verb = verb.strip_prefix('@').unwrap_or(verb);
        if marked {
            if std::mem::replace(&mut command_marker, true) {
                anyhow::bail!("the rebase todo contains more than one @ command");
            }
            if !state.checkout_allowed || repo.workdir().is_none() {
                anyhow::bail!("the rebase todo cannot select a checkout without a worktree");
            }
        }
        if verb == "squash" {
            let index = section_last_step.context("a squash must follow a command in the same fork")?;
            let id = resolve_commit(
                repo,
                value.split_whitespace().next().context("a squash needs a commit ID")?,
            )?;
            if !scope.contains(&id) {
                anyhow::bail!("a squash is outside the editable history");
            }
            if picked.insert(id, index).is_some() {
                anyhow::bail!("a commit is picked more than once");
            }
            steps[index].squash.push(id);
            if marked {
                let target = rebase::PlanParent::Step(index);
                if checkout_target.is_some_and(|checkout| checkout != target) {
                    anyhow::bail!("the @ command and @ reference point to different results");
                }
                checkout_target = Some(target);
            }
            section_has_commit = true;
            continue;
        }
        let parent = cursor.context("the first todo command must follow a fork heading")?;
        let commit = match verb {
            "pick" => {

View on GitHub (pinned to e73179060b)