朋友,你在 GitHub 上传大文件时,是不是碰到过remote: error: File xxx.zip is 150.00 MB; this exceeds GitHub's file size limit of 100 MB这种报错?或者仓库明明没多大,但网页端怎么都传不上去?
我先把结论摆出来:GitHub 单文件硬限制是 100MB,想突破它,唯一安全可靠的路子是 Git LFS。这篇文章不绕弯子,从限制原理、LFS 工作方式、完整操作流程到报错排查,一次讲清楚。不管你是刚接触 Git 的新人,还是被大文件搞到头秃的开发者,都能按步骤操作。
1. 先把 GitHub 的文件限制摸清楚
1.1 硬性限制:单文件 100MB,仓库 1GB 警告线
GitHub 平台对仓库文件体积有一套明确的限制规则,不了解这些规则就盲目 push,大概率会在最后一步被拒。
- 单个文件大小警告线:50MB。超过 50MB 的文件,GitHub 网页端会给出警告提示,建议你使用 Git LFS。
- 单个文件大小硬上限:100MB。任何超过 100MB 的文件,无论通过网页、命令行还是 API,都无法正常 push。这个限制是为了防止极端情况下拖垮 Git 服务端性能。
- 推荐仓库总大小:1GB 内。GitHub 会向超出 1GB 的仓库发送邮件提醒,强烈不建议把仓库做成“网盘”,Git 的设计初衷是管理源码,不是存储大素材。
需要特别注意的是,100MB 限制针对的是单个文件大小,不是单个提交的总大小。你完全可以一次性提交 50 个 80MB 的文件,只要每个文件不超过 100MB,Git 层面不会被拒绝。但这么做会让仓库体积急剧膨胀,后续的 clone 和 fetch 对服务器和协作者都是灾难。
1.2 为什么普通 git add 会因为大文件失败
很多人在本地执行git add largefile.bin时毫无反应,等到了git push阶段才收到服务器拒绝。这是 Git 的工作机制决定的——Git 在本地完成所有快照,服务器在 push 时才做校验。
具体流程是这样的:
git add把文件写入本地对象库(.git/objects),此时没有任何大小限制。git commit生成提交记录,同样只在本地操作。git push时,Git 客户端把对象打包发送到 GitHub 服务器。- 服务器端校验每个 blob 对象的大小,若超过 100MB,直接返回错误,整个 push 过程失败。
所以排查问题时,请以git push阶段的报错为准。本地看不见报错是正常的,不代表文件没问题。
2. Git LFS 才是大文件上传的正路
2.1 Git LFS 的工作原理
Git LFS(Large File Storage)是 GitHub 官方支持的大文件解决方案,核心思路很简单:把大文件的真实内容从 Git 仓库中抽离,存放到独立的 LFS 存储区,Git 仓库里只保存一个指向该文件的指针文件。
指针文件通常只有几百字节,内容是文件哈希和大小信息,格式如下:
version https://git-lfs.github.com/spec/v1 oid sha256:4d7a214f1f4c1b8a2e3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d size 157293522当别人 clone 你的仓库时,Git 只下载指针文件(毫秒级),LFS 客户端再根据指针文件从 LFS 存储区拉取真实内容。这样仓库本体始终保持轻量,大文件既不撑爆 GitHub 配额,也不拖慢 Git 操作。
这个方案有个重要好处:LFS 文件也会被追踪版本历史。你每次修改大文件,LFS 存储区都会保留旧版本,配合 Git 的 checkout、reset 等操作,完全无缝。
2.2 安装并初始化 Git LFS
LFS 不是 Git 自带功能,需要单独安装客户端。
在 macOS 上用 Homebrew:
brew install git-lfs在 Windows 上,推荐从 Git LFS 官网下载安装包,安装完成后使用命令行验证:
git lfs version看到类似git-lfs/3.4.1 (GitHub; windows amd64; go 1.21.5)的输出就算成功。
安装完成后,执行全局启用:
git lfs install这条命令会修改你的 Git 全局配置,在~/.gitconfig中加入 LFS 相关的过滤器配置。输出Updated Git hooks即表示成功。
2.3 选择要跟踪的大文件类型
LFS 不是自动拦截所有大文件,它只处理你在.gitattributes中明确指定的文件类型或路径。这是一把双刃剑——配置精确能避免误伤,配置遗漏则大文件照样超限。
.gitattributes文件放在仓库根目录,声明需要 LFS 管理的文件模式。比如:
*.zip filter=lfs diff=lfs merge=lfs -text *.psd filter=lfs diff=lfs merge=lfs -text *.bin filter=lfs diff=lfs merge=lfs -text *.tar.gz filter=lfs diff=lfs merge=lfs -text models/** filter=lfs diff=lfs merge=lfs -text这里的关键是filter=lfs diff=lfs merge=lfs -text这三个属性。filter=lfs让 Git 在 add/checkout 时调用 LFS 过滤器;diff=lfs让 Git 对 LFS 文件使用二进制 diff 策略;-text表示不要做换行符转换,避免二进制文件被破坏。
也可以用git lfs track命令生成上述规则:
git lfs track "*.psd" git lfs track "*.zip"执行后会自动修改或创建.gitattributes。但我建议你还是手动打开看一眼,确认没有意外覆盖已有规则。
3. 实操:把超过 100MB 的文件推到 GitHub
3.1 完整操作流程
直接给你一份可以照抄的操作清单,我默认你已经完成 LFS 安装并创建了空仓库。
第一步:初始化仓库并安装 LFS 钩子
git init git lfs install第二步:在推送前声明要跟踪的大文件
git lfs track "*.zip" git add .gitattributes这一步很多人会漏掉。.gitattributes本身必须作为普通文件提交,否则其他协作者 clone 仓库后,LFS 规则不被触发,后续 push 依然会失败。
第三步:正常添加大文件并提交
git add release.zip git commit -m "Add release package"此时git status会显示release.zip被识别为 LFS 文件。可以执行git lfs ls-files确认:
git lfs ls-files输出示例:
4d7a214f1f * release.zip第四步:创建远程仓库并推送
git remote add origin git@github.com:yourname/yourrepo.git git push -u origin main如果一切正常,push 阶段会看到 LFS 上传进度:
Uploading LFS objects: 100% (1/1), 157 MB | 5.4 MB/s, done.到这里,超过 100MB 的文件就成功推上去了。
3.2 .gitattributes 文件的维护
.gitattributes是 LFS 方案的命脉,维护不善会引发各种奇怪问题。给你几个实测下来的管理要点:
第一,规则顺序很重要。Git 匹配规则时,越靠后的规则优先级越高。如果你写了这样的规则:
* filter=lfs diff=lfs merge=lfs -text *.txt filter= -text那么*.txt会被排除掉 LFS 跟踪。如果你想让所有二进制文件走 LFS,同时排除部分目录,最好把排除规则放最后。
第二,新增的大类型要及时补充规则。我踩过一个很实际的坑:项目里放了*.zip和*.tar.gz,只给*.zip配置了 LFS,某次构建产物变成了.tar.gz,结果 push 时直接被服务器拒绝。解决办法是建立仓库内部规则,构建物 push 前,先git lfs track检查一遍。
第三,不要轻易删除已提交的 LFS 跟踪规则。如果你把某条规则从.gitattributes中移除,历史提交中的 LFS 文件指针会保留,但新提交的同类文件又会走普通 Git 存储。对于已经存在的大文件,GitHub 依然会拒绝普通的 push。这就是为什么很多人的仓库删了.gitattributes之后反而无法推送。
3.3 如果 LFS 配额不够怎么办
GitHub 免费账户的 LFS 存储配额是1GB 带宽 + 1GB 存储。这里的“带宽”是拉取流量,按每月计算;“存储”是所有历史 LFS 文件的去重后总量。一旦超出配额,git push会报batch response: This repository is over its data quota。
有几种应对思路,按推荐顺序排列:
思路一:删除不需要的 LFS 历史文件。获取当前所有 LFS 文件列表:
git lfs ls-files --all找到确实无用的文件(比如旧的安装包),从 Git 历史中彻底清理掉,LFS 存储才会释放。如果你只是从当前分支删除,历史里还保留,配额不会生效。
思路二:拆分仓库。把大文件集中到一个独立的“资产仓库”中,主仓库通过 Git 子模块或直接引用该仓库的存档链接。这样主仓库可以正常用免费配额,资产仓库单独管理。
思路三:升级付费档。不是广告,但坦白说,如果团队长期需要存放大文件,GitHub 付费方案提供的 LFS 数据包确实能解决根本问题。费用按数据包购买,不算太贵,但对个人项目而言,多数人更愿意先试前两种方案。
4. 常见报错与排查手册
4.1 遇到 “this exceeds GitHub's file size limit of 100 MB”
这条报错在 push 时出现,几乎可以断定:你有一个文件超过 100MB,但它没有被 LFS 追踪。
排查步骤:
- 在本仓库中执行
git rev-list --objects --all | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | awk '$1=="blob" && $3>100000000 {print $4}',找出超过 100MB 的 blob 文件名。 - 如果该文件是必须保留的,用
git lfs track "文件名或通配符"添加规则,然后重新提交。 - 如果文件确实不该存在于仓库(比如误提交的日志文件),需要从历史中删除,而不是仅做一次新的提交。
这里最容易犯的错误是:你执行了git rm largefile.bin并提交,以为就清理干净了。实际上,只要历史提交里还有这个大文件,push 依旧会被拒。GitHub 校验的是整个仓库的对象,不限当前分支的 HEAD。
4.2 如何在本地彻底移除误提交的大文件
需要清理历史中的大文件时,推荐使用git filter-repo。先安装它:
pip install git-filter-repo假设误提交的文件名是wrong_big_file.zip,执行:
git filter-repo --invert-paths --path wrong_big_file.zip该命令会重写整个仓库历史,删除所有包含该文件的提交中的对应文件。执行完成后,强制推送:
git remote add origin git@github.com:yourname/yourrepo.git git push --force --all使用git filter-repo前请务必备份本地仓库。这个工具会移除原有的origin远程配置,而且由于历史被改写,所有协作者的本地仓库都需要重新 clone。
4.3 遇到 “batch response: This repository is over its data quota”
这个报错说明 LFS 配额超限,需要先清点 LFS 使用情况。
git lfs env git lfs ls-files --all查看哪些文件占用了大量空间。处理办法前面提过删除历史或拆分仓库,不再赘述。这里补一个实操小技巧:GitHub 仓库设置页面的 “Data usage” 面板会显示 LFS 存储和带宽的月度用量。查看用量有助于判断是存储超限还是流量超限。如果只是带宽超限,往往要等到下月 1 日自动重置。
4.4 本地已经配置了 LFS,但 push 时 LFS 上传卡死
LFS 上传走的是独立的 HTTPS 连接,经常遇到卡在Uploading LFS objects: 0%的情况。可能原因和排查方向如下:
- 网络代理设置异常。检查环境变量
HTTPS_PROXY、HTTP_PROXY是否指向了不可用的代理。 - DNS 解析问题。LFS 默认通过
github.com获取上传地址,解析失败会导致连接挂起。 - 认证失效。如果使用 HTTPS 方式 clone 仓库并且开启了双重验证,LFS 上传需要提供有效的 access token,而不是普通密码。执行
git config --list | grep lfs检查相关配置。
切换到 SSH 方式 clone 往往能绕过 HTTPS 相关的认证问题:
git remote set-url origin git@github.com:yourname/yourrepo.git4.5 clone 仓库时提示 “LFS: No such file or directory”
这个出现在git clone阶段,说明远程仓库有 LFS 文件,但本地未安装 Git LFS 客户端。
解决办法很简单:
git lfs install git lfs pull如果 clone 已经完成但 LFS 文件缺失,直接进入仓库执行git lfs pull即可补拉。
5. 我的一些体会和建议
Git 本身是文本版本管理工具,强行走大文件路子,路只会越走越窄。GitHub 的 100MB 限制背后是大量开源项目多年踩坑后的经验总结,不是随便拍脑袋定的数字。如果你发现自己频繁需要上传大文件,我建议你停下来想想这个文件是否真的应该进 Git 仓库,而不是急着找工具绕开限制。
有几个比较实用的判断经验,你可以直接套用:
- 编译产物(
.class、.o、.exe),原则上不进 Git 仓库,走 CI 构建或外部制品存储。 - 数据集和媒体素材(
.psd、.mp4、.csv大数据),如果团队确实需要版本管理,Git LFS 是合理选择。 - 依赖包和二进制库(
.jar、.dll、.so),优先使用包管理器锁定版本,而不是直接把二进制塞进仓库。
还有一个小技巧,我实际用下来帮助很大:建立.gitignore与.gitattributes的联动规则。比如你需要在仓库里保留*.zip但不想让生成目录下的临时 zip 被上传,可以这样做:
*.zip filter=lfs diff=lfs merge=lfs -text build/*.zip !filter这样既保证正式发布的 zip 由 LFS 管理,同时让构建目录内的临时文件走普通 Git 流程或直接忽略。
最后提醒一句:如果你从别的仓库迁移并想要保留大文件历史,请务必在迁移前就规划好 LFS 配额。GitHub 对新建仓库的初始 LFS 存储只有 1GB,迁移超过这个体量的大型资源库,大概率会立刻触发配额问题。先清理再迁移,远比迁移后抢救来得轻松。