bigywater.tech
常见报错 · 27

Git push 大文件被拒:再提交一次删除没用,gc 后 .git 从 12432 KB 降到 120 KB,已推送的要重写历史

“remote: error: File … exceeds the file size limit” 这类报错,网上标准答案是“删掉文件再推”,但实际上推不上去的是历史。这篇在本机用一个带体积上限的裸仓库复现被拒,再逐个测:再提交一次删除为什么没用、没推送和已推送两种情况怎么清、LFS 是怎么把大文件挪出 Git 对象库的。

12432 → 120 KB
没推送过的情况:改历史并 reflog 过期 + gc 之后本地 .git 的体积
12412 → 120 KB
已推送的情况:filter-branch 重写并强制推送后,新克隆的 .git 体积
133 字节
LFS 之后 Git 里存的指针对象大小(真正的 12 MiB 内容放在 .git/lfs)

被拒长什么样

实测 · 本机执行输出
    $ git push origin main    (退出码 1)
      remote: fatal: pack exceeds maximum allowed size        
      error: remote unpack failed: unpack-objects abnormal exit
      To <tmp>/remote.git
       ! [remote rejected] main -> main (unpacker error)
      error: failed to push some refs to '<tmp>/remote.git'
    $ git count-objects -vH | grep -E "size-pack|^size:"
      size: 12.02 MiB
      size-pack: 0 bytes

服务端上限由服务端定;本机用 receive.maxInputSize 模拟,表现为 remote rejected (unpacker error)。

为什么删文件没用

实测 · 本机执行输出
    $ git rm -q big.bin && git commit -qm "remove big.bin" && git log --oneline && ls
      2d4da51 remove big.bin
      5f0d9e0 add big.bin (12 MiB)
      e5d18d0 add app
      app.py
    $ git push origin main 2>&1 | head -6
      remote: fatal: pack exceeds maximum allowed size        
      error: remote unpack failed: unpack-objects abnormal exit
      To <tmp>/remote.git
       ! [remote rejected] main -> main (unpacker error)
      error: failed to push some refs to '<tmp>/remote.git'
    $ git ls-tree -r HEAD --name-only; git rev-list --objects --all | grep -c big.bin
      app.py
      1
    (上一行是所有提交里引用 big.bin 的对象数:只要有一个提交里还有它,推送就会带上)

推送的是整段新增历史,文件还在加它的那个提交里。

没推送过

实测 · 本机执行输出
    $ git reset -q --soft origin/main && git commit -qm "add nothing big" --allow-empty && git log --oneline && git push origin main 2>&1 | tail -1
      44696bd add nothing big
      e5d18d0 add app
         e5d18d0..44696bd  main -> main
    .git 目录体积(KB):12432
    $ git reflog expire --expire=now --all && git gc -q --prune=now
    reflog 过期 + gc --prune=now 之后 .git 目录体积(KB):120

reset --soft 重新提交,再让 reflog 过期并 gc,本地才会变小。

已经推送过

实测 · 本机执行输出
    $ git log --format='%h %s' 
      2c079a1 c4
      f7f580d c3
      0fb177f c2
      5225d4b c1
    当前远程克隆下来的(--no-local)的 .git 体积(KB):12412
    $ git filter-branch -f --index-filter 'git rm -q --cached --ignore-unmatch big.bin' -- --all 2>&1 | grep -E 'WARNING|was rewritten' | cut -c1-110
      WARNING: git-filter-branch has a glut of gotchas generating mangled history
      	 set FILTER_BRANCH_SQUELCH_WARNING=1.
      Ref 'refs/heads/main' was rewritten
      Ref 'refs/remotes/origin/main' was rewritten
    $ git log --format='%h %s'
      72de5ee c4
      c66f790 c3
      108dd4c c2
      5225d4b c1
    $ git reflog expire --expire=now --all; rm -rf .git/refs/original; git gc -q --prune=now --aggressive
    $ git push -q --force origin main 2>&1 | tail -1
    重写并强制推送后,新克隆(--no-local,走真正的传输)的 .git 体积(KB):120
    但服务端裸仓库自己的体积(KB):12428——旧对象还在里面,没人 gc 就不会少
    $ git -C remote2.git reflog expire --expire=now --all; git -C remote2.git gc -q --prune=now
    服务端自己 gc --prune=now 之后(KB):112

filter-branch 重写历史,强制推送;服务端的旧对象要服务端自己清理。

LFS

实测 · 本机执行输出
    $ git lfs install --local 2>&1 | tail -1; git lfs track "*.bin" 2>&1; cat .gitattributes
      Git LFS initialized.
      Tracking "*.bin"
      *.bin filter=lfs diff=lfs merge=lfs -text
    $ head -c 12582912 /dev/urandom > big.bin && git add .gitattributes big.bin && git commit -qm "big via lfs" && git cat-file -p HEAD:big.bin
      version https://git-lfs.github.com/spec/v1
      oid sha256:9d259dbdc3e7d89592b457e2f7c1a3db6351ba87ffba0ad00dc09ea09b5bf8bb
      size 12582912
    $ git cat-file -s HEAD:big.bin; ls -l big.bin | awk '{print $5}'; du -sk .git/lfs | cut -f1; git count-objects -vH | grep -E "size-pack|^size:"
      133
      12582912
      12288
      size: 16.00 KiB
      size-pack: 0 bytes

Git 里只存指针,内容放在 LFS 存储。

分步演示

大文件被拒
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64;报错文本都是真实复现时原样捕获的(命令行工具统一用 LC_ALL=C 取英文原文,路径换成 );所有服务都是本机上自己起的、只监听 127.0.0.1,不访问外网;计时用 time.perf_counter,数字每次运行会有小幅抖动。本篇用 git 2.54.0(Apple Git-157)与 git-lfs 3.8.0;“远程”是本机的裸仓库,服务端体积上限用 receive.maxInputSize 模拟(5 MiB),大文件是 12 MiB 的 /dev/urandom 随机数据(不可压缩);提交的哈希里有随机内容的部分每次运行都不同,所以文中只引用与随机内容无关的哈希
    • 没有验证:GitHub、GitLab 等托管平台自己的大文件报错文字与限制数字(单文件 100MB 之类只是记忆,没有验证);git-filter-repo 与 BFG(本机没装);LFS 真正推送到 LFS 服务端的行为与配额;子模块与浅克隆中的行为;filter-branch 在带标签、多分支仓库上的细节(只测了单分支)
    京ICP备2026067502号-1京公网安备11010502063487号