2017年更新:
微软正在为Microsoft/GVFS做出贡献:这是一个Git虚拟文件系统,允许Git处理"地球上最大的代码库"(即Windows代码库,约有350万个文件,当检入到Git存储库时,会产生大约300GB的存储库,并在440个分支上每天生成1,760个"实验室构建"以及数千个拉取请求验证构建)。
GVFS将你的git存储库下面的文件系统虚拟化,以便git和所有工具看到的是一个正常的存储库,但GVFS只在需要时下载对象。
GVFS的一些部分可能会被贡献到上游(即Git本身)。
但与此同时,所有新的Windows开发现在(2017年8月)都在Git上进行。
Update April 2015: 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
缓慢地生成一个packfile。
- 巨大的packfiles,具有从(巨大的)packfile检索数据效率低下的packfile索引。
处理大文件和xdelta
Git不能处理大文件的主要原因是它会通过xdelta
处理这些文件,这通常意味着它会尝试一次性将整个文件的内容加载到内存中。
如果不这样做,它将不得不存储每个文件的每个版本的全部内容,即使您只更改了该文件的几个字节。
那将是磁盘空间非常低效的使用方式,而且Git以其惊人的高效存储库格式而闻名。
不幸的是,xdelta
对于小文件效果很好,但对于大文件则变得非常缓慢且占用内存。
对于Git的主要目的,即管理源代码,这不是一个问题。
翻译:
bup
所做的不是使用
xdelta
,而是我们称之为
"哈希拆分"。我们想要一种通用的方法来高效地备份任何可能会发生小变化的大文件,而不需要每次都存储整个文件。我们逐字节读取文件,计算最后128字节的滚动校验和。
你可以在bupsplit.c
中找到rollsum
函数。它在其工作中表现良好。基本上,它将最后读取的128个字节转换为32位整数。然后我们取滚动校验和的最低13位,如果它们全部为1,则认为这是一个块的结尾。这平均每
2^13 = 8192个字节
发生一次,因此平均块大小为8192个字节。我们根据滚动校验和将这些文件分成块,然后将每个块单独存储(由其sha1sum索引)作为git blob。
使用哈希分割,无论您在文件中添加、修改或删除多少数据,在受影响块之前和之后的所有块都是完全相同的。
哈希分割算法所关心的只是32字节的“分隔符”序列,单个更改最多只能影响一个分隔符序列或两个分隔符序列之间的字节。
就像魔术一样,哈希分割块算法每次都会以相同的方式对文件进行分块,即使不知道它以前如何进行分块。
接下来的问题不太明显:在将一系列块存储为git blob之后,如何存储它们的顺序?每个blob都有一个20字节的sha1标识符,这意味着仅仅是块的简单列表就将占据文件长度的0.25%。
对于一个200GB的文件,那就是488兆的序列数据。
我们使用所谓的“扇出”进一步扩展了hashsplit算法。我们不仅检查校验和的最后13位,而是使用额外的校验和位来产生更多的分割。你最终得到的是一个实际的blob树 - git 'tree'对象是表示这种结构的理想选择。
处理大量文件和git gc
Git旨在处理相对较小且相对不经常更改的存储库。您可能认为您会“频繁”地更改源代码,并且git可以处理比如svn更频繁的更改。但这不是我们讨论的那种“频繁”。
它添加新对象到存储库的方式是#1杀手:它为每个blob创建一个文件。然后您稍后运行'git gc'并将这些文件合并为单个文件(使用高效的xdelta压缩,并忽略不再相关的任何文件)。
'git gc'很慢,但对于源代码存储库来说,结果是超级高效的存储(以及与存储的文件关联的非常快速的访问),这是值得的。
bup
不会这样做。它只是直接写入packfiles。
幸运的是,这些packfiles仍然符合git格式,因此一旦它们被写入,git就可以愉快地访问它们。
处理庞大的存储库(意味着巨量的packfiles)
Git实际上并不是为了处理超级大的存储库而设计的。
大多数git存储库都足够小,可以将它们全部合并到单个packfile中,通常'git gc
'最终会这样做。
大packfile的问题不在于packfile本身 - git旨在期望所有pack的总大小大于可用内存,一旦它可以处理它,它就可以同样有效地处理任何数量的数据。
问题在于packfile索引(.idx
)文件。
每个git中的pack文件(*.pack)都有一个关联的idx文件(*.idx),其中包含了git对象哈希和文件偏移量的排序列表。如果你要查找特定sha1的对象,可以打开idx文件,进行二进制搜索以找到正确的哈希,然后获取相应的文件偏移量,在pack文件中寻找并读取该对象的内容。
二分搜索的性能大约为O(log n),随着pack中哈希数量的增加而增加,但有优化的第一步(可以在其他地方了解详细信息),可以将其略微提高到O(log(n)-7)。但遗憾的是,当你有很多pack时,这种方法会有点失效。
为了改善这种操作的性能,bup引入了midx(读作“midix”和“multi-idx”的缩写)文件。正如名称所示,它们可同时索引多个pack。