Post Views: 33
在向 Git 平台送代码时,可能遇到如下错误:
remote: Find the desired index: <object-id>, size: 140.738MB, exceeds quota 100MB
remote: Please remove the file[s] from history and try again
! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs
这不是网络问题。远端已经收到了 Git 对象包,但在接收提交前检查到其中包含超过 100 MB 限制的单个对象,因此拒绝更新分支。
为什么删除工作区里的文件仍然无法推送
Git 保存的是提交历史。大文件一旦进入某个提交,即使随后执行普通删除并再提交一次,它仍存在于较早的提交中,推送时依然会被发送并触发限制。
正确做法取决于大文件所在的历史范围:
- 只进入了最近、尚未推送的提交:修改这个提交即可。
- 已进入多个本地提交:需要交互式 rebase 或历史过滤。
- 已推送到共享远端:需要改写共享历史并强制推送,必须先与协作者沟通。
第一步:根据对象 ID 找到文件
报错通常会给出对象 ID。可以用下面的命令将它映射到路径:
git rev-list --objects --all | grep <object-id>
Windows PowerShell 可使用:
git rev-list --objects --all | Select-String <object-id>
也可以列出当前历史中最大的对象,帮助排查没有明确对象 ID 的情况:
git rev-list --objects --all > objects.txt
git verify-pack -v .git/objects/pack/*.idx | sort -k3 -n | tail
注意:相同内容可能出现在多个路径中,但只对应一个 Git 对象。应检查所有匹配路径,而不是只处理报错中首先找到的路径。
场景一:大文件只在最后一个、尚未推送的提交中
这是最简单也最常见的情况。先确认本地相对远端多出的提交:
git fetch origin
git log --oneline origin/master..HEAD
将文件从 Git 索引移除,但保留本地文件:
git rm --cached -- path/to/large-file.exe
随后把路径加入
.gitignore:
/path/to/large-file.exe
最后修改最近一次提交:
git add .gitignore
git commit --amend --no-edit
git push origin master
git rm --cached 只取消 Git 跟踪,不会删除工作区里的文件。因为原提交从未成功推送,所以 amend 后通常可以普通推送,不需要
--force。
场景二:大文件存在于多个尚未推送的提交中
推荐使用
git filter-repo 从指定路径清理全部历史:
git filter-repo --path path/to/large-file.exe --invert-paths
如果多个路径引用同一大文件,需要把它们全部列出:
git filter-repo
--path path/to/large-file.exe
--path another/path/large-file.exe
--invert-paths
清理后添加
.gitignore,提交并推送。若改写的提交只存在于本地,一般仍可正常推送;如果远端已有同名旧历史,则需要参照下一节谨慎处理。
git filter-repo 通常需要单独安装,例如 pip install git-filter-repo。执行历史重写前建议创建备份分支或复制仓库。
场景三:大文件已经推送到共享分支
这会改写其他协作者正在使用的提交 ID。先通知团队并暂停向该分支提交,然后运行历史过滤并使用带保护的强制推送:
git filter-repo --path path/to/large-file.exe --invert-paths
git push --force-with-lease origin master
优先使用
--force-with-lease,不要直接使用
--force;前者会在远端出现你未获取的新提交时拒绝覆盖。历史被改写后,其他协作者通常需要重新克隆,或将自己的工作迁移到新的分支历史。
如何验证清理成功
确认路径不再受 Git 跟踪:
git ls-files -- path/to/large-file.exe
git check-ignore -v -- path/to/large-file.exe
第一条命令应无输出,第二条应显示命中的
.gitignore 规则。再确认待推送提交范围内没有该对象:
git rev-list --objects origin/master..HEAD | grep <object-id>
没有输出才表示大对象不在将要推送的可达历史中。
大型二进制文件应该放在哪里
安装包、视频、模型和压缩包通常不适合直接存入普通 Git 仓库。可根据用途选择:
- 软件安装包:Gitee Release 附件、制品仓库或对象存储。
- 必须版本化的大文件:确认托管平台配额后使用 Git LFS。
- 可重新生成的构建产物:只提交源码和构建脚本,由 CI 生成并发布。
.gitignore 只能防止未来未跟踪文件被误加入,不能清理已经存在于历史中的对象。因此最佳实践是:提交前查看
git status 和 staged diff,并在 CI 或 pre-commit hook 中设置文件大小检查。
常见误区
- 只在资源管理器里删除文件:旧提交仍包含大对象。
- 只增加
.gitignore:已经被跟踪的文件不会自动取消跟踪。
- 先提交删除再推送:推送会包含新增大文件的旧提交,仍会失败。
- 看到 push 传输完成就认为成功:远端 pre-receive hook 仍可拒绝更新分支。
- 不加判断直接强推:可能覆盖其他人的远端提交;仅在确实重写了已发布历史时使用
--force-with-lease。
处理这类问题的核心只有一句话:不仅要从当前目录删除或忽略大文件,还必须确保它不再存在于即将推送的任何提交历史中。