
作为常年跟代码仓库打交道的开发者Git Push 卡住、失败这种事几乎每个人都遇到过。我也一样从早期在 Windows 上用 Git Bash 推代码到后来在 macOS 和 Linux 上折腾多账号配置踩过的坑不在少数。最近看到不少人在问 idea git push getaddrinfo() thread failed to start、git push -u origin main 一直提交不上去、git push 卡住这类问题正好把这块内容系统整理一遍。这篇文章围绕 Git Push 失败这个核心场景讲清楚两件事一是为什么会失败二是怎么通过 SSH Key 一劳永逸地解决认证问题。标题里提到的 SSH Key 是重点但光配好 Key 还不够很多失败其实发生在网络链路和 Git 配置层面。我会把从生成密钥到验证推送的完整流程走一遍顺手把高频报错逐个拆开分析适合刚开始接触 Git 的新手也适合被仓库提交问题反复折腾过的老手。1. 先看现象这几种 Push 失败现场你撞上过哪个1.1 卡住不动进度条像被冻住最常见的一种就是输入git push之后终端没有任何报错光标停在那里像是整个进程睡着了一样。Windows 用户还经常碰到一个提示框弹出来让输入用户名密码输入完又弹或者干脆连提示框都不弹就那么干等着。这种卡住和真正的失败不是一回事。卡住往往意味着 Git 在等待某个响应可能是等待凭据输入可能是等待 DNS 解析也可能是等待托管平台服务器的回应。之前有人在群里发谭珅tanshen mingw64 /c/vsomeip_tools (main) $ git push -u origin这个片段说命令敲下去就没反应了这就是典型的环境等待状态不是仓库本身出了问题。1.2 报错信息一闪而过还没看清就失败另一种场景是报错一闪而过尤其是用 IDEA、VS Code 这类 IDE 内置的 Git 功能时弹窗提示 push failed但具体原因被折叠在日志里很多人根本来不及看。idea git push getaddrinfo() thread failed to start这个报错就是我在 JetBrains 系 IDE 里经常看到的一种后面会单独拆解。这类问题麻烦的地方在于用户以为是自己代码有问题反复检查 commit、检查分支实际上代码一点毛病没有问题出在 Git 的底层网络调用或者 IDE 的线程调度上。1.3 SSL 证书报错HTTPS 弹窗反复出现用 HTTPS 方式克隆仓库的人一定会遇到这种第一次 push 弹窗要账号密码输完没过几天又弹。更有甚者在某些网络环境下直接报 SSL certificate problem、unable to get local issuer certificate你明明什么都没改昨天还能推今天就不行了。这些都是 HTTPS 认证方式的天生短板。Git 走 HTTPS 推送时每次都要校验凭据虽然可以靠 credential helper 把密码存下来但一旦网络环境变化、证书链不完整、或者托管平台调整了认证策略问题就接踵而至。1.4 权限拒绝明明账号密码是对的还有一种很气人的输入git push后系统提示输入用户名密码你确认自己没输错结果还是Permission denied (publickey)或者fatal: Authentication failed。这里面有两种可能。如果走的是 HTTPS那多半是账号权限或密码过期的问题尤其是很多平台已经不支持用账号密码直接 push必须用个人访问令牌如果走的是 SSH那就是本机的 SSH Key 压根没配好或者配了但没被仓库托管平台识别。1.5 多个账号互相串提交到了错误的仓库到了后期很多人手里不止一个代码托管平台的账号。GitHub 一个、公司 GitLab 一个、国内 Gitee 可能还有一个。默认情况下 Git 只会读取~/.ssh/id_rsa或~/.ssh/id_ed25519这一把默认密钥多账号全部挤在同一把密钥上要么推送被拒要么提交人的身份显示成别人甚至把本该提交到公司仓库的代码推到个人仓库里去。这种问题排查起来最费时间因为报错五花八门有的直接拒绝有的明明推上去了但提交记录里的作者信息完全不对。这也是为什么我建议所有人在配置 SSH Key 时一次性把多账号方案也规划好。2. 根因拆解为什么 Push 会失败核心卡在认证与网络2.1 HTTPS 与 SSH两种认证方式的本质差异Git 从远端拉取和推送代码底层就两种传输协议HTTPS 和 SSH。绝大多数新手在 GitHub、Gitee 上克隆仓库时默认复制到的链接是 HTTPS 开头的因为平台默认展示的就是这个格式。HTTPS 方式下Git 每次与远端交互都要做身份认证。早期是用户名加密码密码不对就拒绝后来平台普遍改成用户名加个人访问令牌密码这种弱凭据逐步被淘汰。问题在于HTTPS 的凭据校验发生在每次传输之前如果你的凭据没被缓存Git 就要跟你要一次缓存过期了再要一次在某些代理环境或 IDE 内置终端里甚至会出现提示框根本弹不出来的情况。SSH 方式则完全不同。它靠的是公钥和私钥这一对密钥。私钥保存在你本机公钥提前配置到代码托管平台上。推送代码时Git 通过 SSH 协议向远端发起连接远端用你预先存入的公钥来验证你的身份验证通过就直接建立安全通道推送数据。整个过程中不需要反复输入密码不需要担心凭据过期这才叫无痛推送。2.2 Push 失败的真正瓶颈往往不在 Git 本身很多人一遇到 push 失败就怀疑 Git 配置、怀疑代码分支实际上我排查过的大量案例里问题根源至少有三分之一出在 Git 之外的地方。举个很典型的例子git push卡住不动很多人以为是 Git 在跟仓库服务器通信实际上 Git 卡在 DNS 解析上。Git 需要把github.com或gitlab.com解析成 IP 地址才能建立连接如果你所在网络的 DNS 解析超时Git 就会一直等在那里没有任何报错输出。这时候你按CtrlC中断再执行git push -v看详细输出往往能看到连接建立之前的卡点。还有一种情况是系统代理配置。很多开发者的电脑上设置了系统级代理IDE 或终端会读取这些设置Git 也不例外。如果代理设置指向了一个当前不可用的地址Git 的连接请求就会全部堆积在那里表现出来就是 push 卡顿、超时甚至出现Failed to connect之类的报错。2.3 为什么 SSH Key 能无痛推送明白了上面的机制就能理解为什么 SSH Key 是解决 Push 失败的最佳路径。第一SSH Key 是一次配置、长期使用的。私钥存在本地~/.ssh/目录下公钥配置到托管平台后只要你本机的私钥不丢理论上可以一直用不用像 HTTPS 那样定期更新令牌。第二SSH Key 绕开了用户名密码交互。推送时不需要终端弹窗不需要 IDE 弹窗直接建立连接从源头上消除了卡在等待凭据输入这类问题。第三SSH Key 的校验过程有明确的反馈。配置正确时ssh -T gitgithub.com会返回一个欢迎消息比如Hi username! Youve successfully authenticated配置不对几秒钟内就会返回Permission denied (publickey)。这种明确的反馈对于定位问题非常有帮助比 HTTPS 那种笼统的认证失败信息友好得多。3. 手把手配置 SSH Key从生成到验证的完整闭环3.1 生成密钥对ed25519 与 RSA 怎么选配置 SSH Key 的第一步是在本机生成一对密钥。打开终端输入下面的命令ssh-keygen -t ed25519 -C your_emailexample.com-t指定密钥类型-C是注释通常写你的邮箱方便在托管平台上认出这把钥匙是谁的。如果你用的是比较老的系统或者公司内部 GitLab 版本太旧不支持 ed25519那就退一步用 RSAssh-keygen -t rsa -b 4096 -C your_emailexample.com关于 ed25519 和 RSA 怎么选我的建议是默认优先 ed25519它密钥更短、生成更快、安全性更高只有遇到老平台不支持时才用 RSA 4096。现在主流的 GitHub、GitLab、Gitee 都已经支持 ed25519。执行命令后终端会问你要把密钥保存在哪里。默认路径是~/.ssh/id_ed25519直接回车即可。接着会提示设置 passphrase也就是私钥口令。这里我建议设置一个哪怕是个简单口令也行。虽然每次连接时会多一次输入口令的操作但私钥文件一旦泄露没有口令对方也解不开等于多了一道保险。3.2 把公钥添加到托管平台GitHub、GitLab、Gitee 通用逻辑密钥生成后在你的~/.ssh/目录下会多出两个文件id_ed25519是私钥id_ed25519.pub是公钥。公钥可以给别人看私钥绝对不能离开本机。需要添加到托管平台上的是公钥。查看公钥内容cat ~/.ssh/id_ed25519.pub输出是一长串字符以ssh-ed25519或ssh-rsa开头结尾是你刚才填的邮箱。把这串内容完整复制下来。接下来打开你的代码托管平台GitHub进入 Settings → SSH and GPG keys → New SSH key把内容粘贴进去保存GitLab进入 Preferences → SSH Keys粘贴保存Gitee进入设置 → SSH 公钥粘贴保存三个平台的操作逻辑完全一致都是把公钥内容粘到一个文本框里加个标题确认保存。添加完一般立即生效少数平台会有几分钟的缓存延时。3.3 本地验证连接ssh -T 的反馈怎么看公钥添加完成后别急着 push先验证一下本机和托管平台之间的 SSH 链路是否打通。在终端里执行ssh -T gitgithub.com如果你是 GitLab就换成ssh -T gitgitlab.comGitee 是ssh -T gitgitee.com。第一次执行会看到一个提示问你是否确认连接到该主机输入yes回车。如果一切正常你会看到类似下面的输出Hi your_username! Youve successfully authenticated, but GitHub does not provide shell access.看到successfully authenticated就说明 SSH Key 配置成功了。如果看到Permission denied (publickey)说明公钥没有被平台正确识别回头检查一下复制粘贴时是否有空格丢失或换行混入。3.4 切换远端地址把 HTTPS 换成 SSH验证通过后还有一个关键步骤你的仓库远端地址可能还是 HTTPS 的需要切换成 SSH 格式。查看当前远端地址git remote -v如果输出显示的是https://github.com/用户名/仓库名.git这种格式就需要改成 SSH 地址。SSH 格式长这样gitgithub.com:用户名/仓库名.git修改方式有两种。一种是直接删除再重新添加git remote remove origin git remote add origin gitgithub.com:用户名/仓库名.git另一种是一条命令直接改 URLgit remote set-url origin gitgithub.com:用户名/仓库名.git改完后再执行git remote -v确认一下看到git开头的地址就对了。3.5 首次推送的完整操作序列完整的首次推送流程我习惯按照这个顺序操作在项目目录里初始化仓库git init添加所有文件到暂存区git add .创建提交git commit -m first commit把默认分支命名为 maingit branch -M main添加远端地址git remote add origin gitgithub.com:用户名/仓库名.git推送git push -u origin main-u参数的作用是建立上游关联把本地的 main 分支和远端的 main 分支绑定在一起。设置之后后续直接输入git push就行不用每次带origin main。这里有个细节值得注意如果你的仓库里还没有任何提交直接执行git push会报错因为远端没有可推送的分支。所以一定要先git add再git commit保证本地至少有一个提交。4. 高频报错逐一定位getaddrinfo、卡住、-u 提交不上去4.1 getaddrinfo() thread failed to start不是网络问题是线程问题很多人在 IDEA 里看到idea git push getaddrinfo() thread failed to start这个报错时第一反应是网络问题。这其实是误解。getaddrinfo()是系统提供的域名解析函数Git 在执行 push 时会调用它来解析托管平台的域名。报错信息里的thread failed to start说明问题出在线程创建上也就是说你的电脑因为某种原因无法为这次 DNS 解析创建新的线程。我在实际排查中发现这个报错通常有两个来源。第一个是系统资源限制。当你的电脑运行了很多程序线程数达到上限时Java 虚拟机或 Git 进程可能创建线程失败。解决方法是关闭一些不必要的程序释放系统资源然后重启 IDE 再试。第二个是 IDE 的 Git 插件缓存问题。JetBrains 系 IDE 的 Git 集成模块偶尔会处于异常状态内置终端和外部终端表现不一致。遇到这种情况优先尝试 File → Invalidate Caches 清除缓存重启或者干脆在 IDE 的 Terminal 面板里手动执行git push绕开图形界面的推送逻辑。如果重启后仍然频繁出现可以检查一下系统环境变量里是否配置了会导致 Git 子进程异常的 JAVA_TOOL_OPTIONS 或 MAVEN_OPTS 之类的参数这些参数会影响 IDE 内嵌 JVM 的行为。4.2 git push 卡住先判断是认证等待还是网络超时git push卡住的情况我在前面已经提过。现在说下具体的排查思路。当你看到光标一直闪烁、没有任何输出时第一步按CtrlC中断然后用详细模式重新执行git push -u origin main -v-v参数会输出详细的通信日志能帮你定位卡在哪一个环节。我在实践中总结了一个简单的判断方法如果日志停在ssh: connect to host github.com port 22: Connection timed out说明是网络层连接超时SSH 默认的 22 号端口被阻断常见于某些受限网络环境如果日志停在某个仓库对象传输的中途且长时间没有速度变化可能是带宽问题或大文件传输卡顿如果日志完全没有输出连域名解析都没有打印那就要考虑 DNS 解析或系统代理的问题了针对 SSH 的 22 号端口连接超时GitHub 提供了一个备用的 SSH 连接端口 443。很多人不知道ssh.github.com这个域名就是专门为这种情况准备的。你可以在~/.ssh/config文件里加入这样一段配置Host github.com HostName ssh.github.com Port 443 User git这样当 22 号端口不可用时Git 会自动走 443 端口建立 SSH 连接很多网络环境下都能救急。4.3 git push -u origin main 一直提交不上去拆解参数与顺序有人问git push -u origin main为什么一直提交不上去。把这条命令拆开看它做了三件事git push执行推送-u设置上游分支关联origin main把本地 main 分支推送到名为 origin 的远端这条命令一直提交不上去通常存在以下几种情况。第一种是命令执行时终端还停留在旧的凭据状态。HTTPS 方式下之前的认证失败记录被缓存在系统凭据管理器里Git 反复使用旧的失效凭据导致每次 push 都弹出认证失败。解决办法是清理系统凭据管理器里保存的 Git 凭据在 Windows 上是控制面板 → 凭据管理器 → Windows 凭据找到对应的 github.com 条目删掉重来。第二种是本地分支和远端分支的提交历史不一致。如果你在远端已经有过一次初始化提交比如在 GitHub 网页上创建仓库时勾选了Add README file本地又是从空仓库开始提交的两者的历史就分叉了。此时 push 会被拒绝提示non-fast-forward。解决方案取决于你想保留哪边的提交如果远端只有 README 这种无关紧要的文件可以用git pull origin main --rebase把远端提交合并到本地再重新 push如果远端有重要提交就不要轻易覆盖。第三种是origin这个远端名称根本不存在。很多新手在本地仓库里直接执行git push -u origin main但仓库还没有添加过远端地址。此时会报fatal: origin does not appear to be a git repository明明白白告诉你没有这个远端。4.4 SSL 证书相关的报错走 HTTPS 协议的人还会经常遇到SSL certificate problem: unable to get local issuer certificate这类报错。这表示 Git 在建立 HTTPS 连接时无法验证服务器证书的有效性既有可能是本机 CA 证书库不完整也有可能是网络中间设备做了证书替换。网上流传最广的做法是执行git config --global http.sslVerify false关闭 SSL 证书验证。我不建议这么做这会让你所有的 HTTPS 请求都失去加密校验等于把门锁拆了。更稳妥的办法是更新本机 CA 证书库或者直接切换到 SSH 方式绕开 HTTPS 证书链路。如果你是因为公司网络有证书拦截才报错那也是先找网络管理员要正确的根证书而不是关闭验证。4.5 仓库状态导致的推送拒绝non-fast-forward 与 rejected还有一种常见失败报错里会出现! [rejected]和non-fast-forward。这不涉及任何认证和网络问题纯粹是提交历史冲突。当远端分支上有本地没有的提交且这些提交不在本地的祖先链上时Git 会拒绝你直接 push防止覆盖别人的工作。很多人这时候会想到git push --force我必须提醒你--force会直接覆盖远端历史如果远端有别人的提交全都会被抹掉。更安全的做法是使用--force-with-lease它会在强推前检查远端是否有更新如果有更新就拒绝执行避免误伤同事的代码。git push --force-with-lease origin main这条命令是我在所有强推场景下的默认选择宁可多一次失败也不轻易用裸--force。5. 多账号、多仓库场景下的 SSH 配置避坑5.1 多把密钥如何并存~/.ssh/config 的作用前面说了默认情况下 Git 只会找~/.ssh/id_ed25519这一把密钥。当你同时使用 GitHub 个人账号和公司 GitLab 账号时就必须让 SSH 客户端根据不同的域名选择不同的密钥。这就要用到~/.ssh/config文件。假设你有两个账号一个 GitHub一个公司 GitLab配置可以这样写Host github.com HostName github.com User git IdentityFile ~/.ssh/id_ed25519_github Host gitlab.company.com HostName gitlab.company.com User git IdentityFile ~/.ssh/id_ed25519_companyIdentityFile指定了连接对应域名时使用哪一把私钥。这样一来访问 GitHub 用个人密钥访问公司 GitLab 用公司密钥互不干扰。注意GitLab 的地址要写你们公司实际的 GitLab 域名比如gitlab.example.com不是固定的gitlab.com。5.2 全局 user.name 与仓库级配置的坑很多人在多账号场景下遇到一个奇怪的现象明明 push 成功了但提交记录里显示的作者是另一个人。这跟 SSH Key 无关是user.name和user.email配置的问题。Git 的提交作者信息来自两个层面全局配置和仓库配置。全局配置存在于~/.gitconfig对所有仓库生效仓库配置存在于.git/config只对当前仓库生效。如果你在全局配置了个人邮箱又在公司仓库里忘记设置公司邮箱那提交记录里显示的自然是个人邮箱。正确做法是在公司仓库目录下单独设置git config user.name 你的公司名字 git config user.email 你的公司邮箱这个配置只对当前仓库生效不会影响全局。5.3 换电脑、换系统后的迁移注意点换电脑后配 SSH Key有个最容易踩的坑直接把旧电脑的.ssh目录复制到新电脑但忽略了文件权限。在 macOS 和 Linux 上SSH 客户端对私钥文件的权限有严格要求权限过宽直接拒绝使用。如果你把私钥文件复制到新机器后发现ssh -T提示UNPROTECTED PRIVATE KEY FILE不要慌执行chmod 600 ~/.ssh/id_ed25519 chmod 700 ~/.sshWindows 系统则要注意用户名路径问题。多个账号可能在同一个 Windows 用户下操作C:\Users\你的用户名\.ssh是默认的密钥目录配置逻辑和 macOS、Linux 一致。另外一个建议是私钥文件一定要有备份但备份要放在安全的地方。公钥丢了可以随时重新生成并重新添加到平台私钥丢了虽然也可以删除旧公钥重新配但如果你在多个平台、多台机器上都用了同一把密钥重新配置的工作量会很大。我个人的做法是把私钥加密压缩后放在密码管理器里确保任何一台电脑挂掉都不影响重建环境。6. 我这些年总结的几个实操习惯先说一个很多人忽略的点在~/.ssh/config里给每个 Host 加一段ServerAliveInterval 30参数。它的作用是每 30 秒发送一次心跳包保持 SSH 连接活跃。我遇到过很多次 push 大仓库时因为连接长时间空闲被网络设备掐断加了心跳后就再也没出现过这种问题。Host github.com HostName github.com User git IdentityFile ~/.ssh/id_ed25519 ServerAliveInterval 30再说说提交粒度的问题。很多人 push 失败后反复重试其实跟提交方式有很大关系。我建议把大仓库的提交拆小一次提交只做一件事这样每次 push 的数据量小、耗时短即使失败也容易定位是哪次提交出了问题。配合git log --oneline查看提交历史排查效率会高很多。关于git push卡住还有一个经验Windows 用户如果开了实时杀毒软件扫描 Git 目录时可能会拖慢 push 速度。遇到推送慢得离谱的情况可以先把杀毒软件对项目目录的实时扫描暂时关掉试试。我帮人排查过一例最后发现是杀毒软件在逐个扫描 push 过程中临时生成的 pack 文件关掉后速度恢复正常。最后多说一句SSH Key 配好之后日常使用中如果哪天又发现 push 失败先别急着改配置。按照我习惯的顺序排查先ssh -T gitgithub.com验证 SSH 链路通不通通了再看远端地址是不是 SSH 格式再不行就看本地分支和远端分支是否产生了分叉。绝大多数问题走到第三步就能定位出来。把这些基础问题搞定了Git 推送这件事基本不会再给你添堵。