コミット済みなら、reflogでreset直前の位置を探して戻せる
git reset --hardでブランチの先頭を戻しても、それまでのコミットはすぐには削除されません。HEADがどのコミットを指していたかは、手元のリポジトリのreflogに記録されています。git reflogでreset直前の行を探し、そのコミットIDにgit branchで名前を付ければ、git log --onelineに出なくなったコミットを再び参照できます。
戻せるのは一度コミットした内容です。addもコミットもしていなかった編集はreflogに記録されず、reset --hardで上書きされると戻せません。addだけした内容は、後の節のgit fsck --lost-foundで中身を取り出せる場合があります。
戻す操作にもgit reset --hardを使うため、実行前に2つを確かめます。git status --shortが何も表示しないこと(コミットしていない変更があると、それが新たに消えます)と、git log --oneline rescue..HEADが何も表示しないことです。後者に行が出る場合は、誤ってresetした後に同じブランチで新しいコミットを作っています。そのままreset --hard rescueを実行すると、その新しいコミットがブランチから外れるため、git switch rescueで移って作業するなど別の方法を選びます。
git reflog
git branch rescue <戻したいコミットID>
git log --oneline rescue
git status --short
git log --oneline rescue..HEAD
git reset --hard rescue練習用フォルダーを作り、そこへ移動する
以下はGitが使えるBash(WSLやGit Bashなど)用です。PowerShellへそのまま貼る例ではありません。実務のリポジトリではなく、まだ存在しないgit-reflog-practiceというフォルダーで試します。次の2行は1行ずつ実行し、エラーが出たらそこで止めてください。既に同名フォルダーがある場合は、別の新しい名前を選びます。
mkdir git-reflog-practice
cd git-reflog-practice3件のコミットを作り、reset --hardで2件消す
note.txtにbase、step 1、step 2の行を1行ずつ足し、3件のコミットにします。名前とメールアドレスは、この練習リポジトリ内だけに設定する例示値です。リモートへの接続や送信は行いません。
最後のgit reset --hard HEAD~2で、ブランチの先頭はBase fileへ戻ります。git log --onelineはBase fileの1件だけになり、note.txtもbaseの1行に戻ります。コミットIDは環境ごとに変わるため、以下の説明では件名で区別します。
git init
git config user.name 'Practice'
git config user.email 'practice@example.invalid'
printf 'base\n' > note.txt
git add -- note.txt
git commit -m 'Base file'
printf 'step 1\n' >> note.txt
git commit -am 'Add step 1'
printf 'step 2\n' >> note.txt
git commit -am 'Add step 2'
git log --oneline
git reset --hard HEAD~2
git log --oneline
cat note.txtgit reflogでreset直前の行を探す
git reflogは、HEADが移動した記録を新しい順に表示します。HEAD@{0}が現在、HEAD@{1}が1回前の位置です。この練習では次の4行が表示されます(コミットIDは環境ごとに異なります)。
端末で実行すると、各行のIDの後に(HEAD -> master)のようなブランチ名の表示が付く場合があります。下の表ではこれを省いています。ブランチ名はgitのバージョンや設定によりmainなどになります。
1行目の「reset: moving to HEAD~2」が、先ほどのresetです。その下の「commit: Add step 2」の行が、reset直前の先頭です。この行の左端のコミットIDを控えます。
HEAD@{1}などの番号は、HEADが動くたびに1つずつずれます。ブランチの切り替えや別のresetを挟んだ後は、番号ではなく行の内容(resetの直前のcommitの行)とコミットIDで戻す先を決めます。resetの直後であれば、resetが実行前の先頭を記録するORIG_HEADも同じコミットを指しますが、次のresetやmergeなどで書き換わります。
git reflog表は横にスクロールできます →
| 表示(左のIDは例) | 意味 |
|---|---|
| 8eb1b40 HEAD@{0}: reset: moving to HEAD~2 | 現在の位置(Base file) |
| 755da34 HEAD@{1}: commit: Add step 2 | reset直前の先頭。ここへ戻す |
| ea7dabb HEAD@{2}: commit: Add step 1 | 2件目のコミット |
| 8eb1b40 HEAD@{3}: commit (initial): Base file | 最初のコミット |
先にブランチ名を付け、中身を見てから戻す
控えたコミットIDにgit branch rescueで名前を付けます。以下の例では、resetの直後でまだHEADを動かしていないため、HEAD@{1}をそのまま指定しています。名前が付いたコミットは、reflogの番号がずれても、期限で記録が消えても、ブランチが残る限り参照できます。
git log --oneline rescueでAdd step 2、Add step 1、Base fileの3件が出ることを確かめます。次に、git status --shortとgit log --oneline rescue..HEADがどちらも何も表示しないことを見てから、git reset --hard rescueで現在のブランチの先頭をrescueと同じ位置へ戻します。rescue..HEADは「現在のブランチにあってrescueにないコミット」の一覧で、reset後に新しくコミットしていなければ空です。note.txtはbase、step 1、step 2の3行に戻ります。
戻した後のrescueは不要なので、git branch -d rescueで削除します。この練習ではrescueの内容がすべて現在のブランチに含まれているため、-dで削除できます。別のブランチで続きを作業したい場合は、resetで戻さずにgit switch rescueで移る方法もあります。
git branch rescue HEAD@{1}
git log --oneline rescue
git status --short
git log --oneline rescue..HEAD
git reset --hard rescue
git log --oneline
cat note.txt
git branch -d rescueaddだけした内容はgit fsck --lost-foundで取り出す
コミットせずにaddだけしたファイルは、reflogには出てきません。ただし、addした時点の中身はGitの内部に保存されています。git fsck --lost-foundを実行すると、どこからも参照されていない中身が.git/lost-found/other/に書き出されます。
次の例では、memo.txtをaddし、idea.txtは作っただけでaddせずにgit reset --hardを実行します。memo.txtは作業フォルダーから消えますが、addしたdraft Aの中身はlost-foundから読めます。ファイル名は元のmemo.txtではなく、中身から計算されたID(この例では659d723d7fa9d72ff46c43b41626444b7aac0f43)になります。実務のリポジトリでは無関係の古い断片も多数出るため、中身を開いて探す必要があります。
一度もaddしていないidea.txtはGitの管理外なので、reset --hardで消されずに残り、git status --shortで?? idea.txtと表示されます。一方、既にコミット済みのファイルへの編集をaddせずにreset --hardした場合は、その編集はどこにも保存されず、reflogでもfsckでも戻せません。この練習でnote.txtにstep 3を足してaddせずにreset --hardすると、step 3の行は戻りませんでした。
printf 'draft A\n' > memo.txt
git add -- memo.txt
printf 'draft B\n' > idea.txt
git reset --hard
git status --short
git fsck --lost-found
ls .git/lost-found/other/
cat .git/lost-found/other/*
printf 'step 3\n' >> note.txt
git reset --hard
cat note.txtreflogが使えない場面と、記録の期限
reflogは各リポジトリの手元の記録です。別のフォルダーへcloneし直したリポジトリや、別のPCには、元のreset前の記録は含まれません。Git公式の設定の説明では、作業フォルダーがあるリポジトリではreflogが既定で有効、作業フォルダーのないbareリポジトリ(送信先のサーバー側でよく使われる形)では既定で無効です。
記録には期限があります。Git公式のgit gcの説明では、reflogの項目は既定で90日、現在の先頭から辿れない項目(resetで消したコミットの行が該当します)は既定で30日より古いものがgit reflog expireで削除されます。日数はresetを実行した日ではなく、それぞれの行が記録された日時から数えます。addだけした内容はreflogの行を持たないため、git gcの既定では2週間より古いものが削除の対象になります。git gcは、コミットなどの通常操作でリポジトリが大きく増えたときに自動で実行されるため、期限が近いと気付かないうちに消える可能性があります。復旧を思い立ったら、他の作業より先にgit branchで名前を付けてください。
既にpushしたコミットをreset --hardで消し、さらに強制pushした場合は、送信先や他の人の環境と履歴が食い違います。その状態の復旧はこの記事では扱いません。push済みのコミットを取り消したいだけなら、関連記事「push済みのコミットを打ち消す:git revertで履歴を残したまま元に戻す手順」の方法を使います。push前の直近コミットを、変更を残したまま作り直す場合は「直前のコミットを取り消す:git reset --softで変更をステージに残す手順」を参照してください。
2026年10月6日、WSLのLinux・Git 2.53.0・非rootユーザーで掲載例を実行しました。reset後とrescueで戻した後の履歴とnote.txt、reflogの4行の件名、ORIG_HEAD、戻す前のrescue..HEADが空であること、lost-foundに書き出された中身とファイル名、addしなかった編集が戻らないことを照合しています。期限による削除は公式の説明に基づく記載で、日付を進めた検証は行っていません。ホスティングサービスへの送信や実務リポジトリでの動作も、この検証に含みません。