用大文件Git

时间:2013-07-26 18:34:39

标签: git large-files gitlab

情况

我有两台服务器,即生产和开发。在生产服务器上,有两个应用程序和多个(6)数据库(MySQL),我需要将它们分发给开发人员进行测试。所有源代码都存储在开发服务器上的GitLab中,开发人员只能使用此服务器,并且无法访问生产服务器。当我们发布一个应用程序时,master会登录到生产环境并从Git中提取新版本。这些数据库很大(每个超过500M并且数量都在增加),我需要尽可能简单地将它们分发给开发人员进行测试。

可能的解决方案

  • 在将数据库转储到单个文件的备份脚本之后,执行一个脚本,将每个数据库推送到自己的分支。如果开发人员想要更新他的本地副本,他会拉出其中一个分支。

    发现这个不起作用。

  • 生产服务器上的Cron每天都会保存二进制日志,并将它们推送到该数据库的分支中。因此,在分支中,有每日更改的文件,开发人员会提取他没有的文件。当前的SQL转储将以另一种方式发送给开发人员。当存储库的大小变得太大时,我们会将完全转储发送给开发人员并刷新存储库中的所有数据并从头开始。

问题

  • 解决方案可行吗?
  • 如果git正在推送/拉出存储库,是上传/下载整个文件,还是只更改它们(即添加新行或编辑当前文件)?
  • Git可以管理如此大的文件吗? No。
  • 如何设置存储库中保留的修订数量?与新解决方案无关。
  • 有没有更好的解决方案?我不想强迫开发人员通过FTP或类似的东西下载这么大的文件。

6 个答案:

答案 0 :(得分:59)

2017年更新:

Microsoft正在为Microsoft/GVFS做出贡献:一个Git虚拟文件系统,允许Git处理" the largest repo on the planet"
(即:Windows代码库,大约是3.5M文件,当签入Git仓库时,会产生大约300GB的回购,并且除了数千个拉取请求外,还会在440个分支上生成1,760个每日“实验室构建”验证建立)

  

GVFS虚拟化git仓库下的文件系统,以便git和所有工具看到看似正常的仓库,但GVFS只在需要时下载对象。

GVFS的某些部分可能是上游贡献的(对Git本身而言) 但与此同时,all new Windows development is now (August 2017) on Git


2015年4月更新:GitHub建议:Announcing Git Large File Storage (LFS)

使用git-lfs(请参阅 git-lfs.github.com )和支持它的服务器:lfs-test-server,您只能在git repo和大文件中存储元数据别处。

https://cloud.githubusercontent.com/assets/1319791/7051226/c4570828-ddf4-11e4-87eb-8fc165e5ece4.gif

请参阅git-lfs/wiki/Tutorial

git lfs track '*.bin'
git add .gitattributes "*.bin"
git commit -m "Track .bin files"

原始答案:

关于大文件的git限制,您可以考虑 bup (在GitMinutes #24中详细介绍)

design of bup 突出显示了限制git repo的三个问题:

  • 巨大文件xdelta for packfile仅在内存中,对大文件不好)
  • 大量文件,即每个blob一个文件,慢git gc一次生成一个packfile。
  • large packfiles ,包文件索引无法从(巨大的)packfile中检索数据。

处理大文件和xdelta

  

git无法处理大文件的主要原因是它通过xdelta 运行它们,这通常意味着它会尝试加载文件的全部内容马上进入记忆   如果没有这样做,则必须存储每个文件的每个修订版的全部内容,即使您只更改了该文件的几个字节。   这将是一个非常低效的磁盘空间使用,而git以其惊人的高效存储库格式而闻名。

     

不幸的是,xdelta适用于小文件,对于大文件来说速度非常慢且占用大量内存
  对于git的主要目的,即。管理您的源代码,这不是一个问题。

     

我们称之为" hashsplitting ,而不是xdelta代替xdelta。"
  我们想要一种通用的方法来有效地备份任何大文件,这些文件可能会以很小的方式发生变化,而不是每次都存储整个文件。   我们一次读取一个字节的文件,计算最后128个的滚动校验和   字节。

     

rollsum似乎做得很好。 You can find it in bupsplit.c
  基本上,它将读取的最后128个字节转换为32位整数。我们接着做的是取出卷轴的最低13位,如果它们全部是1,我们认为这是一个块的结尾。
  这种情况平均每2^13 = 8192 bytes发生一次,因此平均块大小为8192字节   我们根据滚动校验和将这些文件分成块   然后我们将每个块分别存储(由其sha1sum索引)作为git blob。

     

