1. 卡在 Receiving objects 的晚上:GitHub 下载慢到底卡在哪
1.1 三种典型卡死现场
先说一个很具体的场景。某天晚上我想拉一个开源项目,命令敲下去,先是remote: Enumerating objects走了几秒,然后进入Receiving objects,百分比停在一个数字上不动了。等了五分钟,我以为是网络波动,按了 Ctrl+C 重新来,结果这一次连Enumerating objects都没走完。
这不是偶发情况。Git 拉取 GitHub 仓库卡住,基本逃不过下面三种形态:
- 形态一:
Receiving objects卡在某个百分比。最常见,尤其仓库历史里躺着几个大文件时,进度条压根不往前走。 - 形态二:下载中途直接报错退出。比如
fatal: early EOF、index-pack failed、RPC failed; curl 56这类,本质是传输中断,Git 没法把收到的对象完整拼起来。 - 形态三:
Resolving deltas阶段卡死。这个阶段考验的是本地 CPU 和内存,要解析远端传过来的压缩对象,仓库提交历史一多,进度也可能非常慢,甚至看着像死掉。
这三种形态的“卡”位置不一样,原因也不完全一样,需要分清楚,因为后面选的解决方式完全不同。
1.2 不是所有仓库都卡:真正影响下载速度的因素
网上很多人说“GitHub 下载慢”,其实并不准确。很多几十 MB 的小仓库,直连下载照样能跑完,只是慢点。真正卡死的仓库往往有这几个共同点:
仓库体积大、历史记录重。Git 的特点是会把整个项目的完整提交历史全部拉下来,而不只是当前这份代码。一个仓库现在代码可能只有 50MB,但十年历史里改过的二进制资源、被误提交过的大文件、后来删掉的文件,全都藏在.git对象库里。git clone的时候这些对象一个都跑不掉。
单个文件特别大。比如仓库里塞了一个 100MB 的模型权重、视频、安装包。这种文件一旦传输中断,Git 不会断点续传,而是从头再来,失败概率直线上升。
本地网络到 GitHub 的传输质量不稳定。这个属于客观因素。直连时数据包要经过很多跳数,中间任意一段丢包严重,最终表现就是 Git 进度条停滞,因为 Git 底层走的是 HTTP/HTTPS 传输,TCP 丢包会导致窗口缩小,速度掉到几乎为零。
DNS 解析结果不理想。有些时候域名解析出来的 IP 绕了远路,或者解析结果本身就不稳。你不妨在卡住的时候执行一下:
nslookup github.com如果解析出来的 IP 和你平时不一样,或者 ping 的延迟忽高忽低,那说明域名解析这层就已经不顺畅了。
1.3 反复重试是最差的策略
我在最开始踩过的一个坑就是:卡住就 Ctrl+C,然后重新git clone。这样做不仅浪费时间,还可能让情况越来越糟。
原因很简单,git clone默认不做断点续传,每次都是全新的完整下载。仓库体积越大,失败代价越高。而且频繁从同一网络路径发起大流量请求,很容易触发服务端的限流,让你从“慢”变成“直接拒绝”。我自己遇到过一次连续重试三遍后,远端返回fatal: unable to access的频率比第一次高得多。
正确做法是先git clone --depth 1只拉最新一次提交的快照,绕开庞大的历史对象。但这一步只是临时救急,真正要彻底解决问题,还是要靠换一条更顺的下载路径,也就是下面要说的清华镜像。
2. 清华镜像能管什么,不能管什么
2.1 清华镜像到底是什么
清华大学开源软件镜像站,通常叫 TUNA 镜像站,域名是mirrors.tuna.tsinghua.edu.cn。它做的事情可以用一句话概括:把很多常用开源软件、Linux 发行版、编程语言包管理器、操作系统安装镜像等,从各自的官方源同步到清华的服务器上,提供一个国内访问速度非常快的下载地址。
你可以把它理解成一个“本地仓库”:官方源在美国、欧洲,访问慢;清华镜像在国内,访问快。只要官方源允许同步,它就尽量做到每天甚至更频繁地同步更新。
很多人在使用过程中只记住了 PyPI 镜像和 Anaconda 镜像,实际上它覆盖的范围远不止这些。包括 Python 官方安装包、JDK 发行版、Homebrew 的 bottle 包、Linux 发行版 ISO、Docker 镜像仓库等,都有对应的镜像路径。
2.2 它能直接解决哪些“卡住”场景
结合我自己的使用经验,清华镜像在下面几类场景里几乎是“药到病除”:
第一,Python 依赖包安装卡住。你执行pip install torch,如果直连 PyPI 官方源,大概率会卡在Downloading阶段。把 pip 源切换成清华 PyPI 镜像后,速度通常能快到几 MB/s 甚至更高。
第二,Anaconda 创建环境或安装包卡住。Conda 默认的repo.anaconda.com在国内访问一直不太稳定,配置清华 Anaconda 镜像后,创建环境的成功率会高很多。
第三,下载编程语言或工具链安装包。比如想装 Python 3.10.11,直接去 python.org 下载可能很慢,而清华镜像里有一个python/目录,里面按版本号完整保留了 Windows、macOS、Linux 安装包,直接用浏览器或命令行下载都很快。
第四,想下载某个项目的发行版压缩包而不是整个 Git 仓库。很多项目在 GitHub Releases 中也会同步源码 tar.gz 包。如果你的目标是拿到代码看看,不一定非要用git clone,直接下载压缩包解压就能用。压缩包整体是一个文件,下载时即使慢,也比分段传输更稳。
2.3 它管不了的事:清华镜像不等于 GitHub 全站镜像
这里必须诚实地说明一点:清华镜像并没有提供“把整个 GitHub 所有仓库都镜像一份,然后让你git clone”的服务。你在清华镜像站上找不到git clone https://mirrors.tuna.tsinghua.edu.cn/github.com/xxx.git这种通用地址。它镜像的是具体某个软件、某个发行版、某个包,而不是 GitHub 全站。
所以如果网上有人说“用了清华镜像就能让所有 GitHub 仓库秒 clone”,这个说法是不准确的。真实情况是:
- 清华镜像能帮你说服很多“因为 GitHub 下载而卡住的下游环节”,比如依赖包、安装工具、环境。
- 但如果你想 clone 的仓库本身不在清华镜像覆盖范围内,那就需要配合其他 Git 镜像地址来做中转下载。
这个问题想清楚之后,解决方案反而更好选了:不要迷信单一方案,而是根据项目类型组合使用。
3. 实操:三种能立刻抄走的镜像替换写法
3.1 方案一:用 URL 前缀改写的方式 clone GitHub 仓库
这是最简单、最直接的替代方案。网上常见的镜像前缀是https://gitclone.com/github.com/,使用方法就是把原来的 GitHub 地址从https://github.com/用户名/仓库名.git改成:
git clone https://gitclone.com/github.com/用户名/仓库名.git原理是让 Git 去请求这个镜像服务,由镜像服务转发到 GitHub 拉取数据,再把数据传回给你。因为镜像服务访问 GitHub 的速度通常比你直连更快,所以能明显改善卡住的问题。
不过要提醒一句:这种镜像服务的稳定性和安全性依赖维护方,不同时期不同镜像的可用性差异很大。我的经验是,如果git clone失败,先换镜像地址,而不是反复重试原地址。
针对标题里说的“清华镜像”,如果你发现清华镜像站里恰好有这个仓库的独立镜像,可以优先用清华地址;如果没有,再使用 gitclone 这类通用 Git 镜像前缀。
3.2 方案二:全局改写配置,一劳永逸
如果不想每次都手改 URL,可以用insteadOf配置让 Git 自动帮你替换。执行:
git config --global url."https://gitclone.com/github.com/".insteadOf "https://github.com/"这条命令的意思是:以后所有试图访问https://github.com/开头的地址,Git 都会自动改成https://gitclone.com/github.com/开头。你不需要改变任何git clone命令的写法,原来怎么写还是怎么写。
但注意,这个配置是全局生效的,也会影响git push。大部分镜像站是只读的,不支持推送,所以如果你后续要推送代码,需要先把这个配置去掉:
git config --global --unset url."https://gitclone.com/github.com/".insteadOf更稳妥的做法是按仓库配置,不要设置 global。在某个仓库里执行:
git config url."https://gitclone.com/github.com/".insteadOf "https://github.com/"这样只对该仓库生效,不会误伤其他项目。不过说实话,按仓库配置麻烦,我一般只在确定只是拉取、不推送的仓库里才用全局。
3.3 方案三:浅克隆大幅降低下载量
镜像地址解决的是“传输路径不顺”的问题,但对仓库本身特别大的情况,效果有限。这时候需要结合浅克隆,只拉最新提交,不拉历史:
git clone --depth 1 https://github.com/用户名/仓库名.git--depth 1的参数含义是只拉取最近一次提交对应的文件快照,历史记录全部不要。对一个大仓库来说,下载量可能从几个 GB 降到几十 MB,卡住的概率直线下降。
还可以配合--single-branch,只拉默认分支,避免把仓库里所有分支都拉下来:
git clone --depth 1 --single-branch https://github.com/用户名/仓库名.git如果项目里需要的只是某一个子目录,连整个仓库都不用下,可以用稀疏检出:
git clone --depth 1 --filter=blob:none --sparse https://github.com/用户名/仓库名.git cd 仓库名 git sparse-checkout set 需要/的/目录--filter=blob:none表示先不下载文件内容,只获取目录结构;--sparse开启稀疏模式;git sparse-checkout set再按需拉取指定目录。这套组合对那种“仓库巨大但只需要其中一小部分代码”的场景特别有效。
4. 配置完仍然卡?完整排查链路复盘
4.1 第一步:先确认卡在哪个阶段
镜像地址配好之后,如果还是卡,不要急着换更多镜像,先看清楚输出信息卡在哪。
- 卡在
remote: Enumerating objects阶段,说明请求已经到达远端,远端在准备对象列表,此时卡顿主要是服务端处理大仓库时本身就需要时间,和你的网络关系不大。 - 卡在
Receiving objects阶段,说明本地正在接收数据,此时如果速度低,那就是传输链路的问题,优先怀疑镜像地址、DNS、网络丢包。 - 卡在
Resolving deltas阶段,说明数据已经收完,本地正在解析,此时 CPU 和内存会飙高。如果仓库提交历史很深,这个阶段可能非常慢。解决方案是减少历史提交数量,也就是用--depth 1。
我把这几个阶段的判断整理成了一张表,方便对照:
| 卡住位置 | 问题本质 | 推荐方案 |
|---|---|---|
Enumerating objects | 远端仓库过大,服务端准备慢 | 放弃完整 clone,改浅克隆 |
Receiving objects | 传输链路慢或丢包 | 换镜像地址、检查 DNS、断网重试 |
Resolving deltas | 本地计算资源不足 | --depth 1、增加内存、耐心等待 |
4.2 第二步:查看 DNS 和实际连接速度
镜像地址也挂了的话,先把网络问题拆开看。执行:
nslookup github.com nslookup gitclone.com看看两个域名解析出的 IP 是否正常,是否稳定。然后实测到目标地址的连通性。Windows 上可以用:
Test-NetConnection github.com -Port 443Linux 或 macOS 上可以用:
curl -I --connect-timeout 5 https://github.com如果curl能快速返回响应头,说明网络本身是通的,但 Git 慢可能与库的大小、HTTP 缓冲区设置有关。此时可以试着调大 Git 的 HTTP 缓冲区,减少单次请求失败的概率:
git config --global http.postBuffer 524288000这个参数把 Git HTTP 发送缓冲区调到 500MB,对某些“小水管”场景有一定帮助,但也不是万能的,不要指望设定它就能解决所有卡顿。
4.3 第三步:子模块和 LFS 是隐藏杀手
很多仓库卡住,卡在你看不见的地方。最常见的是子模块。
假设你 clone 的主仓库很小,但里面有.gitmodules文件,指向了多个其他 GitHub 仓库。执行:
git submodule update --init --recursive时,每个子模块都要单独从 GitHub 拉取。如果子模块地址写死的是https://github.com/...,而你前面配置的insteadOf是按前缀替换的,理论上子模块也会自动走镜像。但实际情况是,有些项目在子模块里使用了 SSH 地址或相对路径,就会绕过你的替换规则,继续直连 GitHub。
排查方法是在 clone 完成后执行:
git submodule status看看是不是卡在某个子模块上。如果是,可以手动删除该子模块的地址,改成镜像地址,或者直接只拉取你需要的子模块。
另一个隐藏杀手是 Git LFS。大型仓库经常用 LFS 管理二进制文件,但 LFS 文件不在普通 Git 对象里,而是存放在独立的 LFS 服务器上。默认情况下:
git clone https://github.com/xxx/yyy.git不仅拉 Git 对象,还会同时拉取所有 LFS 文件,体积可能暴涨。如果仓库主要让你卡住的正是 LFS 文件,可以先跳过 LFS:
GIT_LFS_SKIP_SMUDGE=1 git clone https://github.com/xxx/yyy.git这样只拉代码和历史记录,不拉 LFS 二进制文件。之后等你真正需要某个文件时,再单独执行:
git lfs pull --include="路径/文件名"按需拉取,避免一次性把整个仓库的大文件全下载下来。
4.4 第四步:检查系统时间、证书和限流
镜像地址配好了,但 Git 报错SSL certificate problem: certificate has expired,这其实多半不是证书真的过期,而是本地系统时间不对。Git 校验证书时会看有效期,系统时间如果在去年,任何证书都会“已过期”。先校准系统时间,再重启 Git 操作。
如果报错是unable to access或HTTP 403,则可能是镜像源限流。Git 镜像服务一般都有并发和流量限制,你需要换一个镜像源,而不是继续重试同一个。可以用:
git config --global url."https://gitclone.com/github.com/".insteadOf "https://github.com/"也可以临时改用另一个镜像前缀,把原来的 unset 掉再配新的。镜像地址没有哪个是永久有效的,我的习惯是先列出一两个备用,哪个能用用哪个。
5. 把依赖安装也纳入清华镜像体系
5.1 pip 卡住?切清华 PyPI 镜像
很多时候你以为自己在“下载 GitHub 项目”,但项目代码 clone 下来之后,安装依赖才是真正的大头。比如一个 Python 项目,git clone可能 10 秒就完成了,但pip install -r requirements.txt卡了半小时。
针对 Python 包安装,清华镜像是最成熟的解决方案之一。临时指定:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名永久配置:
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple配置完成后,再安装依赖就会优先从清华 PyPI 镜像下载。有些包在 PyPI 上没有编译好的 wheel,需要从源码构建,而源码包可能托管在 GitHub 上;这种情况下 pip 也会去下载 GitHub 上的源码 tar.gz。如果这一步卡住,可以先手动下载对应源码包,放到本地,再通过pip install 本地路径安装,绕开网络下载。
5.2 Anaconda 卡住?配置清华 conda 镜像
创建 Conda 环境时最容易卡住的两个环节是:下载 Python 解释器包、下载 Conda 包索引。直连官方源时,慢是常态,配置清华镜像后通常会有明显改善。
添加清华 Anaconda 镜像通道:
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --set show_channel_urls yes如果需要conda-forge通道,也可以把通道地址换成:
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/配置完成后,建议先执行一次:
conda clean -i清除之前的索引缓存,再创建环境。否则 Conda 可能还在用旧的、拉取失败的缓存,结果就是配置改了但依然卡住。
5.3 Python 和 JDK 安装包也走清华镜像
除了包管理器,编程语言解释器本身的安装包同样可以走清华镜像。以 Python 3.10.11 为例,直接在清华镜像站下载:
https://mirrors.tuna.tsinghua.edu.cn/python/3.10.11/里面按操作系统分好了目录,比如 Windows 的 installer、macOS 的 pkg、Linux 的源码包。比去 python.org 官网下载快很多。
JDK 也有类似场景。在清华镜像站的 Adoptium 镜像路径下,可以找到不同版本、不同平台的 JDK 安装包。比如想装 Temurin 17,直接去对应目录挑.zip或.tar.gz下载,解压后配置环境变量就能用。
这些场景虽然不直接是“Git 下载 GitHub 项目”,但很多开发者在实际操作中遇到的“下载卡住”,一大半都发生在这些工具链环节。把清华镜像用到位,等于把整条工具链的水源都换成了国内缓存。
5.4 Homebrew 和前端依赖的顺带一说
如果你用的是 macOS,Homebrew 安装软件时,也会从 GitHub Releases 下载很多预编译的 bottle 包,卡住的情况很常见。配置清华 Homebrew 镜像可以改善:
export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles前端项目安装 npm 依赖卡住时,可以临时切换 npm 镜像地址:
npm install --registry=https://registry.npmmirror.comMaven 项目依赖下载卡住时,可以配置阿里云 Maven 镜像。这些方案和清华镜像的核心思路一致:换个更快的源,让下载真正落地。
6. 把整个下载过程变成一套可复用的预案
6.1 不同场景直接按表操作
经常有朋友问我“GitHub 下载卡住怎么办”,我现在的习惯是不直接回答一个命令,而是先反问几个问题:卡在哪个阶段?仓库大不大?是要代码还是要安装包?然后按场景给方案。用一张表总结我自己的处理逻辑:
| 场景 | 症状 | 首选方案 | 备选方案 |
|---|---|---|---|
| 小仓库 clone | 慢但能完成 | 直连 | 镜像前缀 |
| 中大型仓库 clone | 卡在 Receiving | --depth 1+ 镜像前缀 | 下载压缩包解压 |
| 仓库含大目录 | 卡死或无响应 | --filter+--sparse按需拉取 | 下载单个 raw 文件 |
| Python 依赖安装 | pip 下载慢 | 清华 PyPI 镜像 | 本地源码包 |
| Conda 环境创建 | 卡在 solving/下载 | 清华 conda 镜像 | 换 conda-forge 通道 |
| 下载 Python/JDK 安装包 | 官网下载慢 | 清华镜像对应目录 | 手动下载后内网分发 |
这套预案的好处是:不用每次遇到卡顿都从零开始想,直接按症状对照方案执行,能省下很多时间。
6.2 我的日常下载习惯
最后分享几个我自己的操作习惯,算是踩坑之后形成的肌肉记忆。
第一,能用压缩包就不 clone。如果我只是想看代码、跑个 demo,直接下载 GitHub 页面的 Code 按钮里那个 “Download ZIP”,解压就能用。这一步绕过了 Git 对象传输,只下载一个文件,稳定性高很多。
第二,clone 前先看仓库体积。GitHub 仓库主页往下拉,能看到语言统计和文件列表。如果明显有.zip、.pth、.bin这种大文件,我会直接加--depth 1,甚至配合GIT_LFS_SKIP_SMUDGE=1,把大文件先隔离在下载范围之外。
第三,不要同时配置太多层镜像。有些人配了 git 的insteadOf,又配了 ssh 地址替换,还设置了 http 代理,结果出现问题后完全不知道是哪个环节出错。我的做法是保持配置精简,一次只启用一层镜像,确认生效后再叠加其他优化。排查时也方便回滚。
第四,关注官方文档而不是二次转述的教程。清华镜像站在官网每个镜像页面都提供了完整的使用说明和更新状态,包括同步时间、磁盘占用、失效公告。遇到配置问题先去看一眼官方页面,很多时候比到处搜索“最新可用地址”更靠谱。
Git 下载 GitHub 项目卡住这件事,说到底是传输路径和仓库体量两个核心变量的问题。清华镜像解决的是一部分路径问题,浅克隆解决的是体量问题,按需拉取解决的是耐心问题。把它们组合起来,基本上九成以上的卡顿场景都能找到出路。