git reset —hardしてしまった時に元に戻す方法

2022/08/15に公開2

「タタターン、タッーン! ($ git reset —-hard)」
「半日分の作業がぁぁぁぁ….😇」
今まさに、こんな状況じゃないでしょうか?

そんな絶望のどん底にいるあなたを救済したい!
本記事ではgit reset —hardを元に戻す方法をご紹介します!

2つのあるので、状況にあった方をご参照ください。

  • 過去のコミットにリセットしてしまった場合
  • コミットせずにリセットしてしまった場合

過去のコミットにリセットしてしまった場合

1つ目は、過去のコミットにリセットしてしまった場合です。
こちらは比較的簡単に復旧できます。

以下のように、1.txt2.txtを作成し、それぞれコミットした状態を考えます。

# Commit: Add 1.txt
$ echo 1 >> 1.txt                                                     
$ git add -A
$ git commit -m "Add 1.txt"

# Commit: Add 2.txt
$ echo 2 >> 2.txt
$ git add -A
$ git commit -m Add 2.txt

$ git log --oneline
0ae8a0a (HEAD -> main) Add 2.txt
6776aad Add 1.txt

ここで、手が滑って1つ目のコミットにリセットしてしまい、2.txtを消してしまいました😇

git reset --hard 6776aad
HEAD is now at 6776aad Add 1.txt

解決法

これを解決するには、git-reflogを使います。
reflogはHEADの動き、つまり自身のgitの操作履歴を参照できるコマンドです。

実行してみると、先ほど実行したreset —-hardが直近のreflogとして出力されます。
さらに、それ以前のコミットも出力されているのがわかります。

$ git reflog
# ↓手が滑ってやってしまった reset --hard
6776aad (HEAD -> main) HEAD@{0}: reset: moving to 6776aad
# Commit: Add 2.txt
0ae8a0a HEAD@{1}: commit: Add 2.txt
# Commit: Add 1.txt
6776aad (HEAD -> main) HEAD@{2}: commit (initial): Add 1.txt

今回は、2.txtを追加したCommit: Add 2.txtに戻したいので、HEAD@{1} を指定してreset —hardをかけることで、元に戻すことができます。

git reset --hard HEAD@{1}

見事、元に戻りました! やったね🎉

git log --oneline
0ae8a0a (HEAD -> main) Add 2.txt
6776aad Add 1.txt

コミットせずにリセットしてしまった場合

続いて、ステージに追加したファイルを、コミットせずにリセットしてしまった場合です。
こちらは、コミットした場合に比べて、少し厄介です。

以下のように
1.txt を作成
1.txtを編集、2.txtを作成
した状態を考えます。

# ① Add 1.txt
$ echo 1 >> 1.txt                                                     
$ git add -A
$ git commit -m "Add 1.txt"

# ② 1.txtを編集、2.txtを作成
$ echo 1 >> 1.txt
$ echo 2 >> 2.txt

$ git add -A
$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   1.txt
        new file:   2.txt

ここで何を血迷ったのかgit reset —hard を実行してしまいました😇

$ git reset --hard
HEAD is now at de7c406 Add 1.txt

解決法

今回はステージに追加しただけで、一度もコミットしていないので、reflogが使えません。

そこで登場するのが、git fsck コマンドです。
fsckはLINUXのfsck(file system check)から来ているとおもわれ、gitにおいてもデータの検査と修復を行います。

まず最初に、git fsck --lost-found で、先ほどコミットされなかったファイルのblobを書き出します。

git fsck --lost-found
Checking object directories: 100% (256/256), done.
dangling blob 0cfbf08886fca9a91cb753ec8734c84fcbe52c9f
dangling blob 6ed281c757a969ffe22f3dcfa5830c532479c726

これで、先ほど消してしまったファイルのblobが、.git/lost-found/other ディレクトリに出力されます。

ls ./.git/lost-found/other
0cfbf08886fca9a91cb753ec8734c84fcbe52c9f # 2.txtのblob
6ed281c757a969ffe22f3dcfa5830c532479c726 # 1.txtのblob

ファイルの中身を見るには、git show {id} で見ることができます。

git show 0cfbf08886fca9a91cb753ec8734c84fcbe52c9f
2

あとは中身を見ながら1つずつファイルを戻していきます。

# 1.txtの編集を戻す
git show 6ed281c757a969ffe22f3dcfa5830c532479c726 > 1.txt
# 2.txtを追加を戻す
git show 0cfbf08886fca9a91cb753ec8734c84fcbe52c9f > 2.txt

復旧できました!やったね🥰

$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   1.txt
        new file:   2.txt

以上、特にコミットしている場合はかんたんに戻せるので、もう一回同じことをする前に、一度試してみることをお勧めします!!

Discussion

shun0104shun0104

今まさに、こんな状況じゃないでしょうか?

まさにこちらでした。。本当にありがとうございます。。!助かりました、、!