Git Stash Gone After Checkout — How to Recover It

Your git stash list is empty after switching branches and your work-in-progress changes have vanished. Find them in the stash reflog or with git fsck and restore them.

You ran git stash, switched to main to review a PR, switched back with git checkout feature/login, ran git stash pop — and now git stash list is empty and your half-finished changes are nowhere to be found.

Fastest fix: first run git status and git diff. If you see your modified files, the stash already applied to the working tree and nothing is lost. If the tree is clean, the stash commit still exists as a dangling object — recover its SHA with git fsck --no-reflog | grep commit, then git stash apply <SHA>. By default Git keeps unreachable objects for at least 30 days (and reachable reflog entries for 90), so recovery is almost always possible.

Why this works: a stash is stored as a real commit object. The pointer lives in refs/stash; older entries live in that ref’s reflog. Dropping, popping, or clearing a stash removes the reference, but the commit object survives in the object database until garbage collection prunes it.

Which bucket are you in?

Run these three commands first — they tell you instantly which recovery path you need.

CommandIf you see…CauseGo to
git status / git diffYour modified filesStash already applied (pop succeeded)Nothing to do
git stash listEntries, but for a different branchWrong stash popped / multiple stashesStep 1
git reflog stashOld SHAs, list emptyStash dropped or popped cleanStep 2
git fsck --no-reflogdangling commit <sha>Reflog entry gone tooStep 3
Nothing anywhereUntracked files removed by git cleanFiles were never in the stashStep 5

Common causes

Ordered by hit rate, highest first.

1. git stash pop applied successfully and dropped the stash automatically

git stash pop is git stash apply followed by git stash drop. If the apply succeeds with no conflict, the stash entry is removed immediately — but your changes are now on disk in the working tree. They were not lost, just applied.

How to spot it: run git status and git diff. Modified files present = stash applied, nothing lost.

2. git stash drop or git stash clear run by mistake

A copy-paste slip, muscle memory, or a shell alias ran git stash drop instead of git stash show. git stash clear removes every entry at once.

How to spot it: git stash list is empty. Note that after a drop, the dropped entry is usually gone from the stash reflog too — so if git reflog stash is also empty, jump straight to git fsck in Step 3.

3. A pop hit a conflict and the tree was cleaned by hand

This is the one most people misremember. In modern Git (and every version for the last decade), git stash pop does NOT drop the stash when it hits a conflict — the official docs state: “Applying the state can fail with conflicts; in this case, it is not removed from the stash list.” So if the pop conflicted, your stash is still in git stash list.

The actual loss happens when, faced with a conflicted tree, the user runs git checkout -- ., git reset --hard, or git stash drop to “clean up,” wiping the half-applied changes. The stash commit still exists by SHA.

How to spot it: git stash list still shows the entry (look there first); if you already discarded it, recover via Step 2/3.

4. The stash you popped belonged to a different branch

You stashed on feature/login, forgot, switched to feature/payments, and stashed again there. Switching back, git stash list shows the payments stash at stash@{0} and the login stash at stash@{1}, so a plain git stash pop grabs the wrong one. Stashes are global to the repo, not per-branch.

How to spot it: git stash list shows the branch in each message: stash@{0}: WIP on feature/payments: .... Pop the right index explicitly: git stash pop stash@{1}.

5. A git worktree doesn’t see the main worktree’s stash

refs/stash is per-worktree by default. A stash created in the main worktree is not listed inside an added worktree (and vice versa), so it looks lost.

How to spot it: git worktree list shows multiple entries. The stash was created in a different worktree’s context — run git stash list from that worktree’s directory.

6. git clean -fd removed untracked files the stash never held

Plain git stash stashes only tracked, modified files. New untracked files are not included unless you pass -u (--include-untracked) or -a (--all). If your missing work was brand-new files, git clean -fd deleted them outright and the stash never had them.

How to spot it: the lost work consisted of files you had never git add-ed. Confirm what a stash actually held with git stash show -p <ref> (look for new file: lines).

Shortest path to fix

Step 1: Check whether the stash is already applied or sitting on another branch

git status
git diff
git stash list

If git diff shows your changes, you’re done. If git stash list shows the entry (possibly under a different branch), apply the right one explicitly:

git stash apply stash@{1}

Step 2: Find dropped stash commits in the stash reflog

git reflog stash --date=iso
# equivalent: git reflog show refs/stash --date=iso

