Blog / Coding tips

Git Stash Pop vs Apply: The Difference (and Conflicts)

The difference between git stash pop and git stash apply is what happens to the stash afterwards. git stash apply puts your saved changes back into your working folder and keeps the stash in the list, so you can apply it again later. git stash pop puts the changes back and then deletes the stash, so it's the same as running git stash apply followed by git stash drop. There's one important exception: if pop runs into a merge conflict, Git does not delete the stash, and you have to drop it yourself once you've fixed the conflict. If you're unsure, use apply. It's the safer of the two, because nothing is thrown away.

Below I show both commands in a real repository, then everything that tends to go wrong: conflicts, how to abort a conflicted pop, popping a specific stash instead of the latest one, stashing one file, untracked files, and how to get back a stash you dropped by mistake. Every command was run with Git 2.47 in a throwaway repo, and the output is copied straight from the terminal. I fixed the author name and dates so the output is repeatable, so your commit hashes will be different from mine.

git stash pop vs apply, side by side

The test repo has one commit containing style.css (black text) and index.html. I changed the colour to navy, stashed it, and then got it back with each command in turn:

$ git status --short
 M style.css

$ git stash
Saved working directory and index state WIP on main: 6a20ca4 First commit

$ git status --short

$ git stash list
stash@{0}: WIP on main: 6a20ca4 First commit

# apply: changes come back, stash stays
$ git stash apply
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   style.css

no changes added to commit (use "git add" and/or "git commit -a")

$ git stash list
stash@{0}: WIP on main: 6a20ca4 First commit

# throw the working copy away and try pop
$ git restore style.css

$ git stash pop
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   style.css

no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (5cc63368befec6f7a909497b31bba70e36148043)

$ git stash list

$ cat style.css
body { color: navy; }

Here's what that shows, step by step:

  1. git stash saved the change and gave me a clean working folder. git status --short printed nothing.
  2. git stash list showed one entry, stash@{0}. Stashes are numbered from 0, newest first.
  3. git stash apply brought the navy colour back, and the stash was still in the list afterwards.
  4. I threw the change away with git restore and ran git stash pop. The change came back again, and this time Git printed Dropped refs/stash@{0} (5cc6336...). The list was empty.

So the summary is:

  • git stash apply: restores the changes, keeps the stash. Use it when you want to apply the same changes to more than one branch, or when you're not sure the apply will go well.
  • git stash pop: restores the changes and removes the stash if it applied cleanly. Use it for the everyday "stash, switch branch, come back, unstash" workflow.
  • git stash drop: removes a stash without applying it. After a successful apply, run git stash drop to tidy up, and you've done exactly what pop does in two safer steps.

Notice the long hash in the Dropped line. Keep it in mind, because it's how you get a stash back if you pop or drop one by accident. That's covered near the end.

Which should you use: pop or apply?

I use pop most of the time, because the list stays short and I don't end up with ten stashes called "WIP on main" that I can't tell apart. I switch to apply in two cases: when I want the same change on two branches (apply on one, switch, apply again), and when I'm unstashing onto a branch that's moved on a lot since I stashed, where a conflict is likely. If a conflict does happen, you'll want that stash to still exist, and with apply there's no doubt that it does.

What happens when git stash pop has a conflict

A conflict happens when the stash and your current branch both changed the same lines. To show it, I stashed the navy colour, then committed a different change to the same line (dark red) as if a teammate's work had landed, and then ran git stash pop:

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

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

$ git stash list
stash@{0}: WIP on main: 6a20ca4 First commit

$ git status --short
UU style.css

$ cat style.css
<<<<<<< Updated upstream
body { color: darkred; }
=======
body { color: navy; }
>>>>>>> Stashed changes

# fix it: keep navy, then mark it resolved
$ printf 'body { color: navy; }\n' > style.css

$ git add style.css

$ git stash drop
Dropped refs/stash@{0} (5cc63368befec6f7a909497b31bba70e36148043)

$ git restore --staged style.css

$ git status --short
 M style.css

The key lines are CONFLICT (content): Merge conflict in style.css and The stash entry is kept in case you need it again. Git stopped with exit code 1, the file is marked UU (unmerged, both modified), and git stash list still shows stash@{0}. This is the moment people get caught out: they assume pop always deletes the stash, fix the conflict, carry on, and a week later find an old stash that still applies the same change.

