Git95 min total · 17 parts
Git Internals and Workflows: Branching, Merging, and Rebasing
Part 12 of 17 · ~3 min
Stashing Work in Progress
Back up to the moment the clinic called about payroll. You were mid-edit on the swap form, with a passing test suite you did not want to disturb and a fix that needed to go out on a different branch immediately.
Committing half a form to your feature branch just to get it out of the way is a bad answer — now there is a junk commit to clean up later. git stash is the right one:
git stash push -m "half-done swap form"
Saved working directory and index state On feature/shift-swap: half-done swap form
Your working tree is now clean, matching the last commit exactly, and your two hours of work are stored safely off to the side. You go fix payroll, cherry-pick it onto release/2.2, come back, and:
git switch feature/shift-swap
git stash pop
Everything is where you left it.
The two ways to retrieve a stash differ in one respect worth knowing:
git stash pop # apply the most recent stash and delete it from the list
git stash apply # apply it and LEAVE it in the list
apply is the safer default when you are uncertain — if the changes land somewhere unexpected, or conflict with what has changed on the branch since, the stash is still there to try again. It is also how you deliberately apply the same shelved work to more than one branch.
Stashes form a stack, and you can address any entry directly:
git stash list
# stash@{0}: On feature/shift-swap: half-done swap form
# stash@{1}: On main: debugging the export loop
git stash show -p stash@{1} # see exactly what is in one, as a diff
git stash apply stash@{1} # restore that specific one
git stash drop stash@{1} # discard it
There is a default that surprises people, and it produces a bug that looks like a ghost. A plain git stash does not include untracked files. New files you have created but never git added stay exactly where they are, in the working tree, following you onto every branch you switch to — which is how you end up with a mysterious swap.test.js appearing on main and a test run that fails for no visible reason.
git stash push -u -m "half-done swap form" # -u (--include-untracked) takes new files too
git stash push -a -m "everything" # -a (--all) also takes ignored files
Use -u by default. The times you want untracked files left behind are rarer than the times you want a genuinely clean tree.
Under the hood, a stash is stored as a commit — actually two or three, tying together your working tree, your index, and the commit you were sitting on. That is why git stash show -p can hand you a diff, why entries survive branch switches and reboots, and why a dropped stash can sometimes be recovered from the reflog. It also means the stash is purely local: it is not pushed, not fetched, and not backed up by anything. A stash you have been carrying for three weeks is work that exists in exactly one place on earth. Land it or commit it to a branch.