通 途

首页 › 推拉代码断流

git clone / push GitHub 反复中途断流:RPC failed 与 early EOF 的真正原因

在 Windows 上克隆或推送一个体积较大的 GitHub 仓库,进度条走到一半就卡死,随后抛出 RPC failed、early EOF,甚至直接提示 The remote end hung up unexpectedly 或 fatal: unable to access。换个 Wi-Fi、换个时间段有时能蒙混过关,有时连续失败十几次,这种时好时坏的表现最容易被误判成网络运营商限速,实际上更多时候是同一条长连接被中途掐断,而不是带宽不够。

报错长什么样:RPC failed、early EOF 这几条报错在说什么

把这几条报错放在一起看:RPC failed 出现在 HTTPS 方式的 git clone/git push 过程中,通常跟在底层传输提示后面;early EOF 是解包阶段发现收到的数据比协商好的长度短,说明数据流被硬生生截断;fatal: unable to access 多半发生在还没建立起有效连接、或连接建立后很快被打断的阶段;The remote end hung up unexpectedly 则是对端主动关闭了连接。四条报错的措辞不同,但共同点是:本地都收到了“连接没能撑到传输结束”的信号,而不是“仓库不存在”或“没有权限”这类明确的业务错误。

正因为它们描述的是同一类失败在不同阶段的表现,孤立地去搜某一条报错、照搬第一条建议改配置,很容易改错方向——真正要先确认的是:断的是这次传输本身,还是别的原因。

真正的原因:大仓库克隆是一次超长单连接,断一次就前功尽弃

git 通过 HTTPS 或 SSH 克隆一个仓库时,服务器会把要发送的对象打成一个 packfile,客户端在同一条连接里从头收到尾。这个过程没有分片续传的概念——连接中途断开,哪怕已经收完了绝大部分,git 也没法从断点接着收,只能整个失败,报出 early EOFRPC failed。仓库越大、历史越长,这条连接需要维持的时间就越久,中途被打断的概率也随之升高,这也是为什么同样的网络环境下,小仓库总能成功,体积很大的仓库却屡屡在某个百分比附近反复失败。

这也解释了为什么浅克隆和分批拉取会明显更容易成功:它们本身并没有让每秒能传的数据变多,起作用的地方是把“这一次连接必须存活多久”这个时间窗口大幅缩短了。连接存活的时间越短,被中途打断的机会自然越少,这才是浅克隆和分批传输真正生效的原理,而不是因为它们绕开了什么限速。

HTTPS 和 SSH 走的不是同一条路,失败方式也不同

https://github.com/... 克隆走的是 443 端口上的一次 HTTPS 请求,中间任何一段路由、任何一个中间设备都可能因为这条连接维持时间太长而把它中断;用 git@github.com:... 走的是 SSH 协议,默认另一个端口,GitHub 也提供走 443 的 SSH 备用方式,这是完全独立的一条通道,沿途经过的中间设备可能不一样。这意味着 HTTPS 方式失败,不代表 SSH 方式一定也会在同一个位置失败——两者只是“同样容易受长连接被打断的影响”,具体在哪一跳被打断、打断后报什么错,会因为端口和协议不同而不同。

http.postBuffer 调大了,为什么还是断?

http.postBuffer 控制的是 git 在本地把要发送的数据攒到多大再一次性发出去,它影响的是客户端这一端怎么组织数据包,跟这条连接能不能在链路上一直保持不断开完全是两件事。把它调大,确实能减少一些因为单个请求体切分不合理暴露出的问题,但如果断流的原因是这条连接本身撑不到传输结束,调大它不会有任何帮助——它顶多让你更快撞到同一个墙。看到这个参数被反复推荐,先确认自己面对的是不是真的因为请求体问题,而不要想当然地把它当成网络不稳定的通用解药。

半分钟判断:分清“连接撑不住”和“仓库/权限本身有问题”

找一个体积很小、确定有权限访问的公开仓库,执行一次浅克隆。如果这个小体量、短耗时的克隆稳定成功,而原本要克隆的大仓库在同一台机器、同一条网络下反复在某个百分比附近断掉,基本可以确认问题出在这次传输需要维持的时间太长,而不是账号权限、仓库地址或解析这些一次性就能验证对错的问题。反过来,如果连这个十秒级的小克隆都直接报 fatal: unable to access,说明问题出现得更早,应该先查地址、凭据和基础连通性,再谈断流。

