返回博客

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

几个令人迷惑的信号同时出现:

  1. HTTP 400 —— 看起来像服务器在拒绝请求,第一反应去查 GitHub 状态页、分支保护规则、Token 权限。
  2. 没有 Writing objects 进度条 —— 说明连上传阶段都没进入,立刻断了。
  3. 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 默认缓冲,必然失败
坑:HTTP 400 不等于服务器拒绝

错误信息 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 524288000

500 MiB(524288000 = 500 × 1024²)覆盖了绝大多数团队项目场景,设更大的值只会白白多占 RAM,没有收益。

另一条路是切换到 SSH 传输,从根本上绕开这个问题:

git remote set-url origin git@github.com:owner/repo.git
git push origin main
正解:两行命令,一劳永逸
git 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,别去翻服务器日志。