Git存储文本和二进制文件的方式之间有区别

时间:2018-12-06 09:00:28

标签: git

每个人似乎都同意的一件事是,Git对于大型二进制blob而言并不是很好。请记住,二进制Blob与大型文本文件不同;您可以毫无问题地在大型文本文件上使用Git,但是Git对于不可渗透的二进制文件不能做很多事情,只能将它当作一个大的黑盒子并按原样提交。

根据https://opensource.com/life/16/8/how-manage-binary-blobs-git-part-7

  

每个人似乎都同意的一件事是,Git并非一帆风顺   二进制斑点请注意,二进制Blob与   大文本文件;您可以在大型文本文件上使用Git,而无需   问题,但是Git对于不可渗透的二进制文件不能做很多事情,除了   将其视为一个大的黑匣子,并按原样提交。

     

假设您拥有令人兴奋的新第一人称呼的复杂3D模型   您正在制作的益智游戏,并以二进制格式保存,   产生一个1 GB的文件。您git提交一次,添加一个   到存储库历史记录的千兆字节。稍后,您给模型一个   不同的发型,并提交您的更新; git无法分辨头发   除了头部或模型的其余部分,因此您已经做出了承诺   另一个千兆字节。然后您更改模型的眼睛颜色并提交   这么小的变化:另一个千兆字节。一台三千兆字节   型号,一时兴起。扩展到所有   游戏中的资产,并且您遇到了严重的问题。

据我了解,文本文件和二进制文件之间没有区别,Git会完整存储每个提交的所有文件(创建校验和的Blob),而未更改的文件仅指向已存在的Blob。我不知道细节如何存储和压缩所有这些blob,这是另一个问题,但我会假设,如果引号中的各个1GB文件或多或少都相同,那么好的压缩算法可以解决这个问题并且如果它们是重复的,则可能可以全部存储在不足1GB的空间中。 此推理应适用于二进制文件和文本文件。

与此相反,引文继续说

  

将其与.obj格式的文本文件进行对比。一提交存储   一切,与其他模型一样,但是.obj文件是一系列的   描述模型顶点的纯文本行。如果你   修改模型并将其保存回.obj,Git可以读取两者   文件,逐行创建更改的差异,并公平地处理   小承诺。模型越精细,则越小   commit get,它是标准的Git用例。这是一个很大的文件,但是   它使用一种叠加或稀疏存储方法来构建完整的   数据当前状态的图片。

我的理解正确吗?报价不正确吗?

2 个答案:

答案 0 :(得分:1)

您说对了,文本和二进制文件实际上只是blob对象。如果故事只有这些,那么事情会更简单,但事实并非如此,事实并非如此。 :-)

(您还可以指示Git对输入文件执行各种过滤操作。在这方面,文本和二进制文件在过滤器的作用方面没有区别,但是有 默认情况下应用过滤器的方面有所不同:如果您使用自动模式,则Git会过滤 Git 认为是文本而不是文本的文件-过滤Git认为是二进制的文件,但这仅在使用自动检测和仅CRLF / LF换行符转换时才重要。)

  

我本来假设,如果报价中的各个1GB文件大致相同,那么一个好的压缩算法可以解决这个问题,并且如果它们全部都小于1GB,则可以全部存储。重复...

也许,也许不是。 Git有两个单独的压缩算法。与Noufal Ibrahim said一样,这两个delta compression中的一个仅适用于Git所说的 pack files 。另一个是zlib,它适用于所有内容。

Zlib是一种通用的压缩算法,它依赖于特定的建模过程(有关背景,请参见Is there an algorithm for "perfect" compression?)。它在纯文本上的性能往往很好,而在某些二进制文件上则不太好。它倾向于使已经压缩的文件更大,因此,如果您已经压缩了1 GB的输入,则在zlib compresson之后它们可能会(略大)变大。但是,所有这些都是通用性。要了解它如何处理您的特定数据,诀窍是对您的特定数据运行它。

Git使用的增量编码发生在zlib压缩之前,并且确实适用于二进制数据。本质上,它会找到在“较早”和“较晚”对象中匹配的长二进制字节序列(此处“宽松”和“较晚”的定义相当宽松,但是由于某些原因,Git在对象上施加了特定的遍历和比较顺序)讨论过here),并在可能的情况下,用“引用较早的对象,从偏移量O抓取N个字节”替换N个字节的长序列。

如果您在大型二进制文件上尝试此操作,事实证明,它通常在具有某种数据局部性的成对相关,大型,未压缩二进制文件对上效果很好,例如“ ”二进制文件往往具有“早期”文件的许多长重复,并且在大型压缩二进制文件或表示可获取数据结构的二进制文件中,非常严重改组了太多(以至于重复的二进制字符串变得非常零散,即不再有 long 了)。因此,这再次取决于数据:请尝试您的特定数据,看看它是否对您有效。

答案 1 :(得分:0)

Git确实完整地存储了文件,因此,如果您有2个二进制文件而只做了很小的更改,它将占用两倍的空间。观察。

% git init                
Initialized empty Git repository in /tmp/x/.git/
{master #}%                                                                                                                                           [/tmp/x]
{master #}% du -sh .git           
100K    .git                         
{master #}% dd if=/dev/urandom of=./test count=1 bs=10M
1+0 records in
1+0 records out                                                                                                                                               
10485760 bytes (10 MB, 10 MiB) copied, 0.102277 s, 103 MB/s
{master #%}% ls -sh test
10M test
{master #%}% git add test
git co%
{master #}% git commit -m "Adds test"
[master (root-commit) 0c12c32] Adds test
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 test
{master}% du -sh .git
11M     .git

我已经创建了一个10MB的文件,并添加并提交了它。现在,该存储库的大小为10MB。

如果我做了一个小小的改动然后再做一次,

{master}% e test # This is an invocation of my editor to change a few bytes.
nil
{master}% git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   test

no changes added to commit (use "git add" and/or "git commit -a")
{master *}% git add test
{master +}% git commit -m "Updates test a little"
[master 99ed99a] Updates test a little
 1 file changed, 0 insertions(+), 0 deletions(-)
{master}% du -sh .git
21M     .git

这将需要20MB。是10MB文件的两倍。

但这是存储库的“ loose object”格式,其中每个Blob都是磁盘上的单独文件。

您可以将所有这些文件打包到一个git pack文件中(在您按下等操作时完成),然后看看会发生什么。

{master}% git gc
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), done.
Total 6 (delta 1), reused 0 (delta 0)
{master}% du -sh .git
11M     .git

现在,它仅在packfile中存储blob和diff。这与仅存储差异的每个提交不同。就是对象本身被打包到一个文件中。