git reset を元に戻すための git reflog コマンド

ファイルの変更はそのままにコミットを戻す $ git reset HEAD^ (--soft) や、ファイルの変更ごとコミットをなくす $ git reset --hard HEAD^ を叩いた後に、それをまた戻す方法があった。git reflog というコマンドを使うと、--hard を使って完全に取り消したコミットでも元に戻せるので紹介。

まずはサンプルとなる環境の紹介。

$ git log  # フォーマットして1行にしている
9adb18a 2018-05-17 Second (HEAD -> master)
08d59b1 2018-05-17 First

このような状態だとする。

まずは $ git reset --soft HEAD^ を試してみよう。

$ git reset --soft HEAD^

$ git log
08d59b1 2018-05-17 First (HEAD -> master)

Second のコミットに含まれていた内容はそのまま保持されるので、git status を見るとそのファイルが modified: 状態で残っている。

さて、これを Second のコミットが存在していた状態に戻してみよう。git reflog の出番だ。

$ git reflog
08d59b1 (HEAD -> master) HEAD@{0}: reset: moving to HEAD^
9adb18a HEAD@{1}: commit: Second
08d59b1 (HEAD -> master) HEAD@{2}: commit (initial): First

$ git reflog を叩くと、このように過去のコミット履歴を眺められる。今回は Second のコミット時点に戻りたいので、2行目の HEAD@{1} という指定を覚えておく。

確認したら、以下のように git reset を叩く。

$ git reset --soft HEAD@{1}

$ git log
9adb18a 2018-05-17 Second (HEAD -> master)
08d59b1 2018-05-17 First

コレで戻すと、Second のコミット ID も元通りに戻せる。

勿論、--hard で戻してしまった時も同様に戻せる。

# 「--hard」オプションで戻す
$ git reset --hard HEAD^
HEAD is now at 08d59b1 First

$ git log
08d59b1 2018-05-17 First (HEAD -> master)
# Second が跡形もなく消えている。ファイルの変更も破棄された

$ git reflog
08d59b1 (HEAD -> master) HEAD@{0}: reset: moving to HEAD^
9adb18a HEAD@{1}: reset: moving to HEAD@{1}                # ← ココに戻す
08d59b1 (HEAD -> master) HEAD@{2}: reset: moving to HEAD^
9adb18a HEAD@{3}: commit: Second
08d59b1 (HEAD -> master) HEAD@{4}: commit (initial): First

# コレで戻る
$ git reset --hard HEAD@{1}
HEAD is now at 9adb18a Second

$ git log
9adb18a 2018-05-17 Second (HEAD -> master)  # ← コミット ID も元通り
08d59b1 2018-05-17 First

これは便利。もう git reset 事故に怯えなくて済むぞ…!