Stashing work in progress
How to shelve half-done changes with git stash so you can switch branches instantly, then pick up exactly where you left off.
What you'll learn
- How git stash saves your working directory and index to a temporary stack — without creating a commit
- The difference between git stash pop and git stash apply, and when to choose each
- Why plain git stash silently ignores untracked files: use -u to include them
Before you start
The last lesson named the trap: merge, rebase, and switching branches all demand a clean working tree — yet real work is rarely clean when the interruption arrives. This lesson is the escape hatch. The shelf has a name, the stash, and once you know it, “I can’t switch, I have uncommitted changes” stops being a wall.
Why not just commit?
A half-done feature commit pollutes your history with a message like “WIP do not merge.” When you eventually push, that noise is permanent. A stash, by contrast, is local-only — it never leaves your machine unless you explicitly export it — and it carries no commit SHA in your branch history. Think of it as a personal sticky note on your desk, not a page in the permanent record.
The stash as a stack
Git stores stashes in a LIFO stack (last in, first out). Every time you run git stash, a new entry is pushed on top. Every time you pop, the top entry is removed. The entries are numbered stash@{0} (top), stash@{1}, stash@{2}, and so on. The number always reflects position in the stack at that moment — if you drop stash@{1}, what was stash@{2} becomes stash@{1}.
The typical flow
Here is the complete stash-switch-fix-return cycle you will use most often.
# You are on feature/payments, mid-change
git stash # shelf everything, restore a clean working dir
git switch hotfix/null-check # switch freely — working dir is clean
# ... make the urgent fix, commit it ...
git switch feature/payments # back to your branch
git stash pop # restore your stashed work and remove it from the stack
After git stash, your working directory looks exactly as it did after your last commit. After git stash pop, it looks exactly as it did before you stashed.
The flow below shows the stash stack during this sequence.
Core commands
Stash and inspect
git stash # stash tracked, modified files (index + working tree)
git stash list # view the whole stack
stash@{0}: WIP on feature/payments: 3f1a8c2 Add cart total
stash@{1}: WIP on feature/payments: 3f1a8c2 Add cart total
Each entry shows the branch it came from and the commit it was made against. Without names they can blur together fast — see Named stashes below.
Restore: pop vs apply
git stash pop # apply stash@{0}, then DROP it from the stack
git stash apply # apply stash@{0}, but KEEP it on the stack
Use pop when you are done with the stash and want the stack clean. Use apply when you want to replay the same changes on multiple branches — or when you are nervous and want the stash as a backup until you confirm everything looks right.
Include untracked files
Named stashes
When the stack grows past one entry, stash@{0} tells you nothing useful. Name them:
git stash push -m "payments: wip cart subtotal logic"
git stash push -m "payments: experiment with tax rounding"
git stash list
stash@{0}: On feature/payments: payments: experiment with tax rounding
stash@{1}: On feature/payments: payments: wip cart subtotal logic
Apply or drop a specific entry by its index:
git stash apply stash@{1} # apply without dropping
git stash drop stash@{1} # drop one entry
git stash clear # drop every entry — no undo
Quick reference
| Command | What it does |
|---|---|
git stash | Shelf tracked modifications, restore clean working dir |
git stash -u | Same, plus untracked files |
git stash push -m "msg" | Stash with a descriptive name |
git stash list | Show the full stack |
git stash pop | Apply top entry and remove it from stack |
git stash apply | Apply top entry, leave it on stack |
git stash drop stash@{n} | Remove one specific entry |
git stash clear | Wipe the entire stack |
In one breath
git stash shelves your modified tracked files onto a local LIFO stack and hands back a clean working directory — no WIP commit, no SHA in history. Switch branches, do the urgent thing, switch back, and git stash pop restores everything exactly (or git stash apply to restore but keep the entry, handy as a backup or to replay on several branches). Two traps: plain git stash silently skips untracked files — use -u to include them — and stashes are local-only, never pushed, cloned, or visible to teammates, so they vanish if you delete the repo. Name them with git stash push -m "..." the moment the stack grows past one, because stash@{0} tells you nothing.
Practice
Before the quiz, predict the outcome: you are mid-edit on feature with one modified tracked file and one brand-new untracked file scratch.py. You run a plain git stash, switch to main, fix a bug, switch back, and git stash pop. Which of the two files came back from the stash, where was the other one the whole time, and which single flag would have shelved both?
Quick check
A question to carry forward
Stop and notice something about everything you have built across these two chapters: commits, branches, merges, rebases, stashes — every last one lives on your machine alone. Unplug your laptop from the network and not one of them would be disturbed, because none has ever left. But the entire reason version control exists is the team — the colleague who needs your feature, the CI server that builds it, the teammate whose work you have to pull in. So how does your private, local repository actually talk to a shared one sitting on a server like GitHub — sending your commits up and bringing theirs down? That handshake is built on remotes, and it opens the next chapter.