还可以顺手换一种协议再测一次:同一个仓库,HTTPS 失败就换 SSH 克隆试试,或者反过来。两者走的通道不同,如果只有一种方式反复断、另一种能稳定跑完,说明问题更偏向那一条具体路径,而不是这台机器或这个账号本身的问题。

确认之后的动手顺序

  1. 先用小仓库做上面的十秒测试,确认失败确实和传输时间过长相关,而不是权限或地址写错。
  2. 把大仓库先按浅克隆处理,能跑通就说明思路对了,再用逐步加深历史的方式一批一批补全,把每一次网络传输的时长都控制在较短范围内。
  3. 推送时同理,避免一次性把所有历史推送出去,分支、分批提交推送,能明显降低单次连接需要维持的时间。
  4. 切换协议再试一次:HTTPS 卡就换 SSH,SSH 卡就换 HTTPS,观察是不是只有一种方式反复出问题。
  5. 如果 http.postBuffer 之前被调到很大,先把它改回默认或适度数值,排除它掩盖了真实报错、或者本身并不是问题所在的可能。
  6. 确认以上几步后问题依旧集中在“长时间单连接容易被打断”这一层,再考虑给这台机器换一条更稳定、能撑住长连接的出口链路,而不是继续加大本地缓冲区或反复重试同一个命令。

一次推拉会同时用到 git 本体、凭据管理器和 SSH 客户端三个可执行文件,逐个配置容易漏掉其中一个。在系统层统一出口之后,这三者用的是同一条路径,不必分别设置。

安卓与 Windows 客户端已上线,macOS 与 Linux 开发中。iPhone 暂无原生客户端。出口服务器的访问日志是关闭状态;会话记录表里没有目标地址、域名或 URL 字段。注册即可使用永久免费套餐,按月发放流量额度。客户端在连上之后会做一次真实探测,探测不通就不显示已连接。

为长连接换一条出口 下载安装包 备用线路

据 Git 官方文档对 smart HTTP 传输的说明,克隆是把对象打成一个 packfile 一次性发过来的;这一次传输断掉,已经传的部分不会留下。

常见问题

为什么小仓库总能克隆成功,大仓库总在某个百分比断?

因为 git 克隆是把整个 packfile 放在同一条连接里从头传到尾,没有断点续传,连接需要维持的时间越长,被中途打断的概率就越高,小仓库连接时间短所以更容易全程跑完。

RPC failed 和 early EOF 是不是网络运营商限速造成的?

这两条报错说明的是数据流在传输过程中被截断,而限速通常表现为速度持续偏低但能传完,二者不是一回事,遇到 RPC failed 或 early EOF 先怀疑连接维持时长,而不是先怀疑限速。

git clone 时用浅克隆为什么能提高成功率?

浅克隆并没有让每秒传输的数据变多,它起作用的地方是把这一次必须维持的连接时长大幅缩短,连接存活得越短,中途被打断的窗口就越小,这才是它真正生效的原理。

HTTPS 克隆失败,换成 SSH 克隆会不会好一些?

HTTPS 和 SSH 走的是不同端口、不同协议栈的两条独立通道,同样容易受长连接被打断的影响,但具体在哪一跳出问题会因协议不同而不同,所以换一种协议是一次有效的排查动作,而不是保证一定能解决。

把 http.postBuffer 调大后还是断,是配置没生效吗?

http.postBuffer 只影响本地把数据攒多大再发送出去,跟这条连接能不能一直撑到传输结束是两件不同的事,如果断流的根源是连接维持时间过长,调大这个参数不会解决问题。

fatal: unable to access 和 RPC failed 是同一种问题吗?

fatal: unable to access 往往出现在连接还没有真正建立起来、或者建立后很快就被打断的更早阶段,而 RPC failed 通常出现在传输已经进行到一半之后,两者都指向连接没能撑到结束,但发生的时间点不同。

分批拉取历史真的有用,还是只是玄学?

分批拉取把原本要在一次连接里传完的历史拆成多次较短的传输,每一次单独维持的时间都变短了,中途被打断的概率随之下降,这是可以解释的机制,不是玄学。

换一个网络环境有时候能成功,有时候还是失败,说明什么?

这种时好时坏恰好说明问题出在连接能不能撑住这么长的传输时间上,而不是这个仓库或这次操作本身有问题,不同网络环境下连接被打断的概率不同,所以表现忽好忽坏。

继续阅读:首页 · 镜像拉取超时 · 依赖装不上 · Python 装包超时