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

时间:2009-02-12 08:52:53

标签: git version-control large-files binaryfiles

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

  1. 手动复制二进制文件。
    • 亲:不确定。
    • Contra:我强烈反对这一点,因为它增加了在设置新网站/迁移旧网站时出错的可能性。建立另一个障碍。
  2. 使用 Git 管理所有内容。
    • Pro:删除'忘记'复制重要文件的可能性
    • Contra:膨胀存储库并降低管理代码库和签出,克隆等的灵活性将需要很长时间。
  3. 单独的存储库。
    • Pro:检查/克隆源代码的速度很快,图像可以在自己的存储库中正确存档。
    • Contra:删除了项目中唯一的 Git存储库的简单性。它肯定会介绍一些我没有想过的其他事情。
  4. 您对此有何看法?

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

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

12 个答案:

答案 0 :(得分:309)

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

答案 1 :(得分:177)

如果程序在没有文件的情况下无法工作,似乎将它们分成单独的仓库是一个坏主意。我们有大型测试套件,我们分成一个单独的回购,但这些是真正的“辅助”文件。

但是,您可以在单独的仓库中管理文件,然后使用git-submodule以理智的方式将它们拉入项目中。所以,你仍然拥有所有来源的完整历史记录,但据我所知,你只有你的图像子模块的一个相关版本。 git-submodule工具应该可以帮助您保持代码的正确版本符合正确版本的图像。

这是来自Git Book的好introduction to submodules

答案 2 :(得分:45)

另一个解决方案,自2015年4月起为Git Large File Storage (LFS)(由GitHub提供)。

它使用git-lfs(请参阅 git-lfs.github.com )并使用支持它的服务器进行测试:lfs-test-server
您只能在git仓库中存储元数据,在其他地方存储大型文件。

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

答案 3 :(得分:30)

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

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

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

您的里程可能会有所不同。

答案 4 :(得分:27)

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

git fat init
git fat push
git fat pull

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

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

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

更新:如果您使用的是Git-SVN网桥,请不要使用git-fat。它最终会从Subversion存储库中删除二进制文件。但是,如果您使用的是纯Git存储库,它可以很好地工作。

答案 5 :(得分:25)

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

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

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

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

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

你可以用同样的方式“清除”巨大的存储库:你的“git clone”会更快。

答案 6 :(得分:12)

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

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

答案 7 :(得分:11)

我想提出的解决方案是基于孤立分支和略微滥用标记机制,以下称为* Orphan标记二进制存储(OTABS)

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

