
Gitで作業していると、うっかり reset --hard
してしまったり、意図せずコミットを消してしまったりすることは珍しくありません。そんなときに覚えておきたいのが reflog
と reset
の活用術です。この記事では、Gitの履歴を救うための具体的なパターンと対処法を紹介します。
よくあるハマりパターン
git reset --hard
で大事なコミットが消えた
ステージや作業ツリーをすべて指定したコミットの状態に戻すreset --hard
。便利な反面、直前のコミットをうっかり飛ばしてしまい、意図しないロールバックが発生しがちです。git commit --amend
で前のコミットを上書きしてしまった
直前のコミットを修正できる--amend
は便利ですが、過去のコミットを上書きしてしまい、あとで「戻したい」と思ったときに困るケースがあります。git stash pop
のコンフリクト後に stash 内容が消えたstash pop
は適用と同時に stash を削除します。コンフリクトが発生しても削除は実行されるため、解決前に元の内容を失ってしまう危険があります。git rebase
で履歴が意図せず書き換えられた
コミット履歴を整理するために使われるrebase
ですが、やり方を誤ると複数のコミットが消えてしまったり、履歴が複雑になって戻せなくなることがあります。git checkout .
で作業中の変更がすべて消えた
ワーキングツリーのすべての変更を破棄するこのコマンドは、保存していなかった修正を一瞬で失う危険があります。
これらの失敗は、絶対に戻せないミスではありません。これから説明するreflog
やreset
コマンドで十分リカバリ可能です。焦らず落ち着いて復旧作業を行いましょう。
▶︎ git reflog
: HEADの移動履歴の閲覧コマンド
git reflog
は、HEADの移動履歴を閲覧するコマンドです。Gitで作業をしていると、reset
や rebase
、checkout
などによってブランチの先頭(HEAD※1)の位置が頻繁に移動します。これらの操作は、git log
では見えなくなることがありますが、reflog
はそのすべてを記録しており、「どのタイミングで、どのコミットに移動したか」を正確に辿ることができます。
このため、操作ミスによって履歴が壊れたように見えても、reflog
を使えば「どこに戻ればよかったのか」「いつ間違ったのか」といったタイミングを特定する手がかりになります。特に reset --hard
や rebase
のような破壊的な操作を行った後に、元の状態に戻したい場合に非常に頼りになるコマンドです。
💡 補足:HEADとは?
GitにおけるHEAD
は「今現在、自分が作業している場所(コミット)」を指すポインタです。基本的には現在のブランチの最新コミットを指していますが、checkout
やreset
によってこの位置が動きます。HEAD
がどこを指しているかによって、次に行われるコミットの位置も変わります。
基本構文
git reflog
reflogの出力例
% git reflog
d0c71f3 (HEAD -> main) HEAD@{0}: commit (amend): 3回目のcommitと4回目の編集
adaf5d2 HEAD@{1}: rebase (finish): returning to refs/heads/main
adaf5d2 HEAD@{2}: rebase (squash): 3回目のcommit
a335901 HEAD@{3}: rebase (reword): 3回目のcommit
ffe0d9b HEAD@{4}: rebase: fast-forward
2400efa HEAD@{5}: rebase (start): checkout HEAD~4
53d46bb HEAD@{6}: commit: 5回目のcommit
fadce32 HEAD@{7}: commit: 4回目のcommit
ffe0d9b HEAD@{8}: commit: 3回目のcommit
2400efa HEAD@{9}: commit: 2回目のcommit
eb84fe0 HEAD@{10}: commit (initial): 1回目のcommit
%
一番上の行が最新のHEADの位置です。この例では、5回commitをした後、rebaseコマンドでcommit履歴を編集しています。
このように HEAD@{0}
は現在の状態、HEAD@{1}
はその1つ前の状態、というようにHEADの移動履歴が記録されています。各操作に応じて、「commit」「reset」「checkout」「merge」などのアクション名とともに、どのような動きがあったかが表示されます。
▶︎ git reset
:指定した履歴まで戻るコマンド
reflog
で目的の過去状態を確認できたら、その履歴のポイントに戻るために使うのが git reset
コマンドです。reset
は Git の中でも非常に強力な履歴操作コマンドで、作業ディレクトリ、インデックス(ステージ)、HEAD の位置を意図した状態に戻すことができます。
git reset
の3つのモード
git reset
には以下の3種類のオプションがあり、それぞれ影響範囲が異なります:
--soft
:- HEAD だけを指定した位置に戻す
- インデックス(ステージ)や作業ツリーはそのまま
- 使いどころ:履歴だけ巻き戻して、変更内容は残したいとき
--mixed
(デフォルト):- HEAD とインデックスを指定した位置に戻す
- 作業ツリーはそのまま
- 使いどころ:コミットとステージの状態を一度クリアにしたいとき
--hard
:- HEAD、インデックス、作業ツリーすべてを完全に巻き戻す
- 使いどころ:完全にその時点の状態に戻したいとき(破壊的なので慎重に!)
使用例
実際に、先ほどreflogで確認した履歴を遡って、rebaseのコマンドが実行される前まで戻ってみます。「53d46bb HEAD@{6}: commit: 5回目のcommit」このcommitまで戻ってみます。そのためコマンドは下記になります。
git reset --hard HEAD@{6}
# rebaseする前まで戻る
% git reset --hard HEAD@{6}
HEAD is now at 53d46bb 5回目のcommit
tamon1028@MaedaTamonMacBook git-sample % git reflog
53d46bb (HEAD -> main) HEAD@{0}: reset: moving to HEAD@{6}
d0c71f3 HEAD@{1}: commit (amend): 3回目のcommitと4回目の編集
adaf5d2 HEAD@{2}: rebase (finish): returning to refs/heads/main
adaf5d2 HEAD@{3}: rebase (squash): 3回目のcommit
a335901 HEAD@{4}: rebase (reword): 3回目のcommit
ffe0d9b HEAD@{5}: rebase: fast-forward
2400efa HEAD@{6}: rebase (start): checkout HEAD~4
53d46bb (HEAD -> main) HEAD@{7}: commit: 5回目のcommit
fadce32 HEAD@{8}: commit: 4回目のcommit
ffe0d9b HEAD@{9}: commit: 3回目のcommit
2400efa HEAD@{10}: commit: 2回目のcommit
eb84fe0 HEAD@{11}: commit (initial): 1回目のcommit
# commit履歴を確認
% git log --oneline
53d46bb (HEAD -> main) 5回目のcommit
fadce32 4回目のcommit
ffe0d9b 3回目のcommit
2400efa 2回目のcommit
eb84fe0 1回目のcommit
git log
でcommit履歴を確認したところ、rebaseコマンドがなかったことになり、commitの数も5回で終了しています。このように書くことで、reflog
で記録された「指定した状態」に完全に戻すことができます。
他にも、変更を保持したい場合には次のように使えます:
git reset --soft HEAD@{6}
このコマンドで、HEAD は過去のコミットに戻りますが、変更内容はステージに残ったままになります。続けて git commit
を実行すれば再コミットが可能です。
⚠️ 注意点
--hard
を使うと作業ツリーの変更が完全に失われるため、事前にgit stash
を使って退避しておくことを強く推奨します。reset
の対象はあくまで「現在のブランチ」に対してのみです。他ブランチに影響はありません。
このように、reflog
× reset
の組み合わせは、「過去の状態に戻りたい」というニーズに応える最も基本で確実な手段です。
ハマったときの復旧フロー
Gitで「やってしまった!」と気づいたとき、パニックにならずに以下の手順を踏むことで、状況を冷静に整理し、安全に復旧できます。
git status
で現在の状態を確認
まずは今の作業ディレクトリとインデックスの状態を確認します。未コミットの変更や、どのブランチにいるかを把握しておくことで、以降の対応方針が決まります。git log
/git reflog
で履歴を調査git log
では通常のコミット履歴を確認できますが、失われたように見えるコミットや移動履歴を含めて確認するにはgit reflog
が重要です。操作ごとにどのコミットへHEADが移動したかが記録されているので、間違えたタイミングを特定できます。- 戻したい状態の
HEAD@{n}
を見つけるreflog
を確認し、望ましい状態にいたタイミング(例:HEAD@{3}
)を探します。コメント欄には操作の種類(例:commit
やreset
)が記載されており、状況の見極めに役立ちます。 git reset
を使ってHEADを戻す
状況に応じて--soft
(変更を残す)、--mixed
(インデックスだけクリア)、--hard
(すべて巻き戻す)のいずれかを選んでreset
します。git reset --hard HEAD@{3}
コミットや作業ツリーの状態も含めて、過去の任意の地点に戻すことが可能です。- 作業内容を退避したい場合は
git stash
を活用
もし変更中のファイルを保持しておきたいなら、reset
や他の操作の前にgit stash
を使って一時的に退避させておきましょう。こうすることで、リスクを最小限に抑えながら安全に操作できます。 - 必要であれば一時ブランチを切っておく
不安な場合は、reflog
で戻したいポイントをブランチ化してから確認するのも手です:git branch recovery HEAD@{3}
これで現在の作業ブランチに影響を与えず、復旧作業を試すことができます。
ケース別対応まとめ
トラブル内容 | 対応コマンド | HEAD の移動履歴から復旧可能 |
---|---|---|
reset –hard で巻き戻した | git reflog → git reset --hard HEAD@{X} | HEAD の移動履歴から復旧可能 |
rebase で消えたコミット | git reflog → git branch tmp HEAD@{X} | 一時ブランチを切って確認 |
stash pop で消えた | git fsck --lost-found | 消えたように見えても復旧可能なことがある |
checkout . で変更を失った | git checkout HEAD -- ファイル名 | ファイル単位での復元が可能 |
💡 補足:
git fsck
とは?
git fsck
は、Git の内部オブジェクトの整合性を確認するためのコマンドです。オプション--lost-found
を付けることで、通常の履歴からは参照されていない「孤立したオブジェクト」(例えば削除されたけどまだ残っているコミットやファイルなど)を.git/lost-found
ディレクトリに一覧表示できます。誤って削除した変更が復元できる可能性があります。
Gitのリカバリに使えるコマンドまとめ
とにかく”やっちまった”と思ったら、これらのコマンドで確認してみましょう。必ず解決の糸口が見つかるはずです。
git reflog # HEAD の履歴を見る
git reset --hard HEAD@{1} # 過去の状態に完全に戻す
git reset --soft HEAD@{1} # ステージングを残して戻す
git checkout HEAD -- file.txt # 特定ファイルを復元
git stash # 作業内容を一時退避
git fsck --lost-found # 消えたデータの可能性を探る
まとめ
Gitは履歴操作を誤っても、多くの場合 reflog
を使って元に戻すことができます。焦らずに状態を確認し、冷静に履歴をたどることが大切です。普段からこまめにブランチを切る・タグをつけるといった予防策もあわせて心がけましょう。