使用Git管理大型二进制文件


523

我正在寻找有关如何处理源代码(Web应用程序)所依赖的大型二进制文件的意见。我们目前正在讨论几种替代方法:

  1. 手动复制二进制文件。
    • 优点:不确定。
    • 相反:我强烈反对这一点,因为它在建立新站点/迁移旧站点时增加了出错的可能性。建立另一个障碍。
  2. Git管理它们。
    • 专业版:消除了“忘记”复制重要文件的可能性
    • 相反:膨胀存储库并降低管理代码库的灵活性,而检出,克隆等将花费相当长的时间。
  3. 单独的存储库。
    • 优点:签出/克隆源代码比以往任何时候都快,并且图像已正确存储在其自己的存储库中。
    • 相反:消除了在项目上拥有唯一的 Git存储库的简单性。它肯定会引入其他我从未想过的东西。

您对此有何经验/想法?

另外:是否有人在多个Git存储库中进行过管理并在一个项目中进行管理?

这些文件是程序的图像,该程序会在其中生成包含这些文件的PDF。文件不会经常更改(像几年一样),但是它们与程序非常相关。没有文件,该程序将无法运行。


26
何时需要对二进制文件进行版本控制该怎么办?我正在考虑寻找从事资产工作的艺术家团队。

3
如果有必要,那么您必须权衡可用资源(磁盘,带宽,CPU时间)和所获得的收益。
pi。

4
请注意,如果没有文件锁定,当多个人需要处理同一个二进制文件时,git并不是很好。
yoyo 2012年

1

1
在这里,他们是bestechvideos.com/tag/gitcasts
doughgle

Answers:


177

如果程序如果没有这些文件就无法运行,则将它们拆分成一个单独的存储库似乎是个坏主意。我们有大型测试套件,可以分成单独的存储库,但它们确实是“辅助”文件。

但是,您也许可以在单独的存储库中管理文件,然后git-submodule以理智的方式将其拉入项目。因此,您仍然拥有所有来源的完整历史记录,但是据我了解,您只会拥有图像子模块的一个相关修订版。该git-submodule工具应帮助您使正确的代码版本与映像的正确版本保持一致。

这是 Git Book 中子模块的很好介绍


11
“据我了解,您只会对图像子模块进行一个相关的修订。” 我认为这是不正确的。
罗宾·格林

22
确实。子模块是完整的Git存储库,它恰好嵌套在父存储库中。它知道其整个历史。您可以减少在其中的提交频率,但是如果在其中存储与在父级中相同的内容,则父级将具有相同的问题。
卡斯卡贝尔2012年

5
如果您有大的二进制文件,并且每隔一定的时间间隔进行更改,则这是一个非常差的解决方案。我们的存储库非常膨胀,因为每次构建时都会在其中存储一个新的二进制文件。如下所述,如果您不在Windows上,则Annex是一个很好的解决方案。如果您使用的是Windows,则只需继续寻找即可。
AA Grapsas 2012年

4
存储库中具有大二进制文件的另一个问题是性能。Git并不是为处理大型二进制文件而设计的,一旦回购规模达到3G +,性能就会迅速下降。这意味着回购中包含大型二进制文件会限制您的托管选项。
zoul 2012年

如果您创造性地滥用子模块,则子模块可以减少结帐数据传输的需求:当您要更新子模块的内容时,创建没有父对象的新提交,然后将超级项目(主git repo)指向没有父对象的新创建的提交。从逻辑上讲,这会为子模块创建断开连接的历史记录,但是作为回报,子模块的任何版本都更易于传输,因为该版本没有历史记录。
Mikko Rantalainen

310

我最近发现git-annex,发现它很棒。它旨在有效地管理大型文件。我将其用于照片/音乐(等)收藏。git-annex的开发非常活跃。可以从Git存储库中删除文件的内容,Git只能跟踪树层次结构(通过符号链接)。但是,要获取文件的内容,在拉/推之后需要执行第二步,例如:

$ git annex add mybigfile
$ git commit -m'add mybigfile'
$ git push myremote
$ git annex copy --to myremote mybigfile ## This command copies the actual content to myremote
$ git annex drop mybigfile ## Remove content from local repo
...
$ git annex get mybigfile ## Retrieve the content
## or to specify the remote from which to get:
$ git annex copy --from myremote mybigfile

有许多可用命令,并且网站上有大量文档。Debian提供了一个软件包


11
哇!支持真棒!这实现了我最近的想法,还有更多。它是用Haskell编写的。顺便说一下,git-media是一个很好的选择。
cdunn2001

33
但是,附件不支持Windows。这对于游戏开发者来说是有问题的。
AA Grapsas 2012年

