git何时以及如何使用增量进行存储?

时间:2015-01-29 19:13:08

标签: git

阅读git的文档他们非常重视的一件事是git存储快照而不是增量。自从我看到Git的课程说Git存储了文件版本之间的差异后,我尝试了以下内容:我在一个空文件夹上初始化了一个git存储库,创建了一个文件lorem.txt,其中包含一些lorem ipsum文本,该文件已暂存并提交。

然后在命令行上使用find .git/objects -type f我列出了对象文件夹中保存的git,并且按预期发现了一个提交对象,该对象指向指向包含我保存的lorem ispum文本的blob对象的树对象。

然后我修改了lorem ipsum文本,为其添加了更多内容,暂存了这个更改并提交了。再次列出文件,我现在可以看到新的提交对象,指向一个新的三个对象和一个新的blob对象。使用git cat-file -p 331cf0780688c73be429fa602f9dd99f18b36793我可以看到新blob的内容。它们正是完整lorem.txt文件的内容,旧内容加上更改。

这符合文档的预期:git存储快照,而不是增量。但是,在互联网上搜索我发现了this SO question。在接受的答案中,我们看到以下内容:

  

虽然这在概念层面上是真实而重要的,但在存储层面并非如此。

     

Git确实使用增量进行存储。

     

不仅如此,它比其他任何系统都更有效率。因为它不保留每个文件的历史记录,所以当它想要进行增量压缩时,需要每个blob,选择一些可能相似的blob(使用包含最接近的先前版本和其他一些版本的试探法),尝试生成增量并选择最小的增量。通过这种方式,它可以(通常取决于启发式方法)利用其他类似文件或比以前更相似的旧版本。 "包装窗口"参数允许delta压缩质量的交易性能。默认值(10)通常会提供不错的结果,但是当空间有限或加速网络传输时,git gc --aggressive使用值250,这使得它运行速度非常慢,但为历史数据提供了额外的压缩。

其中说Git确实使用增量进行存储。据我所知,Git并不是一直使用增量,但只有在检测到它时才有必要。这是真的吗?

我在文件上放了很多lorem文本,因此它的大小为2mb。我认为当对一个大文本文件做一个小改动时,Git会自动使用增量,但正如我所说它没有。

当Git使用增量以及它是如何运作的时候?

2 个答案:

答案 0 :(得分:8)

Git只在" packfiles"中使用增量。最初,每个git对象都被写为一个单独的文件(如您所见)。之后,git可以将许多对象打包到一个文件中,称为" pack文件"。然后压缩包文件,它会自动利用packfile中文件之间的任何重复(或文件内的重复)。

此打包由git repack执行。您可以通过手动调用它来查看它。如果您在git仓库上运行git repack -ad,您应该会看到已用过的磁盘空间和.git/objects下的文件数量,因为文件会合并到包中并进行压缩。

在实践中,您通常不需要运行git repack。默认情况下,Git会定期运行git gc,并在必要时运行git repack。所以放松一下,git有你的背:-)。

优秀的#git book"还有一章关于packfiles有更多解释: http://git-scm.com/book/en/v2/Git-Internals-Packfiles

答案 1 :(得分:2)

Git 2.18(2018年第二季度)记录Documentation/technical/pack-format

中的增量使用情况

commit 011b648Nguyễn Thái Ngọc Duy (pclouds)(2018年5月11日) (由Junio C Hamano -- gitster --合并于commit b577198,2018年5月23日)

  

pack-format.txt:有关包文件格式的更多详细信息

     

当前文档提及OBJ_*常量而没有它们的实际值   值。 git开发人员会知道这些来自cache.h,但那是。{   对想要阅读此文件以实现的人不是很友好   包文件解析器。

     

同样,完整的表示根本没有记录(   “document”基本上是patch-delta.c)。将该C代码翻译成   英语,更多地了解ofs-deltaref-delta的含义。

所以文档现在声明:

  

对象类型

     

有效的对象类型是:

     
      
  • OBJ_COMMIT(1)
  •   
  • OBJ_TREE(2)
  •   
  • OBJ_BLOB(3)
  •   
  • OBJ_TAG(4)
  •   
  • OBJ_OFS_DELTA(6)
  •   
  • OBJ_REF_DELTA(7)
  •   
     

类型5保留用于将来扩展。类型0无效。

     

Deltified representation

     

从概念上讲,只有四种对象类型:commit,tree,tag和   斑点。
  但是为了节省空间,可以将对象存储为“delta”   另一个“基础”对象。
  这些表示被分配了新类型的delta和ref-delta,它们仅在包文件中有效。

     

ofs-deltaref-delta都存储要应用的“delta”   另一个对象(称为“基础对象”)来重建对象   他们之间的区别是,

     
      
  • ref-delta直接编码20字节的基础对象名称。      
        
    • 如果基础对象位于同一个包中,则ofs-delta会对包中基础对象的偏移量进行编码。
    •   
  •   
     

如果基础对象在同一个包中,也可以进行分类    Ref-delta也可以指包外的一个对象(即   所谓的“薄包”)。然而,当存储在磁盘上时,包应该   是自包含的,以避免循环依赖。

     

增量数据是重建对象的一系列指令   来自基础对象。
  如果基础对象已经过分层,则必须首先将其转换为规范形式。每条指令都会向目标对象附加越来越多的数据,直到完成为止   到目前为止,有两条支持的说明:

     
      
  • 一个用于复制源对象和
  • 的字节范围   
  • 用于插入嵌入指令本身的新数据。
  •   
     

每条指令都有可变长度。指令类型已确定   在第一个八位字节的第七位。以下图表如下   RFC 1951中的约定(Deflate压缩数据格式)。

使用Git 2.20(Q8 2018),packstream中的格式错误或精心设计的数据可以使我们的代码尝试读取或写入超过分配的缓冲区并中止,而不是 报告错误,已修复。

  

t5303:使用printf生成delta base

     

delta基本文件的确切字节数非常重要   test-delta帮助器会将其提供给patch_delta(),如果它与增量中给出的大小字节不匹配,则会进行barf。
  使用“echo”可能会在某些平台上出现意外的行结尾(例如,“\r\n”而不仅仅是“\n”。

     

这实际上不会导致测试失败(因为我们已经预期测试delta会抱怨这些虚假的增量),但这意味着我们没有运用我们认为的代码。

     

让我们使用printf代替(我们已经信任我们   当我们生成增量时,字节完美的输出。)