为了账号安全,请及时绑定邮箱和手机立即绑定

Git的打包文件是增量文件,而不是快照?

Git的打包文件是增量文件,而不是快照?

Git
慕妹3242003 2019-11-11 14:20:27
Git与大多数其他版本控制系统之间的主要区别之一是,其他版本控制系统倾向于将提交存储为一系列增量-一个提交与下一个提交之间的变更集。这似乎是合乎逻辑的,因为它是存储有关提交的尽可能少的信息。但是提交历史记录越长,用于比较修订范围的计算就越多。相比之下,Git 在每个修订版中存储整个项目的完整快照。每次提交不会使repo的大小急剧增加的原因是项目中的每个文件都作为文件存储在Git子目录中,以其内容的哈希命名。因此,如果内容没有更改,则哈希值也没有更改,并且提交仅指向同一文件。并且还有其他优化。所有这些对我来说都是有意义的,直到我偶然发现了有关pack文件的信息,Git定期将数据放入其中以节省空间:为了节省空间,Git利用了packfile。这是一种格式,其中Git仅将更改过的部分保存在第二个文件中,并带有指向该文件的指针。这基本上不是回到存储增量吗?如果没有,那有什么不同?如何避免Git遭受其他版本控制系统所遇到的相同问题?例如,Subversion使用增量,而回滚50个版本意味着撤消50个差异,而使用Git,您只需获取适当的快照即可。除非git也将50个差异存储在packfile中...否则是否有某种机制说“在少量增量后,我们将存储一个新快照”,这样我们才不会堆积太大的变更集?Git还能如何避免三角洲的弊端?
查看完整描述

3 回答

?
ibeautiful

TA贡献1993条经验 获得超5个赞

简介:

Git的pack文件经过精心构造,可以有效地使用磁盘缓存,并为常用命令和读取最近引用的对象提供“不错”的访问模式。


Git的包文件格式相当灵活(见文档/技术/包,format.txt,或将打包文件在Git的社区图书)。打包文件以两种主要方式存储对象:“未删除”(获取原始对象数据并进行压缩压缩)或“删除”(针对某个其他对象形成增量,然后对生成的增量数据进行压缩压缩)。数据包中存储的对象可以按任何顺序排列(不必(不必)按对象类型,对象名称或任何其他属性排序),并且可以针对相同类型的其他任何合适的对象制作已删除的对象。


Git的pack-objects命令使用了几种启发式方法,可以为常见命令提供出色的参考位置。这些启发式方法既控制了已删除对象的基础对象的选择,又控制了对象的顺序。每种机制大部分都是独立的,但是它们有一些共同的目标。


Git确实形成了增量压缩对象的长链,但是试探法试图确保只有“旧”对象在长链的末端。core.deltaBaseCacheLimit自动使用增量基本缓存(其大小由配置变量控制 ),并且可以大大减少需要读取大量对象(例如git log

-p)的命令所需的“重建”次数。


增量压缩启发式

典型的Git存储库存储大量对象,因此无法合理地比较所有对象以找到将产生最小增量表示形式的对(和链)。


增量基数选择启发式算法是基于这样的思想,即可以在文件名和大小相似的对象中找到良好的增量基数。每种类型的对象都是单独处理的(即,一种类型的对象永远不会用作另一种类型的对象的增量基础)。


出于增量基数选择的目的,对象(主要)按文件名和大小排序。进入此排序列表的窗口用于限制被视为潜在增量基础的对象数量。如果一个“足够好” 1个没有找到在其窗口中的对象之间的对象增量表示,则该对象将不被增量压缩。


窗口的大小由的--window=选项 git pack-objects或pack.window配置变量控制。增量链的最大深度由的--depth= 选项git pack-objects或pack.depth配置变量控制。该--aggressive选项git gc极大地增加了窗口大小和最大深度,以尝试创建较小的打包文件。


文件名排序会将名称相同(或至少类似结尾(例如.c))的条目的对象聚在一起。大小排序从最大到最小,因此删除数据的增量要优先于添加数据的增量(因为删除增量具有较短的表示形式),因此较早,较大的对象(通常是较新的)倾向于以普通压缩方式表示。


1 什么才算是“足够好”取决于所讨论对象的大小及其潜在的增量基数以及其产生的增量链的深度。


对象排序启发式

对象以“最近引用”的顺序存储在打包文件中。重建最新历史记录所需的对象放在包装中的较早位置,它们将靠近在一起。这通常适用于OS磁盘缓存。


所有提交对象均按提交日期排序(最新的优先),并存储在一起。此放置和排序优化了遍历历史图和提取基本提交信息(例如git log)所需的磁盘访问。


从第一个存储的(最新的)提交开始,从树开始存储tree和blob对象。每棵树以深度优先的方式进行处理,存储所有尚未存储的对象。这会将重建最近提交所需的所有树木和斑点都放在一个位置。接下来,将按照已排序的提交顺序存储尚未保存但以后提交需要的所有树和Blob。


最终对象的排序受增量基本选择的影响很小,因为如果为增量表示选择了一个对象并且尚未存储其基本对象,则将其基本对象存储在已修改对象本身之前。这样可以防止由于读取基础对象所需的非线性访问而导致磁盘高速缓存丢失,而该基础对象后来将“自然地”存储在打包文件中。


查看完整回答
反对 回复 2019-11-11
?
猛跑小猪

TA贡献1858条经验 获得超8个赞

打包文件中增量存储的使用只是实现细节。在那个级别上,Git不知道为什么或从某一个修订版本到下一个版本如何进行更改,而是只知道blob B与blob A非常相似,除了这些更改C。因此,它只会存储blob A并更改C (如果选择这样做-它也可以选择存储Blob A和Blob)。

从打包文件中检索对象时,增量存储不会暴露给调用方。呼叫者仍然看到完整的斑点。因此,在不优化增量存储的情况下,Git的工作方式与以往一样。


查看完整回答
反对 回复 2019-11-11
?
鸿蒙传说

TA贡献1865条经验 获得超7个赞

正如我在“ git的瘦包是什么? ”中提到的那样。


Git仅在packfile中进行删除


我在“ git二进制差异算法(增量存储)是否标准化? ”中详细介绍了用于打包文件的增量编码。

另请参阅“ git何时以及如何使用增量存储? ”。


请注意core.deltaBaseCacheLimit,对于Git 2.0.x / 2.1(2014年第三季度),控制打包文件默认大小的配置很快将从16MB增加到96MB。


参见David Kastrup(2014年5月)的commit 4874f54:


将core.deltaBaseCacheLimit碰撞到96m

默认值16m会导致大型delta链和大型文件的严重抖动。


以下是一些基准测试(的pu变体git blame):


time git blame -C src/xdisp.c >/dev/null

用于git gc --aggressive在SSD驱动器上重新打包的Emacs存储库(v1.9,导致窗口大小为250)。

有问题的文件大约有30000行,大小为1Mb,历史记录约为2500次提交。


16m (previous default):

  real  3m33.936s

  user  2m15.396s

  sys   1m17.352s


96m:

  real  2m5.668s

  user  1m50.784s

  sys   0m14.288s


查看完整回答
反对 回复 2019-11-11
  • 3 回答
  • 0 关注
  • 1448 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号