Tuesday, 21 July 2009

How to recover lost commits in a git repository

Today we had a problem: we lost several git commits when a rebase went wrong. We’re still not sure how this happened, but we were able to recover the commits. Here’s a quick summary of how we managed that, which I hope will help others get out of similar situations.

We tried to abort a rebase that had a lot of conflicts, and it took us from a situation like this:

pre-rebase

to one like this:

post-rebase

Disaster! Our “last big commit on my story” has vanished! We were pretty sure it was still in the repository somewhere, it just wan't reachable from any named branch. We hunted around in git log, git fsck and other commands with no luck. It turns out that the answer lies in git reflog show

$ git reflog show
3330b4e HEAD@{0}: checkout: moving from ms2 to my-story
3330b4e HEAD@{1}: checkout: moving from 3330b4e0b9af502908ce2c59958ec1e7f83a1a4b to ms2
3330b4e HEAD@{2}: checkout: moving from my-story to 3330b4e
5b50352 HEAD@{3}: rebase: updating HEAD
8f4a119 HEAD@{4}: rebase: more work on my story
c715f09 HEAD@{5}: rebase: some work on my story
6e1441d HEAD@{6}: checkout: moving from my-story to 6e1441d03b200d62f9ffc28d902c60a289d2ea3b^0
5b50352 HEAD@{7}: checkout: moving from mainline to my-story
6e1441d HEAD@{8}: commit: more work by the rest of the team
e72f33f HEAD@{9}: checkout: moving from my-story to mainline
5b50352 HEAD@{10}: commit: last big commit on my story
3330b4e HEAD@{11}: commit: more work on my story
a7d49b0 HEAD@{12}: commit: some work on my story
f3d00fb HEAD@{13}: checkout: moving from master to my-story
f3d00fb HEAD@{14}: checkout: moving from mainline to master
e72f33f HEAD@{15}: commit: some work done by other developers
f3d00fb HEAD@{16}: checkout: moving from master to mainline
f3d00fb HEAD@{17}: commit: commit b
2fcece2 HEAD@{18}: commit (initial): commit a

There it is! We now have the SHA for the “last big commit on my story”. We can check that this really is the right one:

$ git show 5b50352

commit 5b503520f132e1a64c510816fcd61cc97626791e
Author: Alex Scordellis 
Date:   Tue Jul 21 20:26:21 2009 +0100

    last big commit on my story

diff --git a/anotherfile.c b/anotherfile.c

  ...diff contents elided...

Now all we had to do was reattach a branch name to that SHA, and we were back where we started:

ascordel@ALEXSCORDEL /c/alex/code/git-recovery
$ git checkout 5b50352
Note: moving to '5b50352' which isn't a local branch
If you want to create a new branch from this checkout, you may do so
(now or later) by using -b with the checkout command again. Example:
  git checkout -b 
HEAD is now at 5b50352... last big commit on my story

ascordel@ALEXSCORDEL /c/alex/code/git-recovery (5b50352...)
$ git checkout -b recovered-my-story
Switched to a new branch 'recovered-my-story'

post-fix

Now we’re back where we were at the beginning. We can start again and try to get the rebase through this time. We still don’t know where it went wrong in the first place, but we were pretty relieved to get our work back after a short period of panic!

20 comments:

  1. Nice one. I had this problem just today :-)

    ReplyDelete
  2. I saw an article not so long ago, the gist of which was, rebase is probably not the right tool to use..

    I'm pretty sure the one I read gave much more complete arguments, but here is an article on Git best practice and another on rebase considered harmful

    Maybe worth a read :-)

    ReplyDelete
  3. @Andy - that sounds like good advice in general. I always feel with git that there are so many different ways to get things done, so thanks for the links to recommended practice.

    In our situation we're actually using git svn. I think this requires a rebase in order to push a linear working history back into the central svn repo.

    ReplyDelete
  4. Thanks for the post. I had a moment of panic myself today thinking I had lost hours of work but was able to recover everything. (Huge sigh of relief)

    I'm not quite sure what I did but looking at my shell history I have a guess. I was working in a branch and I think I meant to type

    git reset HEAD~1

    when I commited something my accident. Instead I typed out

    git rebase HEAD~2

    I don't know where the 2 came from or why I rebased but it's in my shell history so I must of done it.... :-) In any case I thought I was on my branch when really I was working off of an unnamed branch as passing a branch to (HEAD~2) to rebase created a new branch I think.

    Later I went to merge my branch to master and got far less than I thought I should have when the fast forward was complete. After some panic I found this post and wanted to thank you for it.

    Thanks!
    Joel

    ReplyDelete
  5. Thanks! This help me a lot.

    ReplyDelete
  6. Thanks, you saved me a lot of worry too.

    ReplyDelete
  7. Phew, I thought I lost my commit!
    Thanks!

    ReplyDelete
  8. Saved my day!

    ReplyDelete
  9. thanks buddy you made my day...

    ReplyDelete
  10. You just saved my 7 man days. Thank you so muchhhh.

    ReplyDelete
  11. Thank you!! OMG so relieved!

    ReplyDelete
  12. Saved my 2 days of work from being lost... Thanks!

    ReplyDelete
  13. I also have experienced the "short period of panic" till I find your solution... Thanks a lot!

    ReplyDelete
  14. Just ran into exact same problem. your blog helped. Thank you!

    ReplyDelete