How big is a Git commit?
回到 Git。简短的回答是:视情况而定。Git 将“对象”存储为 zlib 压缩数据。因此,提交的确切大小取决于文件的可压缩性(这基本上是文件中重复内容数量的函数)。另一个因素是,Git 最终会将对象文件转换为更高效的包文件(packfiles),通过消除对象间的冗余来节省额外空间。
Back to Git. Short answer: It depends. Git stores “objects” as zlib compressed data. So the exact size of a commit will depend on the compressibility of your files (which is basically a function of the amount of repetition in them). The other factor is that Git eventually turns object files into even more efficient packfiles, saving additional space by getting rid of redundancy across objects.
为了好玩,我做了一些小实验(全部使用“松散对象”,没有创建包文件)。结果如下:64,828 字节 - git init;47,640 字节 - 提交 250 个微小文件;17,145 字节 - 在包含 50 个文件的目录中对一个文件进行 3 字节的更改;8,709 字节 - 在单文件仓库中对文件进行 3 字节的更改;297,873 字节 - 提交一个 583,840 字节的二进制文件;1,622,188 字节 - 提交一个 16,415,223 字节的巨大源文件。
For fun, I did some little experiments (all “loose objects” with no packfiles created). Here’s the results: 64,828 bytes - git init; 47,640 bytes - committing 250 tiny files; 17,145 bytes - committing 3 bytes of change to a file in a directory of 50 files; 8,709 bytes - committing 3 bytes of change to a single-file repo; 297,873 bytes - committing a 583,840 byte binary file; 1,622,188 bytes - committing a huge 16,415,223 byte source file.
由此可见,对于非常小的提交,会有几千字节的开销——具体大小取决于所涉及的文件/目录树的大小。对于文件数量较少的大型提交,.git 中存储的数据量仅为未压缩提交文件大小的 10% 到 50%。换句话说,它非常高效。
So we see kilobytes of overhead for very tiny commits - exactly how much will depend on the size of the file/directory tree involved with the changes. For larger commits with small numbers of files, the stored data in .git will be anywhere from 50% to 10% of the uncompressed committed files. In other words, it’s extremely efficient.
以下是上述数字实验的运行日志。初始化,5 个目录,250 个文件。首先,一个全新的空 Git 目录 (.git) 有多大?$ git init。初始化空的 Git 仓库在 /home/dave/tmp/foo/.git/。$ du -sb .git 得到 64828。大约 64 Kb。(du 的选项是:s 表示汇总,b 表示显示字节。)
Below is the running log of the experiments resulting in the numbers above. Init, 5 directories, 250 files. First, how big is a brand new empty Git directory (.git)? $ git init. Initialized empty Git repository in /home/dave/tmp/foo/.git/. $ du -sb .git 64828. About 64 Kb. (The du options are: s to summarize and b to display bytes.)
现在,我将通过一对嵌套的 shell 循环在目录中创建一堆微小的文件:$ for d in {1..5};do mkdir $d; for f in {1..50};do echo foo > $d/$f; done; done。现在我有 5 个目录中的 250 个文件,内容都是字符串“foo”。我将提交它们:$ git add . $ git commit -m ‘initial commit’。让我们看看现在的 .git 有多大:$ du -sb .git 得到 112468。这次提交在初始空白仓库的基础上增加了约 47 Kb。
Now I’m going to make a bunch of tiny files in directories with a pair of nested shell loops: $ for d in {1..5};do mkdir $d; for f in {1..50};do echo foo > $d/$f; done; done. Now I have 250 files in 5 directories containing the string “foo”. I’ll commit them: $ git add . $ git commit -m ‘initial commit’. Let’s see how big .git is now: $ du -sb .git 112468. The commit has added about 47 Kb to the initial blank repo.
现在,我对一个文件进行微小的更改并提交它:$ echo bar > 1/1 $ git commit -am ‘changed 1/1’ $ du -sb .git 129613。计算得出增加了 17145 字节。所以,在我的仓库中存储大约 3 字节的更改需要 17 Kb。这些空间是从哪里来的?
Now I’ll make one tiny change to one file and commit it: $ echo bar > 1/1 $ git commit -am ‘changed 1/1’ $ du -sb .git 129613. Calculating the difference results in 17145 bytes. So that’s 17 Kb to store about 3 bytes of changes to my repo. Where is that weight coming from?
我将再做一次小提交。提交后,.git 目录增加了 4 个新对象。多亏了 Julia “b0rk” Evans 在 Git 数据模型文档方面出色的工作,我弄清楚了这些新对象是什么。虽然这些文件都是 zlib 压缩数据,但我发现 git show 可以通过哈希标识符显示任何对象的值,而不仅仅是提交!
I’m gonna do another little commit. After the commit, 4 new objects were added to the .git directory. Thanks to Julia “b0rk” Evans’s awesome work on the new Git data model documentation, I can figure out what these new objects are. Although these files are all zlib compressed data, I figured out that git show will show the value of any object by hash identifier, not just commits!
结果是:81… 是我的实际提交;fc… 是已更改目录 1 的树文件,列出了该目录中的所有文件(1 到 50);76… 是文件 1/1 的实际内容(现在是字符串“baz”);92… 是仓库根目录的另一个树对象。这说得通。有一个提交,两个树对象,以及一个文件。
And the results are: 81… is my actual commit; fc… is the tree file for the changed directory 1, listing all of the files in that directory (1 through 50); 76… is the actual contents of file 1/1 (which is now the string ‘baz’); 92… is another tree for the root directory of the repo. That makes sense. There’s a commit, two trees, and one file.
单文件仓库又如何呢?如果仓库只有一个小文件,提交需要多少空间?经过计算,每次提交大约需要 8 Kb,大约是拥有较大文件树的仓库大小的一半。我怀疑这两者的大部分大小都是开销,而更大的提交将看到显著的节省。
How about a repo with just a single little file? How much space does a commit require then? Calculating the difference results in 8 Kb per commit, about half the size of the repo with a larger tree of files. I suspect a lot of the size of both of these is overhead and larger commits will see substantial savings.
“大”文件呢?让我们添加一个“大”(580 Kb)的二进制文件。二进制文件压缩得相当多,所以提交比添加的文件小(580 Kb 的文件仅占用了 300 Kb)。更棒的是,一个巨大的(16.2 Mb)文本源文件:提交一个 16.4 Mb 的源文件仅需 1.6 Mb!我预料到文本会很好地压缩,但这确实令人印象深刻。
“Big” files? Let’s add a “big” (580 Kb) binary file. The binary compresses quite a bit, so the commit is smaller than the added file (300 Kb for a 580 Kb file). Even better, a huge (16.2 Mb) text source file: that’s 1.6 Mb to commit a 16.4 Mb source file! I expected the text to compress nicely, but that’s really impressive.