返回
Featured image of post 关于 Git 大文件上传这件小事

关于 Git 大文件上传这件小事

很多年后,当我在命令行中熟练地操作 Git 的时候,我总会不由地想起从前意气风发的自己。毕竟不知不觉间,三十岁的年龄已然被更年轻的人们嫌弃“苍老”,除却生理上不可逆转的自然衰老,更多的或许是一种心态上的衰老。以前,我是非常鄙夷在 Git 仓库里提交 Word 或者 Excel 文件这种行为的,甚至连理由都给得十分正当,即:这种文件不利于差异的对比和合并。后来,参与的项目越来越多,渐渐认识到 Markdown 始终是一种小众的格式,你没有办法要求所有人都去适应 Markdown。所以,当我说我在心态上变成了一个老人的时候,其实是指,我不再对这件事情那么执着。当然,人生本来就是一个解决麻烦再制造麻烦的过程。当你默许了在 Git 仓库里提交非文本文件的行为,当这些非文本文件随着时间推移变得越来越大时,就出现了 Git 大文件上传、存储等等一系列的问题。因此,今天这篇文章,我们来聊聊 Git 里的大文件。

提交前的未雨绸缪

其实,博主不愿意在 Git 仓库里上传 Word 或者 Excel 文件,一个最为直接的理由是,它会成为我们拉取或者推送代码时的累赘。君不见,腾讯硬生生在手机 QQ 里内置了一个虚幻 4 引擎,想象一下,如果把这么多的文件都放到 Git 仓库里,每次做一点修改该有多痛苦啊!事实上,Github 对文件大小的限制是 100M,Gitlab 对文件大小的限制则是 600M,一旦超过这个限制,就会被判定为大文件。因此,AtlassianGitHub 等组织一起开发了针对 Git 的 Large File Storage 扩展,即:Git LFS。其原理是延迟地下载大文件的相关版本来减少大文件对仓库的影响,具体来说,就是在 checkout 到工作区的时候才会真正去下载大文件的内容。如果大家想了解更多 Git LFS 的细节,可以阅读下面这份文档:https://www.atlassian.com/git/tutorials/git-lfs,这里不再考虑对其进行二次加工。

Git LFS 原理示意图
Git LFS 原理示意图

当你准备向一个 Git 仓库提交大文件的时候,首先,你需要下载和安装 Git LFS 扩展并执行命令:

git lfs install

其次,在 Git 仓库中,你需要通过 git lfs track 命令告诉 Git LFS,你希望它帮你管理哪些文件:

git lfs track "*.jpg"

这里以 *.jpg 为例,它表示希望 Git LFS 对所有的 .jpg 格式的文件进行管理,这一步的结果是生成一个名为 .gitattributes 的文件。因此,理论上你可以直接编辑这个文件来达到相同的目的:

*.jpg filter=lfs diff=lfs merge=lfs -text

最后,你需要确保 .gitattributes 文件能被 Git 仓库追踪:

git add .gitattributes

一旦完成了这个准备工作,你就可以像平时一样提交和推送变更:

git add awesome.jpg
git commit -m "Add a awesome image file"
git push origin main

下图演示了 Git LFS 从初始化到应用的整个过程,可以注意到,这个过程中会生成各种钩子脚本:

Git LFS 的初始化与应用
Git LFS 的初始化与应用

提交后的亡羊补牢

扁鹊见蔡桓公的故事,告诉我们防微杜渐的道理。可如果你和博主一样,是在提交了一个大文件以后,才开始接触 Git LFS 的相关内容。那么,你大概会遇到下面的错误:

Git 上传文件超过 100M 的错误提示
Git 上传文件超过 100M 的错误提示

你可能会想,既然是这个文件的大小超过了 Github 的限制,那我把这个文件删除掉是不是就可以了呢?经过博主一番挣扎,发现这个思路完全是徒劳的。因为这个大文件就像持续了三年的新冠病毒一样,只要存在被它感染过的历史记录,都会在推送时提示上面的错误。所以,删除这个大文件并不能真正解决问题,我们必须要确保此前的提交历史中没有这个大文件。下面分享一个神奇的命令 git filter-branch

git filter-branch -f --index-filter 'git rm --cached --ignore-unmatch /src/CSharpWasm/mono-6.12.0.182-x64-0.msi'

通过这个命令,我们就可以重写整个提交历史:

从提交历史中删除指定的大文件
从提交历史中删除指定的大文件

此时,我们只需要按提交大文件前的步骤,重新提交该文件即可,并且可以突破 100M 的限制:

成功提交一个超过 100M 的大文件
成功提交一个超过 100M 的大文件

本文小结

我写作的话题,基本上可以分为两类:有计划的和无计划的。显然,这一篇属于后者,它就像育碧旗下的开放世界,当你漫游其中总会遇到那些令人意外的支线任务。我个人挺喜欢这种由点及面的认知模式,即从一个特定的问题逐步过渡到一个更为宽泛的知识或者体系上面。在这篇文章里,无法提交一个超过 100M 的大文件是表,不了解 Git LFS 是里,更有趣的一点是,解决问题的过程通常是由“”反推出“”,而撰写博客的过程则是由“”顺推出“”。从这个角度来看的话,对读者“揣着明白装糊涂”这才是最难把握的一个分寸,平时写长文章总担心写不清楚,而写短文章则担心像流水账。对于 Git LFS 来说,Git 仓库存储的其实是一个指针文件,真正的内容则是存储在 LFS 服务器里,主流的代码托管平台如 Github、Gitlab 等都支持 Git LFS,我们只需要安装 Git LFS 的客户端扩展即可。当然,我对非文本文件的合并依然持悲观态度,想象一下,两个人同时修改了一个大文件,一个已经推送到远程,一个已经提交到本地,那么,当他们尝试合并代码的时候,又会发生什么事情呢?作为一名程序员,每天被安排排查和解决问题,简直是家常便饭,可解决问题会有尽头吗?我想,大概率是没有的罢!

参考链接

Built with Hugo v0.107.0
Theme Stack designed by Jimmy
已创作 249 篇文章,共计 935057 字