如果对象已被压缩,为什么git pack-objects做“压缩对象”?

时间:2019-01-12 03:49:05

标签: git

据我了解,存储在[裸] git存储库中的松散对象已压缩...

......为什么git pack-objects(以及所有相关的repackgc命令)有一个很长的 Compressing objects阶段?不应该只是复制它们吗?

例如:

objects/75/f0debd8e421ab3f9cc8b6aeb539796ae86b705已被压缩。在打包文件中,应将此文件按字节顺序逐个复制到其标头之后的位置,因为打包文件格式指定了压缩后的数据就在那里了...那么,如果已经压缩过,为什么还需要重新压缩呢?

如果它可能正在尝试使用其他压缩方式...我怎么不告诉它,而是按原样使用文件?

更新的笔记:

  • 我已经设置了设置和选项,以使增量压缩不会有效发生。增量压缩对于存储2 TB的.NEF图像没有用。

2 个答案:

答案 0 :(得分:3)

  

据我了解,存储在[裸] git存储库中的松散对象已压缩...

是的。但是它们是 zlib-deflate 压缩的。

  

...那么为什么git pack-objects(以及所有相关的repack和gc命令)的压缩对象阶段真的很长?

无论如何,这些命令-git pack-objectsgit repackgit gc仅为您运行git repack-将许多对象组合成一个 pack文件

打包文件是压缩对象的不同方法。一个松散的对象是独立的:Git只需要读取该松散的对象并对其进行zlib充气传递,即可获取该对象的未压缩数据。相比之下,打包文件包含许多对象,其中一些对象首先是 delta压缩的

Delta压缩的工作原理是:实际上,要生成该对象,请首先生成另一个对象。然后在此处添加这些字节和/或在此处删除N个字节。重复此添加和/或删除操作,直到我完成了增量列表为止。(然后,也可以对zata-defdef定义增量指令本身。)您可能会认为这是一种差异,实际上,某些非Git版本控制系统实际上使用diff或它们自己的内部diff引擎来生成其增量压缩文件。

传统上,这使用的观察是某些文件(例如foo.ccfoo.py)会通过在文件中的某些位置添加和/或删除几行而随时间变化,但会保留大容量一样。如果我们可以说:采用所有以前的版本,然后添加和/或删除这些行,则我们可以在比存储其中一个版本少的空间中存储两个版本。

我们当然可以在先前的增量压缩文件之上构建增量压缩文件:获得扩展先前的增量压缩文件并应用这些增量的结果。这些使< em> delta链,它可以根据您的喜好长久,也许可以一直回到最初创建文件的位置。

某些(非Git)系统在这里停止:每个文件都存储为对先前版本的更改,或者,每次存储文件时,系统都会存储最新文件,并翻阅先前的完整副本(是最新版本,因此的完整副本)到将“最新”转换为“上一个”所需的增量中。第一种方法称为正向增量存储,而第二种方法当然是反向增量存储。正向增量通常具有一个严重的缺点,因为提取文件的最新版本需要提取第一个版本,然后应用很长的增量序列,这会花费很多时间。因此,RCS使用反向增量,这意味着获取最新版本的速度很快。它的版本很旧,速度很慢。 (但是,出于技术原因,这只能在RCS称为 trunk 上使用。RCS的“分支”改为使用正向增量。)Mercurial使用正向增量,但偶尔会存储文件的新完整副本,以便使δ链长度短。 SCCS是一种系统,它使用SCCS称为 interleaved delta 的技术,该技术为提取任何文件(但更难生成)提供了线性时间。

Git不会将文件存储为文件。您已经知道文件数据存储为 blob对象,该对象最初只是zlib定义的,否则是完整的。给定一个对象集合,其中一些是文件数据,而有些不是(提交,树或带注释的标记对象),那么根本就不知道哪些数据属于哪个文件。因此,Git要做的是找到一个可能的候选对象:一个看起来与其他对象非常相似的对象,最好说一下从另一个对象开始,然后进行这些增量更改来表达。

压缩所花费的大部分CPU时间都在于寻找良好的链。如果版本控制系统不能很好地选择文件(或对象),则压缩效果将不是很好。 Git使用了一堆试探法,包括窥探树对象以重建文件名(仅基础名,而不是完整路径名),因为否则时间复杂性会变得非常疯狂。但是,即使采用启发式方法,找到良好的增量链也很昂贵。通过“窗口”和“深度”设置,究竟可调价到底有多昂贵。

要详细了解打包文件,打包文件会随着时间的推移而进行多次修订,请参见Documentation/technical directory in Git

答案 1 :(得分:0)

注意:关于--depth argument of git pack-objects,这在“torek”中由How to reduce the depth of an existing git clone?描述为:

<块引用>

delta 链的最大长度,当 Git 对存储在每个包文件中的 Git 对象使用其修改后的 xdelta 压缩时。
这与提交 DAG 特定部分的深度无关(根据每个分支头计算)。

因此,Git 2.32(2021 年第二季度)更加清晰:

"git pack-objects"(man) 的选项采用 --window--depth 等数值不应接受负值;输入验证已收紧。

参见commit 6d52b6acommit 49ac1d3commit 953aa54commit 9535678commit 5489899Jeff King (peff)(2021 年 5 月 1 日)。
(2021 年 5 月 11 日于 Junio C Hamano -- gitster --commit 1af57f5 合并)

<块引用>

pack-objects:将负深度限制为 0

签字人:Jeff King

<块引用>

负的 delta 深度没有意义,代码还没有准备好处理它。
如果在命令行上传递“--depth=-1”,则来自 break_delta_chains() 的这一行:

cur->depth = (total_depth--) % (depth + 1);

触发除以零。
根据 C 标准,这是未定义的行为,但在 POSIX 系统上会导致 SIGFPE 终止进程。
这当然是通知用户命令无效的一种方式,但将其视为“不允许任何增量”会更友好一些,我们已经为 --depth=0 这样做了。