git - 如果 git 功能关闭文件快照,为什么 .git/不会随着时间的推移变得巨大?

标签 git

我一直在阅读 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 notedgit gc 清理未使用的对象。这对保留的对象没有帮助,例如旧版本的 hugefile.txt 或其他任何东西。但是 git gc——Git 认为它可能合适时自动运行的代码——不仅仅是修剪未引用的对象。它还运行 git repack ,它构建或重新构建包文件。

一个包文件存储多个对象,在一个包文件中,对象被增量压缩。 Git 会仔细检查将进入单个包文件的所有对象的集合,并且对于所有 N 个对象,选择其中的一些集合 B 用作增量基础。这些对象只是 zlib 压缩的。其余的 N-B 对象被编码为增量,针对基数,或针对使用这些基数的早期增量编码对象。因此,给定存储在包文件中的对象的键,Git 可以找到存储的对象或增量,如果存储的是增量,Git 也可以找到底层对象,一直到增量基数,并且因此提取完整的对象。

因此,Git 确实使用增量编码,但仅在包文件中使用。它也不是基于文件而是基于对象,所以(至少在理论上)如果你有巨大的树,或者提交消息中有很长的文本,它们也可以相互压缩。

尽管如此,这还不是全部:为了通过网络传输,Git 将构建所谓的瘦包。常规包装和薄包装之间的主要区别与那些三角洲基地有关。给定一个常规包文件和一个哈希 ID,Git 总是可以单独从该文件中检索完整的对象。但是,对于瘦包,Git 可以使用不在该包文件中的对象(只要将瘦包传送到的另一个 Git 声称它拥有这些对象)。接收者需要在收到时“修复”瘦包,但这允许 git fetchgit push 发送增量而不是完整的快照。

关于git - 如果 git 功能关闭文件快照,为什么 .git/不会随着时间的推移变得巨大?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51883141/

相关文章:

git - Jenkins 管道子模块身份验证

git - 将更改从一个分支复制到另一个分支

linux - Nginx 配置版本控制策略

ruby - 基本 ruby​​ 脚本未按预期工作

git - Homebrew:不会在 Mountain Lion 上链接 git

python - 如何通过 Github API 更新 pull 请求

git - 用主分支中的相同文件覆盖当前分支中的单个文件?

git - 删除在错误的 rebase 之后引入的重复提交

git - 将 Git 存储库转换为 VSTS 上的团队项目

git - 基于主 Git 项目创建子项目。