7
我听说Steam放弃了对Windows的支持,而增加了对Linux的支持...;)虽然如此,移植它有多难?我想您的普通游戏开发人员可以做到。
山姆·沃特金斯

4
@EstebanBrenes真正的麻烦之处在于,在常规配置中,Windows符号链接需要提升的特权才能创建。
劳伦斯·霍尔斯特

4
我刚刚找到此页面。它显示现在git annexWindows上也可用。如果有人曾经在Windows中对其进行过测试,我想听听他或她的经验!
Kouichi C. Nakamura

49

自2015年4月以来的另一种解决方案是Git大文件存储(LFS)(由GitHub提供)。

它使用git-lfs(请参阅git-lfs.github.com)并在支持它的服务器上进行了测试:lfs-test-server
您只能将元数据存储在git repo中,而大文件则可以存储在其他位置。

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


3
lfs-test-server声明不用于生产。实际上,我正在生产LFS服务器(github.com/artemkin/git-lfs-server)。它正在进行中,但已经可以使用,我们正在内部对其进行测试。
Stas'Apr

您可以使用git lfs签出此类二进制文件的先前版本吗?
mucaho

1
@mucaho您应该:git checkout的语法不变,并且应该仍然调用lfs smudge脚本。
VonC

31

看看git bup,这是一个Git扩展,可以在Git存储库中智能地存储大型二进制文件。

您希望将其作为子模块,但不必担心存储库变得难以处理。他们的示例用例之一是将VM映像存储在Git中。

我实际上并没有看到更好的压缩率,但是我的存储库中没有太大的二进制文件。

你的旅费可能会改变。


3
bup提供存储(内部使用奇偶校验归档文件来实现冗余,并使用git来进行压缩,重复数据删除和历史记录),但是并没有扩展git。git-annex是一个git扩展,提供了bup存储后端
东武

@Tobu,当我发布此内容时,git附件尚不存在(在主流版本中)
sehe 2012年

2
bup对于管理大文件绝对是有趣的。我想指出UI的不同之处:您可以在任何存储库上下文之外使用bup命令,而git是实现细节。
东武

27

您也可以使用git-fat。我喜欢它仅取决于Python和rsync。它还通过以下自我说明命令来支持通常的Git工作流程:

git fat init
git fat push
git fat pull

此外,您需要将.gitfat文件检入到存储库中,并修改.gitattributes以指定要git fat管理的文件扩展名。

您可以使用normal添加二进制文件git add,它会git fat根据gitattributes规则进行调用。

最后,它的优点是可以在存储库和用户之间共享实际存储二进制文件的位置,并支持任何rsync功能。

更新:如果您使用的是Git-SVN桥,请不要使用git-fat。最终将从Subversion存储库中删除二进制文件。但是,如果您使用的是纯Git存储库,那么它的运行效果很好。


26

我将使用子模块(如Pat Notz)或两个不同的存储库。如果您经常修改二进制文件,那么我将尽量减少清理历史记录的巨大存储库的影响:

几个月前,我遇到了一个非常类似的问题:大约21 GB的MP3文件,未分类(名称错误,id3错误,不知道我是否喜欢该MP3文件...),并在三台计算机上复制了。

我在主Git存储库中使用了外部硬盘驱动器,并将其克隆到每台计算机中。然后,我开始按惯常方式对它们进行分类(推,拉,合并...删除和重命名多次)。

最后,我在.git目录中只有〜6 GB的MP3文件和〜83 GB。我使用git-write-treegit-commit-tree创建了一个没有提交祖先的新提交,并启动了一个指向该提交的新分支。该分支的“ git log”仅显示一次提交。

然后,我删除了旧分支,仅保留了新分支,删除了引用日志,然后运行“ git prune”:之后,我的.git文件夹的权重仅为〜6 GB。

您可以不时以相同的方式“清除”大型存储库:“ git clone”的存储库将更快。


我曾经做过类似的事情,当时我不得不将一个存储库拆分成偶然的合并成两个不同的存储库。有趣的用法模式。:)
pi。

1
这是否与rm -f .git;相同?git init; git添加 ; git commit -m“破坏历史记录。”
帕特诺兹,09年

1
是的,仅在我的mp3盒中是一样的。但有时您不想触摸分支和标签(公共存储库中不减少空间),而是希望加快仅分支的“ git clone / fetch / pull”(用于专用于该分支的空间较少)分支存储库)。
丹尼尔·范朱尔

13

我想提出的解决方案基于孤立分支和对标记机制的轻微滥用,因此被称为*孤立标记二进制存储(OTABS)

