我一直在阅读 git 书。在这本书中,我了解到 git 是通过拍摄您使用的文件的快照而不是像其他 VCS 那样的增量来运行的。这有一些很好的好处。
然而,这让我想知道:随着时间的推移,包含这些快照的 .git/文件夹不应该变得太大吗?有些存储库具有 10,000 多个提交或更多,包含数百个文件。为什么 git 不会变大?
最佳答案
这里的诀窍是这个声明:
git functions through taking snapshots of the files you work with, instead of deltas like other VCSs
既真又假!
Git 的主要对象数据库 — key-value store — 存储四种对象类型。我们不需要在这里详细介绍;我们可以注意到文件——或者更准确地说,文件的内容——存储在 blob 对象中。提交对象然后(间接)引用 blob 对象,因此如果您有一些名为
bigfile.txt
的文件内容并将其存储在 1000 次不同的提交中,那么所有这些提交中只有一个对象,重复使用 1000 次。 (实际上,如果将其重命名为 hugefile.txt
而不更改其内容,则新提交将继续重用原始对象——名称单独存储在树对象中。)一切都很好,但是随着时间的推移,大多数项目中的大多数文件都会积累更改。其他 VCS 将使用 delta encoding 避免单独存储每个文件的每个版本,而不是存储每个文件的全新副本。如果一个 blob 对象是一个完整的、完整的(尽管是 zlib 压缩的)文件,那么您的问题可以归结为:单独的 blob 对象的积累不会使对象数据库的增长速度比使用增量压缩的 VCS 快得多吗?
答案是可以,但 Git 确实使用了增量压缩。它只是在对象数据库级别以下执行此操作。对象在逻辑上是独立的。您将某个对象的 key (哈希 ID)提供给 Git,然后您就可以取回整个对象。但是只有所谓的松散对象才存储为一个简单的 zlib 压缩文件。
作为 Jonathan Brink noted ,
git gc
清理未使用的对象。这对保留的对象没有帮助,例如旧版本的 hugefile.txt
或其他任何东西。但是 git gc
——Git 认为它可能合适时自动运行的代码——不仅仅是修剪未引用的对象。它还运行 git repack
,它构建或重新构建包文件。一个包文件存储多个对象,在一个包文件中,对象被增量压缩。 Git 会仔细检查将进入单个包文件的所有对象的集合,并且对于所有 N 个对象,选择其中的一些集合 B 用作增量基础。这些对象只是 zlib 压缩的。其余的 N-B 对象被编码为增量,针对基数,或针对使用这些基数的早期增量编码对象。因此,给定存储在包文件中的对象的键,Git 可以找到存储的对象或增量,如果存储的是增量,Git 也可以找到底层对象,一直到增量基数,并且因此提取完整的对象。
因此,Git 确实使用增量编码,但仅在包文件中使用。它也不是基于文件而是基于对象,所以(至少在理论上)如果你有巨大的树,或者提交消息中有很长的文本,它们也可以相互压缩。
尽管如此,这还不是全部:为了通过网络传输,Git 将构建所谓的瘦包。常规包装和薄包装之间的主要区别与那些三角洲基地有关。给定一个常规包文件和一个哈希 ID,Git 总是可以单独从该文件中检索完整的对象。但是,对于瘦包,Git 可以使用不在该包文件中的对象(只要将瘦包传送到的另一个 Git 声称它拥有这些对象)。接收者需要在收到时“修复”瘦包,但这允许
git fetch
和 git push
发送增量而不是完整的快照。
关于git - 如果 git 功能关闭文件快照,为什么 .git/不会随着时间的推移变得巨大?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51883141/