git是否在文件之间删除重复数据?

busg9geu  于 2023-08-01  发布在  Git
关注(0)|答案(3)|浏览(119)

如果我的仓库包含了几个相同文件的副本,只做了一些小的修改(不要问为什么),git会通过只存储文件之间的差异来保存空间吗?

k7fdbhmy

k7fdbhmy1#

它可以,但很难说它是否会。在某些情况下,它保证不会 *。
要理解这个答案(及其局限性),我们必须看看git存储对象的方式。在this stackoverflow answerPro Git book中有一个关于“git对象”(存储在.git/objects/中)格式的很好的描述。
当存储像这样的“松散对象”时(git为我们可能称之为“活动”对象所做的),它们是zlib压缩的,就像Pro Git的书上说的那样,但不是压缩的。因此,存储在两个不同对象中的两个不同(不是逐位相同)文件永远不会相互压缩。
另一方面,最终可以将对象“打包”到“打包文件”中。有关包文件的信息,请参阅Pro Git book的另一节。存储在包文件中的对象相对于同一文件中的其他对象进行“增量压缩”。git使用什么标准来选择哪些对象相对于其他对象进行压缩是非常模糊的。下面是Pro Git Book中的一个片段:
当Git打包对象时,它会查找名称和大小相似的文件,并只存储文件从一个版本到下一个版本的增量。你可以查看包文件,看看Git是如何保存空间的。git verify-pack plumbing命令可以让你查看打包的内容。
如果git决定对“pack entry for big file A”和“pack entry for big file B”进行增量压缩,那么--并且 * 只有 --git才能以您要求的方式保存空间。
Git在每次运行git gc(或者更准确地说,通过git pack-objectsgit repack;更高级别的操作,包括git gc,在需要/适当的时候为您运行这些)。此时,git收集松散的对象,和/或爆炸和重新 Package 现有的包。如果您的文件接近但不完全相同,在这一点上彼此进行了增量压缩,您可能会看到一些非常大的空间节省。
但是,如果您随后要修改这些文件,那么您将在工作树中处理扩展和未压缩的版本,然后git add结果。这将产生一个新的“松散对象”,并且根据定义,
不会 * 针对任何东西进行delta压缩(没有其他松散对象,也没有任何包)。
当你克隆一个仓库的时候,通常git会把要传输的对象打包(或者“瘦包”,不是独立的),这样通过Intertubes发送的东西就越小越好。所以在这里,即使对象在源代码存储库中是松散的,您也可以 * 获得增量压缩的好处。同样,一旦你开始处理这些文件(将它们转换为松散对象),你就会失去好处,只有当松散对象再次打包 * 并且 * git的启发式算法将它们相互压缩时,你才能重新获得好处。
这里真实的的要点是,要找到答案,您可以使用Pro Git book中概述的方法简单地尝试一下。

pgpifvop

pgpifvop2#

git只存储文件之间的差异会保存空间吗?
是的,git可以将pack the files转换为压缩格式。
您的磁盘上有两个几乎相同的4K对象。如果Git可以完整地存储其中一个对象,而第二个对象只能作为它和第一个对象之间的增量,那不是很好吗?
事实证明,这是可以的。Git在磁盘上保存对象的初始格式称为松散对象格式。然而,为了保存空间和提高效率,Git有时会将其中的几个对象打包到一个称为packfile的二进制文件中。如果你有太多松散的对象,手动运行git gc命令,或者推送到远程服务器,Git就会这么做。要想知道会发生什么,你可以通过调用git gc命令来手动让Git打包对象:

mbjcgjjk

mbjcgjjk3#

是的,它可以。运行git gc是 * 可能 * 使其发生的魔法。请参阅the answer by @Emil Davtyan here, for instance。@Torek也提到了其中的一些。
特别是看到这个链接:10.4 Git Internals - Packfiles:* 除了quote in this answer here *(强调添加):
很酷的是,尽管在运行gc命令之前磁盘上的对象大小总共约为15 K,但新的包文件只有7 K。通过打包对象,您将磁盘使用量减少了一半
Git是如何做到这一点的?当Git打包对象时,它会查找名称和大小相似的文件,并只存储文件从一个版本到下一个版本的增量。

如何自己试用,看看能保存多少空间

cd path/to/my_repo

# check the size of your repo's .git folder
du -sh .git

# try compressing your repo by running "git garbage collection"
time git gc

# re-check the size of your repo's .git folder
du -sh .git

字符串
以下是我的一些真实的结果:
1.在一个小的文档库中,大部分都是markdown .md文本文档:
1.7M --> 288K:

$ du -sh .git
1.7M    .git
$ git gc
Enumerating objects: 182, done.
Counting objects: 100% (182/182), done.
Delta compression using up to 20 threads
Compressing objects: 100% (178/178), done.
Writing objects: 100% (182/182), done.
Total 182 (delta 103), reused 4 (delta 0), pack-reused 0
$ du -sh .git
288K    .git


1.在一个更大的~150 MB的代码库中,包含代码和一些二进制构建文件:
50M --> 48M:

$ du -sh .git
50M .git
$ time git gc
Enumerating objects: 8449, done.
Counting objects: 100% (8449/8449), done.
Delta compression using up to 20 threads
Compressing objects: 100% (2872/2872), done.
Writing objects: 100% (8449/8449), done.
Total 8449 (delta 5566), reused 8376 (delta 5524), pack-reused 0

real    0m1.603s
user    0m2.098s
sys 0m0.167s
$ du -sh .git
48M .git


1.在一个全新的107 GB目录中,包含来自25年半重复数据的2.1M(210万)文件,其中有人只是一次又一次(数百次)复制相同的300 MB文件夹作为其版本控制系统:
在最初的git gc打包过程后,它自动完成了首次运行git commit以添加所有文件后的11 GB。
git commit在一台配备高速SSD的高端笔记本电脑上花费了11分钟。
因此,由于git gc刚刚在git commit之后自动运行,因此没有任何变化,但是令人印象深刻的是,包含107 GB的210万个文件被压缩到只有11 GB!:
11 GB .git文件夹

$ du -sh .git
11G .git
$ time git gc
Enumerating objects: 190027, done.
Counting objects: 100% (190027/190027), done.
Delta compression using up to 20 threads
Compressing objects: 100% (60886/60886), done.
Writing objects: 100% (190027/190027), done.
Total 190027 (delta 124418), reused 190025 (delta 124417), pack-reused 0

real    0m43.456s
user    0m34.286s
sys 0m6.565s
$ du -sh .git
11G .git


有关更多细节,请参阅我的详细回答,在这里:What are the file limits in Git (number and size)?

参见:

  1. What are the file limits in Git (number and size)?
  2. my answer

相关问题