“remote: error: File … exceeds the file size limit” 这类报错,网上标准答案是“删掉文件再推”,但实际上推不上去的是历史。这篇在本机用一个带体积上限的裸仓库复现被拒,再逐个测:再提交一次删除为什么没用、没推送和已推送两种情况怎么清、LFS 是怎么把大文件挪出 Git 对象库的。
$ 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):120reset --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):112filter-branch 重写历史,强制推送;服务端的旧对象要服务端自己清理。
$ 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 bytesGit 里只存指针,内容放在 LFS 存储。