533

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

  1. 手动复制二进制文件。
    • 临:不确定。
    • 反对:我强烈反对这一点,因为它增加了设置新站点/迁移旧站点时出错的可能性。建立另一个障碍。
  2. 使用Git管理它们。
    • 优点:消除了“忘记”复制重要文件的可能性
    • Contra:使存储库膨胀并降低管理代码库和签出、克隆等的灵活性,这将需要相当长的时间。
  3. 单独的存储库。
    • 优点:签出/克隆源代码的速度与以往一样快,并且图像已正确存档在自己的存储库中。
    • Contra:消除了在项目中拥有唯一的 Git 存储库的简单性。它肯定会介绍一些我没有想到的其他事情。

您对此有何经验/想法?

另外:有没有人有使用多个 Git 存储库并在一个项目中管理它们的经验?

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

4

12 回答 12

313

我最近发现了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上提供了一个软件包。

于 2011-07-09T13:54:28.817 回答
177

如果程序在没有文件的情况下无法运行,那么将它们拆分为单独的存储库似乎是个坏主意。我们有大型测试套件,我们将其分解为单独的存储库,但这些是真正的“辅助”文件。

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

这是Git Book中对子模块的一个很好的介绍。

于 2009-02-12T14:29:01.293 回答
54

自 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

于 2015-04-09T05:53:54.583 回答
34

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

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

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

你的旅费可能会改变。

于 2011-03-21T21:59:54.367 回答
29

你也可以使用git-fat。我喜欢它只依赖于 Python 和rsync. 它还支持通常的 Git 工作流程,具有以下自解释命令:

git fat init
git fat push
git fat pull

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

您使用 normal 添加二进制文件,然后根据您的 gitattributes 规则git add调用。git fat

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

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

于 2013-09-26T04:51:26.027 回答
26

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

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

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

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

然后,我删除了旧分支,只保留了新分支,删除了 ref-logs,然后运行“git prune”:在那之后,我的 .git 文件夹仅重约 6 GB...

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

于 2009-02-12T14:52:57.070 回答
13

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

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

于 2009-02-12T09:12:02.223 回答
13

我想提出的解决方案是基于孤立分支和对标签机制的轻微滥用,以下称为 *Orphan Tags Binary Storage (OTABS)

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

OTABS 的理想特性

  • 它是一个纯 git仅 git的解决方案——它可以在没有任何 3rd 方软件(如 git-annex)或 3rd 方基础设施(如 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到您的 master's.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 访问远程,那就顺其自然吧。您的提供商的基础设施可能会在他们自己的甜蜜时间清理您未引用的提交。如果您在公司环境中,您可以建议您的 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 不会触及您的 master 或任何其他源代码/开发分支。这些分支的提交哈希、所有历史记录和小尺寸不受影响。如果您已经使用二进制文件使源代码历史膨胀,则必须将其作为单独的工作进行清理。这个脚本可能有用。

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

  • 应用一组标准 trics来提高二进制文件的存储效率是一个好主意。频繁运行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>应该为您完成。

于 2015-07-13T18:32:39.187 回答
9

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

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

所以看起来 SVN 处理二进制文件的效率要高得多。

所以我的选择是用于源代码的 Git 和用于文档等二进制文件的 SVN。

于 2010-10-03T03:11:41.550 回答
6

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

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

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

有了这个,我们可以先做一个浅层克隆,然后自动为每种构建类型使用构建系统获取哪些 blob。

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

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

于 2018-12-06T13:55:14.460 回答
2

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

一旦我的 Web 应用程序二进制数据超过 3 GB 标记,我个人就遇到了与 Git 与我的一些云主机的同步失败。当时我考虑过BFT Repo Cleaner,但感觉就像是 hack。从那时起,我开始只将文件保存在 Git 权限之外,而是利用Amazon S3 等专门构建的工具来管理文件、版本控制和备份。

有没有人有使用多个 Git 存储库并在一个项目中管理它们的经验?

是的。Hugo 主题主要是通过这种方式管理的。这有点笨拙,但它完成了工作。


我的建议是为工作选择合适的工具。如果是为一家公司而你在 GitHub 上管理你的代码线,那就付钱并使用 Git-LFS。否则,您可以探索更多创造性的选择,例如使用区块链的去中心化、加密文件存储

要考虑的其他选项包括Minios3cmd

于 2017-09-10T11:33:46.453 回答
0

看看camlistore。它不是真正基于 Git 的,但我发现它更适合您必须做的事情。

于 2014-10-03T10:36:05.507 回答