HN 日本語サマリー

← 一覧へ戻る
プログラミング

Gitコミットのサイズはどれくらいか?

How big is a Git commit? (ratfactor.com)

21 pointsby theanonymousone6 コメント

要約

Gitコミットのサイズは、格納されるデータの圧縮率とGitの内部的なオブジェクト管理方式に依存します。小さな変更でも数キロバイトのオーバーヘッドが発生する一方、大きなファイルや多数のファイルを含むコミットでは、圧縮により元のファイルサイズの10%から50%程度に収まることが実験で示されています。Gitは最終的にパックファイル化することで、オブジェクト間の冗長性を排除し、さらに効率的なストレージを実現します。

全文翻訳

Gitに戻りましょう。短い答えは「場合による」です。Gitは「オブジェクト」をzlib圧縮データとして格納します。そのため、コミットの正確なサイズは、ファイルの圧縮可能性(基本的にファイル内の繰り返し量に依存する関数)によって決まります。https://git-scm.com/docs/gitformat-loose もう一つの要因は、Gitが最終的にオブジェクトファイルをさらに効率的なパックファイルに変換し、オブジェクト間の冗長性を排除することで追加のスペースを節約することです。https://git-scm.com/book/en/v2/Git-Internals-Packfiles 遊びで、いくつかの小さな実験を行いました(すべてパックファイルが作成されていない「ルーズオブジェクト」の状態です)。結果は以下の通りです。 64,828 バイト - git init 47,640 バイト - 250個の小さなファイルをコミット 17,145 バイト - 50個のファイルがあるディレクトリ内のファイルに3バイトの変更をコミット 8,709 バイト - 単一ファイルリポジトリに変更を3バイトコミット 297,873 バイト - 583,840 バイトのバイナリファイルをコミット 1,622,188 バイト - 16,415,223 バイトの巨大なソースファイルをコミット このように、非常に小さなコミットではキロバイト単位のオーバーヘッドが見られます。その正確な量は、変更に関連するファイル/ディレクトリツリーのサイズによって異なります。ファイル数が少ない大きなコミットの場合、.gitに格納されるデータは、圧縮されていないコミット済みファイルの50%から10%の範囲になります。つまり、非常に効率的です。 以下は、上記の数値につながった実験の実行ログです。 初期化、5つのディレクトリ、250個のファイル まず、新しく空のGitディレクトリ(.git)はどれくらいの大きさでしょうか? $ git init Initialized empty Git repository in /home/dave/tmp/foo/.git/ $ du -sb .git 64828 .git 約64 KBです。(duオプションは、sは要約、bはバイト単位での表示です。) 次に、ネストされたシェルループで、ディレクトリ内に多数の小さなファイルを作成します。 $ for d in {1..5};do mkdir $d; for f in {1..50};do echo foo > $d/$f; done; done これで、文字列「foo」を含む5つのディレクトリに250個のファイルができました。 $ ls 1 2 3 4 5 $ cat 2/45 foo これらをコミットします。 $ git add . $ git commit -m 'initial commit' ここで.gitのサイズを見てみましょう。 $du -sb .git 112468 .git コミットにより、初期の空のリポジトリに約47 KBが追加されました。補足:これを考えられる限り最もギークな方法で計算しました。標準のUnixコマンドであるdc(任意精度逆ポーランド記法デスクトップ計算機)を使用します('p'は結果を表示するためのprintコマンドです!)。 $ dc -e '112468 64828 - p' 47640 次に、1つのファイルの1つの小さな変更を行い、それをコミットします。 $ echo bar > 1/1 $ git commit -am 'changed 1/1' $ du -sb .git 129613 $ dc -e '129613 112468 - p' 17145 つまり、リポジトリへの約3バイトの変更を格納するために17 KBかかったということです。この重さはどこから来ているのでしょうか?もう一つ小さなコミットを行います。変更前は以下のようになっています。 $ du -b .git 4096 .git/objects/info 4096 .git/objects/pack 4115 .git/objects/25 4155 .git/objects/7b 4225 .git/objects/af 4115 .git/objects/57 4271 .git/objects/07 4177 .git/objects/61 4255 .git/objects/bb 4299 .git/objects/a8 45900 .git/objects 4336 .git/info 4096 .git/refs/tags 4137 .git/refs/heads 12329 .git/refs 4096 .git/branches 4411 .git/logs/refs/heads 8507 .git/logs/refs 12918 .git/logs 27538 .git/hooks 129613 .git 次に、ファイル1/1を再度変更して文字列「baz」を含ませ、コミットします。変更後は以下のようになります。 4096 .git/objects/info 4096 .git/objects/pack 4115 .git/objects/25 4253 .git/objects/81 <-- 新規 4 KB 4155 .git/objects/7b 4225 .git/objects/af 4115 .git/objects/57 4271 .git/objects/07 4177 .git/objects/61 4302 .git/objects/fc <-- 新規 4 KB 4255 .git/objects/bb 4115 .git/objects/76 <-- 新規 4 KB 4299 .git/objects/a8 4177 .git/objects/92 <-- 新規 4 KB 62747 .git/objects 4336 .git/info 4096 .git/refs/tags 4137 .git/refs/heads 12329 .git/refs 4096 .git/branches 4561 .git/logs/refs/heads <-- 0.1 KB追加 8657 .git/logs/refs 13218 .git/logs 27538 .git/hooks 146759 .git 単一ファイルコミットで4つの新しいオブジェクトができました。Julia "b0rk" Evans氏による新しいGitデータモデルのドキュメント(jvns.ca)の素晴らしい仕事のおかげで、これらの新しいオブジェクトが何であるかを特定できます。これらのファイルはすべてzlib圧縮データですが、git showはコミットだけでなく、ハッシュ識別子によって任意のオブジェクトの値を示すことができることに気づきました!そこで、これらのオブジェクトを表示するために次のようにしました。 $ ls .git/objects/81 b1a9d27494b40679d612b96bb6acaac83afa9c $ git show 81b1a9d27494b40679d612b96bb6acaac83afa9c 結果は以下の通りです。 81… は実際のコミットです fc… は変更されたディレクトリ1.(そのディレクトリ内のすべてのファイル、1から50までをリストしています)のツリーファイルです。 76… はファイル1/1の実際のコンテンツです(現在は文字列「baz」になっています)。 92… はリポジトリのルートディレクトリの別のツリーです。 これは理にかなっています。コミットが1つ、ツリーが2つ、ファイルが1つです。 小さなリポジトリ 単一の小さなファイルしかないリポジトリはどうでしょうか。コミットにはどれくらいのスペースが必要でしょうか? $ git init $ echo foo > foo $ git add foo $ git commit -m 'initial' $ du -sb .git 94208 $ echo bar > foo $ git -am 'change1' $ du -sb .git 102917 $ dc -e '102917 94208 - p' 8709 つまり、コミットあたり8 KBで、これはファイル数が多いツリーを持つリポジトリのサイズの半分程度です。これらの両方のサイズのかなりの部分はオーバーヘッドであり、より大きなコミットでは大幅な節約が見られると予想されます。 「大きな」ファイル? 「大きな」(580 KB)バイナリファイルを追加してみましょう。 $ cp /usr/bin/librewolf . $ du -b librewolf 583840 $ git add . $ git commit -m 'added librewolf' $ du -sb .git 400790 $ dc -e '400790 102917 - p' 297873 バイナリはかなり圧縮されるため、コミットサイズは追加されたファイル(580 KBのファイルに対して300 KB)よりも小さくなります。さらに良いことに、巨大な(16.2 MB)テキストソースファイルです。 $ cp /usr/src/linux...mask.h . $ du -b nbio_7_2_0_sh_mask.h 16415223 $ git add . $ git commit -m 'huge source' $ du -sb .git 2022978 $ dc -e '2022978 400790 - p' 1622188 つまり、16.4 MBのソースファイルをコミットするために1.6 MBかかったということです!テキストがうまく圧縮されることは予想していましたが、これは本当に印象的です。