How do you recover a stash entry that was removed with git stash drop?
answer
- Stashes are commits, not patch files
- Dropping removes the pointer, not the object
- Read the hash the drop command printed
- Unreachable merge commits with WIP messages
- apply accepts a raw commit hash
basics
~20 sA stash entry is a real commit, and dropping it removes only the reflog slot naming it. If you still have the hash the drop printed, git stash apply <hash> works directly; otherwise find it among unreachable commits with git fsck and filter for the WIP message.
solid answer
~40 sEach stash entry is stored as a commit — a merge commit whose message reads `WIP on <branch>: …` — reachable through `refs/stash` and its reflog. `git stash drop` deletes the reflog slot, not the commit, so the commit simply becomes unreachable. The easy path is that drop prints the hash: `Dropped refs/stash@{0} (b7e5c3a…)`, and `git stash apply b7e5c3a` accepts any commit that looks like a stash. If that has scrolled away, list unreachable commits with `git fsck --unreachable` and filter for stash-shaped ones — they are merge commits with a WIP message, so `git log --merges --no-walk --grep=WIP` over those hashes narrows it fast. Then apply it, or `git branch salvage <hash>` if you would rather look before applying. Garbage collection is the deadline, as always.
code
console · 6 lines$ git stash drop
Dropped refs/stash@{0} (b7e5c3a1f0d9e8c7b6a5948372615049382716ff)
$ git stash apply b7e5c3a
# or, to look before leaping:
$ git branch salvage b7e5c3ago deeper
Remember that a dropped stash is not immediately gone and that git stash drop prints the hash you need. Applying that hash directly with git stash apply is the whole recovery in the common case.
Explain the storage model: a stash entry is a merge commit reachable through the refs/stash reflog, so dropping removes a reflog slot and leaves an unreachable commit that fsck can still find.
Generalise it — a dropped stash is just another unreachable object, so the same triage applies: freeze housekeeping, locate, anchor with a branch, and only then apply. Prefer branching over applying onto a dirty tree.
Treat it as a signal. Long-lived stashes are undurable local state that no backup or review process can see; encourage short-lived WIP commits on throwaway branches instead of a stash pile nobody can recover or audit.
## Why a dropped stash is recoverable at all `git stash push` does not store a patch file. It creates commits: one recording the index state, one recording the working tree, optionally one for untracked files, tied together as a merge commit whose message is auto-generated as `WIP on <branch>: <hash> <subject>`. That commit is then recorded in `refs/stash` and its reflog, where each entry is what you address as `stash@{0}`, `stash@{1}`, and so on. `git stash drop` removes one reflog entry (and `git stash clear` removes all of them). The underlying commit is not deleted — it merely loses the only thing pointing at it and becomes unreachable, exactly like a commit orphaned by a hard reset. ## The easy path: the hash Git printed Dropping prints `Dropped refs/stash@{0} (b7e5c3a…)`. That parenthesised hash is the stash commit. `git stash apply` accepts any commit that looks like a stash entry, so `git stash apply b7e5c3a` restores the changes immediately. If you would rather inspect first, `git show b7e5c3a` shows the stashed diff and `git branch salvage b7e5c3a` pins it. Scrolling up in the terminal is a legitimate and often overlooked recovery technique. ## The general path: fsck When the output is gone, the commit is now an unreachable object like any other, so `git fsck --unreachable` will list it among everything else the repository is holding. The output is noisy, but stash commits have two distinguishing features: they are merge commits (two parents, or three when untracked files were included), and their message begins with `WIP on`. That makes filtering straightforward — take the unreachable commit hashes from fsck and feed them to `git log --merges --no-walk --grep=WIP`, which prints just the stash-shaped candidates with their branch and subject, so you can pick out the one you want by the branch name and time. `--no-walk` is important here: it tells log to show exactly the commits you named rather than traversing their ancestry, which would drown you in ordinary history. ## Restoring it Once you have the hash, you have two options: - `git stash apply <hash>` — applies the stashed changes onto your current working tree. Note that applying does not re-create a stash entry; nothing is pushed back onto `refs/stash`. - `git branch salvage <hash>` — turns the stash commit into a branch you can inspect, diff, or cherry-pick from at leisure. This is the safer move when your working tree is not clean or you are not certain this is the right entry. Either way, do it promptly: an unreachable commit is subject to garbage collection, and once pruned there is no local path back. ## Common misreadings People often assume `git stash pop` is safe because it "only removes the entry after applying". In fact `pop` applies and then drops, and if the apply hit a conflict the entry may be retained — but in the successful-apply case the reflog slot is gone just the same. They also assume `git stash list` is the only view of stashes; it is a view of the `refs/stash` reflog, so anything not in that reflog is invisible to it even though the commit still exists. A related trap is `git stash clear`, which drops every entry in one go. The same recovery works, but you may be sifting through many stash-shaped commits, and distinguishing them relies entirely on the branch name and subject baked into the auto-generated message plus the commit timestamps. ## What an interviewer is checking This question is really a test of whether you know that stashes are commits rather than some separate patch storage. A candidate who says "a dropped stash is gone forever" has a mental model in which stash is a magic side-channel; a candidate who says "it is an unreachable commit, so the usual unreachable-commit recovery applies" has the right model and will generalise correctly to resets, rebases, and deleted branches too.
- Why does filtering unreachable commits for merges with a WIP message work?Because git stash push builds the entry as a merge commit — one parent for HEAD, one for the index state, and a third when untracked files are included — and gives it an auto-generated message starting with WIP on <branch>. Those two properties together are distinctive enough to separate stash entries from ordinary orphaned commits.
- Does applying a recovered stash commit put it back in git stash list?No. git stash list reads the reflog of refs/stash, and applying a commit does not write an entry there. If you want it tracked again, create a branch or tag pointing at the commit, or re-stash the changes after applying them.
- How does git stash clear differ from dropping one entry, for recovery purposes?Mechanically it is the same — all reflog entries go, all the commits become unreachable — but the search is harder because many stash-shaped commits now show up at once. You distinguish them by the branch name and base subject in the auto-generated message and by commit timestamps.
Dropping a stash is like tearing a page out of the index at the front of a book. The chapter it referred to is still bound in; you just have to find it by flipping through.
saying these in an interview costs you the question
- Claims a dropped stash is permanently unrecoverable
- Thinks stashes are stored as patch files outside the object database
- Looks in git stash list for an entry already dropped
- Assumes pop is non-destructive because it applies first
- Forgets that garbage collection eventually removes the orphaned commit