大文件Git

情况

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

可能的解决方案

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

    发现这一项无效。

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

问题

  • 解决方案可行吗?
  • 如果git从存储库中推入/拉入,它是上传/下载整个文件,还是只是对其进行更改(即添加新行或编辑当前行)?
  • Git可以管理这么大的文件吗? 没有。
  • 如何设置在存储库中保留多少修订? 新解决方案无关紧要。
  • 有没有更好的解决方案? 我不想强迫开发人员通过FTP或类似方式下载如此大的文件。
Jakub Riedl asked 2020-08-12T09:56:34Z
6个解决方案
60 votes

2017年更新:

微软正在为Microsoft / GVFS做出贡献:一个Git虚拟文件系统,它使Git可以处理“地球上最大的回购协议”
(即:Windows代码库,大约有350万个文件,当签入Git存储库时,产生的存储库约为300GB,除了成千上万的请求请求外,还在440个分支上每天产生1,760个“实验室构建” 验证版本)

GVFS虚拟化了git repo下的文件系统,以便git和所有工具都能看到正常的repo,但是GVFS仅在需要时下载对象。

GVFS的某些部分可能在上游(对Git本身)作出贡献。
但与此同时,所有新的Windows开发现在(2017年8月)都在Git上进行。


2015年4月更新:GitHub建议:宣布Git大文件存储(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中有详细介绍)

bup的设计突出了限制git repo的三个问题:

  • 大型文件(packfile的xdelta仅在内存中,这不适用于大型文件)
  • 大量的文件,这意味着每个blob一个文件,并且缓慢地一次生成一个packfile的git gc
  • 巨大的packfile,但packfile索引无法从(巨大的)packfile中检索数据。

处理大文件和midx

git无法处理大文件的主要原因是它通过midx运行它们,这通常意味着它试图立即将文件的全部内容加载到内存中。
如果不这样做,则即使您仅更改了该文件的几个字节,也必须存储每个文件的每个修订版的全部内容。
那将是磁盘空间的非常低效的使用,而git以其惊人的高效存储库格式而闻名。

不幸的是,midx对于小文件非常有效,而对于大文件却变得非常慢且需要大量内存。
对于git的主要目的,即。 管理您的源代码,这不是问题。

bup代替xdelta的作用是我们称为“ midx”。
我们希望有一种通用的方法来有效备份任何可能以较小方式更改的大文件,而不必每次都存储整个文件。 我们一次读取一个字节,计算最后128个字节的滚动校验和 个字节。

midx似乎做得很好。 您可以在midx中找到它。
基本上,它将最后读取的128个字节转换为32位整数。 然后我们要做的是获取rollsum的最低13位,如果它们全为1,则我们认为这是块的结尾。
平均每O(log(n)-7)发生一次,因此平均块大小为8192字节。
我们根据滚动校验和将这些文件分成多个块。
然后,我们将每个块分别存储(由其sha1sum索引)为git blob。

使用散列拆分,无论在文件中间添加,修改或删除多少数据,受影响的块之前和之后的所有块都绝对相同。
哈希拆分算法最重要的是32字节的“分隔符”序列,单个更改最多只能影响一个分隔符序列或两个分隔符序列之间的字节。
像魔术一样,哈希拆分分块算法每次都将以相同的方式对文件进行分块,即使不知道之前如何对其进行分块。

下一个问题不太明显:将一系列块存储为git blob之后,如何存储它们的序列? 每个Blob都有一个20字节的sha1标识符,这意味着该Blob的简单列表将是文件长度的midx
对于200GB的文件,仅序列数据即为488兆。

我们使用所谓的“扇出”进一步扩展了哈希拆分算法。 我们不仅仅检查校验和的最后13位,而是使用附加的校验和位来产生附加的拆分。
最终得到的是一棵实际的斑点-git'tree'对象最适合表示。

处理大量文件和midx

