Git Stash Explained: Save, List, Apply and Pop Changes

git stash takes your uncommitted changes, saves them on a stack inside the repository, and resets your working directory to the last commit so you can switch tasks. Run git stash to put work away and git stash pop to bring it back.

git stash        # save tracked changes and clean the working directory
git stash pop    # reapply the most recent stash and remove it from the list

What git stash actually saves

The Git documentation describes it as saving "your local modifications away" and reverting "the working directory to match the HEAD commit". In practice that means two things get stored: changes to tracked files you haven't staged, and changes you have staged with git add. What it does not save by default:

  • Untracked files, meaning new files you never added. They stay where they are.
  • Ignored files (anything matched by .gitignore).

Here is a real session with one modified file and one new file:

$ git status --short
 M app.py
?? notes.txt
$ git stash
Saved working directory and index state WIP on main: 8c820ff init
$ git status --short
?? notes.txt

app.py went into the stash; notes.txt was left behind because Git wasn't tracking it. The message WIP on main: 8c820ff init is the default label: the branch and the commit you were on.

How to stash untracked files too

git stash -u     # also stash untracked files (--include-untracked)
git stash -a     # also stash untracked AND ignored files (--all)

Both options remove the files from the working directory after saving them, the same way git clean would. Be careful with -a in projects with large ignored folders such as node_modules or build output: they all go into the stash.

How to see what is in your stash

git stash list

Example output:

stash@{0}: On main: with untracked
stash@{1}: WIP on main: 8c820ff init

stash@{0} is always the most recent entry and stash@{1} the one before it. Git also accepts the bare number, so git stash show 1 means stash@{1}. To see the actual changes inside an entry, add -p:

$ git stash show -p stash@{1}
diff --git a/app.py b/app.py
--- a/app.py
+++ b/app.py
@@ -1 +1,2 @@
 line1
+wip

Without -p you get a summary of changed files and line counts. Add -u to git stash show to include the untracked files stored in the entry.

git stash pop vs git stash apply

CommandReapplies changesRemoves the entryUse it when
git stash popYesYes, if it applied cleanlyYou're done with the stash
git stash applyYesNoYou want to apply the same changes to several branches, or keep a backup
git stash dropNoYesYou applied with apply and want to clean up, or you don't need the changes
git stash clearNoAll entriesYou want an empty stash list

All of them take an optional entry: git stash pop stash@{1}, git stash drop stash@{2}. Without one, they use stash@{0}.

One detail catches people out: by default, applying a stash does not restore what was staged. Files you had added with git add come back as ordinary unstaged changes. Add --index (git stash pop --index) to restore the staged state as well.

How to name a stash

git stash push -m "half-done login form"

The message replaces the generic WIP on main label in git stash list, which matters once you have more than two entries. The older form git stash save "message" still works, but the Pro Git book notes it is being deprecated in favour of git stash push.

How to stash only one file (or only staged changes)

Give git stash push a path after --:

git stash push -m "only config" -- config.yaml

Only files matching the path are stashed and reset; everything else stays in your working directory. The -- separator is required when you leave out the word push, to stop a mistyped subcommand from creating a stash.

Other ways to stash part of your work:

  • git stash --staged (or -S) stashes only what is currently staged.
  • git stash --keep-index stashes everything but leaves staged changes in place too, which is handy for testing exactly what you're about to commit.
  • git stash -p asks hunk by hunk what to stash.

What happens when git stash pop has a conflict

If the branch has changed the same lines since you stashed, pop stops with a conflict. This is real output from Git 2.45:

$ git stash pop
Auto-merging app.py
CONFLICT (content): Merge conflict in app.py
On branch main
Unmerged paths:
  (use "git restore --staged <file>..." to unstage)
  (use "git add <file>..." to mark resolution)
	both modified:   app.py

no changes added to commit (use "git add" and/or "git commit -a")
The stash entry is kept in case you need it again.

The file now contains conflict markers labelled Updated upstream (what's committed) and Stashed changes (your stash):

<<<<<<< Updated upstream
COMMITTED
=======
STASHED
>>>>>>> Stashed changes

Edit the file to the version you want, remove the markers, then run git add app.py. Because the pop didn't finish cleanly, the entry is still in the list (the last line of the output says so), so remove it yourself with git stash drop once you're happy. The full process for editing conflict markers is in how to resolve merge conflicts in Git.

If the conflicts are extensive, an easier route is to apply the stash on a fresh branch starting from the commit where you made it:

git stash branch fix-wip stash@{0}

That creates fix-wip at the original commit, applies the stash there (where it can't conflict), and drops the entry if it succeeded. You can then commit and merge the branch normally.

How to recover a stash you dropped

When you drop or pop a stash, Git prints the commit hash of the entry:

Dropped refs/stash@{0} (949c3bc96b803f5962b7b51c525ed4e570974dd1)

If that hash is still in your terminal, the stash is recoverable until Git garbage-collects it:

git stash apply 949c3bc96b803f5962b7b51c525ed4e570974dd1

If the hash is gone, the Git documentation suggests listing unreachable stash commits (run in Git Bash, macOS or Linux):

git fsck --unreachable | grep commit | cut -d' ' -f3 | xargs git log --merges --no-walk --grep=WIP

Each stash is stored as a merge commit, so this lists candidates with their messages. The --grep=WIP filter only finds stashes with the default label; drop it to see named ones as well. The documentation warns that entries removed with git stash clear "may be impossible to recover", so treat the stash as a short-term shelf, not storage.

When to use a commit instead

A stash has no branch, isn't pushed to the remote, and is easy to forget. If the work will sit for more than a day, a commit on its own branch is safer: git switch -c wip-login, git commit -am "WIP", and later undo that commit with git reset HEAD~1 if you don't want it in history (see how to undo the last commit). Stash is the right tool for short interruptions: pulling a teammate's fix, switching to review a branch, or getting a clean tree for a quick test.

Frequently asked questions

Does git stash save untracked files?
No. By default git stash only saves changes to tracked files, staged or not. New files that were never added stay in your working directory. Use git stash -u (--include-untracked) to stash them too, or git stash -a (--all) to also include ignored files.
Is a stash shared when I push to a remote?
No. Stashes live in your local repository under refs/stash and are not pushed with git push. If you need to move unfinished work to another machine, commit it on a branch and push the branch instead.
Can I stash changes on one branch and apply them on another?
Yes. A stash is not tied to the branch it was made on. Switch branches and run git stash pop or git stash apply; Git applies the changes to whatever is checked out, and reports a conflict if the files differ too much.
What is the difference between git stash and git stash push?
Plain git stash is shorthand for git stash push. Use the explicit push form when you want options such as -m for a message or a pathspec to stash only some files. The older git stash save is deprecated in favour of push.

Sources