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.