おっと手が滑って...reset --hardしちゃった場合...ありませんか?(あったらえらいこっちゃですが...)
本記事ではgit reset --hardを実行した後の復元方法について,addしていない場合,addした場合,commitした場合のそれぞれについて説明する.
addしていない場合(ワーキングディレクトリのみに変更がある場合):
この状況では,変更は完全に失われる.Gitの機能だけでは復元は不可能である.ファイルシステムの復元ツールを使用する必要があるが,成功の保証はない.
例えばgit statusをしてワーキングエリアとステージングエリアの状態を確認する
git status
すると,example.txtがステージングエリア(インデックス)になく,addされていないことがわかる.
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: example.txt
この状態でgit reset --hardをする.
git reset --hard
HEAD が1つ前のコミットを指すようになる.
HEAD is now at 1a2b3c4 Previous commit message
この状態でgit statusをすると
git status
ワーキングエリアやステージングエリアにもexamle.txtの変更差分がなくなっていることがわかる.
On branch main
nothing to commit, working tree clean
このようにaddをしずにreset --hard するとどうすることもできないので要注意
commitしてaddした場合(ステージングエリア(インデックス)に変更がある場合):
example.txtをaddして,git statusをしてワーキングエリアやステージングエリアの状態を確認する.
git add example.txt
git status
するとステージングエリアにexample.txtの変更差分が存在することがわかる.
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: example.txt
この時点でgit reset --hard をすると...
git reset --hard
HEADが1つ前のコミットを指すようになる.
HEAD is now at 1a2b3c4 Previous commit message
この状態でgit statusをすると
git status
ワーキングエリアやステージングエリアにもexamle.txtの変更差分がなくなっていることがわかる.
On branch main
nothing to commit, working tree clean
しかしaddをした場合blobオブジェクトが作成され,どのコミットやタグもそのblobオブジェクトに対して参照を持たない状態になる.そのため参照を持たないblobオブジェクトを探すコマンドが以下である,.
git fsck --lost-found
2d3e4f5g6h7i8j9k0l1m2n3o4p5q6r7s8t9u0という参照されていないblobオブジェクトが出力された.
Checking object directories: 100% (256/256), done.
Checking objects: 100% (1234/1234), done.
dangling blob 2d3e4f5g6h7i8j9k0l1m2n3o4p5q6r7s8t9u0
中身をgit showするとファイルの内容を見ることができる.
git show 2d3e4f5g6h7i8j9k0l1m2n3o4p5q6r7s8t9u0
正しい内容だった場合,該当のblobオブジェクトをaddする.
git add .git/lost-found/other/2d3e4f5g6h7i8j9k0l1m2n3o4p5q6r7s8t9u0
今度こそコミットを忘れない.
git commit -m "ふっかーーつ"
git add したファイルはgit reflogで探せないのか
git reflogとは
HEADが移動する操作(commit branch reset)をした際のローカルリポジトリの操作履歴を参照する
結論から言うと,git addしたファイルはgit reflogに残らない.そのため,git reflogでは参照できない.
git reflogは主にHEADの移動履歴を記録する.つまり,以下のような操作が記録される.
- コミットの作成
- ブランチの切り替え
- リセット操作
このようにresetした履歴は残るがaddした履歴は残らないのでgit reflogではaddしたgit オブジェクトを参照できない.
しかし,git addしたファイルはblobオブジェクトとして保存されるのでgit fsckで参照がないblobオブジェクトを参照できる
blobオブジェクトとは
Gitには主に4種類のオブジェクトがある.
- blob: ファイルの内容
- tree: ディレクトリ構造
- commit: コミットの情報
- tag: タグの情報
commitした場合
コミットをして
git commit -m "New commit"
コミットした際の出力
[main 3f4g5h6] New commit
1 file changed, 1 insertion(+)
これでリセットをした場合...
$ git reset --hard HEAD~1
HEADhは1つ前のコミットを指している.
HEAD is now at 1a2b3c4 Previous commit message
この場合commitをしている(=HEADを移動した)のでreflogで参照できる操作である.ためreflogでcommitをした操作を探す.
git reflog
するとresetした操作やcommitした操作を参照できるので3f4g5h6がコミットした操作で得ることがわかる.
1a2b3c4 (HEAD -> main) HEAD@{0}: reset: moving to HEAD~1
3f4g5h6 HEAD@{1}: commit: New commit
1a2b3c4 HEAD@{2}: commit: Previous commit message
ここで3f4g5h6nにresetする.つまりresetしてしまったらresetで復活させることができる.
git reset --hard 3f4g5h6
これでresetをresetできた.
HEAD is now at 3f4g5h6 New commit