使用hashsplitting,无论您在文件中间添加,修改或删除多少数据,之前的所有块之后受影响的块都是绝对的同样的   对hashsplitting算法至关重要的是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压缩,并忽略任何不再相关的文件)。

     

<强>&#39; git gc&#39;很慢,但是对于源代码存储库,由此产生的超高效存储(以及对存储文件的快速访问相关联)是值得的。

     

bup没有这样做。它只是直接编写packfiles   幸运的是,这些packfiles仍然是git格式的,所以git可以愉快地访问它们一次   他们已经写好了。

处理庞大的存储库(意味着大量的大包装文件)

  

Git实际上并不是为处理超大型存储库而设计的
  大多数git存储库都足够小,以便将它们全部合并到一个包文件中是合理的,这些包文件是git gc&#39;通常最终会这样做。

     

大包文件中有问题的部分不是包文件本身 - git旨在期望所有包的总大小比可用内存大,并且一旦它可以处理它,它几乎可以处理任何数量的关于同样有效的数据   问题是packfile索引(.idx)文件

     

git中的每个packfile(*.pack)都有一个关联的idx*.idx),它是git对象哈希值和文件偏移量的排序列表。
  如果您正在根据其sha1查找特定对象,则打开idx,二进制搜索它以找到正确的哈希值,然后获取相关文件偏移量,在packfile中查找该偏移量,并读取对象内容

     

二进制搜索的性能大约是O(log n),其中包含了哈希数,包含优化的第一步(您可以在其他地方阅读),这有点将其改进为O(log(n)-7)
  不幸的是,当批次包时,会出现故障。

     

为了提高此类操作的性能,bup引入了midx(发音为&#34; midix&#34;以及&#34; multi-idx&#34;的缩写)文件。
  顾名思义,它们一次索引多个包。

答案 1 :(得分:29)

你真的,真的,真的不希望将大型二进制文件检入你的Git存储库。

您添加的每个更新都会累积性地添加到您的存储库的整体大小,这意味着您的Git仓库需要更长时间才能克隆并消耗越来越多的磁盘空间,因为Git存储了整个历史记录。本地分支,这意味着当有人检出分支时,他们不仅需要下载最新版本的数据库;他们还必须下载以前的每个版本。

如果您需要提供大型二进制文件,请将它们分别上传到某个服务器,然后检查带有URL的文本文件,开发人员可以在该文件中下载大型二进制文件。 FTP实际上是更好的选项之一,因为它专门用于传输二进制文件,尽管HTTP可能更简单。

答案 2 :(得分:25)

您可以查看 git-annex 等解决方案,即使用git管理(大)文件,而不将文件内容检入git(!)
(2015年2月:a service hosting like GitLab integrates it natively
请参阅“Does GitLab support large files via git-annex or otherwise?”)

git不管理大文件,正如Amber中的her answer所述。

这并不意味着git有一天无法做得更好 来自GitMinutes episode 9 2013年5月,另见下文),来自Peff (Jeff King),位于36'10'':

(转录物)

  

存在大型存储库的所有其他领域,人们有兴趣存储20或30或40 GB,有时甚至是TB大小的存储库,是的,它来自拥有大量文件,但很多它来自于拥有非常大的文件和非常大的二进制文件,这些文件彼此不能很好地处理。

     

这是一个开放的问题。有几个解决方案:git-annex可能是最成熟的那些,他们基本上没有将资产放入git,他们将大资产放在资产服务器上,并放置指针进入git。

     

我想做类似的事情,资产在git中是概念,即该对象的SHA1是进入树的SHA1的一部分,进入提交ID和所有这些事情   所以从git的角度来看,它是存储库的一部分,但在下面的层次,在对象存储级别,在概念历史图表下方的一个级别,我们已经有多种方式存储对象:我们有loose objects,我们有packed objects,我希望有一种新的方式来存储一个对象,也就是说“我们这里没有它,但它可以通过资产服务器“,或类似的东西。

     

Thomas Ferris Nicolaisen)哦,很酷......

     

git-annex这样的问题是:一旦你使用它们,你就会......锁定你当时做出的决定。你知道,如果你决定哦200 MB是大的,我们将存储在一个资产服务器上,然后,你决定,但是它应该 300 MB ,运气不错:那是永远编码在你的历史中   因此,在git级别上说概念,这个对象是 in git存储库,而不是指向它的指针,而不是指向资产服务器的指针, >实际的对象,然后在存储级别的低级别处理这些细节,然后释放你做出很多不同的决定,甚至更改您稍后决定如何将这些内容存储在磁盘上。

