RubyのGitリポジトリを分析するメモ
前Rubyのsvn mirrorを作ったときは超巨大コミットで失敗したけど、同様に超巨大コミットが残っていないか気になったので。
Gitリポジトリ上の全コミットを列挙する
EDIT: そもそも git log --all --full-history
で良いじゃん。。 --sparse
が必要かどうかは不明。
まぁ賢い方法は無限に有ると思うけど。。
HEAD
のtreeを調べる。このtreeを持つ捏造コミットを用意する。 git show --pretty=raw
かな。
oku@stripe /cygdrive/f/svnmirrors/git/ruby.git
$ git show --pretty=raw
commit 054ae999dc5dfcb182f407bffceec5a52ae7ff6c
tree 60eea099d3426a3daa11b7a4ae853dc28137888c
parent 9f9a0940ddd6dd9cec03a9e13c543cea14f8d38a
author Frank Schmitt <frank@frankschmitt.org> 1644968889 -0800
committer git <svn-admin@ruby-lang.org> 1652317617 +0900
[ruby/uri] Update file.rb
refを列挙する。 show-ref
コマンドでできる。
git show-ref --heads --tags --hash > RR
ファイル RR
には ref のhashだけ書かれるので、 -p <HASH>
の形式になるように行頭に -p
を足しておく。
... また、refが多すぎて xargs
で扱えないのでsplitしておく。
split -l 100 RR
xaa
〜 xal
までのファイルに分割されるので、それぞれをmergeしたコミットを捏造する。
cat xaa | xargs git commit-tree 60eea099d3426a3daa11b7a4ae853dc28137888c -m SUPERCOMMIT
捏造したコミットに rev-list
を掛ければ終わり。
git rev-list 6edd5b26862ee4aa7fa4dd57f9b09d793683a84a | sort | uniq > REVS
前にLinuxのリポジトリ解析でやった奴だな。
誕生日効果
oku@stripe /cygdrive/f/svnmirrors/git/ruby.git
$ cat RR | xargs git commit-tree 60eea099d3426a3daa11b7a4ae853dc28137888c
error: short object ID 5abafda is ambiguous
hint: The candidates are:
hint: 5abafda826 commit 2009-03-07 - add 64bit solaris library.
hint: 5abafdabdb commit 2008-06-15 - add tag v1_8_5_194
fatal: not a valid object name 5abafda
9fdf84eb1b94f50528f0da749b9a1cfff5e4ad75
7ケタ( 5abafda
)で衝突するのか。。
コミットのサイズインパクト
とりあえずテキストファイルについては圧縮も効くしたぶん無料って事で良いだろう。バイナリはデルタ圧縮がかかるとは言え追加されるだけでリポジトリのコストになる。
SOにちょうどやりたい事の質問があった。
git cat-file -s
はファイルサイズを出力し、 git diff-tree -r
でraw形式のtree diffから全てのdiffを取得して cat-file
に掛けている。raw diff formatはgitのドキュメントにある( https://git-scm.com/docs/git-diff-tree#_raw_output_format )。
バイナリだけに絞りたいなら、 --numstat
でサイズが -
になるファイルを探した方が楽かもしれない。でもtext diffは結局捨てることになるから無駄かな。。まぁ前Linuxの全diff取った( https://qiita.com/okuoku/items/9549da6dbfa00f85e232 )ときもCygwin上で30分とかだったので大した問題ではないと思うけど。
WebKitみたいな暴力的なリポジトリに対して実行したらどうなるのかは気になるな。
マージコミットの処理
マージコミットの処理はもっと悩ましい。とりあえず無視してしまうのが良いんだろうか。。 git diff-tree
には -c
オプションがあり、マージ中に編集されたファイルだけをdiffとして表示する機能がある。
例えば、このRubyのコミット https://github.com/ruby/ruby/commit/e0934947d3b5403ee51b0809c4b6c139eb99500a はdiffが存在し、parentのdiffのいずれでもないことがわかる。