GitoxideLabs/gitoxide · error
cannot drop stashed commit
Error message
cannot drop stashed commit {} What it means
During a history rewrite, tix updates stash references so they follow their rewritten commits. If a stashed commit was removed by the rewrite (it is in the `removed` set), there is no new location to point the stash reference at, so tix refuses rather than silently dropping the saved worktree state. The error names the short hash of the stashed commit that could not be migrated.
Solutions
- Unstash (apply) the saved worktree state for that commit before dropping it from history
- Remove the stash reference explicitly if the saved state is no longer needed, then re-run the rewrite
- Keep the commit in the rewritten history (don't mark it removed) so its stash can be migrated
Example fix
// before dropping the stash-owning commit rewrite.remove(commit_id); // after: restore the worktree state first stash::restore_manual(path, bare, commit_id)?; rewrite.remove(commit_id);
Defensive patterns
Strategy: try-catch
Validate before calling
let stashed: Vec<ObjectId> = stash_refs(&repo)?;
if stashed.iter().any(|id| rewrite.removed().contains(id)) {
anyhow::bail!("a stash-owning commit is marked for removal; unstash it first");
} Try / catch
match stash::rewrite_edits(&repo, plan) {
Err(e) if e.to_string().starts_with("cannot drop stashed commit") => {
// extract the id from the message, unstash it, then retry
}
other => other?,
} Prevention
- Before dropping commits in a rewrite, list tix stash references and unstash any whose commit is being dropped
- Audit `removed` commits against stash refs in rewrite automation
- Prefer amending over dropping when a commit carries saved worktree state
When it happens
Trigger: Calling `stash::rewrite_edits` (as part of a rewrite/rebase edit plan) when `rewritten` mapping plus a `removed` set indicate that a commit which owns a stash reference was dropped from the rewritten history.
Common situations: Dropping commits during an interactive rebase while those commits had associated tix stash state; amending histories where a stash-owning commit was filtered out.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- stashes at and would converge on
- the hidden and visible revisions have no editable fork point
- tix stash reference does not use a canonical full commit ID
- rewritten commit already has saved worktree state
- changes can only be stashed at the current HEAD
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/5b1549df20e18181.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/stash.rs:74
Err(err) => {
return Err(anyhow::anyhow!(
"could not inspect a stash reference before rebasing: {err}"
));
}
};
let old = match associated_commit(reference.name().as_bstr()) {
Ok(Some(id)) => id,
Ok(None) => continue,
Err(err) => {
tracing::warn!(name = %reference.name(), error = %err, "ignored malformed tix stash reference");
continue;
}
};
let Some(new) = rewritten.get(&old).copied() else {
continue;
};
if removed.contains(&old) {
anyhow::bail!("cannot drop stashed commit {}", old.to_hex_with_len(7));
}
let new = new.context("a stashed commit cannot disappear during a rewrite")?;
if new == old {
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);View on GitHub (pinned to e73179060b)