Git入門:バージョン管理のきほん
消したはずの作業を取り戻す(reflog)
この回でやること
reset --hard で消えたように見えたコミットを git reflog で取り戻す方法を学びます。reflog の読み方と HEAD@{n} の指定、reflog が手元にしか存在しないという性質まで確認しましょう。
- 読む 約 8 分
reset --hard で、半日分が消えた
ターミナル
$ git reset --hard HEAD~2
HEAD is now at 8f3c1a2 ログイン機能を追加
$ git log --oneline
8f3c1a2 ログイン機能を追加戻す数を 1 つ間違えました。エディタを見ても、書いたはずのファイルが元に戻っています。
ここで手を止めてください。消えた 2 つのコミットは、まだリポジトリの中に残っています。 無くなったのは、そこへたどり着くための道だけです。
コミットはいったん作られると .git の中に保存されます。git log が並べているのは、現在地からたどれるものだけです。reset は現在地を動かす操作なので、たどれなくなったものが見えなくなっただけなのです。
移動の記録から、消えたコミットを探す
道を教えてくれるのが git reflog です。現在地やブランチがどこを指していたかという、移動の記録が残っています。
ターミナル
$ git reflog
8f3c1a2 HEAD@{0}: reset: moving to HEAD~2
9a1f4c7 HEAD@{1}: commit: 検索フォームのテストを追加
3e8b2d5 HEAD@{2}: commit: 検索フォームを実装
8f3c1a2 HEAD@{3}: commit: ログイン機能を追加git log では見えなかった 9a1f4c7 と 3e8b2d5 が並んでいます。1 行の読み方は次のとおりです。
| 部分 | 意味 |
|---|---|
8f3c1a2 | そのとき現在地が指していたコミットの名前 |
HEAD@{0} | 何手前の状態か。{0} が今で、数字が大きいほど過去 |
reset: moving to HEAD~2 | そのとき実行した操作 |
行の後半に操作名が並ぶので、reflog は「事故の直前に自分が何をしたか」の記録でもあります。履歴が説明の付かない形になったら、まずここを上から読んでください。
上書きせずに、別の場所へ拾い上げる
見つけた名前から新しいブランチを作るのが、いちばん安全な救出です。今いるブランチを動かさずに、消えた状態を別の場所に確保できます。
ターミナル
$ git show 9a1f4c7 --stat
検索フォームのテストを追加
tests/search.test.js | 34 ++++++++++++++++++++++++++++++++++
$ git branch rescue 9a1f4c7
$ git switch rescue
Switched to branch 'rescue'先に git show で中身を確かめてから拾えば、取り返しの付かない操作を 1 つも挟まずに済みます。
HEAD@{3} のような番号でも指定できますが、番号は操作するたびにずれます。さきほど画面で見た番号は、打ち直した時点でもう別の行を指しています。控えるなら名前のほうにしてください。
入れ直すと、本当に消える
reflog には、知っておくべき性質があります。これは自分のパソコンの中にしかありません。 git push しても送られませんし、git clone しても相手の reflog は付いてきません。
ここから結論が出ます。事故のあとにフォルダを消して clone し直すと、救出できたはずのコミットが本当に失われます。会社のパソコンで消したものを、自宅のパソコンから探すこともできません。
保存期限もあります。どこからもたどれないコミットは 30 日ほどで整理の対象になるので、気づいたその日のうちに救出してください。
reflog にも救えないものがあります。一度も
git addしていない、コミットになっていない編集です。記録しているのは移動だけなので、コミットになる前の中身は対象外です。こまめにコミットしておく価値は、ここにもあります。
reset --hardで消えたコミットは、道が切れただけでリポジトリには残っているgit reflogは移動の記録。ここから消えたコミットの名前を見つけられる- 拾うのは
git branch <名前> <コミット名>。番号はずれるので名前で指定する - reflog は手元にしかない。事故のあとに clone し直すと本当に失われる