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でブランチをプロテクトして、
メインのブランチは最終コミットに戻して、安心して開発を再開できました。
ごくまれにこういうケースあると思うので、誰かの役に立てたら嬉しいっす。
ではまた〜。