git stash はコミットとしてデータ保存してるんだということを知って、調べると色々分かって面白かったという話。
きっかけはこのツイート
git cat-file -p refs/stash
— エロリツイート (@wand_ta) 2019年11月27日
するとわかるけど、タダのcommitなんだね
refs
ステージングされた変更とローカルの変更がある状態で stash する
コミットが二つできてる。WIP on ...というマージコミットと index on ...というコミット。
WIP on ...にはローカル変更が入った作業ツリーの状態が、index on...にはインデックスの状態が保存されている。
ほんとだ。ローカルの作業ツリーに対するコミットオブジェクトと、その親にインデックスの内容に対するコミットオブジェクトが作られてて、その親が変更元のHEADという構造なんですね。勉強になった。
— Yoichi Nakayama (@yoichi22) 2020年5月20日
reflog
複数 stash した場合はどこに行ってんだろうと思ったので探したら、reflogにあった。
refs/heads にあるのは stash@{0} の値のみで、stash のリストは logs/refs/stash に reflog として保持されていた。
— Yoichi Nakayama (@yoichi22) 2020年5月20日
untracked files
作業ツリー内に履歴管理されてないファイル(HEADのツリーに存在しないパス)がある場合、git stash のオプション -u か -a を指定すればそれも含めて保存してくれる。
オプションによって何が保存されるかは、こないだ整理したものを Qiita に書いてました:
というわけで、履歴管理されてないファイルがあるところで git stash して git log --oneline --graph stash すると、こんなグラフに。
3つの親を持つマージコミットができている。
untracked files も含めて git stash すると3つの親をもつマージコミットになるんだ。実用性のある3つ親のマージコミット初めて見た。
— Yoichi Nakayama (@yoichi22) 2020年5月20日
面白い。