誤ってリベースしてしまった Git のコミットを元に戻す方法


Git をたくさん使っているとブランチを分岐させたりして分けてコミットしたりなんかしてだんだん散らかってくると思います.

僕だけかもしれませんがw.

そして, 別にパブリックなプロジェクトではなく, コントリビュータは私一人だけってなると, 整理整頓じゃないですがいろんなブランチに散らかったコミットを綺麗にするために Git のリベースをしたいなんてことがあるかと思います.

そんな時はまずローカルレボジトリをリベースして, それをリモートにプッシュするかと思います.

ここまではいいのですが, もしプッシュした後に誤ってリベースしてしまったことに気が付いた場合, どうすれば元の状態に戻すことができるのでしょうか.

今回はそのようなやってしまった案件を解決する方法を紹介します.

まず僕が次のようなコミットをしてきたと想定してください:

            A topic
           /
    B --- C --- D master

全然散らかっていないじゃないかというご指摘が聞こえてきそうですが, そこはスルーしていただけるとありがたいです.

とりあえずこのコミットのヒストリを綺麗にするために D を基礎にして A をのっけてリベースしたいと僕は思います.

なのでまず topic ブランチをチェックアウトします:

git checkout topic

そしてそのようにリベースするために次のコマンドを入力します:

git rebase master

するとさっきのコミットのヒストリはこのようになります:

                  A topic
                 /
    B --- C --- D master

そしてその topic ブランチを origin リモートにプッシュします:

git push origin topic

これでやれやれ綺麗になったと一息入れようとしたのもつかの間, 誤ってリベースしてしまったことに気がつきます.

本当は D を基礎にした A ではなく, A を基礎にした D なのだと.

本当は次のようなコミットのヒストリにしたかったのだと:

                 topic
    B --- C --- A --- D master

そんな時は, 次のコマンドを入力します:

git reflog topic

すると次のようなメッセージが表示されます:

<commid id> (HEAD -> topic) topic@{0}: rebase finished: refs/heads/topic onto <commit id>
<commid id> topic@{1}: <commit message>
...

この topic@{0} というのが, topic ブランチでさっきリベースした時の記録で, topic@{1} というのが, リベースする前の記録です.

なので現在の topic ブランチをその topic@{1} の状態に戻すことができれば, リベースする前の状態に戻すことができるので, そうするために次のコマンドを入力します:

git reset --hard topic@{1}

するとコミットのヒストリは次のようにリベースする前の状態に戻ります:

            A topic
           /
    B --- C --- D master

これでめでたく元に戻すことができました.

で今度は A を基礎にした D でリベースするために master ブランチをチェックアウトします:

git checkout master

でそのようにリベースします:

git rebase topic

でコミットのヒストリがこうなりますので:

                 topic
    B --- C --- A --- D master

リモートにプッシュします:

git push origin master

めでたしめでたしです.