GitoxideLabs/gitoxide · error

rewritten commit already has saved worktree state

Error message

rewritten commit {} already has saved worktree state

What it means

After a rewrite, tix stores saved worktree state under a reference named for the commit's NEW id. If a reference with that name already exists (from a previous stash or a collision), creating the new reference would overwrite unrelated saved state, so the operation aborts. The error names the short hash of the rewritten commit whose reference name collides.

Solutions

  1. Apply and then delete the pre-existing stash reference for that commit before re-running the rewrite
  2. Rename or remove the stale reference if the old saved state is no longer needed
  3. Choose a rewrite plan that produces a different destination commit ID for this stash

Example fix

// before re-running the rewrite
rewrite_edits(&repo, plan)?;
// after: clear the colliding ref first
let name = stash::reference(new_id)?;
repo.find_reference(name)?.delete()?;
rewrite_edits(&repo, plan)?;
Defensive patterns

Strategy: validation

Validate before calling

let name = stash::reference(new_id)?;
if repo.try_find_reference(name.as_ref())?.is_some() {
    anyhow::bail!("clear existing stash ref {} before rewriting", name);
}

Prevention

When it happens

Trigger: Calling `stash::rewrite_edits` when `repo.try_find_reference(reference(new))` returns `Some` — i.e. saved worktree state already exists under the rewritten commit's canonical stash name.

Common situations: Re-running a rewrite twice so the same rewritten ID is produced; reusing commit IDs after resets/amendments that kept old stash refs; hash collisions between separate stash sessions.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at gix-tix/src/edit/stash.rs:96

            continue;
        }
        if let Some(other) = destinations.insert(new, old) {
            anyhow::bail!(
                "stashes at {} and {} would converge on {}",
                other.to_hex_with_len(7),
                old.to_hex_with_len(7),
                new.to_hex_with_len(7)
            );
        }
        moves.push((reference.name().to_owned(), reference.target().into_owned(), old, new));
    }

    let mut forward = Vec::with_capacity(moves.len() * 2);
    let mut rollback = Vec::with_capacity(moves.len() * 2);
    for (old_name, target, old, new) in moves {
        let new_name = reference(new)?;
        if repo.try_find_reference(new_name.as_ref())?.is_some() {
            anyhow::bail!(
                "rewritten commit {} already has saved worktree state",
                new.to_hex_with_len(7)
            );
        }
        forward.push(delete_edit(old_name.clone(), target.clone()));
        forward.push(create_edit(new_name.clone(), target.clone()));
        rollback.push(delete_edit(new_name, target.clone()));
        rollback.push(create_edit(old_name, target));
        tracing::debug!(old = %old, new = %new, "prepared tix stash association rewrite");
    }
    Ok(RewriteEdits { forward, rollback })
}

fn create_edit(name: gix::refs::FullName, target: Target) -> RefEdit {
    RefEdit::update(name, target, PreviousValue::MustNotExist, "tix commit stash rewrite")
}

fn delete_edit(name: gix::refs::FullName, target: Target) -> RefEdit {

View on GitHub (pinned to e73179060b)