【问题标题】:When and how does git use deltas for storage?git 何时以及如何使用 deltas 进行存储?
【发布时间】:2015-03-29 03:36:33
【问题描述】:

阅读 git 的文档,他们非常强调的一件事是 git 存储的是快照而不是增量。因为我看到一个关于 Git 的课程说 Git 存储文件版本之间的差异,所以我尝试了以下操作:我在一个空文件夹上初始化了一个 git 存储库,创建了一个包含一些 lorem ipsum 文本的文件 lorem.txt 暂存文件并提交。

然后在命令行上使用find .git/objects -type f,我列出了 git 保存在对象文件夹中的内容,并按预期找到了一个提交对象,该对象指向一个树对象,该对象指向一个包含我保存的 lorem ispum 文本的 blob 对象。

然后我修改了 lorem ipsum 文本,向其中添加了更多内容,进行了此更改并提交了。再次列出文件,我现在可以看到新的提交对象,指向一个新的三个对象和一个新的 blob 对象。使用 git cat-file -p 331cf0780688c73be429fa602f9dd99f18b36793 我可以看到新 blob 的内容。它们正是完整的 lorem.txt 文件的内容,旧内容加上更改。

这符合文档的预期:git 存储快照,而不是增量。但是,在互联网上搜索我找到了this SO question。在接受的答案中,我们看到以下内容:

虽然这在概念级别上是正确且重要的,但在存储级别上并非如此。

Git 确实使用增量存储。

不仅如此,它比任何其他系统都更有效。因为它不保留每个文件的历史记录,所以当它想要进行增量压缩时,它会获取每个 blob,选择一些可能相似的 blob(使用包含先前版本和其他一些最接近近似的启发式算法),尝试生成增量并选择最小的增量。通过这种方式,它可以(通常取决于启发式)利用其他类似文件或比以前更相似的旧版本。 “pack window”参数允许用 delta 压缩质量的交易性能。默认值 (10) 通常会提供不错的结果,但是当空间有限或为了加快网络传输速度时, git gc --aggressive 使用值 250,这使其运行速度非常慢,但会为历史数据提供额外的压缩。

这说明 Git 确实使用 deltas 进行存储。据我了解,Git 并非一直使用增量,而是仅在检测到有必要时才使用。这是真的吗?

我在文件上放置了很多 lorem 文本,所以它的大小为 2mb。我认为当对大文本文件进行小改动时,Git 会自动使用增量,但正如我所说的那样。

Git 什么时候使用 deltas 以及它是如何工作的?

【问题讨论】:

  • git gcgit repack
  • Git 在创建包文件时使用增量压缩来有效地存储对象。这是关于所使用的压缩算法的实现细节——它对于 使用 git 完全无关紧要。
  • 它的对象存储层执行增量压缩,当它的启发式表明重型压缩的回报将是令人满意的,或者当你明确告诉它时。

标签: git


【解决方案1】:

Git 仅在“packfiles”中使用增量。最初,每个 git 对象都写成一个单独的文件(如您所见)。后来,git 可以将许多对象打包到一个文件中,称为“打包文件”。然后压缩包文件,它会自动利用包文件中文件之间的任何重复(或文件内的重复)。

此打包由git repack 执行。您可以通过手动调用它来查看它的运行情况。如果您在 git repo 上运行git repack -ad,您应该会看到.git/objects 下的已用磁盘空间和文件数下降,因为文件被组合成包并压缩。

实际上,您通常不需要运行git repack。 Git 默认定期运行git gc,必要时又运行git repack。所以放松,git 支持你:-)。

优秀的“git book”还有一章关于packfiles的解释: http://git-scm.com/book/en/v2/Git-Internals-Packfiles.

