我先把话说清楚:GitHub 对单文件 50MB 会弹警告,超过 100MB 直接拒绝 push。这不是你网络问题,也不是你命令敲错,是平台硬性限制。但我在实际项目里倒腾过好几次大文件上传,把 Git LFS、Release 附件、外部存储这几条路都摸过一遍,踩了不少坑,也总结出了一套不折腾的流程,这篇直接给你讲明白。
先说一个最常见的场景。很多人手里都有一类项目:要管大量图片、视频、数据集、打包好的前端产物,或者像网上那个 gaoshu705/qzonearchive 一样的 QQ 空间归档备份工具,导出的本地 HTML、图片动辄几百 MB,甚至上 GB。你想把这些历史数据传上去做备份或者分享,直接 git add 就废了。我当时第一次接手类似项目,硬 push 一个 300MB 的压缩包,结果 GitHub 直接拒收,终端里刷了一屏红的。后来才搞清楚,GitHub 对单仓库有明确限制,普通 Git 协议就不是为大文件设计的,你得换个思路。
这篇文章我分几块讲:GitHub 对“大文件”的具体红线到底在哪,为什么不能硬传;主流的 Git LFS 方案怎么落地,安装、跟踪、提交、push 全套给你过一遍;拿 qzonearchive 这类本地归档项目举例,怎么用 LFS 组织仓库,既保证数据完整又不把仓库撑爆;最后是 LFS 之外的其他备用方案,以及我实际用下来遇到的坑和排查思路。整篇都是实操向,命令直接抄,原理我会用大白话解释,不需要你提前懂 LFS。
1. 先弄明白 GitHub 对大文件的限制在哪里
1.1 官方限制其实有几道红线
很多人以为 GitHub 只限制文件大小,实际上它是一个组合规则,我整理成一张表:
| 限制项 | 具体数值 | 触发后的表现 |
|---|---|---|
| 单文件警告线 | 超过 50MB | push 会出现警告,但还能推上去 |
| 单文件硬上限 | 超过 100MB | push 被服务器直接拒绝,报“file is too large” |
| 仓库推荐大小 | 建议控制在 1GB 以内 | 超过后 GitHub 会邮件提醒你优化 |
| 仓库硬上限 | 超过 5GB | 几乎无法正常 clone 和 push |
| LFS 单文件上限 | 2GB | LFS 单文件也不能超过 2GB |
| LFS 总存储配额 | 免费版 1GB 存储 + 1GB/月流量 | 超出后 LFS push 报配额不足 |
我印象最深的是第一次被拒时看到的报错:
remote: error: File archive.zip is 312.50 MB; this exceeds GitHub's file size limit of 100 MB remote: error: GH001: Large files detected. You may want to try Git Large File Storage.那个瞬间我才意识到,Git 本身其实能处理大文件,是 GitHub 服务端加了硬限制。关键是,如果只在本地提交,Git 不会拦你,一旦 push 到远程,服务端就开始审查文件大小。
1.2 硬传大文件真正的风险不只是“传不上去”
就算你卡的刚好在 100MB 以内、侥幸推上去了,仓库也会被拖垮。Git 的存储机制是每次提交保存文件快照,一个大文件哪怕只改一个字节,Git 也会完整保存一个新版本对象。所以大文件在 Git 仓库里改几次,仓库体积会成倍膨胀。我见过一个同事的项目,设计稿源文件 80MB,改了 20 多次,仓库直接飙到 1.6GB,clone 一次能等五分钟。
另一个隐藏问题是平台对仓库体积的封禁策略。GitHub 不会立刻封你的仓库,但超过 1GB 后每次 push 都会收到提醒邮件,长时间不处理有被限制的风险。更麻烦的是,如果大文件已经进入了 Git 历史记录,你光在当前版本里删掉没用,历史对象还在,仓库还是臃肿的。清理历史需要改 commit 树,这会重写所有 SHA,对协作项目来说是一场灾难。
1.3 为什么前端上传方案和 Git 上传不是一回事
热搜词里有一条“前端使用 worker 上传大文件”,这个和 GitHub 场景容易混淆。前端 Worker 处理的是“浏览器到服务器”的大文件分片上传,目的是解决 HTTP 连接超时、断点续传的问题;而 GitHub 的大文件问题是“Git 对象存储模型”和平台硬编码限制的问题。后者不是换个上传方式能绕过去的,必须从存储机制上换方案。理解这一点,你就能明白为什么必须引入 Git LFS 或者换成独立附件存储。
2. Git LFS 方案:上传大文件的正规解法
2.1 Git LFS 到底是什么原理
Git LFS(Large File Storage)是 GitHub 官方推荐的大文件方案。它不是一个独立的网盘,而是一个 Git 扩展,核心思路是“指针文件 + 对象存储分离”。
具体运行机制用大白话讲:你用 LFS 跟踪一个 300MB 的压缩包后,Git 仓库里其实不再存这个压缩包本体,而是存了一个很小的文本文件,里面记录了这个大文件的哈希值和文件大小,这个文本文件大概只有几百字节。真正的大文件本体被 LFS 客户端传到了 GitHub 专门的 LFS 存储服务里。
这样设计有两个好处。第一,Git 仓库本身始终保持苗条,clone 速度不受大文件影响;第二,大文件的新版本会作为独立对象存储,每次修改不会让 Git 历史膨胀。用户端拉取大文件时,LFS 客户端会根据指针文件里的哈希自动去对象存储拉取对应版本。
这个机制和日常使用的网盘同步逻辑有点像:你看到的文件结构完整,但底层数据分散存放。不过 Git LFS 是透明的,你操作命令和普通 Git 几乎一样,只是多了几条跟踪指令。
2.2 安装 git-lfs 客户端
Git LFS 不是一个内置工具,需要单独安装客户端。我这里覆盖最常见的三个环境:
macOS:
brew install git-lfsWindows:直接到官网下载安装包,或者在 Git Bash 里执行:
git lfs installLinux(Ubuntu/Debian):
sudo apt-get install git-lfs装完之后验证一下版本,确保生效:
git lfs version我在 Windows 上遇到过明明装好了,但 Git Bash 不识别命令的情况,后来发现是 PATH 没刷新,重启 Git Bash 就好了。
安装完成后,再执行一次全局初始化:
git lfs install这一步会在你的 Git 配置里写入 LFS 过滤器,让后续所有仓库都自动认识 LFS 指针文件。
2.3 在仓库里跟踪大文件
进入项目目录后,你需要告诉 Git LFS 哪些文件格式或路径要走大文件通道。用git lfs track命令:
git lfs track "*.zip" git lfs track "*.tar.gz" git lfs track "*.mp4" git lfs track "assets/"运行这些命令后,你会在仓库根目录看到一个.gitattributes文件,里面记录着 LFS 跟踪规则。这个文件必须提交到 Git 仓库里,否则团队其他成员 clone 后不会触发 LFS 拉取逻辑。
我习惯把规则写精确一点,比如只跟踪backup/*.zip,而不是所有 zip,避免把一些本来很小的压缩包也推上 LFS,浪费配额。
2.4 提交和推送
跟踪完以后,后续操作和普通 Git 几乎没差别。完整流程跑一遍:
# 把大文件放入项目目录后 git add . git commit -m "add large backup files" git push origin mainpush 时你会看到 LFS 特有的上传进度,类似这种输出:
Uploading LFS objects: 100% (2/2), 328 MB | 1.2 MB/s, done. Enumerating objects: 5, done.看到 "Uploading LFS objects" 这行,说明 LFS 生效了。如果 push 过程中没有这行,那你可能有两类问题:要么根本没跟踪,文件还在走普通 Git 通道;要么文件名和跟踪规则不匹配。
2.5 clone 和拉取时注意什么
其他人在克隆你的仓库时,如果本地装了 git-lfs,会自动拉取 LFS 对象;如果没装,他们只会得到一堆指针文件,而不是真实内容。所以在新环境里第一步仍然是:
git lfs install git clone <仓库地址>如果仓库已经 clone 过了,但 pull 后发现大文件变成指针文件(一个只有几十行的文本),可以手动拉取:
git lfs pull这个命令会按照当前 commit 的指针信息去拉取所有 LFS 对象。
3. 实战案例:处理 qzonearchive 这类归档大仓库
3.1 这类项目为什么特别容易踩大文件红线
qzonearchive 这类 QQ 空间归档备份工具,核心功能是把你的空间数据(日志、相册、留言板)导出成 HTML 静态页面和原始图片。一份完整的备份动辄几百 MB,如果包含视频,单个文件上 GB 都不稀奇。这种项目天然和大文件冲突。
我自己试着把一份 700MB 的 QQ 空间备份推到 GitHub 时,直接触发了 100MB 限制。当时处理步骤就是按 LFS 流程整体过了一遍,这里把完整过程贴出来当模板:
3.2 完整操作流程模板
步骤一:在 GitHub 新建空白仓库,不要勾选任何初始化选项。
步骤二:本地初始化并关联远程:
mkdir qzone-backup cd qzone-backup git init git remote add origin git@github.com:你的用户名/qzone-backup.git步骤三:跟踪可能的大文件格式:
git lfs track "*.jpg" git lfs track "*.png" git lfs track "*.mp4" git lfs track "*.zip" git add .gitattributes git commit -m "configure lfs tracking"图片格式一定要放进 LFS 管理。QQ 空间相册导出后可能几百张原图,单张 5MB,加起来就是几个 GB 级别。虽然单张没超 100MB,但用普通 Git 提交会让仓库对象数量急剧膨胀,LFS 管理更合适。
步骤四:把大文件放入目录,提交并推送:
git add . git commit -m "add qzone archive data" git push -u origin main这里有个小技巧:第一次 push 大文件前,先查看一下当前目录下最大的一些文件,做到心里有数:
find . -type f -size +50M -exec ls -lh {} \;这条命令列出所有超过 50MB 的文件,方便你确认跟踪规则是否覆盖完整。
3.3 push 被拒后的回滚处理
如果你已经在本地提交了大文件,push 时被拒,不要慌,文件还在本地,只是没传上去。最稳妥的做法是用git reset回退到提交前状态,重新跟踪后再提交:
# 软回退到上一个提交,保留工作区改动 git reset --soft HEAD~1 # 重新执行 LFS 跟踪 git lfs track "*.zip" git add .gitattributes git add . git commit -m "add large files via lfs" git push origin main注意,如果大文件已经被推上去了,但你想换成 LFS 方案,那就需要重写历史。这个过程风险较大,我会在后面的常见问题部分细说。
4. LFS 配额用完了怎么办
4.1 免费配额到底有多少
GitHub 的免费 LFS 配额是1GB 存储空间 + 1GB 每月的流量。这个配额非常容易耗尽,尤其在你管理多个仓库、多次提交大文件的时候。
我自己的经验:一个 700MB 的备份仓库,第一次 LFS push 就吃掉了 700MB 流量,第二个仓库再推就直接报配额不足了。说明这个配额对大数据量项目来说是严重偏紧的。
4.2 配额耗尽的排查和处理
当 LFS push 报错时,错误信息通常像这样:
Error uploading LFS objects: The LFS operation failed. Response code: 403 Message: Insufficient LFS storage quota.遇到这个报错,先别急着充钱。我建议按以下步骤排查:
第一步:查看当前仓库用了多少配额。登录 GitHub,进入仓库的 Settings -> Packages and storage,能看到 LFS 使用量明细。
第二步:清理无用的大型历史版本。如果之前提交过多个 LFS 文件版本,旧版本对象依然占用配额。用git lfs prune可以清除本地不再引用的旧版本缓存,但注意这只清理本地,不清理远程。远程历史对象需要重写提交历史才能真正释放。
第三步:降低 LFS 依赖。比如把超大文件(超过几百 MB)放到 Release 附件或外部对象存储,LFS 只留中小型文件。
4.3 多个免费配额怎么组合使用
如果你的项目是公开仓库,LFS 配额和私有仓库是分开计算的。有多个 GitHub 账号的话,可以把不同项目分散到不同账号下,但这样管理成本很高,不如直接清理配额。
另一个做法是把大文件拆分为多个小于 2GB 的分卷压缩包。比如原始备份 4.6GB,用 split 命令拆成 5 个包:
split -b 800M archive.tar.gz part_但要注意:拆分后每个分卷独立存储,clone 仓库时所有分卷都会下载,实际占用流量不变,所以这不是省配额的办法,只是为了绕过单文件 2GB 限制。
4.4 数据安全性注意事项
用 LFS 存的是不可变对象,即使你在仓库里删除了某个 LFS 大文件的新版本,旧版本对象依然保留在服务器上。如果文件包含敏感信息,这个特性很危险。我建议:
- 上传前确认内容可公开,不要用 GitHub 存私人敏感数据
- 定期检查仓库内大文件内容,确认没有泄漏隐私
- 涉及个人归档数据时,可以考虑本地加密压缩后再上传,避免服务端明文存储带来的隐私风险
5. LFS 的替代方案:Release 附件和外部存储
5.1 GitHub Release 附件适合什么场景
如果你的目的不是让大文件进入版本管理,只是分享一个结果包,那么用 GitHub Release 附件最合适。它没有 LFS 配额限制,单个附件上限 2GB,总空间无硬性限制。
操作方式:在仓库的 Releases 页面新建 Release,填好版本号,直接把大文件拖进去上传。命令行方式也支持:
gh release create v1.0.0 archive.zip --title "归档备份 v1.0.0" --notes "完整数据备份"这种方式的好处是用户下载时走浏览器,也不需要安装 LFS。缺点是没有版本管理能力,每次更新都是独立附件,新旧文件之间没有关联。适合发布安装包、数据集快照、导出的完整归档。
5.2 外部对象存储的接入方式
对于超过 2GB 或者需要长期保存的数据集,我建议直接放到云对象存储(OSS/COS/S3),然后在仓库里放一个下载链接文档或者写一个自动化下载脚本。这些都是我自己实践过比较可靠的外部存储方案,不占 GitHub 配额,也没有拉取流量限制。
以阿里云 OSS 为例(其他平台类似),简单流程就是:上传文件到 Bucket,设置公开读权限,然后在 README 里给下载链接。如果文件大了,建议用 API 分片上传,避免连接超时。
这是从“把文件放 Git 仓库”到“把文件放外部”的思路转变,很多刚接触的人会不适应,但这是处理超大文件的最终预后。Git 本身擅长管代码,不擅长管大数据,把两者分开,你的仓库会健康很多,协作体验也会好很多。
5.3 几个方案的横向选型对比
| 方案 | 单文件上限 | 配额限制 | 适合场景 | 不适合场景 |
|---|---|---|---|---|
| 普通 Git push | 100MB 硬拒 | 仓库总量 1GB 内 | 代码、文档、小配置文件 | 任何超过 50MB 的文件 |
| Git LFS | 2GB | 免费 1GB 存储 + 1GB/月 | 中等大文件,需要版本管理 | 超大独立归档包 |
| Release 附件 | 2GB | 无 | 发布版本、安装包、结果分发 | 需要历史版本关联 |
| 外部对象存储 | 取决于平台 | 按量付费,空间大 | 超大数据集、海量媒体文件 | 想要统一版本管理 |
从实际维护角度看,我现在的习惯是:源文件如果是项目运行必要的资源,用 LFS;如果是给用户下载的成品包,用 Release 附件;如果是机器学习和数据科学项目的数据集,直接外部存储。这样仓库体积小,协作顺畅,配额也好控制。
6. 常见问题与排查技巧实录
6.1 push 时没有出现 LFS 上传进度
这是最常遇到的坑。git lfs track "*.zip"后,push 一个 zip 文件,但终端输出里完全看不到 LFS 上传字样,文件还是走普通 Git 通道。原因通常有两种:
第一,没有先提交.gitattributes。如果.gitattributes文件本身没被提交,远程仓库不知道有 LFS 跟踪规则,push 时 Git LFS 客户端不会接管管这些文件的传输。解决方法是先确保.gitattributes已git add并 commit。
第二,规则写错了路径。比如你写的是*.zip,但文件实际在子目录下且文件名是大写.ZIP。LFS 规则匹配是区分大小写的,严格匹配路径模式。用git lfs track时可以在仓库根目录跑,不要在子目录里跑,否则规则会变成相对路径模式。
6.2 克隆正常但大文件变成了指针文件
这个问题的本质是当前环境没有安装 git-lfs 客户端或者没有执行git lfs install。安装后执行git lfs pull即可补齐。还有一种情况是你 clone 时用的命令不是git clone,而是通过 GitHub 网页下载的 zip 包,那里面永远是 LFS 指针文件,必须走 git 协议或安装 LFS 客户端才能获取真实内容。
6.3 大文件已经推上去了,如何迁移到 LFS
最尴尬的情况是:你已经把大文件用普通 Git push 到远程了,现在想把文件改成 LFS 管理。这需要重写提交历史,把大文件从 Git 对象中“摘除”,换成 LFS 指针。
GitHub 官方没有提供一条命令自动完成这件事,推荐用git lfs migrate,操作如下:
git lfs migrate import --include="*.zip" --everything执行后,所有历史版本中符合规则的 zip 文件都会被替换为 LFS 指针,仓库 Git 对象体积大幅缩小。然后你需要强制推送改写后的历史:
git push --force origin main这里必须提醒一句:--force会覆盖远程分支历史,如果仓库有其他人协作,务必先确认所有人都已备份,否则会搞乱大家的工作区。做完迁移后,让团队成员重新 clone 或git lfs pull拉取真实文件。
6.4 LFS 配额耗尽后 push 一直失败
当你 push 一个 LFS 文件时,服务端会立刻检查配额。免费配额 1GB 用完后再 push 会直接 403。有一些投机取巧的方法,例如删除远端 LFS 文件后配额不会立即释放,需要等待日终统计周期,但实际成功率不稳定。最稳妥的还是换方案:
- 把该仓库需要的 LFS 文件压缩导出,发到 Release 附件
- 修改
.gitattributes,去掉 LFS 跟踪,重新提交后 Git 会尝试把文件作为普通文件上传,但这类传超过 100MB 仍然会被拒绝 - 如果文件必然超过 100MB,那 LFS 还是唯一途径,只能选择付费扩容
6.5 提速技巧:LFS 并发和缓存
如果你有大量 LFS 文件要推送,默认的并发数可能偏保守。修改全局并发参数:
git config --global lfs.concurrenttransfers 8在带宽较好的网络下,从默认的 3 提升到 8 能明显加快批量上传速度。
另外,git lfs prune可以定期清理本地旧版本缓存,给本地磁盘腾出空间。这个命令只删除本地不再需要的对象,不影响远程存储。
7. 关于大文件仓库,我最后想说的几件事
从一开始被 GitHub 拒之门外,到学会用 LFS、Release 附件和外部存储搭配,我自己踩过不少坑。基本的判断逻辑很简单:仓库里尽量只放源码和配置,带有“数据”属性的大文件,一律从 Git 历史中隔离出去。不只是为了通过平台限制,更重要的是保留 Git 仓库的敏捷性。一个动辄几个 GB 的仓库,clone 速度慢,分支切换要等半天,协作体验会变得很糟糕。
还有一个细节很多人会忽略:.gitignore和.gitattributes要一起管好。.gitignore负责不让大文件意外进入提交区域,.gitattributes负责精确控制哪些文件走 LFS。我习惯先把大文件路径写进.gitignore,等确实需要版本管理时再移出来加入 LFS 跟踪规则,这样能有效避免误操作把几十 GB 的临时文件一锅端进仓库。
另外,所有大文件上传之前,一定要检查内容隐私。Git 历史一旦推上去就相当于泼出去的水,强制重写虽然能覆盖,但任何已经 clone 过你仓库的人手里都留有旧数据。归档备份类项目尤其要注意这一点,QQ 空间导出、相册原图这类内容往往包含个人隐私,务必确认授权公开再上传。我通常的做法是敏感数据加密压缩后再上传,密钥走其他渠道交接,不放在同一位置。
关于 LFS 配额,我的建议是把它当稀缺资源来用。能用 Release 附件解决的,绝不占 LFS 名额;能压缩的文件,绝不原样上传。如果你需要管理大量历史数据,建议认真考虑外部对象存储方案,虽然多了一层配置成本,但长远看更省心。
最后分享一个我常用的校验技巧:上传完成后,在另一个目录克隆一份仓库,执行git lfs pull,重点抽查几个大文件能否正常打开。这一个动作能帮你发现八成以上的 LFS 配置错误,别等到别人下载后才发现文件损坏,那时候去排查问题就费劲了。