TL; DR 12-01-2017如果可以使用github的LFS或其他第三方,则应该使用。如果不能,请继续阅读。请注意,此解决方案是骇客,应这样对待。

OTABS的理想特性

  • 它是一个纯git仅git的解决方案-无需任何第三方软件(如git-annex)或第三方软件(如github的LFS)即可完成工作。
  • 有效地存储二进制文件,即不会使存储库的历史膨胀。
  • git pullgit fetch(包括)git fetch --all仍然具有较高的带宽效率,即默认情况下并非所有大型二进制文件都从远程提取。
  • 它可以在Windows上运行
  • 它将所有内容存储在一个git仓库中
  • 它允许删除过时的二进制文件(与bup不同)。

OTABS的不良特性

  • git clone可能会导致效率低下(但不一定取决于您的用法)。如果部署此解决方案,则可能必须建议您的同事使用git clone -b master --single-branch <url>而不是git clone。这是因为git clone默认会从字面上克隆整个存储库,包括通常不希望浪费带宽的内容,例如未引用的提交。取自SO 4811434
  • 它使git fetch <remote> --tags带宽效率低下,但不一定使存储效率低下。您可以随时建议您的同事不要使用它。
  • 您将git gc不得不定期使用技巧来从不需要的文件中清除存储库。
  • 它不如bupgit-bigfiles高效。但这分别更适合您要尝试的工作和更现成的。您可能会遇到成千上万个小文件或千兆字节范围内的文件的麻烦,但请继续阅读以获取解决方法。

添加二进制文件

在开始之前,请确保已提交所有更改,工作树是最新的,并且索引中不包含任何未提交的更改。最好将所有本地分支推送到远程(github等),以防万一发生灾难。

  1. 创建一个新的孤立分支。git checkout --orphan binaryStuff会成功的 这将产生一个与其他分支完全断开连接的分支,并且您将在该分支中进行的第一个提交将没有父项,这将使其成为根提交。
  2. 使用清理索引git rm --cached * .gitignore
  3. 深吸一口气,然后使用删除整个工作树rm -fr * .gitignore。内部.git目录将保持不变,因为*通配符与之不匹配。
  4. 复制到您的VeryBigBinary.exe或VeryHeavyDirectory /中。
  5. 添加并提交。
  6. 现在,它变得很棘手-如果将它作为一个分支推送到远程,则所有开发人员在下次调用git fetch其阻塞连接时都将下载它。您可以通过推送标签而不是分支来避免这种情况。如果他们有键入的习惯,这仍然会影响您同事的带宽和文件系统存储git fetch <remote> --tags,但请继续阅读以寻求解决方法。来吧git tag 1.0.0bin
  7. 推送您的孤儿标签git push <remote> 1.0.0bin
  8. 这样一来,您就永远不会偶然推送二进制分支,可以将其删除git branch -D binaryStuff。您的提交不会被标记为垃圾回收,因为指向它的孤立标记1.0.0bin足以使其存活。

签出二进制文件

  1. 我(或我的同事)如何将VeryBigBinary.exe检出到当前工作树中?例如,如果您当前的工作分支是master,则可以简单地进行git checkout 1.0.0bin -- VeryBigBinary.exe
  2. 如果您没有1.0.0bin下载孤儿标签,则将失败,在这种情况下,您必须git fetch <remote> 1.0.0bin事先下载。
  3. 您可以将添加VeryBigBinary.exe到您的主目录中.gitignore,这样您的团队中就不会有人偶然使用二进制文件污染项目的主要历史记录。

完全删除二进制文件

如果您决定从本地存储库,远程存储库和同事的存储库中完全清除VeryBigBinary.exe,则可以:

  1. 删除遥控器上的孤立标签 git push <remote> :refs/tags/1.0.0bin
  2. 本地删除孤立标记(删除所有其他未引用的标记)git tag -l | xargs git tag -d && git fetch --tags。取自SO 1841341,稍作修改。
  3. 使用git gc技巧在本地删除您现在未引用的提交。git -c gc.reflogExpire=0 -c gc.reflogExpireUnreachable=0 -c gc.rerereresolved=0 -c gc.rerereunresolved=0 -c gc.pruneExpire=now gc "$@"。它还将删除所有其他未引用的提交。取自SO 1904860
  4. 如果可能,请在遥控器上重复git gc技巧。如果您是自托管存储库,则可能是这样,而某些git提供程序(例如github)或某些公司环境中可能无法实现。如果您使用的服务提供商托管的ssh不能使您远程访问ssh,请放手。您的提供商的基础结构可能会在自己的美好时光中清理未引用的提交。如果您在公司环境中,则可以建议IT部门执行cron作业垃圾回收,每周大约一次。只要您建议您的同事始终使用,git clone -b master --single-branch <url>而不管它们是否在带宽和存储方面都不会对您的团队产生任何影响git clone
  5. 您所有想摆脱过时的孤儿标签的同事都只需执行步骤2-3。
  6. 然后,您可以重复添加二进制文件以创建新的孤立标记的步骤1-8 2.0.0bin。如果您担心同事打字git fetch <remote> --tags,实际上可以重新命名1.0.0bin。这将确保下次他们获取所有标签时,旧标签1.0.0bin将不被引用并标记为后续垃圾回收(使用步骤3)。当您尝试覆盖遥控器上的标签时,您必须-f像这样使用:git push -f <remote> <tagname>

