What is Git stash and when should you use it (vs. WIP commits, worktrees, autostash)
· Category: Git
Short answer
git stash snapshots your uncommitted working-tree and index changes onto a hidden stack, then resets the working tree to HEAD. It's a fast escape hatch when you need a clean tree right now (to switch branches, pull, or rebase) but aren't ready to commit.
git stash push -m "WIP: login form validation"
# or, since Git 2.13, just:
git stash -m "WIP: login form validation"
Restore with pop (which removes the entry from the stack on success) or apply (which keeps it):
git stash pop # restore the most recent and drop it
git stash apply stash@{2} # restore a specific entry, keep the stack
Reference: git-stash(1).
What stash does and doesn't capture by default
By default git stash saves:
- Modified tracked files (working tree)
- Staged changes (index)
By default it does not save:
- Untracked files (new files Git has never seen)
- Ignored files (matched by
.gitignore)
This is the #1 source of "where did my changes go?" reports — a brand new file you've been editing isn't tracked yet, so git stash leaves it sitting in the working tree. Use the right flag:
git stash -u # also stash untracked files (--include-untracked)
git stash -a # also stash ignored files (--all). Use sparingly.
-a is rarely what you want — it pulls in node_modules/, .venv/, build artifacts, everything. Reach for -u 95% of the time.
Steps for the canonical workflow
# 1. You're mid-feature, urgent bug report comes in.
git status
# modified: src/login.js
# modified: src/api.js
# ?? src/login.test.js <- untracked; will be missed without -u
# 2. Stash with a message and include untracked.
git stash push -u -m "WIP: login form validation"
# 3. Working tree is now clean — switch and fix the bug.
git switch main
git pull --ff-only origin main
git switch -c hotfix/csrf-token
# ... fix, commit, push, PR ...
# 4. Back to your feature branch.
git switch feature/user-login
# 5. Restore the stash. pop drops it from the stack on success;
# if there's a conflict, the stash is kept so you can retry.
git stash pop
# 6. If pop conflicts, resolve and then drop manually.
git stash drop
Inspecting the stash stack
git stash list
# stash@{0}: On feature/user-login: WIP: login form validation
# stash@{1}: On main: experimental cache layer
git stash show -p stash@{0} # show the diff
git stash show --stat stash@{0} # files-changed summary
stash@{N} references are positional and shift when you push/drop. Treat them as ephemeral — never paste a stash@{3} into a script you might run tomorrow.
Partial and selective stashes
Two common variants beyond plain git stash:
Stash everything except what's already staged
git add src/login.js # what you want to keep working on
git stash --keep-index # stash everything else; staged work stays
This is the canonical way to "test only the staged subset" — useful before running a pre-commit hook or running tests on the exact thing you're about to commit.
Stash a hunk-by-hunk subset
git stash push -p -m "stash only the api.js refactor"
-p (--patch) walks you through each hunk and asks Stash this hunk [y,n,q,a,d,/,e,?]?. Useful when one file got too many changes and you only want to set part aside.
When stash is the wrong tool
Stash is convenient but lossy in two ways: messages are easy to misplace in the stack, and stashes don't get pushed to remotes — a wiped laptop loses every stash. For anything you might want to recover next week, use one of these instead:
A WIP commit
git add -A
git commit -m "WIP: do not push" --no-verify
# later, when ready to clean up:
git reset HEAD~1 # undo the commit, keep the changes
# or, to amend it into the next real commit:
git commit --amend --no-edit
A WIP commit is a real commit on a real branch — git push works, git log shows it, you can rebase it. Most teams that have worked through "I lost three days to a corrupted stash" end up preferring this for anything beyond a few minutes' interruption.
git worktree for parallel branches
If you find yourself stashing every time the bug-bot pings you, you don't need stash — you need a second working tree:
git worktree add ../myrepo-hotfix main
cd ../myrepo-hotfix
git switch -c hotfix/csrf-token
# fix, commit, push. The original feature branch is untouched in the other directory.
git worktree list shows everything; git worktree remove ../myrepo-hotfix cleans up. See git-worktree(1).
rebase --autostash and pull --autostash
If you're stashing only because git rebase or git pull --rebase complained about uncommitted changes, the autostash option does it implicitly:
git pull --rebase --autostash
# or, set it once:
git config --global rebase.autoStash true
git config --global pull.rebase true
After this, git pull on a dirty tree just works.
Common issues
git stash pop left a conflict and the stash is gone.
It isn't — when pop hits a conflict it keeps the stash on the stack and exits non-zero. Run git stash list and you'll see stash@{0} is still there. Resolve the conflict, then git stash drop to remove the entry.
My untracked file disappeared after git stash.
You used git stash without -u, then later cleaned the working tree with git clean -fd or similar. Untracked files that weren't stashed and were then cleaned are gone — Git never knew about them. Always use git stash -u when you have untracked changes that matter.
git stash apply says "could not restore untracked files from stash".
Usually means a file with the same path now exists in the working tree, blocking restoration. Move or remove the conflicting path, then re-run git stash apply.
The stash is on the wrong branch.
git stash records which branch you stashed from (visible in git stash list) but it's a label, not a constraint — you can apply on any branch. To explicitly move work between branches, switch first, then apply: git switch other-branch && git stash apply stash@{0}.
My stash stack is full of unlabeled WIP on main.
You forgot to pass -m. Going forward, alias it: git config --global alias.stash-push '!f() { git stash push -u -m "$@"; }; f'. Old stashes can be inspected one at a time with git stash show -p stash@{N}; drop ones you don't need.
Tips
git stash branch newbranch stash@{2}creates a new branch from the commit the stash was based on and applies the stash on top — the cleanest way to "promote" an old stash to real branch when the original branch has moved on.- Stashes are stored as commits under
refs/stash. They survivegit gconly as long as the reflog entry survives — by default 90 days for unreachable objects (gc.reflogExpireUnreachable). Don't treat stash as long-term storage. - For separating commits you've already made (rather than uncommitted work), see how to amend a Git commit or how to cherry-pick a commit in Git.
- If you stashed the wrong thing and need to recover after
git stash drop, the stash commit is still ingit fsck --unreachablefor a while:git fsck --unreachable | grep commitand thengit stash apply <sha>.