如果git基于文件的快照,为什么.git/目录不会随着时间变得非常庞大?

13

我一直在阅读git书籍。在这本书中,我了解到git的功能是通过对你正在处理的文件进行快照来实现的,而不像其他版本控制系统那样使用增量。这带来了一些极好的好处。

然而,这让我想知道:随着时间的推移,包含这些快照的.git/文件夹不应该变得过大吗?有一些具有10,000个或更多提交记录和数百个文件的存储库。为什么git不会变得过大?


虽然不是完全重复,但这篇文章解释了Git如何使用增量压缩技术:https://dev59.com/PF4c5IYBdhLWcg3wLXvI - torek
可能是Git如何存储文件?的重复问题。 - phd
https://stackoverflow.com/questions/33455666/git-why-exactly-is-the-claim-git-is-based-on-differences-between-files-wrong - phd
1个回答

22
这里的技巧在于这个说法:
Git 通过对你使用的文件进行快照而不是像其他版本控制系统一样使用增量来进行操作。
既真又假!
Git 的主要对象数据库——一个键值存储——存储了四种对象类型。我们不需要在这里讲解所有细节;我们只需要指出,文件或更准确地说是文件内容存储在blob对象中。接着,commit对象间接地引用 blob 对象,因此如果你有一个名为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发送增量而不是完整快照。

这里的“deflated”是指压缩还是未压缩版本? - user13267

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接