reflog で取り戻す — 消えたように見えるコミット
rebase で作り直したとき、git branch -D で剥がしたとき、reset --hard で戻しすぎたとき。git log から消えたコミットは、まだ手元に残っています。手がかりは、HEAD が通った場所の記録である git reflog です。1-5 の reset の回で見た一番短い使い方の先として、1行の読み方、取り戻し方の2つ(付箋を貼り直すか、付箋ごと戻すか)、3つの場面での探し方、reflog でも取り戻せないものを見て、第3部を締めくくります。
- 上のターミナルは、実際に打ったときと同じ出力です(Git は英語で答えます)
- 下の中で起きていることは、コマンドの結果、Git の中の何が変わったかを図にしたものです。丸がコミット、黒い札がブランチ、ピンクの札が HEAD です
- どの付箋からも辿れなくなったコミットは、reflog に記録が残っているあいだ、グラフに薄く(点線で)残ります。本物の Git の
git log --allには出てこないコミットです - 右の説明をスクロールして読み進めると、左のターミナルでコマンドが1つずつ打たれます。
→キーでも1つずつ進みます(←で1つ戻ります。スマホでは「次へ」ボタン)。最後まで進んでから→を押すと次の区間へ移ります。黄色く光っているのが、そのコマンドで変わった場所です
1. おさらい — reflog は HEAD の足あと
1-5 git resetでは、reset --hard HEAD~2 で戻しすぎたあと、git reflog で1つ前の場所を確かめて git reset --hard HEAD@{1} で戻しました。reflog(リフログ)は、HEAD がいたコミットを新しい順に並べた記録で、HEAD@{0} が今、HEAD@{1} が1つ前です。
行が増えるのは HEAD が動いたときです。commit・switch・reset・merge・rebase・pull のたびに1行ずつ積まれます。push・fetch・git branch -D のように HEAD を動かさない操作では増えません。そして、reflog の行が指しているコミットは、付箋から外れていても Git が掃除しません。「消えた」と思ったら、まず git reflog です。
2. reflog の1行を読む
1行は、b365067 HEAD@{2}: rebase (pick): メモを作る のように3つの部分でできています。先頭の ID はその操作のあとで HEAD がいたコミット、HEAD@{2} は今から数えて何回前か、コロンの後ろはどの操作でそこへ来たかです。操作の書き方は決まっています。
| reflog の書き方 | 何が起きたか |
|---|---|
commit: バターを追加 | コミットして進んだ(commit (amend) は直前のコミットの作り直し) |
checkout: moving from main to feature | main から feature へ乗り換えた。git switch でもこう書かれる |
reset: moving to HEAD~2 | reset で付箋を動かした |
merge feature: Fast-forward | 早送りで取り込んだ。マージコミットを作ったときは Merge made by the 'ort' strategy.(3-1) |
rebase (start): checkout main | rebase を始めた。この上の行は作り直したコミット(3-2) |
rebase (pick): メモを作る | コミットを1つ作り直した |
rebase (finish): returning to refs/heads/feature | rebase が終わり、feature を新しい先頭へ付け替えた |
pull --ff-only: Fast-forward | git pull --ff-only で早送りした(2-3) |
番号には落とし穴があります。番号は「今から何回前か」なので、操作のたびに1つずつずれます。取り戻すコマンドを打つ直前に git reflog を見直すか、番号の代わりに先頭の ID を書いてください。
PowerShell では { } が特別な意味を持つので、git reset --hard HEAD@{1} は fatal: Needed a single revision で止まります。'HEAD@{1}' と引用符で囲んでください。Claude Code が PowerShell で打って同じエラーになったときも、原因はこれです。
3. 取り戻し方は2つ — 付箋を貼り直すか、付箋ごと戻すか
| コマンド | 何が動くか | 今いるブランチ | 書きかけ |
|---|---|---|---|
git branch 名前 HEAD@{n} | 見つけたコミットに新しい付箋を1枚貼る | そのまま | そのまま |
git reset --hard HEAD@{n} | 今のブランチの付箋をそこへ動かす | 付箋が動き、それまでの先頭は外れる | 消える |
迷ったら、まず git branch で付箋を貼ります。何も消さずに、薄く残っていたコミットを付箋から辿れるようにするだけだからです。git log --oneline --all で見比べ、今のブランチを戻すと決めてから reset に進んでも遅くありません。そのときは先に git status で書きかけが無いことを確かめます。
4. rebase をやめて元に戻す
rebase はコミットを相手の先へ作り直すので、ID が変わり、元のコミットは置き去りになります(3-2)。終わったあとで「前の形がよかった」となったら、reflog で rebase の前の場所を探します。
rebase をやめて元に戻す — rebase (start) の1つ下
featureは、mainの「卵を追加」から分かれたブランチです。ここでメモのコミットを2つ作ります。$ git switch feature Switched to branch 'feature'新しいファイル
notes.txtを作ります。$ echo "牛乳は低脂肪" > notes.txtステージに載せます。
$ git add notes.txt1つ目のコミット
10e0cadができました。$ git commit -m "メモを作る" [feature 10e0cad] メモを作る 1 file changed, 1 insertion(+) create mode 100644 notes.txtメモにもう1行足します。
$ echo "卵はLサイズ" >> notes.txt2つ目のコミット
2cd8380ができました。featureはmainと枝分かれしています。$ git commit -am "メモを足す" [feature 2cd8380] メモを足す 1 file changed, 1 insertion(+)3-2 の回で見た rebase です。2つのコミットが
mainの先に、新しい IDb365067・1cce4ffで作り直されました。元の2つは付箋から外れ、グラフに薄く残っています。$ git rebase main Successfully rebased and updated refs/heads/feature.ここで「やっぱり rebase する前に戻したい」となったとします。下から
commit→rebase (start)→rebase (pick)が2つ →rebase (finish)の順で、rebase する前にいた場所はrebase (start)の1つ下の行、HEAD@{4}の2cd8380です。$ git reflog -5 1cce4ff (HEAD -> feature) HEAD@{0}: rebase (finish): returning to refs/heads/feature 1cce4ff HEAD@{1}: rebase (pick): メモを足す b365067 HEAD@{2}: rebase (pick): メモを作る e5c0a62 HEAD@{3}: rebase (start): checkout main 2cd8380 HEAD@{4}: commit: メモを足すまず、元のコミットに付箋
before-rebaseを貼ります。今いるfeatureには触らないので、何も消えません。薄かった2つのコミットが、付箋から辿れるようになりました。$ git branch before-rebase HEAD@{4}--allで見ると、rebase の後の2つと前の2つが両方並びます。どちらを残すかは、見比べてから決められます。$ git log --oneline --all 1cce4ff (HEAD -> feature) メモを足す b365067 メモを作る 2cd8380 (before-rebase) メモを足す 10e0cad メモを作る e5c0a62 (main) パンを追加 8b41d07 卵を追加 3f2a9c1 牛乳を追加featureそのものを rebase の前に戻すなら、git reset --hardで付箋を動かします。今度は rebase で作ったほうの2つが薄くなりました。書きかけが無いことをgit statusで確かめてから打つコマンドです。$ git reset --hard before-rebase HEAD is now at 2cd8380 メモを足すfeatureとbefore-rebaseが同じ2cd8380を指し、rebase する前の形に戻りました。before-rebaseは、要らなくなったらgit branch -dで剥がせます。$ git log --oneline --all 2cd8380 (HEAD -> feature, before-rebase) メモを足す 10e0cad メモを作る e5c0a62 (main) パンを追加 8b41d07 卵を追加 3f2a9c1 牛乳を追加
rebase (pick) の行は作り直したコミットの数だけ増えますが、rebase (start) の1つ下が「rebase を打つ直前にいた場所」なのはいつも同じです。コンフリクトで止まっている途中なら、3-2 の git rebase --abort で戻れます。
Claude Code は git rebase --autostash を延べ22回打っていました。rebase のあとで様子がおかしければ、「rebase の前の位置に before-rebase という付箋を貼って」と頼むのが安全です。
5. -D で剥がしたブランチを取り戻す
1-3 ブランチと HEADでは、取り込んでいないコミットがあるブランチを、-d は断り、-D は確認せずに剥がすのを見ました。剥がすと、そのブランチ自身の reflog(git reflog show menu)も一緒に消えます。それでも手がかりは2つ残ります。1つ目は Deleted branch menu (was d24492c). の was の後ろの ID、2つ目が reflog です。
-D で剥がしたブランチを貼り直す
献立を試しに書くブランチ
menuを作って乗ります。$ git switch -c menu Switched to a new branch 'menu'新しいファイル
menu.txtを作ります。$ echo "月曜はカレー" > menu.txtステージに載せます。
$ git add menu.txtmenuだけが1つ先へ進みました。「献立を書く」は、menuの付箋からしか辿れません。$ git commit -m "献立を書く" [menu d24492c] 献立を書く 1 file changed, 1 insertion(+) create mode 100644 menu.txtmainに戻ります。乗り換えるたびに、reflog にはcheckoutの行が増えます。$ git switch main Switched to branch 'main'もう要らないと思って、
-Dで付箋を剥がしました。(was d24492c)は「剥がす前はd24492cを指していた」という意味で、この ID がそのまま手がかりになります。「献立を書く」は薄くなりました。$ git branch -D menu Deleted branch menu (was d24492c).--allでも「献立を書く」は出てきません。menuの付箋が無くなったので、ブランチから辿る道はもうありません。$ git log --oneline --all e5c0a62 (HEAD -> main) パンを追加 8b41d07 卵を追加 3f2a9c1 牛乳を追加HEAD の reflog は残っています。
checkout: moving from menu to mainは「menuを離れた」行で、その1つ下の行が、離れる直前にmenuでいた場所です。HEAD@{1}のd24492cが「献立を書く」です。$ git reflog -3 e5c0a62 (HEAD -> main) HEAD@{0}: checkout: moving from menu to main d24492c HEAD@{1}: commit: 献立を書く e5c0a62 HEAD@{2}: checkout: moving from main to menuそこに、同じ名前の付箋を貼り直します。
git branch menu d24492cと ID で書いても同じです。今いるmainには触りません。$ git branch menu HEAD@{1}「献立を書く」が戻りました。
-Dで剥がしても、コミットは付箋が外れていただけです。$ git log --oneline --all d24492c (menu) 献立を書く e5c0a62 (HEAD -> main) パンを追加 8b41d07 卵を追加 3f2a9c1 牛乳を追加
探し方は、そのブランチを離れた行(checkout: moving from menu to …)の1つ下を見るです。離れる直前、HEAD はそのブランチの先頭にいたからです。乗った行(moving from main to menu)の ID は乗った時点の場所なので、そのあとコミットを重ねていれば先頭ではありません。一度も乗らずに剥がしたブランチは HEAD の reflog に出てこないので、was の ID が頼りです。
6. reset のあとで作業を進めてしまったら
1-5 の場面は、reset の直後に気づいたので HEAD@{1} で済みました。気づかないまま次の作業をコミットすると、番号がずれるうえに、reset --hard で戻すと今度は新しいコミットが外れます。
reset のあとで作業を進めてしまったら
1-5 の reset の回と同じ始まりです。「バターを買う」を1行足します。
$ echo "バターを買う" >> todo.txtmainがfbb4973の「バターを追加」に進みました。$ git commit -am "バターを追加" [main fbb4973] バターを追加 1 file changed, 1 insertion(+)1つだけ戻すつもりが、2つ戻してしまいました。1-5 では、ここですぐ
git reset --hard HEAD@{1}で戻しました。今回は気づかずに作業を続けます。$ git reset --hard HEAD~2 HEAD is now at 8b41d07 卵を追加新しいファイル
notes.txtを作ります。$ echo "メモ: 牛乳は低脂肪" > notes.txtnotes.txtをステージに載せます。$ git add notes.txtmainは「卵を追加」の上に「メモを作る」を積みました。「パンを追加」と「バターを追加」は薄いままです。$ git commit -m "メモを作る" [main e672214] メモを作る 1 file changed, 1 insertion(+) create mode 100644 notes.txtHEAD@{1}はもう「バターを追加」ではありません。そのあとコミットしたので番号が1つずれ、HEAD@{1}は reset の行(8b41d07)になりました。戻したいのはHEAD@{2}のfbb4973です。$ git reflog -3 e672214 (HEAD -> main) HEAD@{0}: commit: メモを作る 8b41d07 HEAD@{1}: reset: moving to HEAD~2 fbb4973 HEAD@{2}: commit: バターを追加ここで
git reset --hard HEAD@{2}と打つと、今度は「メモを作る」がmainから外れます。両方残したいので、rescueという付箋を貼ります。mainはそのままです。$ git branch rescue HEAD@{2}「メモを作る」と「バターを追加」の2本が、どちらも付箋から辿れるようになりました。2本をつなぐなら、3-1 の merge か 3-2 の rebase です。
$ git log --oneline --all e672214 (HEAD -> main) メモを作る fbb4973 (rescue) バターを追加 e5c0a62 パンを追加 8b41d07 卵を追加 3f2a9c1 牛乳を追加
今のブランチに守りたいコミットがあるときは、reset ではなく branch で付箋を貼ります。2本ができたら、どちらかに寄せるか、3-1 の merge や 3-2 の rebase でつなぎます。
7. reflog でも取り戻せないもの
reflog が覚えているのは HEAD が指したコミットだけです。次の3つは取り戻せません。
一度もコミットしていない書きかけ
1-5 の reset --hard も、1-4 の git checkout -- ファイル も、コミットしていない変更を消します。消えたあとで reflog を見ても、何も見つかりません。
reflog にも残らないもの — コミットしていない書きかけ
「バターを買う」を1行足します。
$ echo "バターを買う" >> todo.txtコミットしました。コミットになったものは、reflog に記録されます。
$ git commit -am "バターを追加" [main fbb4973] バターを追加 1 file changed, 1 insertion(+)続けて「チーズを買う」を書き足します。まだコミットしていない書きかけです。
$ echo "チーズを買う" >> todo.txt行き先を書かない
git reset --hardは、付箋を動かさずに、ステージと作業ツリーを HEAD の中身で上書きします。$ git reset --hard HEAD is now at fbb4973 バターを追加「チーズを買う」は消えました。
$ cat todo.txt 牛乳を買う 卵を買う パンを買う バターを買う増えたのは
reset: moving to HEADの1行だけで、指しているのは「バターを追加」と同じfbb4973です。reflog はコミットの記録なので、書きかけの「チーズを買う」はどの行にもありません。$ git reflog -2 fbb4973 (HEAD -> main) HEAD@{0}: reset: moving to HEAD fbb4973 HEAD@{1}: commit: バターを追加もう一度、書きかけの「ジャムを買う」を足します。
$ echo "ジャムを買う" >> todo.txtgit checkout -- ファイルは、そのファイルの書きかけを捨てます(1-4 の回)。HEAD は動きません。$ git checkout -- todo.txt Updated 1 path from the index今度は reflog に1行も増えていません。HEAD が動かない操作は記録されないので、消えた書きかけを探す手がかりはどこにも残りません。
$ git reflog -2 fbb4973 (HEAD -> main) HEAD@{0}: reset: moving to HEAD fbb4973 HEAD@{1}: commit: バターを追加
書きかけを守るには、消える操作の前にコミットするか、1-7 stashで退避しておくしかありません。小さな「作業中」のコミットでもかまいません。コミットになっていれば、あとで付箋から外しても reflog から拾えます。
ほかの PC の記録
reflog は手元の PC の、そのリポジトリの中だけの記録で、push しても GitHub には送られません。3-3 force pushで Windows から force push したせいで Mac のコミットが GitHub から外れたなら、見に行く先は Mac です。Mac の main がまだそのコミットを指しているか、Mac の git reflog に残っています。force push の前に Windows で fetch していたなら、Windows の git reflog show origin/main(追跡ブランチの reflog。この教材の画面では再現していません)にも残っています。
時間がたったもの
Git はときどき自動で片付け(git gc)をし、古い記録を消します。既定では reflog の行はおよそ90日、付箋から外れたコミットを指す行は30日で消され、そのあとコミット本体も掃除されます。取り戻すなら、気づいたその日に付箋を貼るのが確実です。
8. Claude Code の許可画面で見かけたら
| コマンド | 何が起きるか | 消えうるもの |
|---|---|---|
git reflog / git reflog -10 | HEAD が通った場所を見るだけ | なし |
git branch 名前 HEAD@{n} | 見つけたコミットに付箋を1枚足す。今のブランチ・作業ツリーには触らない | なし(同じ名前の付箋があれば止まる) |
git reset --hard HEAD@{n} | 今のブランチの付箋を動かし、ステージと作業ツリーも上書きする | コミットしていない書きかけ。それまでの先頭は付箋から外れる(reflog に残る) |
git branch -D 名前 | 確認せずに付箋を剥がす。そのブランチの reflog も消える | 注意 そのブランチにしか無いコミットへの道筋(HEAD の reflog が残るあいだは戻せる) |
見分け方は、付箋を足すだけか、今のブランチの付箋を動かすかです。取り戻しの場面で git reset --hard HEAD@{n} を求められたら、先に git status を見て、迷うなら「まず git branch で付箋を貼って」と頼み直してください。
まとめ
git reflogは HEAD が通った場所の記録。付箋から外れたコミットも、reflog の行が指しているあいだは消えない- 1行は「操作のあとで HEAD がいた ID」「何回前か(
HEAD@{n})」「何をしたか」。番号は操作のたびにずれる git branch 名前 HEAD@{n}は付箋を足すだけで安全。git reset --hard HEAD@{n}は今のブランチごと戻し、書きかけを消す- rebase の取り消しは
rebase (start)の1つ下。-Dで剥がしたブランチはwasの ID か、そのブランチを離れた行の1つ下 - コミットしていない書きかけ、ほかの PC の記録、およそ90日(外れたものは30日)を過ぎたものは取り戻せない
第3部では、歴史をつなぐ merge、作り直す rebase、GitHub の付箋を付け替える force push を見て、最後に取り戻し方を見ました。merge と rebase は途中なら --abort、終わったあとは reflog。force push で外れたコミットは、それを持つ PC の手元か reflog です。コミットになってさえいれば、ほとんど取り戻せます。許可画面で迷ったら、「書きかけが消えるか」と「GitHub の付箋が付け替わるか」の2点を見る。これが第3部で身につけたい見分け方です。
次の第4部では、Claude Code そのものに目を向けます。変更の確認 → ステージ → コミット → push の流れで Claude Code が打つコマンドを読み、ここまでの全コマンドを許可画面の「安全・注意・危険」の3段で整理し直します。最後に、複数の作業を並べて進める worktree を見ます。