Learning Objectives
By the end of this lesson you will be able to:
- Stash your changes with a message that still means something later
- Include untracked files deliberately rather than by accident
- Stash only some of your changes
- Read
git stash listand inspect a stash without restoring it
The Basic Command
git stash push -m "fixture rewrite, 2/3 done"
Saved working directory and index state On test/fixture-rewrite: fixture rewrite, 2/3 done
Your working directory is now clean, exactly as if you had just committed. The changes are on the stash stack.
git status
On branch test/fixture-rewrite
nothing to commit, working tree clean
You will also see the older form git stash save "message", and bare git stash with no arguments. push is the current spelling and the one that supports every option below. Bare git stash works but gives you an unnamed stash, which the next lesson will show you regretting.
Always Write a Message
Without -m, Git generates one from your last commit:
stash@{0}: WIP on test/fixture-rewrite: 8f3c2a1 Update config
That describes a commit you did not make changes to. It tells you nothing about what is in the stash.
With -m:
stash@{0}: On test/fixture-rewrite: fixture rewrite, 2/3 done — retry logic still missing
Now three days later you know whether to pop it or drop it. This costs eight seconds and it is the single most useful habit in this module.
What Gets Stashed by Default
This is the trap. By default git stash push takes:
- ✅ Modified tracked files
- ✅ Staged changes
- ❌ Untracked files — brand-new files Git has never seen
- ❌ Ignored files
So if your work-in-progress includes a new test file you have not yet run git add on, the default stash leaves it sitting in your working directory. Your tree is not actually clean, and switching branches carries that file along.
The fix:
git stash push -u -m "fixture rewrite including new helper"
-u (or --include-untracked) takes untracked files too. Use it whenever your work created new files, which for a tester writing tests is most of the time.
There is also -a (--all), which additionally stashes ignored files. That is rarely what you want — it sweeps up node_modules, build output and local .env files. Reach for -u, not -a.
Stashing Only Part of Your Work
By path, when the rest should stay in your working directory:
git stash push -m "just the config experiment" -- config/test-env.yaml
Interactively, choosing hunk by hunk:
git stash push -p
Git walks you through each change and asks whether to stash it. Useful, but slow — if you find yourself needing it often, your commits are probably too large.
Keeping the staged part in place, stashing only unstaged work:
git stash push --keep-index
The classic use is verifying a commit in isolation: stage exactly what you intend to commit, stash everything else with --keep-index, run the tests, and you have proven that this commit alone passes.
Reading the Stack
git stash list
stash@{0}: On test/fixture-rewrite: fixture rewrite, 2/3 done
stash@{1}: On main: quick check of timeout behaviour
stash@{2}: WIP on test/old-branch: 1a2b3c4 Initial smoke suite
It is a stack: stash@{0} is always the most recent, and every new stash pushes the others down. That is why stash@{1} today may be stash@{2} tomorrow — never write a stash reference in a note, write its message.
Each entry records which branch it came from, which is the detail that saves you when you find three of them.
Looking Inside Without Restoring
git stash show stash@{0}
tests/fixtures/user.js | 24 ++++++++++++------
config/test-env.yaml | 3 ++-
2 files changed, 18 insertions(+), 9 deletions(-)
The file list — usually enough to identify a stash.
git stash show -p stash@{0}
The full diff, if you need to be sure. Neither command changes anything.
Pro Tip: Stash immediately before switching branches, not "in a minute". The gap between deciding to stash and actually stashing is where people switch branches by reflex and end up carrying half a rewrite onto a hotfix.
Key Takeaways
git stash push -m "message"clears your working tree and saves the changes- Always write a message; the generated one describes your last commit, not your work
- The default skips untracked files — use
-uwhen your work created new files -aalso sweeps ignored files and is almost never what you want- Stash part of your work with a path argument,
-p, or--keep-index git stash listshows a stack wherestash@{0}is newest;git stash show [-p]inspects safely