目前不是一个高优先级的项目......


3年后,2016年4月,Git Minutes 40包括对{31}左右Michael Haggerty from GitHub的采访(谢谢Christian Couder for the interview)。

他是specialized in reference back-end for quite a while 他在后端引用David Turner's work作为目前最有趣的内容。 (见David's current "pluggable-backends" branch of his git/git fork

(转录物)

  

Christian Couder(CD):目标是将git refs存储在数据库中,例如?   Michael Haggerty(MH):是的,我把它看作两个有趣的方面:​​第一个是简单地能够插入不同的源条目引用。条目引用存储在文件系统中,作为松散引用和packed references的组合   松散引用是每个引用一个文件,压缩引用是一个包含许多引用列表的大文件。

     

这是一个很好的系统,特别是对于本地使用;因为它对普通人来说没有任何真正的性能问题,但它确实有一些问题,比如在删除引用后你不能存储引用reflogs,因为可能与使用类似的创建的较新引用存在冲突名。还有一个问题是引用名称存储在文件系统上,因此您可以使用名称相似但大小写不同的引用   所以这些是可以通过一般使用不同的参考后端系统来解决的问题   而David Turner的补丁系列的另一个方面是在名为 lmdb 的数据库中存储引用的更改,这是一个非常快速的基于内存的数据库,与文件相比具有一些性能优势-结束。

[关于更快包装和参考补丁广告的其他考虑因素]

答案 3 :(得分:24)

rsync可能是有效更新数据库开发人员副本的一个很好的选择。

它使用增量算法逐步更新文件。这样它只传输已更改或新的文件块。他们当然仍然需要先下载完整的文件,但以后的更新会更快。

基本上,你可以获得与git fetch类似的增量更新,而不会增加git clone提供的初始拷贝。损失没有历史,但听起来你不需要。

rsync是大多数Linux发行版的标准版本,如果你需要在Windows上有一个打包的端口可用:http://itefix.no/cwrsync/

要将数据库推送给开发人员,您可以使用类似于以下命令:

rsync -avz path/to/database(s) HOST:/folder

或者开发人员可以通过以下方式获取所需的数据库:

rsync -avz DATABASE_HOST:/path/to/database(s) path/where/developer/wants/it

答案 4 :(得分:1)

从git-stashed代码引用的文件的辅助存储是大多数人去的地方。 git-annex确实看起来非常全面,但许多商店只使用FTP或HTTP(或S3)存储库来存储大型文件,例如SQL转储。我的建议是将git repo中的代码与辅助存储中的文件名绑定起来,方法是将一些元数据 - 特别是校验和(可能是SHA) - 填入哈希值,以及日期。

  • 因此每个aux文件都有一个basename,date和SHA(对于某些版本n)sum。
  • 如果你有野外文件转换,只使用SHA会产生一个微小但真正的哈希冲突威胁,因此包含一个日期(纪元时间或ISO日期)。
  • 将生成的文件名放入代码中,以便非常具体地通过引用包含aux chunk。
  • 以这样的方式构造名称,即可以轻松地编写一个小脚本来git grep所有的aux文件名,这样任何提交的列表都很容易获得。这也允许旧的在某些时候退役,并且可以与部署系统集成,以便在从git repo激活代码之前将新的aux文件拉出到生产中而不会破坏旧的(尚未)。 / LI>

将巨大的文件塞入git(或大多数回购)会对git的性能产生一段不利的影响 - 例如git clone实际上不应该花费20分钟。而通过引用使用文件意味着一些开发人员根本不需要下载大块(与git clone形成鲜明对比),因为大多数只与生产中部署的代码相关。当然,您的里程可能会有所不同。

答案 5 :(得分:0)

有时上传大文件会造成问题和错误。通常会发生这种情况。 git主要支持上传少于50MB的文件。要在git仓库中上传超过50MB的文件,用户需要安装另一个助手来配合上传大文件(.mp4,.mp3,.psd)等。

在git中上传大文件之前,您需要了解一些基本的git命令。这是要在github上载的配置。它需要安装 gitlfs.exe

lfsinstall.exe

安装它



那么您应该使用git的基本命令以及一些不同的

git lfs install
git init
git lfs track ".mp4"
git lfs track ".mp3"
git lfs track ".psd"
git add .
git add .gitattributes
git config lfs.https://github.com/something/repo.git/info/lfs.locksverify false 
git commit -m "Add design file"
git push origin master` ones

您可能会发现lfs.https://github.com/something/repo.git/info/lfs.locksverify false就像 push 命令中的说明一样,如果不使用它就可以进行推送