Each line gives a SHA, e.g. abc1234 refs/stash@{0}: WIP on feature/login: 3fa1b2c add session token. Note the SHA (call it STASH_SHA). Inspect before applying:

git stash show -p STASH_SHA   # full patch

A clean git stash drop often empties this reflog, so if there’s nothing here, go to Step 3.

Step 3: Find dangling stash commits with git fsck

When the reflog is gone, the stash commit is a dangling object. List dangling commits and grep for the WIP on summary stash commits carry:

git fsck --no-reflog | awk '/dangling commit/ {print $3}' | while read sha; do
  printf '%s  ' "$sha"
  git show --no-patch --format='%s' "$sha"
done | grep 'WIP on'

A stash commit has 2–3 parents (HEAD at stash time, the index, and optionally an untracked-files commit if -u/-a was used), which helps confirm you found the right object.

Step 4: Restore the recovered stash

# Apply the recovered SHA straight to the working tree:
git stash apply STASH_SHA

Or re-register it as a proper stash entry first (then it shows up in git stash list):

git stash store -m "restored dropped stash" STASH_SHA
git stash pop

(git stash store is the porcelain for this; git update-ref refs/stash STASH_SHA does the same at the plumbing level.)

Step 5: Handle a pop that left conflicts

If a pop conflicted and you want to keep going (do NOT clean the tree — the stash is still in the list):

git checkout --theirs src/login.ts   # or resolve by hand, then:
git add src/login.ts
git stash drop                        # only after every conflict is resolved

Step 6: Recover untracked files removed by git clean

Untracked files deleted by git clean are not in Git’s object database, so reflog/fsck cannot bring them back. Your only options are editor local history or a filesystem snapshot:

# VS Code:  right-click the file (or its folder) > Open Timeline > pick a Local History entry
# JetBrains: right-click in Project view > Local History > Show History
# macOS:    enter Time Machine for the folder; Linux/ZFS: ls .zfs/snapshot/

How to confirm it’s fixed

git status   # your files show as modified / staged again
git diff      # the expected lines are back

If you re-registered the stash with git stash store, also confirm git stash list shows it before you pop or drop it.

Prevention

  • Prefer git stash apply over git stash pop — apply keeps the entry as a safety net even after a successful apply; drop it manually once you’re happy.
  • Name your stashes: git stash push -m "login form WIP: session token handling" so git stash list is readable.
  • Include untracked files: git stash push -u (or -a for ignored + untracked too) so new files aren’t left behind.
  • Dry-run before cleaning: git clean -nfd prints what would be deleted without deleting it.
  • Extend the recovery window: git config --global gc.reflogExpireUnreachable 90.days (the default for unreachable/dropped entries is 30 days; reachable reflog entries default to 90).
  • Use git worktree add ../login feature/login to work on several branches at once instead of stash-checkout-stash cycles.
  • For anything you’d be sad to lose, commit it instead of stashing: git commit -m "WIP: do not merge". Commits are reachable from a branch and easy to find with git log.

FAQ

Q: Does git stash pop delete my stash if it hits a merge conflict? A: No. Per the official docs, when the apply fails with conflicts the entry “is not removed from the stash list.” Check git stash list — it’s still there. Resolve the conflicts, then git stash drop manually. The usual cause of real loss is cleaning the conflicted tree by hand (e.g. git reset --hard).

Q: Can I recover a stash that was dropped more than 30 days ago? A: Maybe, if git gc hasn’t pruned the object yet (loose objects default to a 2-week prune grace period, but unreferenced stash objects often survive far longer until a gc runs). Try git fsck --unreachable --no-reflog and inspect each commit <sha> with git show. There’s no guarantee after garbage collection.

Q: I dropped the stash but git reflog stash is empty. Now what? A: A successful git stash drop removes the entry from the stash reflog, so the reflog won’t help. Use git fsck --no-reflog (Step 3) to find the dangling commit by its WIP on message, then git stash apply <sha>.

Q: How do I see a stash’s full diff without applying it? A: git stash show -p stash@{0} prints the full patch. Use --stat instead of -p for a file summary, or git stash show -p STASH_SHA for a recovered SHA.

Q: Why does my stash show up in one worktree but not another? A: refs/stash is per-worktree. Run git stash list from the directory of the worktree where you created the stash (git worktree list shows them all).

Q: Are stashes shared across clones or pushed to the remote? A: No. Stashes are purely local — git push/git fetch never touch refs/stash. To share WIP across machines, commit to a draft branch (git switch -c draft/my-feature) and push that.

Tags: #git #version-control #Troubleshooting