You’re halfway through a change when someone asks you to look at a production bug on main. git stash clears your working tree in one command, you switch branches, fix the bug, come back and git stash pop. That’s what stash is for, and it’s great at it.
The trouble starts when stash gets used as storage. Three weeks later, git stash list shows seven entries all called “WIP on main,” one of them contains the only copy of a half-finished migration, and popping it conflicts with everything that changed since. Interviewers ask about stash to see whether you know its edges: what it doesn’t save, what happens on conflict, and when a commit on a branch is the better tool.
What a plain git stash saves, and what it skips
In a test repository with one modified tracked file and one new file:
$ git status --short
M config.ini
?? retry.py
$ git stash
Saved working directory and index state WIP on main: cd06efa Initial
$ git status --short
?? retry.pyThe modification to config.ini was stashed. The new file retry.py wasn’t, because it’s untracked, and plain git stash only saves changes to tracked files (staged and unstaged). If you now switch to another branch and start editing, retry.py comes along, and it’s easy to commit it to the wrong branch or delete it while “cleaning up.”
To include untracked files, use -u (--include-untracked). -a (--all) also includes ignored files, which is rarely what you want because it sweeps up node_modules and build output. And name it:
$ git stash push -u -m "retry: raise limit, add helper"
Saved working directory and index state On main: retry: raise limit, add helper
$ git status --short
$ git stash list
stash@{0}: On main: retry: raise limit, add helperA clean working tree now, and a stash entry you’ll recognise later. Unnamed stashes are labelled with the branch and the commit subject they were made on, so several stashes made on main on the same day are indistinguishable without git stash show -p stash@{2}.
One more subtlety: stash records what was staged separately, but git stash pop doesn’t restore that split unless you pass --index. Without it, everything comes back as unstaged changes. If you’d carefully staged part of a file with git add -p, that work is lost (not the changes, just the staging).
When pop conflicts
A stash stores your changes relative to the commit you made it on. While it sits there, the branch moves on. In the test repository, someone commits a change to the same line:
$ echo "limit = 15" > config.ini && git commit -qam "Set limit to 15 for load test"
$ git stash pop
Auto-merging config.ini
CONFLICT (content): Merge conflict in config.ini
...
The stash entry is kept in case you need it again.
$ git stash list
stash@{0}: On main: retry: raise limit, add helper
$ git status --short
UU config.ini
?? retry.pyTwo things to notice. First, pop normally applies and then drops the stash, but on conflict it keeps the stash. That’s protective, but people often don’t read the message, resolve the conflict, carry on, and leave a stale stash in the list that they later pop again by accident, re-applying old changes. After resolving, check git stash list and git stash drop the entry deliberately.
Second, the working tree is now in a half-merged state with conflict markers in config.ini, and there’s no git stash pop --abort. To back out, you have to reset the files yourself (git checkout -- config.ini or git reset --hard if nothing else is uncommitted), and the stash is still there to try again. That’s why it’s worth using git stash apply rather than pop when you expect trouble: apply never drops the entry, so you can retry freely and drop it only when you’re satisfied.
git stash branch: apply where it was made
When a stash is old and the branch has moved, there’s a cleaner option. git stash branch <name> creates a new branch at the exact commit the stash was made on, applies the stash there, and drops it if that succeeds:
$ git stash branch retry-work stash@{0}
Switched to a new branch 'retry-work'
On branch retry-work
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: config.ini
Untracked files:
(use "git add <file>..." to include in what will be committed)
retry.py
no changes added to commit (use "git add" and/or "git commit -a")
Dropped stash@{0} (3362eb63d3d208c0d3d9357bd650051510244a34)No conflict, because the stash applies to the base it was made on. Now the work is on a real branch. Commit it, then merge or rebase onto main and handle the conflict the normal way, with the other commit’s message and history in front of you. This turns a fragile stash into ordinary Git work.
Why stash is a poor place to keep work
Even when it applies cleanly, stash has properties that make it a bad long-term home:
- Local only. Stashes aren’t pushed. A laptop failure or a fresh clone loses them, and teammates can’t see or pick up the work.
- No context. No branch, no history, often no name. Six weeks later nobody remembers what
stash@{4}was for. - Easy to destroy.
git stash clearremoves all of them, andgit stash dropon the wrong index removes one. They can be found again withgit fsckfor a while (covered in the reflog recovery guide), but that’s not a plan. - Drifts. The longer a stash waits, the more likely it conflicts, as shown above.
So the rule of thumb: if you’ll come back within the hour, stash is fine. If it might be tomorrow, commit it on a branch, even as wip: retry helper, tests missing. You can push that branch as a backup and tidy the commit later with git commit --amend or an interactive rebase before anyone reviews it.
Worktrees for real context switches
If the interruption is a bigger job, like reviewing a colleague’s branch or fixing a production bug that’ll take an afternoon, git worktree avoids touching your current work at all:
git worktree add ../shop-hotfix main
cd ../shop-hotfix # a second checkout sharing the same repositoryYour original directory keeps its uncommitted changes exactly as they were, even mid-merge, and you work on the hotfix in the other directory. When you’re done, git worktree remove ../shop-hotfix. Stash isn’t involved, so nothing can conflict on the way back.
Interview practice
A developer says: “I keep about ten stashes going, one for each idea I’m trying. Yesterday I popped one and got conflicts, so I ran git reset --hard to start over, and now I can’t find my changes.” What happened, can the work be recovered, and what would you suggest instead?
Answer
The pop hit a conflict, so Git applied what it could and kept the stash entry. Then git reset --hard discarded the half-applied changes in the working tree. If the stash was kept, the work is still in git stash list and nothing is lost; they can retry with git stash branch tmp stash@{n}, which applies it on its original base commit without conflicts. If they’d already dropped it while experimenting, the stash commit is unreachable but usually still in the object database: git fsck --no-reflogs --unreachable lists unreachable commits, and stash commits have subjects like “WIP on main” or the custom message, so they can be found and applied with git stash apply <sha> before garbage collection removes them.
For the future: ten parallel ideas are ten branches, not ten stashes. Each idea gets a branch with a WIP commit, which has a name, a base commit, history, and can be pushed as a backup. Stash stays for quick interruptions of minutes. For longer interruptions, use git worktree add so the current work isn’t touched at all. And when a stash is old, use git stash branch or git stash apply instead of pop, so a conflict never costs the stash itself.