Recovering from a bad git-rebase
by Ross Burton
Being a good Git citizen I often use git add -p and git rebase -i to produce a perfect patch series that doesn’t reflect the reality of creating it, but is far easier to review and understand later. Then, of course, a rebase can go terribly wrong…
Today I was doing one last rebase to squash a few typo fix commits, and accidentally squashed the wrong commits. I was left with a single commit that claimed to do one thing, but actually has several unrelated (but more important) changes. Urgh. One solution would be to do another interactive rebase and use git add -p to split the commit, but I thought I’d try using the reflog.
The Git reflog is a log of all changes that have happened to the tree, including all the stages of apparently destructive and deep rebase operations.
$ git reflog
...
9f46daa HEAD@{142}: rebase -i (finish): returning to refs/heads/gtkdoc
...
8d7f266 HEAD@{166}: rebase -i (squash): glib-2.0: cleanup thanks to new gtk-doc.bbclass
a8f06b1 HEAD@{167}: rebase -i (squash): updating HEAD
0ac2f59 HEAD@{168}: rebase -i (fixup): # This is a combination of 3 commits.
a8f06b1 HEAD@{169}: rebase -i (fixup): updating HEAD
cccef4d HEAD@{170}: rebase -i (fixup): # This is a combination of 2 commits.
a8f06b1 HEAD@{171}: rebase -i (fixup): updating HEAD
c7ef1a6 HEAD@{172}: checkout: moving from gtkdoc to c7ef1a6
14bdd14 HEAD@{173}: commit: glib squash
...
From the bottom up, the checkout of a hash is the start of the rebase. Then there are some fixups and squashes. whoops! The top squash shouldn’t be squashed, but picked. Never mind, we can checkout the tree as it was before the rebase with git checkout -b gtkdoc2 14bdd14, which I’ve highlighted. Double check that the tree is what you expect, and then the old branch can be deleted and the new one renamed. Braver people may want to simply git reset --hard 14bdd14, but the double safety net is useful.
Update: for a more concise method, see glandium’s reply in the comments.
Awesome, I’m sure that’ll save my bacon at some point – so thanks in advance
Consider this mini-guide as payment for buildhistory.
It’s probably easier to look at the reflog of your branch, instead of looking at the reflog of HEAD: git reflog show master.
And if you just want to come back to the state before your rebase, simply git reset –hard master@{1}
Ah, useful. Thanks.
Thanks, I’m sure this will save my skin in the future too.
I’ve been habitually creating temporary backup branches each time I do an interactive rebase (git branch tmp1, git branch tmp2 and so on). Looks like I can now get more careless if there’s such an easy way to recover