DEV Community

Cover image for Recover a lost Git stash in two steps
Mehdi M.
Mehdi M.

Posted on • Edited on • Originally published at blog.mehdi.cc

Recover a lost Git stash in two steps

Sometimes you end up stashing code, then at some point those stashes get cleaned. And one day, you may encounter the situation I faced this week:

Fine, this code is merged, so let’s delete the related stashes. Done! And… hey… wasn’t this part of the feature supposed to be in the codebase? Is the stash I just deleted lost forever?

Fortunately, I managed to recover lost stashes. I’m not a Git expert, but here’s what worked for me after going through various readings (including some Stack Overflow answers).

Here’s the two-steps recovery procedure.

1. List lost stashes

Let’s run this command for a project where all stashes were trashed:



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


Enter fullscreen mode Exit fullscreen mode

It returns a list of lost stashes, ordered by date.
Ho, 3 lost stashes!

  • To quit the list of stashes, press the Q key.
  • To navigate in a long stashes list, use up and down arrows.
  • For Windows user, maybe johnwait’s comment will help you during the battle.

2. Send a lost stash back where it comes from

Let’s use the commit hash of the second stash:



git update-ref refs/stash 4b3fc45c94caadcc87d783064624585c194f4be8 -m "My recovered stash"


Enter fullscreen mode Exit fullscreen mode

And that’s it! You’ll find your stash as usual, using git stash list or by having a look in your favorite Git client.

Gotchas

1. I still can’t see my recovered stash

Retry using the --create-reflog parameter (thanks studoggithub):



git update-ref refs/stash 4b3fc45c94caadcc87d783064624585c194f4be8 --create-reflog -m "My recovered stash"

Enter fullscreen mode Exit fullscreen mode



  1. My Git isn’t in English

If your Git isn’t in English, you’ll have to run alias git='LANG=en_GB git' each time you want to recover a set of stashes (thanks mathieuschopfer).

Some advices

Commit messages are healthy

Always use a commit message using git stash save -m "My commit message": without message, the only informations helping to identify a stash are its timestamp and the branch it was saved from, which may not be enough compared to a strong explicit name.

Commit messages also help Git clients:

  • GitUp, the Git client I use, completely fails at showing unnamed stashes. That’s probably why you can’t create a stash in GitUp without giving it a name, which is great!
  • The well-known SourceTree succeeds at showing unnamed stashes, but as you can guess, the list isn’t friendly to browse: Unnamed stashes in SourceTree

Yes, git stash apply > git stash pop

Unlike git stash pop, git stash apply does not remove the stash from the list of stashes, which can avoid some loss.

Branches > stashes

Finally, I’d recommend to avoid git stash. Instead, try to use a branch. This seems obvious but it only comes to me as I was finding a way to recover a stash: maybe I should use temporary branches instead of stashes. Using the Git Flow method at work, this could have come to my mind before encountering a painful experience.

If you have any stash hint or experience that you want to share, comments are welcome.

Top comments (25)

Collapse
 
htps611 profile image
htps

Saved my day ❤️

Collapse
 
johnwait profile image
johnwait

On Windows, in a good old command window (your usual cmd.exe), step 1. could be translated to:

for /f "tokens=3" %a in ('git fsck --unreachable ^| find "commit"') do @git log --merges --no-walk %a

If your Git speaks français, and you're one to choose the more complicated path, you could use something like:

for /f "tokens=1,2,3,4" %a in ('git fsck --unreachable ^| find "commit"') do @if "%c"=="inatteignable" (@git log --merges --no-walk %d) else (@git log --merges --no-walk %c)

or, really, keep it simple with alias git='LANG=en_GB git' instead.

Bonus

For PowerShell aficionados, here's a command that should work regardless of your Git's locale (well, as long as a commit is still referred to as commit):

(git fsck --unreachable | Select-String "commit") -split '\s+' |
Select-String -pattern "^[0-9a-fA-F]{40}$" |
ForEach-Object { git log --merges --no-walk $_ }