OTABS的理想属性

  • 它是一个纯git git only 解决方案 - 它可以在没有任何第三方软件(如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. 添加&amp;&amp;提交它。
  7. 现在它变得棘手 - 如果你把它作为分支推入遥控器,所有开发人员都会在下次调用git fetch时堵塞它们的连接。您可以通过推送标签而不是分支来避免这种情况。如果他们习惯输入git fetch <remote> --tags,这仍然会影响您的同事的带宽和文件系统存储,但请继续阅读以解决此问题。继续git tag 1.0.0bin
  8. 推送您的孤儿代码git push <remote> 1.0.0bin
  9. 因此,您永远不会意外推送二进制分支,您可以将其删除git branch -D binaryStuff。您的提交不会被标记为垃圾收集,因为指向它的1.0.0bin上的孤立标记足以使其保持活动状态。
  10. 签出二进制文件

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

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

      1. 删除远程git push <remote> :refs/tags/1.0.0bin
      2. 上的孤儿标记
      3. 在本地删除孤立标记(删除所有其他未引用的标记)git tag -l | xargs git tag -d && git fetch --tags。取自SO 1841341略有修改。
      4. 使用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
      5. 如果可能,请重复遥控器上的git gc技巧。如果您自己托管您的存储库,并且某些git提供程序(如github)或某些企业环境可能无法实现,则可能会出现这种情况。如果您正在使用不向您提供ssh访问权限的提供商进行托管,请将其保留。您的提供商的基础架构可能会在自己的甜蜜时间内清理您的未引用提交。如果您在企业环境中,您可以建议您的IT运行一个cron作业垃圾,每周左右收集一次遥控器。只要您建议同事始终git clone -b master --single-branch <url>而不是git clone,他们是否对您的团队在带宽和存储方面没有任何影响。
      6. 想要摆脱过时的孤儿标签的所有同事只需要执行第2-3步。
      7. 然后,您可以重复添加二进制文件的步骤1-8来创建新的孤立标记2.0.0bin。如果您担心同事输入git fetch <remote> --tags,您实际上可以再次命名1.0.0bin。这将确保下次获取所有标记时旧的1.0.0bin将被取消引用并标记为后续垃圾收集(使用步骤3)。当您尝试覆盖遥控器上的标签时,您必须使用-f,如下所示:git push -f <remote> <tagname>
      8. <强>后记

        • OTABS无法触及您的主人或任何其他源代码/开发分支。提交哈希值,所有历史记录以及这些分支的小尺寸不受影响。如果您已经使用二进制文件膨胀了源代码历史记录,则必须将其作为单独的工作进行清理。 This script可能有用。

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

        • 最好应用set of standard trics来提高二进制文件的存储效率。频繁运行git gc(没有任何其他参数)使得git通过使用二进制增量来优化文件的底层存储。但是,如果您的文件在提交提交时不太可能保持相似,则可以完全关闭二进制增量。此外,因为压缩已压缩或加密的文件(如.zip,.jpg或.crypt)没有意义,git允许您关闭底层存储的压缩。不幸的是,它也是影响源代码的全有或全无设置。

        • 您可能希望编写OTABS的部分脚本以便更快地使用。特别是,从完全删除二进制文件到#{1}} git钩子的脚本步骤2-3可能会给git fetch提供一个引人注目的但也许是危险的语义(&#34;获取并删除所有的内容)已经过时&#34;)。

        • 您可能希望跳过完全删除二进制文件的第4步,以中央存储库膨胀为代价保留远程上所有二进制更改的完整历史记录。随着时间的推移,本地存储库将保持精益。

        • 在Java世界中,可以将此解决方案与update结合使用,以创建完全存储在您的版本控制中的可重现的离线构建(使用maven比使用gradle更容易)。在Golang世界中,建立此解决方案来管理您的GOPATH而不是maven --offline是可行的。在python世界中,可以将它与virtualenv结合起来,以生成一个独立的开发环境,而无需从头开始依赖PyPi服务器进行每个构建。

        • 如果您的二进制文件经常更改,例如构建工件,那么编写一个解决方案可能是一个好主意,该解决方案在孤立标记go get中存储5个最新版本的工件,{{1每个版本monday_bin tuesday_bin等的}},...,friday_bin以及孤立标记。您可以每天轮换1.7.8bin并删除旧的二进制文件。通过这种方式,您可以获得两个世界中最好的:保留源代码的整个历史记录,但只保留二进制依赖项的相关历史记录。获取给定标记的二进制文件也很容易获取包含其所有历史记录的完整源代码:2.0.0bin应该为您完成。

答案 8 :(得分:9)

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

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

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

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

答案 9 :(得分:2)

  

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

一旦我的网络应用程序二进制数据<3>高于3 GB标记,我个人已经与我的一些云主机遇到了与Git 的同步失败。我当时考虑BFT Repo Cleaner,但感觉就像是黑客。从那时起,我开始将文件保留在Git范围之外,而是利用专用工具(如Amazon S3)来管理文件,版本控制和备份。

  

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

是。 Hugo themes主要以这种方式管理。这有点胖,但它完成了工作。

我的建议是为工作选择合适的工具。如果是公司,你在GitHub上管理你的代码行,就要付钱并使用Gi​​t-LFS。否则,您可以探索更多创意选项,例如分散式加密file storage using blockchain

要考虑的其他选项包括Minios3cmd

答案 10 :(得分:1)

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

如果Git和GitHub开发人员并使它足够用户友好(例如,他们可以说是still haven't achieved for submodules),那么这个新选项最终可能会成为二进制文件问题的最终解决方案。

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

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

甚至已经有了--filter=blob:limit<size>,它可以限制要提取的最大Blob大小。

我在How do I clone a subdirectory only of a Git repository?

上提供了该功能外观的最小限度的详细示例。

答案 11 :(得分:-1)

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