TL;DR
git addだけされて未コミットのファイル群がある場合、
ls -lT .git/objects/ | sort -k6
これで列挙されるディレクトリ群で、タイムスタンプが最新のものが、「最後にgit addした日時」である可能性が高い。
各ディレクトリ内のファイルはzlibで圧縮されているので、最新時刻のディレクトリ内のファイルを解凍した中身と、現在git add済みのファイルを比較して一致すれば間違いない。
背景
複数のGitリポジトリを1つのエディタで開いて色々と作業していた時に、git statusしたら、git addだけされて未コミットの状態(ステージングエリアに入っているだけ)のファイルがいくつか見つかりました。
そのファイルは読むためにエディタで開いていましたが、編集した記憶はありません。
以下のどっちなのか分からず、不安になりました。
- 実は操作ミスで今日編集してaddしちゃった
- かなり前にaddだけしてcommit忘れて放置しちゃった
ところが、git addした時刻って、gitの標準コマンドでは調べられないっぽいんですよね。お助けコマンドのgit reflogにも、reset記録は出てくるけどaddはノーマーク。
でも、条件によってはなんとか調べることができたので、一応メモとして書いておきます。
実演
.git/objects 内のディレクトリ群
.git配下のファイルを直接確認します。仕組みを理解しやすいために、git initから順番に実演します。
(シェルプロンプトはfish標準の「Informative Vcs」で、gitの状態が見やすくなっています。)
初期状態の.git/objects内には、info packの2ディレクトリがある。
~ $ mkdir repo
~ $ cd repo
~/repo $ git init
Initialized empty Git repository in /Users/the_red/repo/.git/
~/repo (main|✔) $ ls -l .git/objects/
total 0
drwxr-xr-x 2 the_red staff 64 8 4 11:32 info
drwxr-xr-x 2 the_red staff 64 8 4 11:32 pack
空ファイルを作ってみるが、これだけでは変わらず。
~/repo (main|✔) $ touch .gitignore
~/repo (main|…) $ ls -l .git/objects/
total 0
drwxr-xr-x 2 the_red staff 64 8 4 11:32 info
drwxr-xr-x 2 the_red staff 64 8 4 11:32 pack
git addしてみると、e6というフォルダが増えている!
~/repo (main|…) $ git add .
~/repo (main|●1) $ ls -l .git/objects/
total 0
drwxr-xr-x 3 the_red staff 96 8 4 11:33 e6
drwxr-xr-x 2 the_red staff 64 8 4 11:32 info
drwxr-xr-x 2 the_red staff 64 8 4 11:32 pack
コミットしてみると、さらにフォルダが2つ増えた。
addとcommitのどちらの場合もフォルダが作られるようですね。
コミットのハッシュ値は44becd9なので、44ディレクトリがコミットに相当するよう。
~/repo (main|●1) $ git commit -m 'Initial commit'
INFO[0000] opening .
INFO[0000] scan time: 301 microseconds
INFO[0000] No leaks found
[main (root-commit) 44becd9] Initial commit
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 .gitignore
~/repo (main|✔) $ ls -l .git/objects/
total 0
drwxr-xr-x 3 the_red staff 96 8 4 11:33 44
drwxr-xr-x 3 the_red staff 96 8 4 11:33 82
drwxr-xr-x 3 the_red staff 96 8 4 11:33 e6
drwxr-xr-x 2 the_red staff 64 8 4 11:32 info
drwxr-xr-x 2 the_red staff 64 8 4 11:32 pack
しかし11:33のディレクトリが3つもあって、順番が分からない。秒まで知りたい。
Macでは-lTオプションを付けると秒まで出してくれる。
(Linuxでは--full-timeらしい)
~/repo (main|✔) $ ls -lT .git/objects/
total 0
drwxr-xr-x 3 the_red staff 96 8 4 11:33:52 2022 44
drwxr-xr-x 3 the_red staff 96 8 4 11:33:52 2022 82
drwxr-xr-x 3 the_red staff 96 8 4 11:33:25 2022 e6
drwxr-xr-x 2 the_red staff 64 8 4 11:32:35 2022 info
drwxr-xr-x 2 the_red staff 64 8 4 11:32:35 2022 pack
~/repo (main|✔) $
あとは、ディレクトリ順のソートを時系列にソートしたい。普通はディレクトリがもっともっと多いので、目視ではとても探せないので。
パイプでsort -k6を実行すると、スペース区切りで左から6番目の「8(月)」以降だけを見てソートしてくれる。
~/repo (main|✔) $ ls -lT .git/objects/ | sort -k6
total 0
drwxr-xr-x 2 the_red staff 64 8 4 11:32:35 2022 info
drwxr-xr-x 2 the_red staff 64 8 4 11:32:35 2022 pack
drwxr-xr-x 3 the_red staff 96 8 4 11:33:25 2022 e6
drwxr-xr-x 3 the_red staff 96 8 4 11:33:52 2022 44
drwxr-xr-x 3 the_red staff 96 8 4 11:33:52 2022 82
いい感じ!
ためしに、ファイルの中身を書き換えてまたaddしてみる。
~/repo (main|✔) $ echo .env >> .gitignore
~/repo (main|✚1) $ git add .
~/repo (main|●1) $ ls -lT .git/objects/ | sort -k6
total 0
drwxr-xr-x 2 the_red staff 64 8 4 11:32:35 2022 info
drwxr-xr-x 2 the_red staff 64 8 4 11:32:35 2022 pack
drwxr-xr-x 3 the_red staff 96 8 4 11:33:25 2022 e6
drwxr-xr-x 3 the_red staff 96 8 4 11:33:52 2022 44
drwxr-xr-x 3 the_red staff 96 8 4 11:33:52 2022 82
drwxr-xr-x 3 the_red staff 96 8 4 11:40:27 2022 4c
最下部に、新しいディレクトリ4cってのが出来てますね。
ここまでで、以下の2点が確認できたことになります。
- 最終のadd時刻は
8/4 11:40:27(4c) - コミット
44becd9直前のadd時刻は8/4 11:33:25(e6)
ls -lT .git/objects/ | sort -k6
これで列挙されるディレクトリ群で、タイムスタンプが最新のものが、「最後にgit addした日時」である可能性が高い。
ここまで確認すればほぼ十分な気はするけど、さすがにタイムスタンプだけでは不安だったりもするので、もう少し深堀りする方法も次に書きます。
.git/objects/**/ 内のファイル群
もっと確実に調べたいなら、該当ディレクトリ(今回は4c)内をさらに確認してあげると良い。
~/repo (main|●1) $ ls -lT .git/objects/4c/
total 8
-r--r--r-- 1 the_red staff 20 8 4 11:40:27 2022 49bd78f1d08f2bc09fa0bd8191ed38b7dce5e3
ファイルが1つある。
add済みのファイルが複数ある場合は、ここにも複数ファイルができる。
catで見てあげれば良いかと思いきや、、、
~/repo (main|●1) $ cat .git/objects/4c/49bd78f1d08f2bc09fa0bd8191ed38b7dce5e3
xK��OR0e�K�+�=v⏎
バイナリらしく、そのままでは見れない。
fileコマンドで正体を確認。
~/repo (main|●1) $ file .git/objects/4c/49bd78f1d08f2bc09fa0bd8191ed38b7dce5e3
.git/objects/4c/49bd78f1d08f2bc09fa0bd8191ed38b7dce5e3: zlib compressed data
zlib形式で圧縮されたデータとのこと。
zipとかgzipならOS標準コマンドで解凍できるんだけど、
zlibを解凍するには、別途ツールが必要。僕はこれをインストールしました。
brew tap ebc-2in2crc/zlibcmd
brew install zlibcmd
これでzlibコマンドが使えるようになったので、解凍してみる。
~/repo (main|●1) $ cat .git/objects/4c/49bd78f1d08f2bc09fa0bd8191ed38b7dce5e3 | zlib --decompress
blob 5.env
-
blobがファイルタイプ、 -
5が容量(改行入れて5バイト) -
.envがファイルの中身
ですね。今回はこれで間違いない!
ちなみにファイル名.gitignoreは、別の場所で管理されています。
もっとちゃんとやろうとすると色々とあるんだろうけど、今回はこれくらいで止めておきますw
git gcには要注意
僕はほとんど使わないんですけど、git gcというコマンドで、
一時ファイルを綺麗に掃除してgitの動作を軽くすることができます。
これがまさに、今回調べた.git/objects配下を消すという行為なので、
git gcしてしまったら、この記事の調査方法は使えなくなりますのでご注意!
(git reflogも使えなくなります)
~/repo (main|●1) $ ls -lT .git/objects/ | sort -k6
total 0
drwxr-xr-x 2 the_red staff 64 8 4 11:32:35 2022 info
drwxr-xr-x 2 the_red staff 64 8 4 11:32:35 2022 pack
drwxr-xr-x 3 the_red staff 96 8 4 11:33:25 2022 e6
drwxr-xr-x 3 the_red staff 96 8 4 11:33:52 2022 44
drwxr-xr-x 3 the_red staff 96 8 4 11:33:52 2022 82
drwxr-xr-x 3 the_red staff 96 8 4 11:40:27 2022 4c
~/repo (main|●1) $ git gc
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Writing objects: 100% (4/4), done.
Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
~/repo (main|●1) $ ls -lT .git/objects/ | sort -k6
total 0
drwxr-xr-x 4 the_red staff 128 8 4 12:19:52 2022 info
drwxr-xr-x 4 the_red staff 128 8 4 12:19:52 2022 pack
参考リンク
Gitのステージング領域の正体を探る
https://engineering.mercari.com/blog/entry/2017-04-06-171430/
ファイル時刻の秒単位まで取得
https://tech.withsin.net/2016/12/26/ls-full-time/
おわりに
冒頭に書いた疑問
- 実は操作ミスで今日編集してaddしちゃった
- かなり前にaddだけしてcommit忘れて放置しちゃった
は、後者であることが分かりました。
add分は念の為別ブランチに退避してGitHubでブランチをプロテクトして、
メインのブランチは最終コミットに戻して、安心して開発を再開できました。
ごくまれにこういうケースあると思うので、誰かの役に立てたら嬉しいっす。
ではまた〜。