Losing in-progress work is one of the fastest ways to feel helpless in front of a computer. This article about recovering lost git stashes was initially published 5 years ago on Dev.to and has a steady 10000 views per year, so I guess it happens to a lot of persons.
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 be like:
Ooops, I think I just deleted stashes that I needed! Are they lost forever? 😨
Fortunately, I managed to recover them. Here’s the two-steps procedure that worked for me after going through various readings (and others), and also some general tips.
Step 1: list lost stashes
In your project where stashes are trashed:
git fsck --unreachable | grep commit | cut -d ' ' -f3 | xargs git log --merges --no-walk
It returns a list of lost stashes, ordered by date.
commit 7754fed19955d2958d952ab2836b22631b036b5
Merge: f0a125c 36f580 e508036
Author: Mehdi Merah <xx@xxx.com>
Date: Fri Apr 27 16:41:17 2018 +0200
On regions-components: After Regions component
commit 43c45c94caadcc87d783064624585c194f4be8
Merge: 13703fd bc8220e 4005629
Author: Mehdi Merah <xx@xxx.com>
Date: Sat Apr 21 20:03:44 2018 +0200
On master: Before Regions components
commit bb615e44ed99b5a5622ead0c4bbb7b4acc19767
Merge: c6a7b3 127203e 45dcc54
Author: Mehdi Merah <xx@xxx.com>
Date: Sat Apr 7 19:39:50 2018 +0200
On master: Wololo
# So, basically this anatomy:
commit {stash commit hash}
Merge: {parent commit hash…}
Author: {author name} <{author email}>
Date: {stash date with timezone}
On {branch name}: {stash commit message}
(END)
- To quit the list of stashes, press the Q key.
- To navigate in a long stashes list, use the up and down arrows.
- For Windows user, maybe johnwait’s comment will help you during the battle:
johnwait’s comment
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!)
Step 2: send a lost stash back where it comes from
Let’s use the commit hash of the second stash (from the previous list):
git update-ref refs/stash 4b3fc45c94caadcc87d783064624585c194f4be8 -m "My recovered stash"
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"
It did the trick for that person using git 2.22.0 on Ubuntu 18.04.
2. My Git client 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), otherwise the unreachable
flag in git fsck --unreachable
might be in another language, so you can either:
- alias the language like Mathieu (put in in your
.bashrc
file so it becomes permanent); - adapt the first command: translate
unreachable
into your git language and update thecut
command.
Advices
Commit messages are healthy
I always add a commit message to stash using git stash save -m "My commit message"
: without message, the only way to identify a stash are its timestamp and the branch it was saved from, which might not be enough compared to human-written words.
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:
Prefer git stash apply
over git stash pop
Unlike git stash pop
, git stash apply
does not remove the stash from the list of stashes, which can avoid accidental loss.
Prefer branches over stashes
Stashes serve a different purpose than branches. Wherever it makes sense to you, commit your code to a new branch instead of stashing it. You’re gonna start a new branch anyway if you use a branching model like Git Flow.