【发布时间】:2012-03-17 16:38:45
【问题描述】:
Git 使用增量压缩来存储彼此相似的对象。
此算法是否已标准化并用于其他工具?是否有描述格式的文档?它与 xdelta/VCDIFF/RFC 3284 兼容吗?
【问题讨论】:
标签: git compression binary-diff vcdiff
Git 使用增量压缩来存储彼此相似的对象。
此算法是否已标准化并用于其他工具?是否有描述格式的文档?它与 xdelta/VCDIFF/RFC 3284 兼容吗?
【问题讨论】:
标签: git compression binary-diff vcdiff
我认为用于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 illustrates 和 diff-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
*/
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-delta和ref-delta都存储要应用于的“增量” 另一个对象(称为“基础对象”)来重建对象。
它们之间的区别是,
- ref-delta 直接编码 20 字节的基础对象名称。
- 如果基础对象在同一个包中,ofs-delta 将编码基础对象在包中的偏移量。
如果基础对象在同一个包中,它也可以被删除。
Ref-delta 也可以引用包外的对象(即 所谓的“瘦包”)。然而,当存储在磁盘上时,包应该 自包含以避免循环依赖。增量数据是重建对象的指令序列 来自基础对象。
如果基础对象是 detified,则必须首先将其转换为规范形式。每条指令都会将越来越多的数据附加到目标对象,直到完成。
目前支持的指令有两种:
- 一个用于从源对象复制一个字节范围和
- 用于插入嵌入指令本身的新数据。
每条指令的长度都是可变的。指令类型确定 由第一个八位字节的第七位。下图如下 RFC 1951 中的约定(Deflate 压缩数据格式)。
【讨论】:
Git delta 编码是基于复制/插入的。
这意味着派生文件被编码为可以表示副本的操作码序列 指令(例如:从偏移量 x 开始的基本文件 y 字节复制到目标缓冲区)或 插入指令(例如:将下一个 x 字节插入目标缓冲区)。
作为一个非常简单的例子(取自论文'File System Support for Delta Compression'),假设我们要创建一个 delta 缓冲区来将文本“代理缓存”转换为 “缓存代理”。 生成的指令应该是:
翻译成git的编码变成:
(字节1-3代表第一条指令)
(字节4-6代表第二条指令)
(字节7-8代表最后一条指令)
请注意,在最后一个复制指令中没有指定偏移量,这意味着偏移量为 0。其他位 当需要更大的偏移量/长度时,也可以在复制操作码中设置。
此示例中的结果 delta 缓冲区有 8 个字节,压缩效果不大 因为目标缓冲区有 12 个字节,但是当这种编码应用于大文本文件时,它可以 产生巨大的影响。
我最近推了一个node.js library到github 它使用 git delta 编码实现了 diff/patch 函数。这 code 应该更具可读性 并且比 git 源代码中的评论更重要。
我也写了一些 tests 那解释 每个示例中使用的输出操作码,格式与上述类似。
【讨论】:
此算法是否已标准化并用于其他工具?
pack 格式是公共 API 的一部分:用于推送和获取操作的传输协议使用它通过网络发送更少的数据。
除了参考之外,它们至少在其他两个主要的 Git 实现中实现:JGit 和 libgit2。
因此,不太可能对格式进行向后不兼容的更改,并且在这个意义上可以被认为是“标准化”的。
来自 docs 的这个惊人的文件将 Pack 算法中使用的启发式描述为 Linus 对一封电子邮件的有趣评论:https://github.com/git/git/blob/v2.9.1/Documentation/technical/pack-heuristics.txt
【讨论】: