🪵

git reflogを日時で参照する

2021/11/15に公開

HEAD@{1}HEAD@{2}のような連番ではなく、日時で reflog を参照する方法について

reflog

gitはHEADとブランチの更新を .git/logs/ 以下にログとして記録しており、それを利用してHEADやブランチを以前の状態に戻すことができる。

どの状態に戻すかを選ぶ際には、git reflogでログ一覧を表示し、n=0,1,2,... と番号が振られたログから対象を選択する。

$ git reflog master
bcb4952 (HEAD -> master) master@{0}: reset: moving to bcb4952
ee82dd9 master@{1}: reset: moving to HEAD~~~~~
06c2533 master@{2}: reset: moving to HEAD~
5dc4526 master@{3}: merge match-etc: Merge made by the 'recursive' strategy.
06c2533 master@{4}: merge match-mapping: Merge made by the 'recursive' strategy.
bd11aaa master@{5}: reset: moving to bd11
fabf16d master@{6}: merge match-mapping: Fast-forward
bd11aaa master@{7}: merge black: Merge made by the 'recursive' strategy.
4e246a2 master@{8}: commit: import in the top level
4965c99 master@{9}: commit (amend): print unified diff of ast.dump outputs
a4bb0ad master@{10}: commit: print unified diff of ast.dump outputs

一覧の中から例えば対象として master@{3} を選んだ場合には、

$ git reset --hard master@{3}

としてブランチの状態を戻すことができる。

どれかはわからない。いつならわかるけど。

何番を選べば良いのかわからないという場合がある。例えば reset を何度もやっていて混乱してきたのでやり直したくなったけど、それがいくつ前の状態だったのかわからなくなってしまった場合。

もし、いつの状態に戻ればいいかがわかるなら、<refname>@{<n>} の代わりに <refname>@{<date>} を指定することができる。日時の指定は色々な形式が使えて、例えば

$ git log -1 --oneline "master@{Thu Nov 14 00:00:00 2021 +0900}"
5dc4526 Merge branch 'match-etc'
$ git reset --hard "master@{Thu Nov 14 00:00:00 2021 +0900}"

のように絶対的な時刻でも良いし、

$ git reset --hard master@{yesterday}

とか

$ git reset --hard "master@{2 hours ago}"

のように相対的な指定もできる。

ログに時刻を付けて表示する

上で書いたように、git log で見て、あ、これだ!とわかったならそれでいいが、履歴を時刻付きで表示して、その中から選びたいかもしれない。そういう時は reflog に date オプションをつけると、連番だったところに日時が表示される。

$ git reflog --date=default master
bcb4952 (HEAD -> master) master@{Sun Nov 14 10:34:24 2021 +0900}: reset: moving to bcb4952
ee82dd9 master@{Sun Nov 14 10:33:15 2021 +0900}: reset: moving to HEAD~~~~~
06c2533 master@{Sun Nov 14 10:33:11 2021 +0900}: reset: moving to HEAD~
5dc4526 master@{Thu Nov 11 22:16:16 2021 +0900}: merge match-etc: Merge made by the 'recursive' strategy.
06c2533 master@{Thu Nov 11 20:07:44 2021 +0900}: merge match-mapping: Merge made by the 'recursive' strategy.
bd11aaa master@{Thu Nov 11 20:07:33 2021 +0900}: reset: moving to bd11
fabf16d master@{Thu Nov 11 20:06:45 2021 +0900}: merge match-mapping: Fast-forward
bd11aaa master@{Wed Oct 27 05:15:29 2021 +0900}: merge black: Merge made by the 'recursive' strategy.
4e246a2 master@{Wed Oct 27 04:24:17 2021 +0900}: commit: import in the top level
4965c99 master@{Wed Oct 27 04:19:46 2021 +0900}: commit (amend): print unified diff of ast.dump outputs

相対的な時刻で表示したければ次のようにする。

$ git reflog --date=relative master
bcb4952 (HEAD -> master) master@{15 minutes ago}: reset: moving to bcb4952
ee82dd9 master@{16 minutes ago}: reset: moving to HEAD~~~~~
06c2533 master@{16 minutes ago}: reset: moving to HEAD~
5dc4526 master@{3 days ago}: merge match-etc: Merge made by the 'recursive' strategy.
06c2533 master@{3 days ago}: merge match-mapping: Merge made by the 'recursive' strategy.
bd11aaa master@{3 days ago}: reset: moving to bd11
fabf16d master@{3 days ago}: merge match-mapping: Fast-forward
bd11aaa master@{3 weeks ago}: merge black: Merge made by the 'recursive' strategy.
4e246a2 master@{3 weeks ago}: commit: import in the top level
4965c99 master@{3 weeks ago}: commit (amend): print unified diff of ast.dump outputs

dateオプションで他にどんな値が指定できるかについては、reflogのマニュアルの記述 git reflog show is an alias for git log -g --abbrev-commit --pretty=oneline; see git-log(1) for more information. にしたがって、git logのマニュアルのdateオプションのところを見ればよい。

役に立った事例

rebase してたらよくわからない状態になってしまったという相談を受けて、git reflog --date=default の出力を眺めることでどういう操作をしていたかを把握し、対処方法を見つけることができた。
ログに日時が添えられていると、同じ時間帯の複数の操作をひとかたまりと認識して、大まかな操作の流れを把握できる。次にそれぞれのかたまりの詳細を見ていくことで、操作履歴の全体を容易に理解できる。
n=0,1,2,...という連番でのっぺりと並べられた操作履歴を眺めるのと比べ、時間帯による構造が導入される分、理解しやすいのだと思った。

参考

Discussion