2017 年更新:
微软正在为Microsoft/GVFS做出贡献:一个 Git 虚拟文件系统,它允许 Git 处理“地球上最大的存储库”
(即:Windows 代码库,大约有 350 万个文件,当签入到 Git 存储库时,生成大约 300GB 的存储库,除了数千个拉取请求验证构建之外,还每天在 440 个分支中生成 1,760 个“实验室构建”)
GVFS 将您的 git 存储库下的文件系统虚拟化,以便 git 和所有工具看到看似正常的存储库,但 GVFS 仅在需要时下载对象。
GVFS 的某些部分可能会贡献给上游(给 Git 本身)。
但与此同时,所有新的 Windows 开发现在(2017 年 8 月)都在 Git 上。
2015 年 4 月更新:GitHub 提议:宣布 Git 大文件存储 (LFS)
使用git-lfs(参见git-lfs.github.com)和支持它的服务器:lfs-test-server,您只能将元数据存储在 git repo 中,而将大文件存储在其他地方。每次提交最多 2 Gb。
见git-lfs/wiki/Tutorial:
git lfs track '*.bin'
git add .gitattributes "*.bin"
git commit -m "Track .bin files"
原答案:
关于大文件的 git 限制是什么,您可以考虑bup(在GitMinutes #24中有详细介绍)
bup的设计突出了限制 git repo 的三个问题:
- 大文件(packfile 的 xdelta只在内存中,这对大文件不好)
- 大量文件,这意味着每个 blob 一个文件,并且
git gc
一次生成一个包文件的速度很慢。
- 巨大的包文件,包文件索引从(巨大的)包文件中检索数据效率低下。
处理大文件和xdelta
git 不能处理大文件的主要原因是它运行它们xdelta
,这通常意味着它试图一次将文件的全部内容加载到内存中。
如果它不这样做,它就必须存储每个文件的每个修订的全部内容,即使您只更改了该文件的几个字节。
那将是对磁盘空间的非常低效的使用,而 git 以其惊人的高效存储库格式而闻名。
不幸的是,xdelta
它适用于小文件,但对于大文件来说却非常缓慢且占用大量内存。
对于git的主要目的,即。管理您的源代码,这不是问题。
bup 代替 xdelta 所做的就是我们所说的“ hashsplitting
.”。
我们想要一种通用的方法来有效地备份任何可能以小方式更改的大文件,而无需每次都存储整个文件。我们一次读取一个字节,计算最后 128 个字节的滚动校验和。
rollsum
似乎在它的工作上做得很好。你可以在bupsplit.c
.
基本上,它将最后读取的 128 个字节转换为 32 位整数。然后我们要做的是取rollsum的最低13位,如果它们都是1,我们认为这是一个块的结尾。
这种情况平均每 发生一次2^13 = 8192 bytes
,因此平均块大小为 8192 字节。
我们根据滚动校验和将这些文件分成块。
然后我们将每个块单独存储(由其 sha1sum 索引)作为 git blob。
使用散列拆分,无论您在文件中间添加、修改或删除多少数据,受影响块之前和之后的所有块都绝对相同。
对哈希分割算法而言,重要的是 32 字节的“分隔符”序列,一次更改最多只能影响一个分隔符序列或两个分隔符序列之间的字节。
就像魔术一样,hashsplit 分块算法每次都会以相同的方式分块您的文件,即使不知道它之前是如何分块的。
下一个问题不太明显:在将一系列块存储为 git blob 之后,如何存储它们的序列?每个 blob 都有一个 20 字节的 sha1 标识符,这意味着简单的 blob 列表将是20/8192 = 0.25%
文件长度。
对于一个 200GB 的文件,这就是 488 兆的序列数据。
我们使用所谓的“扇出”进一步扩展了 hashsplit 算法。我们不只检查校验和的最后 13 位,而是使用额外的校验和位来产生额外的拆分。
你最终得到的是一个实际的 blob 树 - git 'tree' 对象非常适合表示。
处理大量文件和git gc
git 旨在处理相对不频繁更改的大小合理的存储库。您可能会认为您“经常”更改源代码,并且 git 处理的更改比svn
可以处理的更频繁。
但这与我们所说的“经常”不同。
#1 杀手是它向存储库添加新对象的方式:它为每个 blob 创建一个文件。然后您稍后运行“git gc”并将这些文件合并为一个文件(使用高效的 xdelta 压缩,并忽略任何不再相关的文件)。
' git gc
' 很慢,但是对于源代码存储库,由此产生的超高效存储(以及对存储文件的相关快速访问)是值得的。
bup
不这样做。它只是直接写入包文件。
幸运的是,这些包文件仍然是 git 格式的,所以一旦它们被写入,git 就可以愉快地访问它们。
处理巨大的存储库(意味着大量的巨大包文件)
Git 实际上并不是为处理超大型存储库而设计的。
大多数 git 存储库都足够小,可以合理地将它们全部合并到一个包文件中,git gc
通常最终会这样做。
大型包文件的问题部分不是包文件本身 - git 旨在期望所有包的总大小大于可用内存,一旦它可以处理它,它几乎可以同样有效地处理任何数量的数据。
问题是 packfile 索引 ( .idx
) 文件。
git 中的每个 packfile ( *.pack
) 都有一个关联的idx
( *.idx
),它是 git 对象哈希和文件偏移量的排序列表。
如果您正在根据其 sha1 查找特定对象,则打开 idx,对其进行二进制搜索以找到正确的哈希,然后获取关联的文件偏移量,在包文件中查找该偏移量,并读取对象内容。
二进制搜索的性能与包中哈希的数量有关O(log n)
,经过优化的第一步(您可以在其他地方阅读它)在某种程度上将其改进为O(log(n)-7)
.
不幸的是,当你有很多包时,这会有点崩溃。
为了提高此类操作的性能,bup 引入了midx
(发音为“midix”和“multi-idx”的缩写)文件。
顾名思义,它们一次索引多个包。