【讨论】:

    【解决方案2】:

    Git 2.18(2018 年第二季度)在 Documentation/technical/pack-format 中记录了增量使用情况

    参见Nguyễn Thái Ngọc Duy (pclouds)commit 011b648(2018 年 5 月 11 日)。
    (由 Junio C Hamano -- gitster -- 合并于 commit b577198,2018 年 5 月 23 日)

    pack-format.txt:关于包文件格式的更多细节

    当前文档提到了OBJ_* 常量,但没有它们的实际 价值观。一个 git 开发者会知道这些来自cache.h 但那是 对想读这个文件来实现的人不是很友好 一个包文件解析器。

    同样,deltified 表示根本没有记录( “文档”基本上是补丁-delta.c)。将该 C 代码转换为 更多关于 ofs-deltaref-delta 的意思的英语。

    所以文档现在指出:

    对象类型

    有效的对象类型是:

    • OBJ_COMMIT(1)
    • OBJ_TREE (2)
    • OBJ_BLOB(3)
    • OBJ_TAG (4)
    • OBJ_OFS_DELTA (6)
    • OBJ_REF_DELTA (7)

    类型 5 保留用于未来扩展。类型 0 无效。

    细分表示

    概念上只有四种对象类型:commit、tree、tag 和 blob。
    但是为了节省空间,可以将对象存储为 另一个“基础”对象。
    这些表示被分配了新的 s-delta 和 ref-delta 类型,这仅在包文件中有效。

    ofs-deltaref-delta 都存储要应用于的“增量” 另一个对象(称为“基础对象”)来重建对象。
    它们之间的区别是,

    • ref-delta 直接编码 20 字节的基础对象名称。
      • 如果基础对象在同一个包中,ofs-delta 将编码基础对象在包中的偏移量。

    如果基础对象在同一个包中,它也可以被删除。
    Ref-delta 还可以引用包之外的对象(即 所谓的“瘦包”)。然而,当存储在磁盘上时,包应该 自包含以避免循环依赖。

    增量数据是重建对象的指令序列 来自基础对象。
    如果基础对象是 detified,则必须首先将其转换为规范形式。每条指令都会将越来越多的数据附加到目标对象,直到完成。
    目前支持的指令有两种:

    • 一个用于从源对象复制一个字节范围和
    • 用于插入嵌入指令本身的新数据。

    每条指令的长度都是可变的。指令类型确定 由第一个八位字节的第七位。下图如下 RFC 1951 中的约定(Deflate 压缩数据格式)。


    在 Git 2.20(2018 年第 4 季度)中,packstream 中格式错误或精心制作的数据会使我们的代码尝试读取或写入超出分配的缓冲区并中止,而不是 报错,已修复。

    t5303:使用printf 生成增量基数

    增量基础文件的确切字节数很重要
    test-delta 助手会将其提供给 patch_delta(),如果它与 delta 中给定的大小字节不匹配,则会失败。
    在某些平台上使用“echo”可能会导致意外的行结束(例如,“\r\n”而不仅仅是“\n”)。

    这实际上不会导致测试失败(因为我们已经预期 test-delta 会抱怨这些虚假的 deltas),但意味着我们没有执行我们认为的代码。

    让我们改用printf(我们已经相信它会给我们 当我们生成增量时的字节完美输出)。


    使用 Git 2.25(2020 年第一季度),在包含许多包文件的存储库中,通过使用效率低下的搜索算法,避免两次注册同一个包文件的过程成本过高,已得到纠正。

    参见Colin Stolley (ccstolley)commit ec48540(2019 年 11 月 27 日)。
    (由 Junio C Hamano -- gitster -- 合并于 commit 6d831b8,2019 年 12 月 16 日)

    packfile.c: 加速加载大量包文件

    签字人:Colin Stolley
    协助人:Jeff King

    在启动时加载包文件时,我们会遍历每个文件的内部包文件列表一次,以避免重新加载已经加载的包文件。此检查以二次方时间运行,因此对于维护不善且包含大量包文件的存储库,它可能会非常慢。

    在我们加载它们时添加一个包含包文件名称的哈希图,以便检查已加载包的平均运行时成本保持不变。

    向 p5303 添加性能测试以显示加速。

    现有的 p5303 测试运行时间受其他因素支配,并没有显示出明显的加速。
    p5303 中的新测试清楚地揭示了在不良情况下的加速。
    在这个测试中,我们创建了 10,000 个包文件并测量了 git rev-parse 的启动时间,它除了加载包之外几乎没有其他作用。

    以下是新 p5303 测试的数字:

    Test                         HEAD^             HEAD
    ---------------------------------------------------------------------
    5303.12: load 10,000 packs   1.03(0.92+0.10)   0.12(0.02+0.09) -88.3%
    

    [jc:通过 peff 压缩了在 install_packed_git() 中调用 hashmap 的更改]
    签字人:Junio C Hamano

    【讨论】:

    • 当服务器在git fetch 期间将包发送到客户端时,我是否正确地说使用了对包外对象的引用?然后客户端需要事先告诉服务器它想要获取的分支的提示(哈希)。
    • @dma_k 关于客户端告诉服务器它想要获取的内容,请参阅:stackoverflow.com/a/52452772/6309
    猜你喜欢
    • 1970-01-01
    • 2013-04-05
    • 2014-08-03
    • 2015-02-21
    • 2019-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-01
    相关资源
    最近更新 更多