git用于处理大小相对较小的存储库,这些存储库很少更改。 您可能会认为自己“经常”更改源代码,而git处理的更改要比midx处理得多。
但这与我们所说的“频率”不同。

#1杀手is是向存储库添加新对象的方式:它为每个Blob创建一个文件。 然后,您稍后运行'git gc'并将这些文件合并为一个文件(使用高效的xdelta压缩,并忽略不再相关的任何文件)。

'midx'的速度很慢,但是对于源代码存储库而言,值得的是超级高效的存储(以及对存储文件的快速访问)。

midx不这样做。 它只是直接写入packfiles。
幸运的是,这些packfile仍然是git格式的,因此git可以一次愉快地访问它们 他们写的。

处理巨大的存储库(意味着大量的巨大packfile)

Git实际上并不是为处理超大型存储库而设计的。
大多数git存储库都足够小,以至于可以将它们全部合并到一个packfile中,这通常是最终由'midx'完成的。

大型packfile的问题部分不是packfile本身-git被设计为期望所有pack的总大小大于可用内存,并且一旦可以处理,它几乎可以有效地处理任何数量的数据。
问题是packfile索引(midx)文件。

git中的每个packfile(midx)都有一个关联的O(log(n)-7)*.idx),这是git对象哈希值和文件偏移量的排序列表。
如果要基于其sha1查找特定对象,请打开idx,对其进行二进制搜索以找到正确的哈希,然后获取关联的文件偏移量,在packfile中查找该偏移量,然后读取对象内容。

二进制搜索的性能约为midx,其中包含哈希值的数量,并且经过优化的第一步(您可以在其他地方阅读)可以将其改进为O(log(n)-7)
不幸的是,当您有很多包装时,这种情况会有所改善。

为了提高这种操作的性能,bup引入了midx(发音为“ midix”,缩写为“ multi-idx”)文件。
顾名思义,它们一次索引多个包装。

VonC answered 2020-08-12T10:01:18Z
31 votes

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

您添加的每个更新将累计增加存储库的整体大小,这意味着Git存储库将花费越来越长的时间来克隆和使用越来越多的磁盘空间,因为Git在本地存储了分支的整个历史记录, 这意味着当有人签出分支机构时,他们不必下载最新版本的数据库; 他们还必须下载每个以前的版本。

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

Amber answered 2020-08-12T10:01:49Z
26 votes

rsync可能是有效更新数据库开发人员副本的不错选择。

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

本质上,您获得了与git fetch类似的增量更新,而没有git clone会提供的不断扩展的初始副本。 损失没有历史,但听起来您不需要。

