Categories
git git-stash recovery

How do I recover a dropped stash in Git?

2230

I frequently use git stash and git stash pop to save and restore changes in my working tree. Yesterday, I had some changes in my working tree that I had stashed and popped, and then I made more changes to my working tree. I’d like to go back and review yesterday’s stashed changes, but git stash pop appears to remove all references to the associated commit.

I know that if I use git stash then .git/refs/stash contains the reference of the commit used to create the stash. And .git/logs/refs/stash contains the whole stash. But those references are gone after git stash pop. I know that the commit is still in my repository somewhere, but I don’t know what it was.

Is there an easy way to recover yesterday’s stash commit reference?

4

3577

Once you know the hash of the stash commit you dropped, you can apply it as a stash:

git stash apply $stash_hash

Or, you can create a separate branch for it with

git branch recovered $stash_hash

After that, you can do whatever you want with all the normal tools. When you’re done, just blow the branch away.

Finding the hash

If you have only just popped it and the terminal is still open, you will still have the hash value printed by git stash pop on screen (thanks, Dolda).

Otherwise, you can find it using this for Linux, Unix or Git Bash for Windows:

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

…or using Powershell for Windows:

git fsck --no-reflog | select-string 'dangling commit' | foreach { $_.ToString().Split(" ")[2] }

This will show you all the commits at the tips of your commit graph which are no longer referenced from any branch or tag – every lost commit, including every stash commit you’ve ever created, will be somewhere in that graph.

The easiest way to find the stash commit you want is probably to pass that list to gitk:

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

…or see the answer from emragins if using Powershell for Windows.

This will launch a repository browser showing you every single commit in the repository ever, regardless of whether it is reachable or not.

You can replace gitk there with something like git log --graph --oneline --decorate if you prefer a nice graph on the console over a separate GUI app.

To spot stash commits, look for commit messages of this form:

        WIP on somebranch: commithash Some old commit message

Note: The commit message will only be in this form (starting with “WIP on”) if you did not supply a message when you did git stash.

26

  • 92

    Jaydel took the words out of my mouth. This post saved my job 🙂 I would just like to add – remembering the date you worked on whatever you lost makes it easier to browse gitk for what you are looking for.

    Sep 4, 2015 at 17:52


  • 4

    @Codey: Because PowerShell. I don’t know if MsysGit ships an AWK binary. Googling tells me that something like %{ $_.Split(' ')[2]; } should do the equivalent of the {print $3} in that awk command in PowerShell, but I don’t have a Windows system to test that, and you still need an equivalent for the /dangling commit/ part. Anyway, just run git fsck --no-reflog and look at the output. You want the hashes from the “dangling commit <commitID>” lines.

    Oct 2, 2015 at 3:55

  • 8

    It’s worth mentioning that the commit message will only have the string “WIP” if you didn’t provide your own message when stashing (i.e. by doing git stash save "<message>").

    Jul 29, 2016 at 1:19

  • 26

    If you know when the drop happened, you can use this one-liner to get the list of dangling commits by increasing time: git fsck --no-reflog | awk '/dangling commit/ {print $3}' | xargs -L 1 git --no-pager show -s --format="%ci %H" | sort the last entry is probably the one you want to stash apply.

    May 15, 2017 at 9:31

  • 18

    git stash apply {ref} restored a dropped stash! git is so great it should be illegal!

    Nov 4, 2017 at 7:32


785

If you didn’t close the terminal, just look at the output from git stash pop and you’ll have the object ID of the dropped stash. It normally looks like this:

$ git stash pop
[...]
Dropped refs/[email protected]{0} (2ca03e22256be97f9e40f08e6d6773c7d41dbfd1)

(Note that git stash drop also produces the same line.)

To get that stash back, just run git branch tmp 2cae03e, and you’ll get it as a branch. To convert this to a stash, run:

git stash apply tmp
git stash

Having it as a branch also allows you to manipulate it freely; for example, to cherry-pick it or merge it.

9

  • 63

    You can also do git stash apply commitid then git stash to get a new stash.

    Nov 23, 2011 at 20:11

  • 34

    Note that if git auto-merges the stash and has conflicts, it won’t show you the hash.

    – James

    Sep 26, 2013 at 20:53

  • 34

    @James: Then again, if those conflicts are a result of running git stash pop, it won’t drop the stash either, so that’s normally not a problem.

    – Dolda2000

    Sep 27, 2013 at 3:23

  • 3

    There was no SHA in my git stash pop output. 🙁

    Oct 19, 2017 at 16:36

  • 2

    @Honey: That’s the point of git stash pop. If you want to apply the stash without dropping it, use git stash apply instead. Additionally, if you want to apply a change to several branches, you can also cherry-pick the commit instead.

    – Dolda2000

    Mar 21, 2019 at 12:26


292

Just wanted to mention this addition to the accepted solution. It wasn’t immediately obvious to me the first time I tried this method (maybe it should have been), but to apply the stash from the hash value, just use “git stash apply “:

$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219

When I was new to git, this wasn’t clear to me, and I was trying different combinations of “git show”, “git apply”, “patch”, etc.

1

  • 4

    Do note that this applies (duh!) the stash to the current working tree. If the tree is dirty, you might want to either use a temporary branch or stash first, apply the stash from the SHA-1, stash again and then pop the second to last stash (called [email protected]{1}).

    – musiKk

    Mar 24, 2014 at 10:28