【问题标题】:Is the git binary diff algorithm (delta storage) standardized?git 二进制差异算法(增量存储)是否标准化?
【发布时间】:2012-03-17 16:38:45
【问题描述】:

Git 使用增量压缩来存储彼此相似的对象。

此算法是否已标准化并用于其他工具?是否有描述格式的文档?它与 xdelta/VCDIFF/RFC 3284 兼容吗?

【问题讨论】:

    标签: git compression binary-diff vcdiff


    【解决方案1】:

    我认为用于pack files 的差异算法与delta encoding 之一相关联:initially (2005) xdelta,然后是libXDiff
    但是,如下所述,它转向了自定义实现。

    不管怎样,就像mentioned here

    Git 在包文件中进行 deltification。
    但是当你通过 SSH 推送时,git 会生成一个包含提交的包文件,另一方不会 有,而且这些包是薄包,所以它们也有增量......但是远程端随后为这些薄包添加了基础,使它们独立。

    (注意:创建许多包文件,或在大包文件中检索信息是成本高昂,并解释为什么 git 不能很好地处理大文件或大仓库。
    在“git with large files”查看更多信息)

    This thread也提醒我们:

    据我的记忆和理解,实际上 packfiles 和 deltification(LibXDiff,而不是 xdelta最初是因为网络带宽(这比磁盘空间成本高得多),以及使用单个映射文件而不是大量松散对象的 I/O 性能

    这个2008 thread中提到了LibXDiff。

    但是,从那时起,算法已经发展,可能是自定义算法,正如 2011 thread illustratesdiff-delta.c 的标题所指出的那样:

    所以,严格来说,Git 中的当前代码与 libxdiff 代码完全没有任何相似之处。
    然而,这两种实现背后的基本算法是相同的

    研究 libxdiff 版本可能更容易理解它是如何工作的。

    /*
     * diff-delta.c: generate a delta between two buffers
     *
     * This code was greatly inspired by parts of LibXDiff from Davide Libenzi
     * http://www.xmailserver.org/xdiff-lib.html
     *
     * Rewritten for GIT by Nicolas Pitre <nico@fluxnic.net>, (C) 2005-2007
     */
    

    更多关于packfiles the Git Book:


    Git 2.18 adds to the delta description 在这个新的documentation section 中,现在(2018 年第二季度)指出:

    对象类型

    有效的对象类型是:

    • 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.661346.n2.nabble.com/diff-ing-files-td6446460.html这样的2011年线程时,最终的算法可能是一个自定义的算法
    • 2008年显然使用了libXDiff:git.661346.n2.nabble.com/…
    • 那个 2011 线程是一个很好的链接。选择引用:“所以,严格来说,Git 中的当前代码与 libxdiff 代码根本没有任何相似之处。但是两种实现背后的基本算法是相同的。”
    • 我想知道他们是否只是将压缩 algorithm 从 libxdiff 更改为 custom,或者磁盘上的格式是否也发生了变化(听起来很麻烦)。
    • @Thilo:我已将 2011 年的帖子包含在答案中以获得更多可见性。
    【解决方案2】:

    Git delta 编码是基于复制/插入的。

    这意味着派生文件被编码为可以表示副本的操作码序列 指令(例如:从偏移量 x 开始的基本文件 y 字节复制到目标缓冲区)或 插入指令(例如:将下一个 x 字节插入目标缓冲区)。

    作为一个非常简单的例子(取自论文'File System Support for Delta Compression'),假设我们要创建一个 delta 缓冲区来将文本“代理缓存”转换为 “缓存代理”。 生成的指令应该是:

    1. 从偏移量 7 复制 5 个字节(从基本缓冲区复制“缓存”)
    2. 插入两个空格
    3. 从偏移量 0 复制 5 个字节(从基本缓冲区复制“代理”)

    翻译成git的编码变成:

    (字节1-3代表第一条指令)

    • 0x91(10010001),分为
      • 0x80 (10000000)(最高有效位设置使其成为“从基址复制到输出”指令)
      • 0x01 (00000001)(意思是'前进一个字节并使用它作为基本偏移量)
      • 0x10 (00010000)(前一字节作为长度)
    • 0x07(偏移量)
    • 0x05(长​​度)

    (字节4-6代表第二条指令)

    • 0x02(由于没有设置MSB,这意味着'将接下来的两个字节插入输出')
    • 0x20(空格)
    • 0x20(空格)

    (字节7-8代表最后一条指令)

    • 0x90(10010000),分为
      • 0x80 (10000000)(表示“复制”)
      • 0x10 (00010000)(前一字节作为长度)
    • 0x05(长​​度)

    请注意,在最后一个复制指令中没有指定偏移量,这意味着偏移量为 0。其他位 当需要更大的偏移量/长度时,也可以在复制操作码中设置。

    此示例中的结果 delta 缓冲区有 8 个字节,压缩效果不大 因为目标缓冲区有 12 个字节,但是当这种编码应用于大文本文件时,它可以 产生巨大的影响。

    我最近推了一个node.js library到github 它使用 git delta 编码实现了 diff/patch 函数。这 code 应该更具可读性 并且比 git 源代码中的评论更重要。

    我也写了一些 tests 那解释 每个示例中使用的输出操作码,格式与上述类似。

    【讨论】:

    【解决方案3】:

    此算法是否已标准化并用于其他工具?

    pack 格式是公共 API 的一部分:用于推送和获取操作的传输协议使用它通过网络发送更少的数据。

    除了参考之外,它们至少在其他两个主要的 Git 实现中实现:JGitlibgit2

    因此,不太可能对格式进行向后不兼容的更改,并且在这个意义上可以被认为是“标准化”的。

    来自 docs 的这个惊人的文件将 Pack 算法中使用的启发式描述为 Linus 对一封电子邮件的有趣评论:https://github.com/git/git/blob/v2.9.1/Documentation/technical/pack-heuristics.txt

    【讨论】:

    • 好点(并且比我的“历史”答案更新)。 +1
    • @VonC 谢谢!这个问题非常开放,您的回答和蒂亚戈的回答也都包含有用的见解。我很高兴能在像你们这样的其他伟大程序员的答案旁边得到答案。 :)
    猜你喜欢
    • 2011-11-06
    • 2016-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-05
    • 2014-07-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多