如果您在Windows上需要rsync,则它是大多数linux发行版的标准部分,有可用的打包端口:[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
PeterSW answered 2020-08-12T09:57:03Z
26 votes

您可以查看git-annex之类的解决方案,该解决方案是使用git管理(大)文件,而无需将文件内容检入git(!)
(2015年2月:类似GitLab的服务托管机构将其本地集成:
请参阅“ GitLab是否通过git-annex或其他方式支持大文件?”)

git不管理大文件,正如Amber在回答中解释的那样。

但这并不意味着git一天将无法做得更好。
摘自GitMinutes第9集(2013年5月,另见下文),来自Peff(杰夫·金(Jeff King)),36'10'':

(成绩单)

大型存储库还有其他所有领域,人们有兴趣存储20 GB,30 GB或40 GB,有时甚至是TB大小的存储库,是的,它来自拥有大量文件,但是其中很多 从拥有彼此处理不佳的超大文件和超大二进制文件。

这是一个开放的问题。 有几种解决方案:git-annex可能是其中最成熟的解决方案,它们基本上不将资产放入git中,而是将大型资产放在资产服务器中,然后将指针放入git中。

我想做类似的事情,其中资产在概念上是git,即对象的SHA1是进入树的SHA1的一部分,进入提交ID以及所有其他东西。
因此,从git的角度来看,它是存储库的一部分,但在以下级别,在对象存储级别,在概念历史图下方的级别,我们已经有多种存储对象的方式:我们有松散的对象, 有打包的对象,我想也许有一种新的方式来存储对象,即“我们在这里没有它,但是资产服务器可以使用它”之类的东西。

(Thomas Ferris Nicolaisen)噢,酷...

诸如pluggable-backends之类的事情的问题是:一旦使用它们,就会……永远锁定您当时所做的决定。 您知道,如果您确定200 MB很大,然后我们将存储在资产服务器上,然后您决定,应该是300 MB,祝您好运:这永远存在于您的历史中。
因此,从概念上讲,在git级别上,该对象位于git存储库中,而不是指向该对象的某个指针,也不是指向资产服务器的某个指针,因此实际对象在那里,然后以较低的价格处理这些细节。 级别的存储级别,这样您就可以腾出时间来做出很多不同的决定,甚至以后可以更改有关实际如何将内容存储在磁盘上的决定。

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


3年后的2016年4月,Git Minutes 40接受了来自GitHub约31'的Michael Haggerty的采访(谢谢Christian Couder的采访)。

他从事参考后端已有相当长的一段时间了。
他认为David Turner在后端的工作是目前最有趣的。 (请参阅David的git / git fork的当前“ pluggable-backends”分支)

(成绩单)

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

因此,这是一个很好的系统,尤其是对于本地使用而言。 因为它对普通人没有任何实际的性能问题,但确实存在一些问题,例如删除引用后就无法存储引用reflog,因为与使用类似方法创建的较新引用可能存在冲突 名称。 还有一个问题,引用名称存储在文件系统上,因此您可以使用名称相似但大小写不同的引用。
因此,通常可以通过具有不同的参考后端系统来解决这些问题。
大卫·特纳(David Turner)修补程序系列的另一方面是对将引用存储在称为lmdb的数据库中的更改,这是一个真正的基于内存的快速数据库,相对于文件后端具有一些性能优势。

[遵循有关加快包装和参考补丁广告的其他考虑事项]

VonC answered 2020-08-12T10:04:01Z
2 votes

大多数人都会去辅助存储从git存放代码引用的文件。 git-annex看起来确实很全面,但是许多商店只使用FTP或HTTP(或S3)存储库来存储大型文件,例如SQL转储。 我的建议是通过将一些元数据(特别是校验和(可能是SHA))填充到哈希和日期中,从而将git repo中的代码与辅助存储中的文件名称相关联。

  • 因此,每个aux文件都会获得基本名称,日期和SHA(对于某些版本n)总和。
  • 如果您的文件周转率很高,则仅使用SHA会带来很小但真正的散列冲突威胁,因此会包含一个日期(纪元时间或ISO日期)。
  • 将生成的文件名放入代码中,以便包含aux块,特别是通过引用。
  • 以这样一种方式来构造名称,即可以轻松地将一个小脚本编写为git grep所有aux文件名,以便轻松获取任何提交的列表。 这也使得旧文件可以在某个时候退役,并且可以与部署系统集成,以便在从git repo中激活代码之前,将新的aux文件投入生产,而不会破坏旧文件(尚未)。

一段时间后,将大量文件塞入git(或大多数存储库)会对git的性能产生不良影响-例如,git clone确实不需要二十分钟。 鉴于通过引用使用文件意味着某些开发人员根本不需要下载大块代码(与git clone形成鲜明对比),因为多数情况仅与生产中已部署的代码有关。 当然,您的里程可能会有所不同。

Alex North-Keys answered 2020-08-12T10:04:48Z
0 votes

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

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

从lfs install.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命令期间的说明(如果不使用它进行推送)

Ariful Islam answered 2020-08-12T10:05:29Z
translate from https://stackoverflow.com:/questions/17888604/git-with-large-files