Git 为哪些类型的二进制文件保留增量?

What types of binary files does Git keep deltas for?

我们正在处理一个需要迁移到 Git 的非常大的项目。不幸的是,它还包含大量二进制文件,其中一些是 zip-s、dll-s 等。目前,无法从版本控制系统中删除这些二进制文件。

我想详细了解 Git 如何为二进制文件保留增量,如果有,以及哪些没有。我知道这可以通过 .gitattributes 文件进行配置,但是文件类型是否需要明确列出,或者是否有一个预定义的默认集可以自动识别和处理...?

git 将愉快地将您提供给它的每个文件存储在存储库中,无论是否为二进制文件,任何大小。它会神奇地知道如何压缩增量,也适用于二进制文件(我认为您无法控制)。如果您不使用 git-lfs,这甚至是正确的。这不是推荐的做法,因为它会使您的存储库变大。由于 git 工作副本始终包含整个历史,因此它将永远保持大。如果你使用 git-lfs,至少你的工作副本中只有最新版本的大文件(缺点是你需要连接到服务器才能进行更多操作,如颠覆)。

是否可以选择将二进制文件拆分到它们自己的存储库中并将其作为模块嵌入?

首先,让我们了解一些术语。文件存储为 blob 对象。这些是四种对象类型之一,其他三种是 committreeannotated tag

Git的模型是所有个对象在逻辑上是独立的。一切都通过其哈希 ID 密钥存储在数据库中。要检索任何对象,您首先要知道它的哈希 ID,您可以从某物或其他人那里获得该哈希 ID。1 您将该哈希 ID 提供给一个对象-getter,然后它要么直接查找存储它的对象,根本没有机会进行增量压缩——这就是 Git 所说的 松散对象 ——或者,如果失败,Git 查看 打包文件 ,它们将多个单独的对象打包在一起并提供增量压缩的机会。2

那么,您正在寻找的是关于哪些 blob 对象 Git 选择对这些包文件中的哪些其他 blob 对象进行增量压缩的信息。随着时间的推移,答案发生了一些变化,因此没有单一的正确答案——但有一些控制旋钮,包括你提到的 .gitattributes

实际的增量格式是 modification of xdelta. It can, literally, compress (or "deltify") any binary data against any other binary data—but the results will be poor unless the inputs are well-chosen. It's the input choices that are the real key here. Git also has a technical documentation file describing how objects are chosen for deltification。这会考虑文件路径名称,尤其是最终路径组件名称。

请注意,如果增量化无法使对象变小,则该对象根本就没有进行增量压缩。 object的原始文件大小也是这里的一个输入,core.bigFileThreshold(在Git1.7.6引入)设置了一个size值:这个级别以上的文件永远不会完全没有感觉。

因此,您可以通过以下两种方式之一阻止 Git 考虑对文件(实际上是对象)进行去层化:

  • 设置core.bigFileThreshold使对象太大,或者
  • 使对象的路径名与指定了 -delta.gitattributes 行相匹配。

请注意,使用 Git-LFS 时,大文件根本不会存储在 Git 中。相反,一个大文件(由 Git-LFS 设置定义)被一个间接名称替换(在 git add 时间)。 Git 然后将此间接名称存储为 blob 对象(使用原始文件的路径)。当 Git 提取对象时,Git-LFS 在允许它进入您的工作树之前检查它。 Git-LFS 检测到对象的数据被间接名称替换,并使用间接名称从另一个(单独的,根本不是 Git-)服务器检索 "real" 数据.所以 Git 根本看不到大文件的数据:相反,它只看到这些间接名称。


1例如,我们可能以 master 之类的分支名称开始,这会为我们提供最新的(或 提示) 提交哈希 ID。该哈希 ID 使我们能够访问提交对象。提交列出了树的哈希 ID。一旦我们获得树,它就会列出一些 blob 的哈希 ID 以及文件名。所以,现在我们知道 master 的提示提交中 README 版本的哈希 ID,如果这就是我们要查找的内容的话。或者,我们使用提交数据找到一个更旧的提交,我们用它来找到另一个更旧的提交,依此类推,直到我们到达我们想要的提交;然后 然后 我们使用树来查找文件的 blob ID(和名称)。

2通常情况下,一个对象只能"deltified"与同一包中的其他对象竞争。出于传输目的,Git 提供了所谓的 thin pack,其中对象可以针对省略的其他对象进行增量压缩,但假定在另一个对象上可用传输机制的一侧。其他Git必须"fatten up"瘦包。

Hence, you can prevent Git from considering a file (object, really) for deltification by either of two ways:

  • set core.bigFileThreshold so that the object is too big, or
  • make the object's path name match a .gitattributes line that has -delta specified.

torek's 中的那部分现在清楚地记录在 Git 2.31(2021 年第一季度)中:

参见 commit 3a837b5 (21 Feb 2021) by Christian Walther (cwalther)
(由 Junio C Hamano -- gitster -- in commit ccf6861 合并,2021 年 2 月 25 日)

doc: mention bigFileThreshold for packing

Signed-off-by: Christian Walther

Knowing about the core.bigFileThreshold configuration variable is helpful when examining pack file size differences between repositories.
Add a reference to it to the manpages a user is likely to read in this situation.

git pack-objects 现在包含在其 man page 中:

CONFIGURATION

Various configuration variables affect packing, see git config (search for "pack" and "delta").

Notably, delta compression is not used on objects larger than the core.bigFileThreshold configuration variable and on files with the attribute delta set to false.

git repack 现在包含在其 man page 中:

Delta compression is not used on objects larger than the core.bigFileThreshold configuration variable and on files with the attribute delta set to false.