How to resolve a stash pop conflict

  1. Open the file and pick the version you want. Everything between <<<<<<< Updated upstream and ======= is your branch; everything between ======= and >>>>>>> Stashed changes is the stash. Delete the markers and keep the right code.
  2. Run git add style.css to mark it as resolved.
  3. Run git stash drop to remove the stash, because pop didn't do it for you.
  4. Optionally run git restore --staged style.css. Resolving a conflict leaves the file staged, while a clean pop leaves it unstaged, so this puts things back the way you'd expect.

How to abort a git stash pop

If the conflict is a mess and you'd rather go back to how things were before the pop, use git reset --merge:

$ git status --short
UU style.css

$ git reset --merge

$ git status --short

$ git stash list
stash@{0}: WIP on main: 6a20ca4 First commit

The conflicted file went back to the committed version, and the stash is still safe in the list, so you've lost nothing. You can then try again on a different branch, or use git stash branch (below). Be careful with this if you had other uncommitted changes before the pop. git reset --merge tries to keep changes that weren't part of the merge, but commit or stash them first if they matter.

"Your local changes would be overwritten"

If you have uncommitted changes to a file that the stash also touches, Git won't even try:

$ git stash pop
error: Your local changes to the following files would be overwritten by merge:
    style.css
Please commit your changes or stash them before you merge.
Aborting
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   style.css

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

$ git stash list
stash@{0}: WIP on main: 6a20ca4 First commit

Nothing was changed and the stash was kept. Commit your current changes, or stash them too, and then pop again.

Use git stash branch for a stash that won't apply

If the branch has moved on so much that the stash keeps conflicting, git stash branch <name> creates a new branch from the commit you were on when you made the stash, applies the stash there, and drops it if that worked:

$ git stash branch navy-theme
Switched to a new branch 'navy-theme'
On branch navy-theme
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   style.css

no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (5cc63368befec6f7a909497b31bba70e36148043)

$ git branch --show-current
navy-theme

$ git stash list

No conflict this time, because the stash is applied to the exact code it was made from. You can commit it on the new branch and merge or rebase it properly later.

Pop or apply a specific stash

git stash pop and git stash apply use the newest stash, stash@{0}, unless you say otherwise. Once you have more than one, give them names with git stash push -m so you can tell them apart:

$ git stash push -m "navy text"
Saved working directory and index state On main: navy text

$ git stash push -m "welcome paragraph"
Saved working directory and index state On main: welcome paragraph

$ git stash list
stash@{0}: On main: welcome paragraph
stash@{1}: On main: navy text

# look inside before you apply
$ git stash show stash@{1}
 style.css | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git stash show -p stash@{1}
diff --git a/style.css b/style.css
index 6f5b686..7eca41b 100644
--- a/style.css
+++ b/style.css
@@ -1 +1 @@
-body { color: black; }
+body { color: navy; }

# pop the older one, not the newest
$ git stash pop stash@{1}
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   style.css

no changes added to commit (use "git add" and/or "git commit -a")
Dropped stash@{1} (dfff164ecc62b9ca8d558a5313f71f2176386d80)

$ git stash list
stash@{0}: On main: welcome paragraph

What's going on:

  • git stash push -m "navy text" saves a stash with a readable message instead of the default WIP on main: <commit>.
  • The newest stash is always stash@{0}, so when the second stash was made, "navy text" moved down to stash@{1}.
  • git stash show stash@{1} lists which files a stash changes, and git stash show -p stash@{1} shows the full diff. Check before you apply, especially for old stashes.
  • git stash pop stash@{1} applied and dropped the older stash, and "welcome paragraph" stayed as stash@{0}.

In recent versions of Git you can shorten stash@{1} to just 1, so git stash pop 1 and git stash apply 1 work too. In PowerShell, the curly brackets have a special meaning, so put the name in quotes: git stash pop 'stash@{1}'.

The numbers shift every time you push or pop, so stash@{1} today may be a different stash tomorrow. Always run git stash list straight before popping by number.

Stash specific files, untracked files and the index

Stash or pop a specific file

You can stash only some files by listing them after --. And to get one file back out of a stash without applying the rest, use git restore --source:

# stash only style.css
$ git stash push -m "just the css" -- style.css
Saved working directory and index state On main: just the css

$ git status --short
 M index.html

# bring back only one file from a stash (stash is untouched)
$ git restore --source=stash@{0} -- style.css

$ git status --short
 M index.html
 M style.css

$ git stash list
stash@{0}: On main: just the css

git stash push -- style.css stashed only the CSS, leaving the work in index.html alone. Then git restore --source=stash@{0} -- style.css copied that one file out of the stash. Unlike pop, this doesn't touch the stash list, so drop the stash yourself when you're done. On older versions of Git, git checkout stash@{0} -- style.css does the same job.

Untracked files and git stash pop --index

Two defaults surprise people. First, git stash ignores new files that Git isn't tracking yet. Second, pop and apply bring staged changes back as unstaged. Here are both, with the fixes:

$ git status --short
M  style.css
?? app.js

$ git stash
Saved working directory and index state WIP on main: 6a20ca4 First commit

$ git status --short
?? app.js

# -u includes untracked files
$ git stash -u
Saved working directory and index state WIP on main: 6a20ca4 First commit

$ git status --short

# plain pop: style.css comes back unstaged
$ git stash apply -q

$ git status --short
 M style.css
?? app.js

# pop --index: staged files come back staged
$ git stash pop -q --index

$ git status --short
M  style.css
?? app.js

After a plain git stash, the new app.js was still sitting in the folder (??). That's a problem if you're stashing to switch branches with a clean folder. git stash -u (short for --include-untracked) stashed it as well, and the folder was completely clean. Use -a (--all) if you also want ignored files such as build output, though you rarely need it.

style.css was staged (M in the first column) when I stashed it. A plain apply gave it back unstaged (M, second column). git stash pop --index restored the staging too. If you'd carefully staged part of your work before stashing, --index saves you from doing it again.

Recover a stash you popped or dropped by mistake

A popped or dropped stash isn't deleted straight away. The commit that holds it stays in your repository until Git's garbage collection prunes unreachable objects, and by default that only removes objects more than two weeks old. So act soon, and don't run git gc in the meantime. Here's how to get one back:

$ git stash drop
Dropped refs/stash@{0} (5cc63368befec6f7a909497b31bba70e36148043)

$ git stash list

# the hash from the Dropped line still works
$ git stash apply 5cc63368befec6f7a909497b31bba70e36148043
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   style.css

no changes added to commit (use "git add" and/or "git commit -a")

$ git status --short
 M style.css

# lost the hash? search for unreachable stash commits
$ git fsck --no-reflogs --unreachable | grep commit | cut -d' ' -f3 | xargs git log --merges --no-walk --oneline
5cc6336 WIP on main: 6a20ca4 First commit

If the Dropped refs/stash@{0} (5cc6336...) line is still in your terminal, just apply that hash: git stash apply <hash>. If you've closed the terminal, the git fsck command searches the repository for commits that nothing points to any more. Stashes are stored as merge commits, so git log --merges filters the list down to them, and each one shows its stash message. Apply the one you want by its short hash.

The same command also finds stashes lost to git stash clear, which deletes every stash at once without asking, so I avoid that one. For the same "undo a mistake" idea with commits instead of stashes, see my post on how to undo the last commit in Git and keep your changes.

FAQ

What is the difference between git stash pop and git stash apply?

Both restore stashed changes. apply keeps the stash in the list afterwards; pop deletes it if the changes applied without conflicts. git stash pop is the same as git stash apply followed by git stash drop.

Does git stash pop delete the stash if there is a conflict?

No. When pop hits a conflict, Git keeps the stash and prints "The stash entry is kept in case you need it again." Fix the conflict, git add the file, then run git stash drop yourself.

How do I pop a specific stash in Git?

Run git stash list to find its number, then git stash pop stash@{2} (or git stash pop 2 in recent Git). In PowerShell, quote it: git stash pop 'stash@{2}'.

How do I undo a git stash pop?

If the pop conflicted, run git reset --merge; the stash is still in the list. If it applied cleanly and you want to stash the changes again, run git stash again. If you need the old stash back exactly, apply the hash from the Dropped line, or find it with git fsck as shown above.

Does git stash include untracked files?

Not by default. Use git stash -u to include new untracked files, or git stash -a to include ignored files too.

If you ever stash a file that should never have been in Git at all, such as .env, read my guide on how to remove a file from the repo but keep it locally. The full list of stash options is in the official git stash documentation. You can see the projects I use these commands on, with links to their repositories, on my projects page.

// note

How to read this note.

This is a learning note from studying the web. It is one small topic, written so I can remember it. It is not a course and not a claim that I have finished the subject.

If a sentence is wrong, say so from the contact page and name this title. Drafts never appear here. Related notes, when they exist, are other published posts, and the same sample rule applies to each of them.

Related notes