后记

  • OTABS不会影响您的母版或任何其他源代码/开发分支。提交哈希,所有历史记录以及这些分支的较小大小均不受影响。如果您已经用二进制文件充实了源代码历史记录,则必须将其清理为单独的工作。该脚本可能有用。

  • 确认可以在Windows上使用git-bash使用。

  • 最好应用一组标准度量标准,以使二进制文件的存储效率更高。频繁运行git gc(不带任何其他参数)使git通过使用二进制增量优化文件的基础存储。但是,如果您的文件不太可能在提交与提交之间保持相似,则可以完全关闭二进制增量。此外,由于压缩已压缩或加密的文件(如.zip,.jpg或.crypt)没有意义,因此git允许您关闭基础存储的压缩。不幸的是,这是一个全有或全无的设置,也会影响您的源代码。

  • 您可能需要脚本化OTABS的各个部分,以便更快地使用。特别是,从将二进制文件完全删除updategit挂钩中的脚本步骤2-3 可以为git fetch(“获取并删除所有过期的内容”)提供引人注目的但可能是危险的语义。

  • 您可能要跳过“ 完全删除二进制文件”的第4步,以将所有二进制更改的完整历史记录保留在远程计算机上,而这要付出中央存储库膨胀的代价。随着时间的流逝,本地存储库将保持精简。

  • 在Java世界中,可以将此解决方案与结合使用,maven --offline以创建可复制的脱机版本,该版本完全存储在您的版本控件中(使用Maven比使用gradle容易)。在Golang世界中,可以用此解决方案来管理GOPATH而不是go get。在python世界中,可以将其与virtualenv结合使用以产生一个自包含的开发环境,而无需依赖PyPi服务器进行任何从头开始的构建。

  • 如果您的二进制文件经常改变,像构建工件,这可能是一个好主意,脚本解决方案,它存储了文物5个最新版本的孤儿标签monday_bintuesday_bin...,friday_bin和也是一个孤儿标签为每个版本1.7.8bin 2.0.0bin等等。您可以weekday_bin每天旋转和删除旧的二进制文件。这样,您可以充分利用两个世界:保留源代码的整个历史记录,但仅保留二进制依赖项的相关历史记录。获取给定标签的二进制文件也很容易,而无需获取所有带有其所有历史记录的源代码:git init && git remote add <name> <url> && git fetch <name> <tag>应该为您完成。


“您必须定期使用git gc”-在那里停止阅读。为什么有人放弃自己的最后一条安全带来支持某些黑客行为?
user1643723 '16

@ user1643723 git gc并非不安全运行。默认情况下,所有悬空的提交都将安全地保存在硬盘上至少30天:git-scm.com/docs/git-gc
Adam Kurkiewicz

感谢您的详细撰写。我想尝试这种方法,以将某些二进制依赖项存储在我的GitHub存储库中,以便在有人克隆该存储库时默认情况下不下载它们,但可以手动下载并更新本地存储库。但是,此步骤出现错误:git push <remote> 1.0.0bin- remote: error: GH001: Large files detected. You may want to try Git Large File Storage。看起来GitHub不再支持此功能?该二进制文件的大小为100MB。
user5359531

1
老实说,如果允许您使用github进行工作,是什么使您无法使用LFS?github的家伙们一直在努力创建这个产品,他们甚至为您托管它,并且围绕使用它优化了基础结构。此hack的目的是当您确实不能使用LFS或其他第三方并且您使用的是纯git解决方案时。
亚当·库基维奇

我还更新了答案,以更清楚地了解此解决方案的实际漏洞。
亚当·库基维奇

13

我认为,如果您可能经常修改这些大文件,或者打算大量修改git clonegit checkout,那么您应该认真考虑使用另一个Git存储库(或者也许是另一种访问这些文件的方式)。

