使用 Git 管理大型二进制文件
-
22-08-2019 - |
题
我正在寻找有关如何处理我的源代码(Web 应用程序)所依赖的大型二进制文件的意见。我们目前正在讨论几种替代方案:
- 手动复制二进制文件。
- 专业人士:没有把握。
- 魂斗罗:我强烈反对这一点,因为它增加了设置新站点/迁移旧站点时出错的可能性。建立了另一个障碍。
- 管理它们全部 git.
- 专业人士:消除了“忘记”复制重要文件的可能性
- 魂斗罗:使存储库变得臃肿并降低了管理代码库、签出、克隆等的灵活性。需要相当长的时间。
- 单独的存储库。
- 专业人士:签出/克隆源代码的速度一如既往地快,并且图像正确地存档在自己的存储库中。
- 魂斗罗:消除了拥有的简单性 唯一的 项目上的 Git 存储库。它肯定引入了一些我没有想到的其他事情。
您对此有什么经验/想法?
还:有人有使用多个 Git 存储库并在一个项目中管理它们的经验吗?
这些文件是用于生成包含这些文件的 PDF 的程序的图像。这些文件不会经常更改(如数年),但它们与程序非常相关。如果没有这些文件,程序将无法运行。
解决方案
如果程序在没有这些文件的情况下无法运行,那么将它们分成一个单独的存储库似乎是一个坏主意。我们有大型测试套件,我们将它们分解为单独的存储库,但这些是真正的“辅助”文件。
但是,您也许可以在单独的存储库中管理文件,然后使用 git-submodule
以理智的方式将它们拉入您的项目。因此,您仍然拥有所有源代码的完整历史记录,但据我了解,您只会拥有图像子模块的一个相关修订版。这 git-submodule
工具应该帮助您使代码的正确版本与图像的正确版本保持一致。
这里有一个好的 子模块简介 来自 Git 书籍。
其他提示
我发现 git 附件 最近我觉得很棒。它专为高效管理大文件而设计。我用它来收藏我的照片/音乐(等)。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
有许多可用的命令,并且网站上有很棒的文档。软件包可在 德班.
自 2015 年 4 月以来的另一个解决方案是 Git 大文件存储 (LFS) (由 GitHub 提供)。
它用 git-lfs (看 git-lfs.github.com)并使用支持它的服务器进行了测试: lfs-测试服务器:
您只能将元数据存储在 git 存储库中,而将大文件存储在其他地方。
看一下 git bup 这是一个 Git 扩展,可以在 Git 存储库中智能地存储大型二进制文件。
您希望将其作为子模块,但您不必担心存储库变得难以处理。他们的示例用例之一是将 VM 映像存储在 Git 中。
我实际上没有看到更好的压缩率,但我的存储库中没有真正大的二进制文件。
你的旅费可能会改变。
您还可以使用 吉特脂肪. 。我喜欢它只依赖于现有的 Python 和 rsync
. 。它还支持通常的 Git 工作流程,具有以下不言自明的命令:
git fat init
git fat push
git fat pull
此外,您需要将 .gitfat 文件签入您的存储库并修改您的 .gitattributes 以指定您想要的文件扩展名 git fat
管理。
您可以使用正常的方式添加二进制文件 git add
, ,这又调用 git fat
基于您的 gitattributes 规则。
最后,它的优点是二进制文件实际存储的位置可以在存储库和用户之间共享,并支持任何内容 rsync
做。
更新:如果您使用 Git-SVN 桥,请勿使用 git-fat。它将最终从您的 Subversion 存储库中删除二进制文件。但是,如果您使用纯 Git 存储库,它的工作效果会非常好。
我会使用子模块(如 Pat Notz)或两个不同的存储库。如果您过于频繁地修改二进制文件,那么我会尝试最大程度地减少庞大存储库清理历史记录的影响:
几个月前我遇到了一个非常相似的问题:大约 21 GB 的 MP3 文件,未分类(错误的名称、错误的 id3、不知道我是否喜欢该 MP3 文件...),并在三台计算机上复制。
我使用了带有主 Git 存储库的外部硬盘驱动器,并将其克隆到每台计算机中。然后,我开始按照习惯的方式对它们进行分类(推、拉、合并......多次删除和重命名)。
最后,我的 .git 目录中只有约 6 GB 的 MP3 文件和约 83 GB 的文件。我用了 git-write-tree
和 git-commit-tree
创建一个新的提交,没有提交祖先,并启动一个指向该提交的新分支。该分支的“git log”仅显示一次提交。
然后,我删除了旧分支,只保留新分支,删除了引用日志,然后运行“git prune”:之后,我的 .git 文件夹仅重约 6 GB...
您可以不时地以相同的方式“清除”巨大的存储库:你的“git克隆”会更快。
在我看来,如果您可能经常修改这些大文件,或者您打算制作大量文件 git clone
或者 git checkout
, ,那么您应该认真考虑使用另一个 Git 存储库(或者可能是访问这些文件的另一种方式)。
但是,如果您像我们一样工作,并且您的二进制文件不经常修改,那么第一次克隆/签出将会很长,但之后它应该像您想要的那样快(考虑到您的用户继续使用他们的第一个克隆存储库)有)。
我想提出的解决方案是基于孤儿分支和标签机制的轻微滥用,以下称为*孤儿标签二进制存储 (奥塔巴斯)
TL;DR 12-01-2017 如果您可以使用 github 的 LFS 或其他第 3 方,那么您一定应该使用。如果你不能,那么请继续阅读。请注意,此解决方案是一种黑客行为,应如此对待。
OTABS 的理想特性
- 它是一个 纯git 和 仅限 git 解决方案——它无需任何第 3 方软件(如 git-annex)或第 3 方基础设施(如 github 的 LFS)即可完成工作。
- 它存储二进制文件 有效率的, , IE。它不会使您的存储库的历史记录变得臃肿。
git pull
和git fetch
, , 包括git fetch --all
仍 带宽效率, , IE。默认情况下,并非所有大型二进制文件都是从远程拉取的。- 它适用于 视窗.
- 它将所有内容存储在一个 单个 git 存储库.
- 它允许 删除 过时的二进制文件(与 bup 不同)。
OTABS 的不良特性
- 它使
git clone
可能效率低下(但不一定,取决于您的使用情况)。如果您部署此解决方案,您可能需要建议您的同事使用git clone -b master --single-branch <url>
代替git clone
. 。这是因为 git clone 默认情况下是字面意义上的克隆 全部的 存储库,包括您通常不想浪费带宽的东西,例如未引用的提交。取自 SO 4811434. - 它使
git fetch <remote> --tags
带宽效率低下,但存储效率不一定低下。您可以随时建议您的同事不要使用它。 - 你必须定期使用
git gc
清除存储库中不再需要的任何文件的技巧。 - 它的效率不如 布普 或者 git 大文件. 。但它分别更适合您想要做的事情并且更现成。您可能会在处理数十万个小文件或千兆字节范围内的文件时遇到麻烦,但请继续阅读以获取解决方法。
添加二进制文件
在开始之前,请确保您已提交所有更改,您的工作树是最新的,并且您的索引不包含任何未提交的更改。将所有本地分支推送到远程(github 等)可能是一个好主意,以防发生任何灾难。
- 创建一个新的孤立分支。
git checkout --orphan binaryStuff
会成功的。这会产生一个与任何其他分支完全断开连接的分支,并且您将在该分支中进行的第一个提交将没有父级,这将使其成为根提交。 - 使用清理你的索引
git rm --cached * .gitignore
. - 深呼吸并使用删除整个工作树
rm -fr * .gitignore
. 。内部的.git
目录将保持不变,因为*
通配符不匹配。 - 复制您的 VeryBigBinary.exe 或 VeryHeavyDirectory/。
- 添加它&&提交它。
- 现在它变得棘手 - 如果你将它作为分支推送到远程,所有开发人员都会在下次调用时下载它
git fetch
堵塞他们的连接。您可以通过推送标签而不是分支来避免这种情况。如果您的同事有打字的习惯,这仍然会影响他们的带宽和文件系统存储git fetch <remote> --tags
, ,但请继续阅读以获取解决方法。继续吧git tag 1.0.0bin
- 推送您的孤儿标签
git push <remote> 1.0.0bin
. - 为了避免意外推送二进制分支,您可以将其删除
git branch -D binaryStuff
. 。您的提交不会被标记为垃圾回收,因为有一个孤儿标签指向它1.0.0bin
足以让它活下去。
检查二进制文件
- 我(或我的同事)如何将 VeryBigBinary.exe 检出到当前工作树中?例如,如果您当前的工作分支是 master,您可以简单地
git checkout 1.0.0bin -- VeryBigBinary.exe
. - 如果您没有孤立标签,这将会失败
1.0.0bin
下载,在这种情况下你必须git fetch <remote> 1.0.0bin
预先。 - 您可以添加
VeryBigBinary.exe
进入你主人的.gitignore
, ,这样您团队中的任何人都不会意外地用二进制文件污染项目的主要历史记录。
完全删除二进制文件
如果您决定从本地存储库、远程存储库和同事的存储库中完全清除 VeryBigBinary.exe,您可以:
- 删除遥控器上的孤立标签
git push <remote> :refs/tags/1.0.0bin
- 本地删除孤立标签(删除所有其他未引用的标签)
git tag -l | xargs git tag -d && git fetch --tags
. 。取自 所以1841341 稍加修改。 - 使用 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 - 如果可能,请在远程重复 git gc 技巧。如果您自行托管存储库,则这是可能的,而某些 git 提供商(例如 github 或在某些企业环境中)可能无法实现。如果您托管的提供商不允许您通过 ssh 访问远程设备,那就随它去吧。您的提供商的基础设施可能会在他们自己的最佳时间清理您未引用的提交。如果您在公司环境中,您可以建议您的 IT 运行 cron 作业,每周左右收集一次远程垃圾。无论他们这样做或不这样做,都不会对您的团队在带宽和存储方面产生任何影响,只要您建议您的同事始终这样做
git clone -b master --single-branch <url>
代替git clone
. - 所有想要删除过时的孤立标签的同事只需应用步骤 2-3。
- 然后您可以重复步骤 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 部分脚本以实现更快的使用。特别是,编写步骤 2-3 的脚本 完全删除二进制文件 进入一个
update
git hook 可以为 git fetch 提供令人信服但可能危险的语义(“获取并删除所有过时的内容”)。您可能想跳过第 4 步 完全删除二进制文件 以中央存储库膨胀为代价,在远程保留所有二进制更改的完整历史记录。随着时间的推移,本地存储库将保持精简。
在 Java 世界中,可以将此解决方案与
maven --offline
创建一个完全存储在版本控制中的可重现的离线构建(使用 Maven 比使用 gradle 更容易)。在 Golang 世界中,可以基于此解决方案来管理您的 GOPATH,而不是go get
. 。在 python 世界中,可以将其与 virtualenv 结合起来生成一个独立的开发环境,而无需从头开始每次构建都依赖 PyPi 服务器。如果您的二进制文件经常更改(例如构建工件),那么编写一个解决方案来将 5 个最新版本的工件存储在孤立标记中可能是个好主意
monday_bin
,tuesday_bin
, ...,friday_bin
, ,以及每个版本的孤立标签1.7.8bin
2.0.0bin
, , ETC。您可以旋转weekday_bin
并每天删除旧的二进制文件。这样您就可以两全其美:你保留 全部的 您的源代码的历史记录,但仅 相关的 二进制依赖关系的历史记录。获取给定标签的二进制文件也很容易 没有 获取完整的源代码及其所有历史记录:git init && git remote add <name> <url> && git fetch <name> <tag>
应该为你做。
SVN 似乎比 Git 更有效地处理二进制增量。
我必须决定文档的版本控制系统(JPEG 文件、PDF 文件和 .odt 文件)。我刚刚测试了添加 JPEG 文件并将其旋转 90 度四次(以检查二进制增量的有效性)。Git 的存储库增长了 400%。SVN 的存储库仅增长了 11%。
所以看起来 SVN 对于二进制文件的效率要高得多。
所以我选择使用 Git 来获取源代码,使用 SVN 来获取文档等二进制文件。
git clone --filter
来自 Git 2.19 + 浅克隆
如果 Git 和 GitHub 开发人员使其足够用户友好(他们可以说是这样),这个新选项最终可能会成为二进制文件问题的最终解决方案 子模块还没有实现 例如)。
它实际上允许只获取服务器所需的文件和目录,并与远程协议扩展一起引入。
有了这个,我们可以首先进行浅克隆,然后自动使用构建系统为每种类型的构建获取哪些 blob。
甚至已经有一个 --filter=blob:limit<size>
这允许限制要获取的最大 blob 大小。
我提供了一个最简单的详细示例来说明该功能的外观: 如何仅克隆 Git 存储库的子目录?
我正在寻找有关如何处理我的源代码(Web 应用程序)所依赖的大型二进制文件的意见。您对此有什么经验/想法?
我个人曾经遇到过 Git 同步失败 一旦我的 Web 应用程序二进制数据被标记,我的一些云主机就会使用 超过 3 GB 标记. 。我考虑过 BFT 回购清理器 当时,但感觉就像是黑客攻击。从那时起,我开始将文件保留在 Git 权限之外,而不是利用 专用工具 例如用于管理文件、版本控制和备份的 Amazon S3。
有人有使用多个 Git 存储库并在一个项目中管理它们的经验吗?
是的。 雨果主题 主要是这样管理的。这有点笨拙,但它完成了工作。
我的建议是 选择适合工作的工具. 。如果是为一家公司,并且您在 GitHub 上管理代码行,请付费并使用 Git-LFS。否则,您可以探索更多创造性的选择,例如去中心化、加密 使用区块链的文件存储.
看一下 卡姆利商店. 。它并不是真正基于 Git,但我发现它更适合您要做的事情。