Stashing Changes

7 min read

You're halfway through writing a checkout regression test. The file is messy, the test doesn't run yet, and you definitely don't want to commit it. Then a developer pings you: "Can you pull main and run the smoke tests against this hotfix?" You can't switch branches with those uncommitted changes — Git might warn, refuse, or worse, leak them onto the wrong branch. The clean answer is git stash: save your half-done work to a hidden shelf, get a clean working directory, do the urgent thing, and pop the work back exactly where you left it.

What stashing actually does

git stash takes every uncommitted change in your working directory and staging area, packages them into an entry on a hidden stack, and resets your working directory to a clean state matching the last commit. The changes aren't gone — they live in .git/stash until you ask for them back. Switching branches, pulling, merging, even checking out an old commit all become safe.

The two-line workflow

The bare-minimum sequence:

git stash
# ...do whatever urgent thing required a clean working tree...
git stash pop

Run git stash:

git stash
Saved working directory and index state WIP on feature/checkout-tests: 4c48901 Add cart fixture

git status confirms a clean tree:

git status
On branch feature/checkout-tests
nothing to commit, working tree clean

You can now git switch main, git pull, run anything you like. When you're back on the original branch:

git stash pop
On branch feature/checkout-tests
Changes not staged for commit:
        modified:   cypress/e2e/checkout.spec.js
Dropped refs/stash@{0} (a8f2c91...)

Your half-finished test is back exactly as you left it. Pop removes the stash entry as it applies it; git stash apply applies but keeps the entry on the stack.

Naming a stash

The default message ("WIP on feature/...") is forgettable when you have several stashes. Add a description:

git stash push -m "WIP: search tests, debounce assertion incomplete"

(git stash save "..." is the older syntax — it still works, but push -m is the modern form.)

Multiple stashes

You can stash multiple times. List them:

git stash list
stash@{0}: On feature/checkout-tests: WIP: search tests, debounce assertion incomplete
stash@{1}: On feature/login-tests: WIP: invalid-password fixture
stash@{2}: On main: WIP: tweak cypress.config.js timeout

The newest stash is always stash@{0}. Apply a specific one:

git stash apply stash@{2}

Or pop a specific one:

git stash pop stash@{1}

Drop one without applying:

git stash drop stash@{0}

Wipe the whole stack (irreversible — be sure):

git stash clear

Inspecting a stash without applying

Curious what's in a stash before deciding? git stash show and git stash show -p:

git stash show stash@{0}
 cypress/e2e/checkout.spec.js | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

Add -p for the full patch:

git stash show -p stash@{0}

You see the diff exactly as it would land if you popped.

Stash vs commit — when to use which

Both park work somewhere safe; they're suited to different intentions:

  • Stash = "I'll be back in five minutes." Useful for genuine interruptions where the work isn't worth a commit message. The stash stack is a personal scratchpad.
  • Commit (with a WIP: prefix) = "This is a meaningful checkpoint." If you're stopping for the day, switching computers, or pushing to a backup remote, commit. git commit -m "WIP: search tests — debounce assertion not yet passing" is a perfectly valid checkpoint that you can amend (Chapter 4, Lesson 4) or squash later.

A useful rule: stash for minutes, commit for hours. If the work has lived in a stash for more than a workday, turn it into a WIP: commit before something happens to your machine.

A real QA scenario

Mid-afternoon. You're three test cases into feature/checkout-tests. None of them pass yet — you're still building the page object. Your tech lead messages: "Hotfix going out in ten minutes — can you pull main and run smoke?"

git status                                          # confirm dirty tree
git stash push -m "WIP: checkout cases, page object half-built"
git switch main
git pull
npx cypress run --spec "tests/smoke/**"             # smoke suite
# ...all green, hotfix ships...
git switch feature/checkout-tests
git stash pop
git status                                          # back where you were

Total context cost: 30 seconds. No half-baked work merged anywhere it shouldn't be, no uncommitted edits lost.

The full stash flow

Step 1 of 6

Working with uncommitted changes

Mid-task on a feature branch — files edited but not committed.

Stashing untracked files

By default, git stash only stashes tracked files. Brand-new files (untracked) stay in the working directory. To include them:

git stash push -u -m "WIP: includes new fixture file"

-u = include untracked. There's also -a (--all) which includes ignored files too — almost never what you want.

⚠️ Common mistakes

  • Forgetting which branch a stash belongs to. git stash list shows the branch each stash was created on, but git stash pop applies to whatever branch you're currently on. Pop a checkout-tests stash on top of a login-tests branch and you may get a conflict-laden mess. Always confirm your branch (git status) before popping.
  • Treating stash as long-term storage. Stashes are stored in .git/stash, which is local to your machine — they don't push, don't sync, don't survive a rm -rf or a wiped laptop. Anything you'd cry about losing belongs in a commit, not a stash.
  • Not naming stashes. Three days later, stash@{2}: WIP on feature/foo: 4c48901... tells you nothing about what's inside. Always git stash push -m "..." with a description so future-you can actually choose the right one.

🎯 Practice task

Run a full interruption-and-resume cycle. 15-20 minutes.

  1. In your qa-sandbox repo (or any practice repo), make at least one edit to a tracked file. Don't commit. Confirm with git status that the file is modified.
  2. Run git stash push -m "WIP: my first stash". Run git status again — confirm the working tree is clean.
  3. Run git stash list. Confirm your stash is there with its message.
  4. Switch to another branch (or stay on the same one and pretend), do something — even just ls — and switch back.
  5. Run git stash pop. Confirm the change is restored and the stash is gone from git stash list.
  6. Multiple stashes: make two different edits, stashing each with a unique message: WIP: change A and WIP: change B. Run git stash list. Apply only the older one with git stash apply stash@{1}.
  7. Stretch: create a brand-new untracked file. Run git stash push -m "no-u test" — confirm git status still shows it (untracked files aren't stashed by default). Now run git stash push -u -m "with -u" — confirm the file is gone. Pop and confirm it returns.

The next lesson moves from saving work to inspecting it: log, diff, and blame — the three commands that turn Git from a black box into a readable record.

// tip to track lessons you complete and pick up where you left off across devices.