GitHub克隆/推送速度慢这个话题几乎隔几天就会在技术群里被捞起来一次。症状非常固定git clone跑了一半进度条卡在Receiving objects接着一行RPC failed; curl 18 transfer closed with outstanding read data remaining直接终结任务或者git push推到 90%客户端报The remote end hung up unexpectedly。很多人第一时间会想换网络、上工具但其实不依赖任何外部网络调整仅靠 3 行 Git 指令就能把绝大多数“能连但慢、慢到断”的问题压下去。这篇文章不谈玄学只讲指令原理、适用边界和一套可复制的排查思路适合总在 Git 传输上翻车的开发者、运维和经常维护开源仓库的人。1. 先判断你的慢是“真慢”还是“假断”1.1 典型症状进度条卡死的三种现场Git 传输慢和网络下载慢不一样它很少真的“匀速慢”更多是“一阵一阵卡死最后连都连不上”。我自己见过三种高频现场第一种Enumerating objects阶段卡住。仓库只有几百兆但 Git 要先把所有对象枚举出来这个阶段如果长时间不动往往是服务器端在压缩包或者本地 DNS 解析后连到的节点链路质量很差。此时git clone -v能看到 TCP 连接已建立但数据就是不动。第二种Receiving objects进度条停在某个百分比比如 37%、68%过几十秒后直接报错。这类是最典型的“低速误杀”或者“缓冲区溢出”。不是断开而是 Git 在等待数据传输时发现一段时间内速度低于阈值主动放弃了。第三种git push时推不上去。本地仓库挺完整前面编译、提交都正常但一 push数据包发到一半远端就挂断了。这种通常是 HTTP 通道在传输大对象时缓冲设置不够或者网络中间设备掐断了长时间空闲的连接。这三个场景的共同点是网络链路并没有彻底不可用Git 客户端却在传输层做了过多“安全动作”。知道这一点很重要因为后面那 3 行 Git 指令本质上就是用来关掉这些动作的。1.2 用 git 输出和错误码锁定故障点在动手改配置前我建议先花两分钟复现一下问题拿到更多信息。不要上来就复制网上一堆配置那样出了问题都不知道是哪条引起的。可以先加verbose参数重跑一次git clone -v https://github.com/user/repo.git如果想看到底层 HTTP 传输细节还可以临时打开两个环境变量export GIT_TRACE1 export GIT_CURL_VERBOSE1 git clone https://github.com/user/repo.git你会看到大量的curl日志包括连接了哪个 IP、TLS 握手用时多少、是哪个阶段断开的。看到这些之后再对照下面的错误码就清楚多了报错片段大概率原因curl 18 transfer closed with outstanding read data remaining连接建立后数据传输中断curl 56 failure when receiving data from the peer对端发完数据前关闭了连接Unable to rewind rpc post dataHTTP POST 缓冲回卷失败常见于大文件推送The remote end hung up unexpectedly服务端主动断开也可能是本地缓冲太小Failed to connect to github.com port 443: Connection timed outTCP 层面连不上改配置无效如果你发现错误集中在“连接建立后但传输中途断开”那恭喜这正是 3 行 Git 指令的射程范围。如果是Connection timed out那属于 TCP 链路问题后面第 4 章会讲对应的处理思路。2. 3 行 Git 指令拆解为什么这三条能续命网上一提到 GitHub 克隆慢就会有人贴出三条命令但很少解释它们到底改了什么。这里先给出完整的三行git config --global http.postBuffer 524288000 git config --global http.lowSpeedLimit 1 git config --global http.lowSpeedTime 999999这三行的作用不是“加速”而是不要因为传输慢而自杀。放在一个生活化的类比里你点了一份外卖配送员在路上堵车你要做的不是催他每三秒报一次位置而是把“我等不下去”的时限从 3 分钟改成 11 天。Git 默认有些策略太激进碰到跨地域的不稳定链路时会把“卡顿”误判为“断连”然后主动放弃。2.1 第一行postBuffer 解决“写不进去”的假死http.postBuffer改的是 HTTP 发送缓冲区的大小默认值通常是 1MB。这个值的影响在推送大对象时尤其明显。你可以把 Git 通过 HTTPS 推送数据想象成用一根吸管往瓶子里注水。吸管很细没关系但如果储水槽只有 1MB水灌满之后Git 就必须停下来等待服务端确认确认完了再倒下一批。如果这个确认环节因为网络延迟变得很慢吸管里面就容易出现“堵住”的现象最终连接假死。把缓冲区调到 500MB 左右相当于换了个大储水槽同样一次推送可以少很多次停下来等待整个流程顺滑得多。524288000这个数字就是 500MB。如果你的机器内存比较小比如只有 1GB也可以折中用536870912或者降到5242880050MB。我用的时候发现500MB 在绝大多数开发机上都没有压力Git 也不是真的把 500MB 全塞在内存里只是给了一个上限。2.2 第二、三行lowSpeedLimit/lowSpeedTime 关掉“误杀”http.lowSpeedLimit和http.lowSpeedTime是一对组合参数含义是当传输速度低于lowSpeedLimit字节/秒并且持续了lowSpeedTime秒Git 就认为这条连接已经没有价值主动中断。默认情况下这个阈值和持续时间都是 0理论上不会触发。但问题在于很多人用的系统发行版、Git GUI 客户端或者历史遗留的全局配置会在不知情的情况下注入一组数值。比如有的环境默认把lowSpeedLimit设成了 1000lowSpeedTime设成了 30。这在局域网没问题但跨境访问 GitHub 时瞬时速度低于 1KB/s 太常见了只要持续 30 秒Git 就会决定“你太慢了我不等了”。所以第二行把速度阈值设成1第三行把持续时间设成999999等效于只要传输速度没有完全变成 0并且持续 11 天以上Git 就绝不主动断开。这会让你在慢速但不中断的网络里慢慢磨也能把仓库拉完。2.3 三行指令的正确落地姿势三条命令执行完后建议校验一下避免你改了全局配置但被路径下的局部配置覆盖git config --global --list | grep http如果看到了三条配置说明已经生效。接着可以重新跑一次刚才失败的操作。注意一点这三行只对 HTTPS 方式的 Git 传输生效对你用浏览器下载 zip 包、或者用 SSH 协议都没有作用。另外如果是公司统一维护的 Git 服务器别在全局配置里塞这些值直接使用git -c http.postBuffer524288000 -c http.lowSpeedLimit1 -c http.lowSpeedTime999999 clone https://...这样可以做到只针对当前这条命令生效不影响其他仓库。3. 光改配置不够配合浅克隆和 SSH把传输量降一半3.1 浅克隆与单分支只下载你需要的提交如果仓库本身很大比如历史提交几千次、里面还躺着不少大文件那么再怎么调缓冲也只能保证“不死”并不能让速度变快。真正的提速逻辑是少传数据。Git 官方早就提供了浅克隆方案你可以只取最新的一次提交git clone --depth 1 --single-branch --branch main https://github.com/user/repo.git--depth 1表示只拉取最近 1 条提交记录--single-branch表示只拉main分支。这样克隆下来的仓库体积往往只有完整仓库的十分之一甚至更小。原因是 Git 不再需要为历史上所有版本生成打包数据服务端的 CPU 开销也大幅降低通常能明显缓解Enumerating objects阶段的卡顿。如果你已经完成了一个全量克隆只是想裁掉历史也可以用git fetch --depth 1这会让你本地变为浅仓库。但要注意浅克隆对后续的git push有限制如果你想给这个仓库提交代码并推送通常需要先执行git fetch --unshallow拉全历史。所以我的建议是以读为主就浅克隆以写为主还是老老实实全量拉取。3.2 SSH 认证协议绕开 HTTP 层的额外开销HTTPS 克隆慢除了网络因素还因为 HTTP 层做了很多 TLS 握手的开销每次连接都要经历一次完整的证书验证。而 SSH 协议走的是长连接对频繁操作更友好也更稳定。切换方式很简单先生成一对密钥如果已经有的话可以跳过ssh-keygen -t ed25519 -C 你的邮箱然后把~/.ssh/id_ed25519.pub的内容复制到 GitHub 的 SSH keys 设置页。接下来在仓库目录里执行git remote set-url origin gitgithub.com:user/repo.git用ssh -T gitgithub.com验证一下通了之后后续的 clone 和 push 都会走 SSH 通道。我自己维护开源项目时凡是频繁 push 的仓库都换成了 SSH基本没再遇到 HTTP 通道那种莫名的中断。3.3 大仓库推送技巧如果你的仓库里有大量图片、压缩包即使三行配置和 SSH 都用了push 依然可能很吃力。这时候还可以考虑 Git LFSLarge File Storage把图片、二进制文件托管到 LFS 存储里。git lfs install git lfs track *.zip git add .gitattributes git commit -m track zip files with LFS git push origin mainLFS 本身也是 Git 官方扩展好处是普通小文件照常走 Git 协议大文件单独通过 LFS 端点传输。但要注意 GitHub 免费版有 LFS 配额限制超大仓库还是需要规划一下。还有一个很多人忽略的点git push之前先跑一次git fsck检查对象完整性。本地仓库如果有一些损坏对象推送时会导致服务端反复尝试速度感和实际耗费时间都会被放大。4. 从报错反推常见 GitHub 克隆失败场景与对症下药4.1 RPC failed; curl 18 / 56 的排查这条报错是 GitHub 克隆慢场景里的“主角”。先说结论出现curl 18时按顺序做三件事能解决八成问题。第一步执行那三行 Git 指令。这能排除客户端主动断连的因素。第二步如果还是断就换 SSH 协议因为 SSH 不依赖 HTTP 的缓冲机制。第三步用浅克隆测试把问题缩小到“仓库本身数据量太大”还是“网络传输链路不稳定”。curl 56和curl 18技术细节不同现象却很相似都是接收数据时中断。除了上面三步还可以尝试强制使用 HTTP/1.1避免 HTTP/2 多路复用在弱网环境下的连接重置git config --global http.version HTTP/1.1这条配置对某些网络环境效果明显。要命的是很多人不知道它和 postBuffer 是互补关系两者一起配上之后原本频繁断开的克隆很多就一次通过了。4.2 “Unable to rewind rpc post data”与缓冲区如果你的报错里出现Unable to rewind rpc post data那基本就是 HTTP POST 缓冲区的锅。这个错误在推送大对象时特别常见Git 试图重新定位 HTTP POST 的数据流位置但因为缓冲区太小或者网络不稳定而失败。处理方法依然是把http.postBuffer调大。如果调大后依然无效还可以尝试降低打包时的压缩窗口减少内存负担git config --global pack.window 0pack.window控制 Git 在打包时对对象做增量压缩的“参考窗口”大小。默认值越大压缩率越高但消耗内存和 CPU 也越多。在低速网络上过高的压缩率意味着服务端需要更多时间准备数据包客户端也需要更多时间解压反而拖慢整体速度。把这个值设成 0相当于放弃增量压缩直接传完整对象虽然名义上“数据量更大”但因为省掉了前后端的压缩等待实际体验常常更快。4.3 推送时 “error: failed to push some refs” 的处理这个报错很多人误以为是速度问题其实它是“远端有更新本地没有拉取”导致的拒绝推送。但在跨域环境下拉取本身也会触发前面那些网络问题所以你要做的不是硬推而是先把同步做好。推荐设置git config --global pull.rebase true然后每次推送前先git pull --rebase把本地改动重新放到远端最新提交之后再执行git push。这样做的好处是如果远端有新提交你不会因为 merge 提交而和上游历史纠缠。拉取时也可以用git fetch --depth 1再 rebase降低历史数据消耗。如果这还解决不了再看看是否本地仓库有对象损坏或者分支名和远端不一致。很多人卡在这一步其实是把问题归错方向了。5. 一套完整的提速方案从 clone 到 push 的实战复盘5.1 实践场景克隆一个带大量图片的 Hugo 博客仓库我自己维护过一个 Hugo 博客仓库主题里塞了大量截图和压缩包完整仓库差不多 2.3GB历史提交 2000 多次。在未做任何配置之前在国内网络环境下执行git clone https://github.com/user/blog.git每次都会卡在Receiving objects的 70% 左右然后报curl 18。当时的电脑是 Windows 10Git 版本 2.39属于最常见的环境。我记录了一下修复全过程你可以完全照着操作。5.2 分步执行过程诊断、配置、切换协议、复爬第一步我先执行了三行基础配置git config --global http.postBuffer 524288000 git config --global http.lowSpeedLimit 1 git config --global http.lowSpeedTime 999999接着再次克隆发现断开的频率降低了但速度依然很慢Receiving objects从 70% 熬到了 92%还是断了。这让我意识到瓶颈不只是客户端超时仓库本身的传输量太大。第二步我改用浅克隆看看分支主干的体积是否可接受git clone --depth 1 --single-branch --branch main https://github.com/user/blog.git结果只用了不到 10 分钟就把最新版本拉下来了。但作为维护者我不能一直停留在浅克隆状态后面需要全量历史才能正确生成站点归档。于是我先保留浅仓库作为工作副本再用git fetch --unshallow补齐历史这一步虽然慢但不会中断因为前期的三行配置保证了“慢但不死”。第三步我重新生成了一对 ed25519 key把 remote 从 HTTPS 切换成 SSHgit remote set-url origin gitgithub.com:user/blog.git git push -u origin main之后日常的git push明显比 HTTPS 稳除非遇到网络完全断开的阶段几乎不再有推送失败的情况。整套流程下来我没有使用任何第三方工具或非官方接口纯粹利用 Git 自身的配置和协议特性就把这个 2.3GB 仓库的日常维护成本降了下来。5.3 几条可长期使用的 Git 配置沉淀如果你也想一劳永逸可以把下面这段配置写到~/.gitconfig里作为个人 Git 环境的基础配置[user] name yourname email youremail [http] postBuffer 524288000 lowSpeedLimit 1 lowSpeedTime 999999 version HTTP/1.1 [pack] window 0 [pull] rebase true我个人的使用经验是postBuffer和lowSpeedTime组合适合所有使用 HTTPS 的场景http.version HTTP/1.1只在出现莫名的 curl 56 时才有必要如果你所在网络环境完全正常不设也可以。pack.window0会稍微增加传输包体积如果你常推大仓库且内存足够也可以不设置。最后再分享一个小技巧如果你要拉取的仓库很大但只是想看看代码或跑一下编译先用--depth 1浅克隆运行没问题后再考虑要不要补全历史。很多“克隆慢”的痛点其实是“不必要的数据量”放大了“网络波动”的影响。把这两件事分开处理GitHub 克隆/推送速度慢的问题大多数时候真的没那么难缠。