(Really useful post btw!)

Collapse
 
neelmagicedtech profile image
neel-magicedtech

This command for windows saved my life and job. Thankyou so much!!

Collapse
 
meduzen profile image
Mehdi M.

Thanks! A link to your comment has been added to the article.

Collapse
 
studoggithub profile image
studog-github

This almost worked for me. I found that the update-ref command created the stash ref correctly but git stash list still did not show anything.

I added the --create-reflog parameter on a second try, and then things worked.

git version 2.22.0 on Ubuntu 18.04

Collapse
 
meduzen profile image
Mehdi M.

Thanks! I don’t have this issue in Git 2.20.1 (macOS 10.14.16) but added a note in the article.

Collapse
 
ericus123 profile image
AMANI Eric

Thanks a lot. This saved me .

Collapse
 
samar_hussain profile image
Samar Hussain

Mehdi bro you have no idea how big of a trouble you saved me from. I accidentally stashed and cleared hard work of my several days and night (My mistake I shouldn't have piled up that big). And your post just saved my life. I was almost crying. Thanks a lot dude. God bless you.

Collapse
 
meduzen profile image
Mehdi M.

Hey Samar, you know what? I feel there's a lot of gratitude in your message, so I want to say thank you for having taken the time to put it down. It sincerely made my day better (and today is not the easiest one for me 😅).

I hope everything's fine with your work, now. Sharing is caring. ✌️

Collapse
 
mathieuschopfer profile image
Mathieu Schopfer

This may fail if you don't use git in English. To quickly fix it:
alias git='LANG=en_GB git'

Collapse
 
meduzen profile image
Mehdi M.

:o

What is different when you use Git in another language? I didn't know it existed.

Collapse
 
mathieuschopfer profile image
Mathieu Schopfer

In French (I cannot tell for other languages), commit seems to have been translated by objet commit.

git fsck --unreachable returns
objet commit inatteignable 977ee79082f2e1179c3d2156f8f0e6c66682ea2d
instead of
unreachable commit 977ee79082f2e1179c3d2156f8f0e6c66682ea2d

Thus, cut -d ' ' -f3 returns inatteignable instead of the commit tag.

Thread Thread
 
meduzen profile image
Mehdi M.

Ha oui, carrément. :D

Gonna update the article. Thanks a lot!

Collapse
 
rstrausslogyx profile image
Randy Strauss • Edited

I tried both commands (git 2.21.1):
git update-ref refs/stash b68ecd901f90158d7c41edf2d2d3868e3599ca29 -m "My recovered stash"
git update-ref refs/stash b68ecd901f90158d7c41edf2d2d3868e3599ca29 --create-reflog -m "My recovered stash"

both give usage (below, removing the '-d' and '-stdin' stuff:
usage: git update-ref [] []
-m reason of the update
--no-deref update not the one it points to
-z stdin has NUL-terminated arguments
--create-reflog create a reflog

Is refs/stash the refname? Or is the sha the refname?
What's the "new-val" and "old-val" - values of what?

This page seems to help:
gist.github.com/joseluisq/7f0f1402...

git log --graph --oneline --decorate ( git fsck --no-reflog | awk '/dangling commit/ {print $3}' )
and then
git stash apply YOUR_WIP_COMMIT_HASH

Thanks for the hope, and being a stepping stone to most of what I needed. :?)

(4 of my files weren't stashed, though. It turns out NASA's backup utility has been silently failing for months, so I've lost a few days work. Under-staffed projects are a pain. Luckily, the project and my attitude don't really matter...)

Collapse
 
xrzhuang profile image
xrzhuang

this saved my life

Collapse
 
rjean99 profile image
rjean99

Mine too!

Collapse
 
pedroverceze profile image
Pedro Verceze

Thanks!!
It is good to know, after the fist command, pick your hash and use:

$ git stash apply e3cf5932f4816f5b0022190ce6b871f51cf882de

It worked for me

Collapse
 
shankarshastri profile image
ShankarShastri

Thanks, it saved my day !!