我一直在阅读git书籍。在这本书中,我了解到git的功能是通过对你正在处理的文件进行快照来实现的,而不像其他版本控制系统那样使用增量。这带来了一些极好的好处。
然而,这让我想知道:随着时间的推移,包含这些快照的.git/文件夹不应该变得过大吗?有一些具有10,000个或更多提交记录和数百个文件的存储库。为什么git不会变得过大?
我一直在阅读git书籍。在这本书中,我了解到git的功能是通过对你正在处理的文件进行快照来实现的,而不像其他版本控制系统那样使用增量。这带来了一些极好的好处。
然而,这让我想知道:随着时间的推移,包含这些快照的.git/文件夹不应该变得过大吗?有一些具有10,000个或更多提交记录和数百个文件的存储库。为什么git不会变得过大?
bigfile.txt的文件内容,并将其存储在1000个不同的提交中,则所有这些提交中只有一个对象,被重复使用了1000次。(实际上,如果你将其重命名为hugefile.txt而不更改其内容,则新的提交仍然会继续重用原始对象——名称单独存储在树对象中。)
这都很好,但随着时间的推移,大多数项目中的大多数文件确实会积累变化。其他版本控制系统将使用增量编码,而不是存储每个文件的每个版本的全新副本,以避免分别存储每个文件的每个版本。如果 blob 对象是完整的且完好无损(尽管经过 zlib 压缩)的文件,则你的问题归结为:分开存储 blob 对象是否会使对象数据库增长比使用增量压缩的 VCS 更快?
答案是会,但 Git 仅在对象数据库的层次下使用增量压缩。对象在逻辑上是独立的。您提供某个对象的哈希 ID,就可以获取整个对象。但只有所谓的松散对象存储为简单的 zlib-deflated 文件。
正如Jonathan Brink 指出,git gc 清理未使用的对象。这对于保留的对象(例如旧版本的 hugefile.txt 等)没有帮助。但是,Git 在认为适当时自动运行 git gc ——它不仅修剪未引用的对象,还运行 git repack,构建或重新构建包文件。
一个pack文件可以存储多个对象,在pack文件内部,对象都是增量压缩的。Git会检查所有要放入单个pack文件的对象集合,并为所有N个对象选择一些B个对象作为增量基础。这些对象仅仅是zlib-deflated。剩余的N-B个对象被编码为增量,其中用到了它们早先依赖的增量基础或基础本身。因此,给定存储在pack文件中的对象的键,Git可以找到存储的对象或增量,并且如果存储的是增量,Git也可以找到底层的对象,一直到增量基础,并从而提取完整的对象。
因此,Git确实使用增量编码,但仅限于pack文件内部。它也不是基于文件,而是基于对象,所以(至少在理论上),如果您有巨大的树或提交消息中的长文本,则可以相互压缩。
即使这还不是全部的故事:对于通过网络传输,Git将构建所谓的“thin packs”。常规pack和thin pack之间的关键区别在于delta bases。给定一个常规pack文件和哈希ID,Git总是可以仅从该文件中检索完整对象。但是,对于thin pack,Git允许使用那些“不在该pack文件中”的对象(只要另一个Git声称它有这些对象)。接收方需要在接收时“修复”thin pack,但这允许git fetch和git push发送增量而不是完整快照。