本文发表于 919 天前,其中的信息可能已经事过境迁
文章摘要

很多年后,当我在命令行中熟练地操作 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 的客户端扩展即可。当然,我对非文本文件的合并依然持悲观态度,想象一下,两个人同时修改了一个大文件,一个已经推送到远程,一个已经提交到本地,那么,当他们尝试合并代码的时候,又会发生什么事情呢?作为一名程序员,每天被安排排查和解决问题,简直是家常便饭,可解决问题会有尽头吗?我想,大概率是没有的罢!

参考链接

赞赏博主
相关推荐 随便逛逛
浅议分布式链路追踪与日志的整合 最近阅读了一篇关于分布式链路追踪和日志系统整合的文章,主要介绍了在.NET中利用Activity、ActivitySource和ActivityListener等API实现分布式追踪的方法。这些API可以被视为微软对OpenTelemetry规范的实现,其中每个Activity对应一个Span。文章还介绍了如何利用自定义TraceId和NLog.DiagnosticSource等工具实现将TraceId渲染到日志中,以实现更好的日志查询和分析。最后,作者分享了如何利用OpenTelemetry SDK自动采集HttpClient和ASP.NET Core的诊断信息,并探讨了分布式追踪与日志整合的实践方法。文章以深入浅出的方式介绍了相关概念和技术,提供了有关链路追踪和日志系统整合的有益见解。
EFCore 实体命名约定库:EFCore.NamingConventions 在软件开发中,数据库是一个重要话题,有不同的流派,一些人支持手写SQL,另一些喜欢ORM。介绍了EFCore.NamingConventions库,可在EFCore中指定实体命名约束规则,简化属性与数据库表字段映射,提高团队效率。示例展示了使用SnakeCaseNamingConvention生成数据库表字段,强调约定大于配置的重要性。团队应记录数据库规范,以避免配置混乱或新人不了解约定。这个库有利于标准化命名,提高开发效率。
评论 隐私政策