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
- Apply and then delete the pre-existing stash reference for that commit before re-running the rewrite
- Rename or remove the stale reference if the old saved state is no longer needed
- 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
- Before rewriting, list tix stash references and resolve ones whose names will collide with rewritten IDs
- Avoid re-running identical rewrites without cleaning up stash refs from the first run
- Use `try_find_reference(reference(new))` as a pre-flight check in automation
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
- tix stash reference does not use a canonical full commit ID
- already has saved worktree state
- could not inspect a stash reference before rebasing
- One or more errors occurred - checkout is incomplete
- cannot drop stashed commit
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)