The most common use of git stash looks like this: you're mid-feature, someone asks for a hotfix on main, you run git stash, switch branches, fix the issue, come back, run git stash pop, and carry on. That workflow is completely valid — and it uses about 10% of what stash can do.
What stash actually is
git stash takes your uncommitted changes — both staged and unstaged by default — and stores them as a pair of commits on a hidden refs stack (refs/stash). Each stash entry is a full snapshot: a merge commit whose parents are the working tree state (index and untracked changes) and the current HEAD. You get the stack for free; each git stash call pushes a new entry, and git stash pop or git stash apply restores from the top.
The critical distinction between pop and apply: pop removes the stash entry after applying it; apply leaves it on the stack. If you want to apply a stash to multiple branches (e.g., test the same in-progress changes in two contexts), apply is what you want.
Named stashes
The default stash message is a timestamp and commit hash, which tells you nothing when you're looking at a stack of five entries three days later. Give them names:
git stash push -m "wip: refactor auth middleware"
Now git stash list shows readable entries:
stash@{0}: On main: wip: refactor auth middleware
stash@{1}: On feature/payments: half-done checkout form
Restore a specific one by index without touching others:
git stash apply stash@{1}
Partial stash: the one people don't know exists
If you have changes across multiple files and only want to stash some of them — because one set of changes is ready to commit and another isn't — you have two options:
By file:
git stash push path/to/file another/file
This stashes only the listed files and leaves everything else in your working tree, staged or unstaged as it was.
By hunk (patch mode):
git stash push -p
This drops into the same interactive hunk selection as git add -p: you step through each change chunk and choose y (stash this hunk), n (leave it), or s (split into smaller hunks). Invaluable for the common situation where you've been building two unrelated things in parallel in the same file and want to cleanly separate them.
Stashing untracked and ignored files
By default, git stash ignores untracked files (files that have never been git add'd). If you want new files included in the stash:
git stash push -u # include untracked
git stash push -a # include untracked AND gitignored files
The -a flag is rarely what you want (it stashes build artifacts, .env files, whatever's in your working tree that git ignores) but -u is often needed when new files are part of the work-in-progress you're trying to set aside.
Creating a branch from a stash
If a stash has been sitting long enough that the branch it was made on has diverged significantly, applying it might hit conflicts. The clean solution:
git stash branch new-branch-name stash@{0}
This creates a new branch starting at the commit where the stash was made, applies the stash there (where it will apply cleanly, since it was branched from that exact point), and drops the stash entry on success. You get a clean working tree on a new branch with all the stashed changes applied.
The git stash show command
Before applying a stash you haven't touched in a while, check what's in it:
git stash show stash@{1} # file list with +/- counts
git stash show -p stash@{1} # full diff
This gives you a clear diff of what the stash contains — exactly what will be applied to your working tree — before you commit to it.
Clearing the stack
If you've accumulated a pile of old stash entries:
git stash drop stash@{2} # remove one specific entry
git stash clear # remove all entries
clear is permanent — stash entries are not tracked by git's reflog by default (they can be with --include-untracked), so once cleared they're gone. Use it when you're sure.
Git reference when you need it
For the broader git command set, our Git Cheatsheet covers the full command reference with flags — searchable and organized by workflow phase. And if you're dealing with stash conflicts that left conflict markers in your files, the Git Conflict Remover strips the <<<<<<</=======/>>>>>>> markers cleanly so you can start fresh from either side.
