Skip to content
Blog

Git Stash: A Practical Beginner's Guide

5 min read Git, Version control, CLI

git stash takes the changes you haven’t committed yet, puts them on a shelf, and gives you a clean working tree. Later you take them off the shelf and carry on. That’s it. You don’t need a throwaway commit, and nothing lands in your history.

I don’t use it every day, and I’d say that’s how it should be. But when I need it, nothing else does the job as cleanly. This guide covers the situations where it actually saved me, plus the handful of flags I wish someone had told me about earlier.

What git stash actually saves

By default, git stash saves:

  • changes to tracked files that you haven’t staged yet
  • changes you have staged with git add

It does not save:

  • untracked files (new files Git has never seen)
  • ignored files

That second list catches everyone at least once. You stash, switch branches, and your new file is still sitting there. See stashing untracked files below.

Use case 1: stashing before you switch branches

Say you’re on a feature branch called user-profile, halfway through changes across a few files. Then someone pings you: there’s a bug on main and it needs fixing now.

You don’t want to commit half-done work and clutter your history. So stash it:

git stash

Your working tree is clean again. Switch to main, fix the bug, commit, push. Then come back and restore your work:

git switch user-profile
git stash pop

git stash pop applies the stash and removes it from the stash list. git stash apply applies it but keeps it, which is handy if you want the same changes on more than one branch.

Use case 2: keeping several stashes with names

Sometimes you’ve got two half-finished things going. Stash each one with a message so you can tell them apart later:

git stash push -m "Task: implement payment gateway"
git stash push -m "Feat: table view in dashboard"

You’ll see older tutorials (including an older version of this post) use git stash save "message". That still works, but it’s deprecated. git stash push -m is the current way, and it also lets you stash specific files.

Now list them:

git stash list
stash@{0}: On user-profile: Feat: table view in dashboard
stash@{1}: On user-profile: Task: implement payment gateway

The newest stash is always stash@{0}. To bring back a specific one, use its index:

git stash apply stash@{1}

Or find it by a word in its message, so you don’t have to count:

git stash apply "stash^{/payment}"

The quotes matter. zsh and PowerShell both do their own thing with curly braces, and you’ll get a confusing error without them.

Use case 3: stashing when a pull conflicts

This happened to me a few times while building the Moksha Innovision website with my teammate Harsh. We’d both be editing the same files, I’d try to git pull, and Git would refuse because my local changes would be overwritten.

The fix is to stash, pull, then put your changes back:

git stash
git pull origin main
git stash pop

If your changes and the pulled changes touch the same lines, pop stops with a conflict, just like a merge. Open the files, fix the conflict markers, then:

git add .
git stash drop

When git stash pop hits a conflict, it does not drop the stash, so your changes are still safe on the shelf. That’s why you drop it yourself once everything’s resolved.

If you do this dance a lot, Git can do it for you:

git pull --rebase --autostash

Or turn it on for good:

git config --global rebase.autoStash true

Stashing untracked files

As mentioned, new files aren’t stashed by default. Add -u (short for --include-untracked):

git stash -u

If you also want ignored files (build output, .env, and so on), use -a (--all). I rarely want that, so be careful with it.

Stashing only some files

You don’t have to stash everything. Pass paths after --:

git stash push -m "only the api changes" -- src/api/

Or pick changes interactively, hunk by hunk:

git stash -p

On Git 2.35 and newer, you can also stash just what’s staged:

git stash push --staged

Looking inside a stash before applying it

Not sure what’s in stash@{1}? Check before you apply it:

git stash show stash@{1}      # which files changed
git stash show -p stash@{1}   # the full diff

Turning a stash into a branch

If the stash turns out to be a real piece of work, make it a branch:

git stash branch payment-gateway stash@{1}

This creates a branch from the commit you were on when you stashed, applies the stash there, and drops it if everything applies cleanly. It’s also the easiest way out when a stash won’t apply on your current branch because too much has changed.

Cleaning up

Stashes pile up quietly. Every now and then, clean them out:

git stash drop stash@{0}   # remove one
git stash clear            # remove all of them

git stash clear doesn’t ask for confirmation. Run git stash list first.

Recovering a stash you dropped by accident

It happens. A dropped stash is still a commit object for a while. It’s just not referenced anymore. You can usually find it:

git fsck --no-reflog | awk '/dangling commit/ {print $3}'

Check the hashes with git show <hash> until you find yours, then:

git stash apply <hash>

Quick reference

CommandWhat it does
git stashStash tracked changes (staged and unstaged)
git stash push -m "msg"Stash with a name
git stash -uAlso stash untracked files
git stash listShow all stashes
git stash show -p stash@{n}Show the diff of a stash
git stash apply stash@{n}Apply a stash, keep it in the list
git stash popApply the latest stash and remove it
git stash branch <name>Create a branch from a stash
git stash drop stash@{n}Delete one stash
git stash clearDelete every stash

When not to use stash

A stash is a shelf, not storage. If you’re parking work for more than a day or two, a work-in-progress commit on its own branch is safer. It has a name, it shows up in git log, and you can push it. If you regularly need two branches checked out at once, look at git worktree instead.

For the quick “hold on, I need to do something else first” moments, though, git stash is exactly the right tool.

Written by Anit Jha, a DevOps, tools and automation engineer at Apple. Found a mistake? Tell me on X or LinkedIn.

Keep reading

All posts