git push 报 HTTP 400 却提示「Everything up-to-date」——罪魁是 1MB 的上传缓冲区
那天仓库积压了五十多个提交没推,里头有字体、图标、文档等二进制资产。执行 git push 后终端一闪,既没有进度条,也没有网络超时提示,直接输出一堆红色错误——然后最后一行若无其事地打出 Everything up-to-date。我盯着屏幕看了好几秒:到底推上去了还是没推?
现象
终端完整输出如下:
error: RPC failed; HTTP 400 curl 22 The requested URL returned error: 400
send-pack: unexpected disconnect while reading sideband packet
fatal: the remote end hung up unexpectedly
Everything up-to-date
几个令人迷惑的信号同时出现:
- HTTP 400 —— 看起来像服务器在拒绝请求,第一反应去查 GitHub 状态页、分支保护规则、Token 权限。
- 没有
Writing objects进度条 —— 说明连上传阶段都没进入,立刻断了。 Everything up-to-date—— 这句话描述的是 fetch 状态,不是 push 结果;实际上什么都没推上去。
反复重试,每次结果完全一致,排除偶发网络抖动。
根因
git 在 HTTPS 传输时,会先把整个 pack 文件写入一块内存缓冲,再通过 libcurl 一次性 POST 给服务端。这个缓冲的默认上限由配置项 http.postBuffer 控制,默认值只有 1 MiB(1048576 字节)。
当 pack 文件超过这个阈值,libcurl 会在本地就放弃上传,返回一个笼统的 HTTP 400 给 git 客户端。错误本质上发生在客户端,不是服务端拒绝了你的推送。
可以用下面的命令验证 pack 大小:
git count-objects -vH
# count: 5521
# size: 59.27 MiB ← 59 MiB >> 1 MiB 默认缓冲,必然失败错误信息 RPC failed; HTTP 400 极具误导性,很容易让人去检查 Token 权限、分支保护、文件大小限制(GitHub 单文件上限是 100 MB,和这个 bug 无关)。真正的根因在客户端:http.postBuffer 默认 1 MiB,pack 稍大即溢出。
SSH 传输走的是流式管道,完全不受此限制,不会踩这个坑。
正解
把 http.postBuffer 调大到 500 MiB 再推:
git config http.postBuffer 524288000
git push origin main推送立刻出现正常的进度条,完整上传:
Writing objects: 100% (5521/5521), 59.27 MiB | 4.12 MiB/s, done.
d039c8c..b5e421a main -> main
如果你的仓库经常携带二进制资产,建议全局持久化:
git config --global http.postBuffer 524288000500 MiB(524288000 = 500 × 1024²)覆盖了绝大多数团队项目场景,设更大的值只会白白多占 RAM,没有收益。
另一条路是切换到 SSH 传输,从根本上绕开这个问题:
git remote set-url origin git@github.com:owner/repo.git
git push origin maingit config --global http.postBuffer 524288000
git push origin main本地 repo 状态完好,无需重做任何提交,bump 完配置直接重推即可。
排查备忘
| 观测现象 | 指向 |
|---|---|
| 推送时没有进度条,立即失败 | http.postBuffer 溢出(本文场景) |
| 推送时有进度条,跑到一半失败 | 服务端 hook 或单文件超限 |
| 偶发,重试能过 | 网络抖动 / CDN 超时 |
| 每次都失败,SSH 能推 HTTPS 不行 | 十有八九是缓冲区问题 |
一句话外卖
HTTPS 推送报 HTTP 400 且无进度条——先查
git count-objects -vH,包超 1 MiB 就调http.postBuffer,别去翻服务器日志。