tags:

views:

2558

answers:

5

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?

Note that this isn't critical for me today because I have daily backups and can go back to yesterday's working tree to get my changes. I'm asking because there must be an easier way!

+7  A: 

git fsck --unreachable | grep commit should show the sha1, although the list it returns might be quite large. git show <sha1> will show if it is the commit you want.

git cherry-pick -m 1 <sha1> will merge the commit onto the current branch.

Nathan Jones
Ah, that certainly improves on my method! I have 130 unreachable commits right now so I still needed the last half of my solution.
Greg Hewgill
+5  A: 

I just constructed a command that helped me find my lost stash commit:

for ref in `find .git/objects | sed -e 's#.git/objects/##' | grep / | tr -d /`; do if [ `git cat-file -t $ref` = "commit" ]; then git show --summary $ref; fi; done | less

This lists all the objects in the .git/objects tree, locates the ones that are of type commit, then shows a summary of each one. From this point it was just a matter of looking through the commits to find an appropriate "WIP on work: 6a9bb2" ("work" is my branch, 619bb2 is a recent commit).

I note that if I use "git stash apply" instead of "git stash pop" I wouldn't have this problem, and if I use "git stash save message" then the commit might have been easier to find.

Update: With Nathan's idea, this becomes shorter:

for ref in `git fsck --unreachable | grep commit | cut -d' ' -f3`; do git show --summary $ref; done | less
Greg Hewgill
A: 

Git is probably storing this stuff away in the reflog of your branch. Look in .git/logs for the details.

David Plumpton
I couldn't find it in the reflog, and popping the stash seems to have done a really good job of cleaning up the .git/logs directory.
Greg Hewgill
+29  A: 

Try this:

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

It’s much faster than passing --unreachable, though it doesn’t show you all the unreachable commits, only the tips of the tree.   [The --no-reflog switch is available since 1.5.2. Without it, you’ll miss any commits you worked on only recently, although that doesn’t matter for recovering stashes.]

The easiest way to then get at your commit is to pass that list to gitk:

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

This will launch a repo browser showing every single commit in the repository ever, regardless of whether it is reachable or not. From there you can use the context menu to create a branch for the unreachable commits you are interested in. After that you can do whatever you want with them with all the normal tools.

When you’re done, just blow those branches away again.

Aristotle Pagaltzis
Wow, this is fantastic - totally saved my butt. Thanks!
Scotty Allen
Just saved my butt too! Thanks!
Scott
You are my hero. I owe you a beer.
docgnome
Make that two beers.
docgnome
@docgnome - keep coming back for more, huh?
Max A.
Saved my day too, thanks a lot :)
Romuald Brunet
This answer is invaluable!
Will Vousden
+6  A: 

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.

Wade