但是,如果您像我们一样工作,并且您的二进制文件不经常修改,那么第一次克隆/签出将很长,但是之后它应该和您想要的一样快(考虑到用户继续使用第一个克隆存储库,他们会有)。


13
而且,单独的存储库不会使结帐时间变短,因为您仍然必须检出两个存储库!
Emil Sit

如果您不断清理“二进制存储库”的历史记录,则@EmilSit单独的存储库会使结帐时间大大缩短。而且,开发人员不会每次都被迫检出两个存储库。
FabienAndre

为什么不让主模块的构建脚本从第二个存储库中提取二进制文件,并一次一地提取它们(例如:stackoverflow.com/questions/1125476/…)。
akauppi 2014年

1
即使您的二进制文件不经常更改,如果您出于协作目的而经常将分支推送到存储库中,大文件仍然会终止工作流程。
Timo Reimann 2014年

9

SVN似乎比Git更有效地处理二进制增量。

我必须决定用于文档的版本控制系统(JPEG文件,PDF文件和.odt文件)。我刚刚测试了添加JPEG文件并将其旋转90度四次(以检查二进制增量的有效性)。Git的存储库增长了400%。SVN的存储库仅增长了11%。

因此,看起来SVN与二进制文件相比效率更高。

因此,我的选择是对源代码使用Git,对文档等二进制文件使用SVN。


33
添加这4个文件后,您只需要运行“ git gc”(重新打包和垃圾回收)。Git不会立即压缩所有添加的内容,因此您将具有文件组压缩(在大小方面更为有效),并且不会单独压缩每个添加的对象。但是即使没有“ git gc”,git最终还是会为您完成压缩(无论如何,它会注意到已经积累了足够的解压缩对象)。
夜莺,2010年

24
@jpierson我创建了一个空的git存储库,并添加(并提交了)一个大小为41MB的全白bmp图像,这导致了一个总git存储库的大小为328KB。经过git gc总git仓库大小减少到184KB。然后,我将一个像素从白色更改为黑色,并进行了此更改,整个git存储库的大小增加到388KB,然后git gc将整个git存储库的大小减小到184KB。这表明git在压缩和查找二进制文件增量方面非常出色。
塔德,2011年

6
@jpierson旁注:我只是评论了二进制增量。如果Git管理具有大(GB大小)文件的存储库,它将消耗掉所有内存并进行交换。为此,请使用git-annex(已在其他答案中提及)...
2011年

12
@JanDvorak-没有人提到它,因为它是完全不真实的。Subversion副本很便宜-svnbook.red-bean.com/en/1.7/svn.branchmerge.using.html-在页面中间。
Joris Timmermans

12
@Tader:您的考试很糟糕。实际上,从git的角度来看,您所说的二进制文件更像是一个文本文件-比特流是按字节对齐的,并且要进行有意义的本地化比较。毕竟,更改一个像素基本上等同于更改文本文件中的一个字符(如今,谁在使用未压缩的位图?)尝试对小型视频,压缩图像,虚拟机,zip文件或其他内容进行相同的实验-您会发现git无法有效地处理增量;确实,使用不可压缩的数据根本不可能。
Eamon Nerbonne

4

git clone --filter 来自Git 2.19 +浅层克隆

如果Git和GitHub开发人员并使它对用户足够友好(例如,对于子模块来说,他们可能还没有实现),那么这个新选项最终可能成为二进制文件问题的最终解决方案。

它实际上只允许获取服务器所需的文件和目录,并且是与远程协议扩展一起引入的。

这样,我们可以先进行浅表克隆,然后针对每种类型的构建,自动使用构建系统获取哪些Blob。

甚至已经有一个--filter=blob:limit<size>允许限制最大blob大小来获取。

我提供了一个功能最少的详细示例:如何仅克隆Git存储库的子目录?


2

我正在寻找有关如何处理源代码(Web应用程序)所依赖的大型二进制文件的意见。您对此有何经验/想法?

一旦我的Web应用程序二进制数据的缺口超过3 GB,我个人就遇到了Git与某些云主机的同步失败。当时我考虑过BFT Repo Cleaner,但感觉就像是黑客。从那时起,我就开始将文件保留在Git权限范围之外,而是利用诸如Amazon S3之类的专用工具来管理文件,版本控制和备份。

是否有人在多个Git存储库中进行过管理并在一个项目中进行管理?

是。雨果主题主要通过这种方式进行管理。这有点笨拙,但可以完成工作。


我的建议是为工作选择合适的工具。如果是公司用,并且您要在GitHub上管理代码行,请付钱并使用Gi​​t-LFS。否则,您可以探索更多创造性的选择,例如使用区块链进行分散式加密文件存储

要考虑的其他选项包括Minios3cmd


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.