在 Linux 终端里操作 GitHub 仓库,SSH 登录和推送几乎是绕不开的一步。我差不多用了一年 HTTPS 才下定决心切到 SSH,原因倒不是懒,而是早期看的教程都只给步骤,不解释本地密钥、GitHub 公钥、远端地址、ssh-agent 这四样东西到底是怎么咬合在一起的。只要中间一步报错,就不知道从哪查起。这篇教程我会按真实操作顺序走一遍:先讲清楚为什么选 SSH,再生成密钥、把公钥交给 GitHub、验证连通性、完成首次推送,最后把推送过程中最常见的报错和多账号场景一起收掉。目标是让一个刚接触 Linux 和 Git 的读者,照着走也能一次跑通,并且以后再遇到报错时自己知道怎么查。1. 为什么我最终把GitHub仓库从HTTPS切到SSH1.1 HTTPS推送不是不能用,但每次都要你证明一次身份如果你新克隆一个仓库时用的是 https://github.com/YourName/my-demo.git 这类地址,Git 在每次 push 的时候都会要求你提供凭据。过去直接填账号密码就行,但现在 GitHub 的 HTTPS 推送已经不接受账户密码,只接受 Personal access token。token 本质上是一个带权限、带有效期的临时字符串,复制的时候容易漏字符,过期之后又要重新生成。很多人的第一个感受就是:麻烦,但又能怎样。1.2 SSH的本质:私钥签名,公钥验签SSH 方式完全不同。你本地会生成一对密钥,一个叫私钥,一个叫公钥。私钥只留在自己的机器里,公钥可以公开地交给 GitHub。推送时,本机用私钥对当前请求做签名,GitHub 拿到签名后用你存放在账号下的公钥去验证。验证通过就放行,整个过程不需要你手工输入任何密码,也看不到 token 在命令行里乱晃。可以这样理解:公钥是把锁,私钥是你手里的钥匙。你把锁交给 GitHub,把钥匙挂在腰间,每次要进门就拧一下;钥匙丢了才是大问题,锁本身即使公开也无所谓。这也是为什么公钥文件可以大咧咧地贴到网页上,而私钥文件一旦疑似泄露就要立刻吊销重配。1.3 一次配置,长期免密:适用人群和前置条件切到 SSH 的收益在长期使用中非常明显:同一个账号,一天推十次也好,本地跑脚本批量更新仓库也好,都不需要再弹密码。尤其是你在 Linux 服务器上做自动部署时,HTTPS 加 token 只能把 token 写进脚本,而 SSH 配上之后可以真正做到无人值守。适用人群很宽:只要你在 Linux 环境下用命令行操作 GitHub,或需要从服务器拉代码、自动部署,都建议切。前置条件也很简单:本地装好 git 和 OpenSSH 客户端即可,现在主流发行版都自带。确认一下:git --version、ssh -V 都能输出版本号,就没什么好担心的。2. 生成密钥对:三个最容易被忽略的细节2.1 一条命令生成符合GitHub要求的密钥生成密钥的命令很简单,在终端执行下面这行:ssh-keygen -t ed25519 -C "用于识别的注释" -f ~/.ssh/id_ed25519-C 后面的注释只是一个标签,通常写你的用途或邮箱,会出现在公钥文件末尾,不参与校验。-f 指定保存路径和文件名,如果省略,会默认使用 ~/.ssh/id_ed25519。接下来命令会问你要不要设置 passphrase,最后生成两个文件:id_ed25519 是私钥,id_ed25519.pub 是公钥。记住,只有带 .pub 的那个文件可以给别人看。2.2 为什么优先选ed25519而不是RSAGitHub 现在支持 ed25519、ECDSA 和 RSA 至少 2048 位。个人建议新环境一律用 ed25519。它的密钥更短、签名速度更快,安全强度在同等长度下远高于 RSA,OpenSSH 6.5 以上的客户端都能直接用。只有当你需要在某些非常老旧、内核和 OpenSSH 版本都很低的服务端环境中使用时,才回头考虑 RSA,命令是 ssh-keygen -t rsa -b 4096 -C "注释" -f ~/.ssh/id_rsa。RSA 不是不能用,但从新配置的角度讲,没有理由再从头去选它。2.3 passphrase不是必填项,但我建议你填ssh-keygen 会问两次密码短语。很多人图省事直接敲两次回车,私钥就以明文躺在磁盘上。如果这台机器不是你一个人能碰的,这就是一个很明显的风险点:私钥文件等于万能钥匙,谁复制走谁就能冒充你。设置一个 passphrase 之后,每次使用私钥都需要输入一次短语;如果嫌麻烦,可以用 ssh-agent 把已解锁的私钥缓存起来,之后一段时间内不再重复输入。我自己的做法是:个人电脑上设置 passphrase,并用 ssh-agent 保存;在 CI 或一次性容器里才用没有 passphrase 的只读密钥。注意:passphrase 不要用登录密码或生日,就用一句只有你自己知道的短语。如果你不想每次使用都输入 passphrase,可以先把 ssh-agent 拉起来,再把私钥加进去:eval "$(ssh-agent -s)" ssh-add ~/.ssh/id_ed25519之后这一会话内使用私钥就不会再重复询问。不过要理解,这只是把解锁后的私钥缓存在内存里,机器重启后需要重新 ssh-add。如果是在图形桌面环境,许多发行版也会自动启动一个 ssh-agent 进程,逻辑是一样的。2.4 权限和目录:配置前先让SSH读得了文件生成完密钥,先说权限。~/.ssh 目录要设为 700,私钥文件要设为 600,公钥文件通常 644 即可。如果权限过宽,SSH 会拒绝读取,并提示 Permissions 0664 for '...' are too open。执行一次:chmod 700 ~/.ssh chmod 600 ~/.ssh/id_ed25519这一步虽小,却是绝大多数“明明密钥存在却报 Permission denied”的元凶。另外一个容易忽略的点是:HOME 环境变量如果被改到了别的路径,SSH 会到错误的目录找密钥。后面排错部分还会再提这一点。3. 把公钥交给GitHub并验证连通性3.1 在网页上新建SSH Key时只会用到.pub文件在 Linux 终端查看公钥内容:cat ~/.ssh/id_ed25519.pub输出是一行以 ssh-ed25519 AAAA... 开头的文本。把这行全部复制,登录 GitHub,进入 Settings → SSH and GPG keys → New SSH key。Title 可以写“我的笔记本”或“某服务器”,Key type 选 Authentication Key,Key 框里粘贴公钥。很多人会犯的错是:误把 id_ed25519(没有 .pub)当成公钥粘上去。私钥文件内容是以 OPENSSH PRIVATE KEY 开头,粘贴后会直接被 GitHub 拒绝,因为格式不对。记住,私钥永远不应该上传到任何网站。3.2 ssh -T git@github.com 的成功输出长什么样公钥添加完成后,回到终端测试:ssh -T git@github.com第一次连接时,SSH 会询问是否信任 github.com 的主机密钥,输入 yes 后会自动追加到 ~/.ssh/known_hosts。如果一切正常,会看到类似输出:Hi YourName! You've successfully authenticated, but GitHub does not provide shell access.到这一步,SSH 登录部分已经完成。看到这句话不要慌,GitHub 确实不给你 shell,它只是利用 SSH 作为传输层的认证通道,这句话恰恰代表认证成功。3.3 首次连接的主机指纹确认:别直接敲yes第一次连接时弹出的主机密钥指纹,很多人看都不看就 yes,这其实是 SSH 防中间人攻击的关键一环。正确姿势:把提示里的指纹值与 GitHub 官方文档公布的 SSH 主机密钥指纹对比,确认一致后再输入 yes。如果指纹对不上,说明链路中间可能存在问题,理论上不应继续连接。已知主机信息会被保存在 ~/.ssh/known_hosts 中,以后每次连接会自动比对,指纹变了会直接拒绝连接并提示 Host key verification failed,这也是 SSH 的安全机制在发挥作用。3.4 验证失败的常见信号与下一步判断如果 ssh -T git@github.com 报 Permission denied (publickey),先不要重复执行,按下述顺序检查:一,确认公钥确实已经添加到这个 GitHub 账号,且没有被误删;二,确认 ssh 读的是哪把密钥,看连接时是否加载了正确的私钥;三,确认 ~/.ssh 和私钥文件权限。如果提示 Could not resolve hostname github.com,通常说明地址输错,检查你有没有把 git@github.com 拼成 git@github.comm。如果是 Connection timed out,说明当前网络环境无法连通 GitHub 服务,先把基础网络确认好再继续排查 SSH 本身。4. 仓库初始化与首次推送:一个完整的0到1流程4.1 设置提交身份:user.name和user.emailSSH 解决的是“你能访问这个仓库”,而提交记录里的作者信息由 Git 的 user.name 和 user.email 决定。这是两个独立的问题,但新手经常把它们混在一起。推荐在具体项目内配置:git config user.name "YourName" git config user.email "your_email@example.com"不加 --global 的写法只对当前仓库生效,避免影响机器上其他项目。注意 user.email 最好和 GitHub 账号绑定的邮箱一致,否则提交记录不会关联到你的 GitHub 头像和主页。4.2 本地提交和远端地址的两种关联方式进入仓库目录后:git init git add . git commit -m "Initial commit"接着把远端地址指向 GitHub:git remote add origin git@github.com:YourName/my-demo.git如果之前添加过 HTTPS 地址,可以用 set-url 覆盖:git remote set-url origin git@github.com:YourName/my-demo.git然后用 git remote -v 检查当前地址。SSH 地址的格式是 git@github.com:OwnerName/repo.git,注意中间是冒号而不是斜杠,OwnerName 是仓库所属用户或组织,repo 是仓库名。一旦写错,推送时会报 Repository not found.。4.3 main分支改名与git push -u的含义GitHub 新建仓库默认的主分支是 main,而你本地 init 之后默认分支可能是 master,取决于 Git 全局配置。推送前统一分支名是一个好习惯:git branch -M main git push -u origin main-M 表示把当前分支强制改名为 main,即使本地已有 main 也不会冲突。最后面的 -u 参数,会把本地分支和远端 main 建立追踪关系。一次带 -u 之后,后续直接 git push 或 git pull 就能知道和哪个远端分支对应,不需要每次写全。4.4 远端已有文件时的冲突处理:不要一上来就强推如果仓库是在网页上新建且勾选了初始化 README、.gitignore,远端就存在一次提交,而你本地的首次提交与远端没有共同祖先,直接推送会被拒绝。我见过不少人的第一反应是 git push -f 强制覆盖,这在个人空仓库里可能没事,但在协作仓库里会改写远端历史,非常危险。正确流程是先把远端改动合并进来:git pull --rebase origin main git push -u origin main--rebase 的作用是把你本地的提交重新排到远端提交之后,得到一条干净的历史,而不是产生一个多余的 Merge 提交。如果合并过程中有冲突,就逐个文件解决后 git add,再 git rebase --continue,直到完成。5. 推送报错排查:我实际走过的路径和方法5.1 常见报错速查与根因对照先把最常遇到的几种报错整理成一张表,方便以后对号入座。报错关键词直接原因优先排查方向Permission denied (publickey)SSH 公钥认证未通过密钥路径、权限、GitHub 账号公钥Host key verification failedknown_hosts 中没有可信主机记录或指纹变了核对指纹,清除旧记录后重新验证Repository not found.仓库不存在或无访问权限remote URL、仓库名、私有仓库权限Could not resolve hostname github.com地址拼写或 DNS 解析问题remote -v 检查地址Connection timed out网络层无法连接 GitHub先确认基础网络连通key_load_public: invalid format把私钥文件当成了公钥使用检查 IdentityFile 路径是否指到了私钥5.2 Permission denied (publickey) 的完整排查链路遇到这个报错,我的排查顺序是固定的一套:先开调试输出:ssh -vT git@github.com输出里会列出尝试加载的密钥文件,以及实际向服务端提供的公钥,比如 Offer public key .../.ssh/id_ed25519.pub。看这里的路径和你期望的是否一致。如果输出显示尝试使用某个密钥文件但文件不存在,检查 ~/.ssh/config 里 IdentityFile 的路径,也检查 HOME 环境变量是否指向了其他地方。如果密钥文件正确、被 Offer 了,但仍然拒绝,大概率是 GitHub 账号侧的问题:公钥没保存成功、公钥被删除、或这个公钥不属于你正在推送的账户。如果权限有问题,SSH 会在更早的阶段就报 too open,所以排错时先做一次 chmod 也不亏。我记得帮 A 同学处理过一次这样的问题:他所有文件都在,权限也改好了,但 ssh -T 就是失败。最后用 ssh -G github.com 查看最终生效配置,发现 config 里末尾的空行缩进异常,IdentityFile 被解析成多了一个多余字符。这类坑很隐蔽,看到正常配置却推不上去时,先怀疑配置文件而不是加密算法。5.3 Host key verification failed 其实是个安全提醒严格来说,Host key verification failed 不是认证失败,而是 SSH 在阻止你访问一个“指纹与记忆不符”的主机。这有两种情况:一是这台机器从未连过 GitHub,known_hosts 里没有记录;二是 GitHub 的主机密钥真的变了,或者你曾经连接过某个未知主机,地址被保存在 known_hosts 中。排查方法分两步:ssh-keygen -R github.com上面这条命令会从 known_hosts 中移除 github.com 的旧记录。之后再执行 ssh -T git@github.com,重新确认指纹后 yes 即可。另外,如果 known_hosts 里有很多旧记录,不想一条条删,也可以直接定位到对应行再删。5.4 排错时提高信息量的工具:ssh -v、ssh -G、ssh-add -l日常排错我离不开三条命令:ssh -vT git@github.com:显示握手全过程,核心看它尝试了哪些密钥文件、服务端接受与否。ssh -G github.com:打印指定主机的最终生效配置,包括 HostName、User、IdentityFile。这个命令不会真的发起连接,非常适合检查 SSH config 是否被写乱。ssh-add -l:列出当前 ssh-agent 中已经加载的密钥。如果 agent 里缓存了不相关的私钥,SSH 可能在多密钥场景下用错身份。这三条命令组合起来,基本能把 80% 的推送报错定位到具体环节。遇到问题时不要上来就卸载重装,那等于跳过诊断直接做手术。6. 一台Linux机器管理多个GitHub身份6.1 什么时候需要多个密钥对一个人完全可能同时拥有个人 GitHub 账号和工作用 GitHub 账号,或者一台服务器要同时拉取两个不同组织的仓库。如果只配置一个密钥对,选择把公钥加到哪个账号都别扭;一旦私钥泄露,所有关联账号都会受影响。更清晰的做法是每个身份生成独立的密钥对,并通过 ~/.ssh/config 按别名分流。6.2 SSH config 按别名分流:把github-work映射到github.com生成第二把密钥:ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_work -C "work-account"然后把 work 公钥添加到工作账号的 SSH Keys 里。接着在 ~/.ssh/config 中追加:Host github.com HostName github.com User git IdentityFile ~/.ssh/id_ed25519 Host github-work HostName github.com User git IdentityFile ~/.ssh/id_ed25519_work关键点在于:当你用别名 github-work 作为连接目标时,SSH 会先匹配 Host github-work 这个块,实际连接 HostName 指向的 github.com 主机,并使用该块指定的私钥。于是同一台机器上,git@github.com 走个人密钥,git@github-work 走工作密钥,互不干扰。还要注意不要写一个宽泛的 Host * 块把默认身份带偏,除非你很明确自己需要它。6.3 多账号下的remote URL写法与验证写仓库 remote 时,把原地址里的 github.com 整体替换为别名:个人仓库:git@github.com:YourName/personal.git 工作仓库:git@github-work:YourCompany/private.git测试时也一样:ssh -T git@github-work成功的输出会显示工作账号的用户名,这就代表第二把密钥已经生效。这里要特别提醒:Host 块的别名必须和你 URL 里 @ 符号后面的字符串完全一致,多一个字符或少一个字符都会匹配到默认配置,然后报 Permission denied。我吃过这个亏,最后是用 ssh -G 才发现别名写错了。另外,如果 agent 里已经缓存了默认的 id_ed25519,而你当前想用 work 密钥,可以执行 ssh-add ~/.ssh/id_ed25519_work 手动加入;如果各种缓存混在一起,用 ssh-add -D 清空后重新加载需要的密钥。6.4 提交邮箱不一致导致“推送成功但记录不是你的”多账号场景下,还需要注意 git config user.name 和 user.email。SSH 只管你是谁,而提交记录的作者取决于 Git 配置。如果你用工作密钥推送,但当前仓库的 user.email 设置成了个人邮箱,提交照样能推上去,但 GitHub 会把这条提交归到个人账号名下。所以每个仓库克隆完之后,先确认本仓库配置:git config user.name git config user.email不符合预期就单独改本仓库,不要动全局配置。如果发现有历史提交用错了邮箱,GitHub 支持把多条提交重新关联到正确账号,但那比一开始就配置对要麻烦得多。6.5 迁移到新机器时的.ssh目录备份清单换电脑是不可避免的事。迁移时不要只拷一个 id_ed25519,完整的备份清单应该是:~/.ssh/id_ed25519(私钥,必须加密传输) ~/.ssh/id_ed25519.pub(公钥) ~/.ssh/config(多账号分流规则) ~/.ssh/known_hosts(已信任主机指纹,可选)新机器上恢复后,把目录权限重新设为 700/600,再用 ssh -T git@github.com 验证一次就能继续。如果你私钥设置了 passphrase,新机器需要重新输入一次并加入 ssh-agent。我在实际配置中最常看到的失败,不是命令敲错,而是权限和路径这两个容易被忽略的细节。偶尔也会遇到配置文件写得很漂亮但连接总是用错密钥的情况,这时候别急着怀疑加密算法,先跑一遍 ssh -G 看看最终生效值比什么都有用。只要你把每个环节的产物都理清楚了——本地的私钥、网页上的公钥、config 里的别名——整套 SSH 登录和推送流程就能顺畅运转起来。愿这篇教程能帮你从第一步直接走到多账号管理的阶段,而不是停留在“复制粘贴完不知道下一步怎么办”。
在 Linux 终端里操作 GitHub 仓库,SSH 登录和推送几乎是绕不开的一步。我差不多用了一年 HTTPS 才下定决心切到 SSH,原因倒不是懒,而是早期看的教程都只给步骤,不解释本地密钥、GitHub 公钥、远端地址、ssh-agent 这四样东西到底是怎么咬合在一起的。只要中间一步报错,就不知道从哪查起。这篇教程我会按真实操作顺序走一遍:先讲清楚为什么选 SSH,再生成密钥、把公钥交给 GitHub、验证连通性、完成首次推送,最后把推送过程中最常见的报错和多账号场景一起收掉。目标是让一个刚接触 Linux 和 Git 的读者,照着走也能一次跑通,并且以后再遇到报错时自己知道怎么查。1. 为什么我最终把GitHub仓库从HTTPS切到SSH1.1 HTTPS推送不是不能用,但每次都要你证明一次身份如果你新克隆一个仓库时用的是 https://github.com/YourName/my-demo.git 这类地址,Git 在每次 push 的时候都会要求你提供凭据。过去直接填账号密码就行,但现在 GitHub 的 HTTPS 推送已经不接受账户密码,只接受 Personal access token。token 本质上是一个带权限、带有效期的临时字符串,复制的时候容易漏字符,过期之后又要重新生成。很多人的第一个感受就是:麻烦,但又能怎样。1.2 SSH的本质:私钥签名,公钥验签SSH 方式完全不同。你本地会生成一对密钥,一个叫私钥,一个叫公钥。私钥只留在自己的机器里,公钥可以公开地交给 GitHub。推送时,本机用私钥对当前请求做签名,GitHub 拿到签名后用你存放在账号下的公钥去验证。验证通过就放行,整个过程不需要你手工输入任何密码,也看不到 token 在命令行里乱晃。可以这样理解:公钥是把锁,私钥是你手里的钥匙。你把锁交给 GitHub,把钥匙挂在腰间,每次要进门就拧一下;钥匙丢了才是大问题,锁本身即使公开也无所谓。这也是为什么公钥文件可以大咧咧地贴到网页上,而私钥文件一旦疑似泄露就要立刻吊销重配。1.3 一次配置,长期免密:适用人群和前置条件切到 SSH 的收益在长期使用中非常明显:同一个账号,一天推十次也好,本地跑脚本批量更新仓库也好,都不需要再弹密码。尤其是你在 Linux 服务器上做自动部署时,HTTPS 加 token 只能把 token 写进脚本,而 SSH 配上之后可以真正做到无人值守。适用人群很宽:只要你在 Linux 环境下用命令行操作 GitHub,或需要从服务器拉代码、自动部署,都建议切。前置条件也很简单:本地装好 git 和 OpenSSH 客户端即可,现在主流发行版都自带。确认一下:git --version、ssh -V 都能输出版本号,就没什么好担心的。2. 生成密钥对:三个最容易被忽略的细节2.1 一条命令生成符合GitHub要求的密钥生成密钥的命令很简单,在终端执行下面这行:ssh-keygen -t ed25519 -C "用于识别的注释" -f ~/.ssh/id_ed25519-C 后面的注释只是一个标签,通常写你的用途或邮箱,会出现在公钥文件末尾,不参与校验。-f 指定保存路径和文件名,如果省略,会默认使用 ~/.ssh/id_ed25519。接下来命令会问你要不要设置 passphrase,最后生成两个文件:id_ed25519 是私钥,id_ed25519.pub 是公钥。记住,只有带 .pub 的那个文件可以给别人看。2.2 为什么优先选ed25519而不是RSAGitHub 现在支持 ed25519、ECDSA 和 RSA 至少 2048 位。个人建议新环境一律用 ed25519。它的密钥更短、签名速度更快,安全强度在同等长度下远高于 RSA,OpenSSH 6.5 以上的客户端都能直接用。只有当你需要在某些非常老旧、内核和 OpenSSH 版本都很低的服务端环境中使用时,才回头考虑 RSA,命令是 ssh-keygen -t rsa -b 4096 -C "注释" -f ~/.ssh/id_rsa。RSA 不是不能用,但从新配置的角度讲,没有理由再从头去选它。2.3 passphrase不是必填项,但我建议你填ssh-keygen 会问两次密码短语。很多人图省事直接敲两次回车,私钥就以明文躺在磁盘上。如果这台机器不是你一个人能碰的,这就是一个很明显的风险点:私钥文件等于万能钥匙,谁复制走谁就能冒充你。设置一个 passphrase 之后,每次使用私钥都需要输入一次短语;如果嫌麻烦,可以用 ssh-agent 把已解锁的私钥缓存起来,之后一段时间内不再重复输入。我自己的做法是:个人电脑上设置 passphrase,并用 ssh-agent 保存;在 CI 或一次性容器里才用没有 passphrase 的只读密钥。注意:passphrase 不要用登录密码或生日,就用一句只有你自己知道的短语。如果你不想每次使用都输入 passphrase,可以先把 ssh-agent 拉起来,再把私钥加进去:eval "$(ssh-agent -s)" ssh-add ~/.ssh/id_ed25519之后这一会话内使用私钥就不会再重复询问。不过要理解,这只是把解锁后的私钥缓存在内存里,机器重启后需要重新 ssh-add。如果是在图形桌面环境,许多发行版也会自动启动一个 ssh-agent 进程,逻辑是一样的。2.4 权限和目录:配置前先让SSH读得了文件生成完密钥,先说权限。~/.ssh 目录要设为 700,私钥文件要设为 600,公钥文件通常 644 即可。如果权限过宽,SSH 会拒绝读取,并提示 Permissions 0664 for '...' are too open。执行一次:chmod 700 ~/.ssh chmod 600 ~/.ssh/id_ed25519这一步虽小,却是绝大多数“明明密钥存在却报 Permission denied”的元凶。另外一个容易忽略的点是:HOME 环境变量如果被改到了别的路径,SSH 会到错误的目录找密钥。后面排错部分还会再提这一点。3. 把公钥交给GitHub并验证连通性3.1 在网页上新建SSH Key时只会用到.pub文件在 Linux 终端查看公钥内容:cat ~/.ssh/id_ed25519.pub输出是一行以 ssh-ed25519 AAAA... 开头的文本。把这行全部复制,登录 GitHub,进入 Settings → SSH and GPG keys → New SSH key。Title 可以写“我的笔记本”或“某服务器”,Key type 选 Authentication Key,Key 框里粘贴公钥。很多人会犯的错是:误把 id_ed25519(没有 .pub)当成公钥粘上去。私钥文件内容是以 OPENSSH PRIVATE KEY 开头,粘贴后会直接被 GitHub 拒绝,因为格式不对。记住,私钥永远不应该上传到任何网站。3.2 ssh -T git@github.com 的成功输出长什么样公钥添加完成后,回到终端测试:ssh -T git@github.com第一次连接时,SSH 会询问是否信任 github.com 的主机密钥,输入 yes 后会自动追加到 ~/.ssh/known_hosts。如果一切正常,会看到类似输出:Hi YourName! You've successfully authenticated, but GitHub does not provide shell access.到这一步,SSH 登录部分已经完成。看到这句话不要慌,GitHub 确实不给你 shell,它只是利用 SSH 作为传输层的认证通道,这句话恰恰代表认证成功。3.3 首次连接的主机指纹确认:别直接敲yes第一次连接时弹出的主机密钥指纹,很多人看都不看就 yes,这其实是 SSH 防中间人攻击的关键一环。正确姿势:把提示里的指纹值与 GitHub 官方文档公布的 SSH 主机密钥指纹对比,确认一致后再输入 yes。如果指纹对不上,说明链路中间可能存在问题,理论上不应继续连接。已知主机信息会被保存在 ~/.ssh/known_hosts 中,以后每次连接会自动比对,指纹变了会直接拒绝连接并提示 Host key verification failed,这也是 SSH 的安全机制在发挥作用。3.4 验证失败的常见信号与下一步判断如果 ssh -T git@github.com 报 Permission denied (publickey),先不要重复执行,按下述顺序检查:一,确认公钥确实已经添加到这个 GitHub 账号,且没有被误删;二,确认 ssh 读的是哪把密钥,看连接时是否加载了正确的私钥;三,确认 ~/.ssh 和私钥文件权限。如果提示 Could not resolve hostname github.com,通常说明地址输错,检查你有没有把 git@github.com 拼成 git@github.comm。如果是 Connection timed out,说明当前网络环境无法连通 GitHub 服务,先把基础网络确认好再继续排查 SSH 本身。4. 仓库初始化与首次推送:一个完整的0到1流程4.1 设置提交身份:user.name和user.emailSSH 解决的是“你能访问这个仓库”,而提交记录里的作者信息由 Git 的 user.name 和 user.email 决定。这是两个独立的问题,但新手经常把它们混在一起。推荐在具体项目内配置:git config user.name "YourName" git config user.email "your_email@example.com"不加 --global 的写法只对当前仓库生效,避免影响机器上其他项目。注意 user.email 最好和 GitHub 账号绑定的邮箱一致,否则提交记录不会关联到你的 GitHub 头像和主页。4.2 本地提交和远端地址的两种关联方式进入仓库目录后:git init git add . git commit -m "Initial commit"接着把远端地址指向 GitHub:git remote add origin git@github.com:YourName/my-demo.git如果之前添加过 HTTPS 地址,可以用 set-url 覆盖:git remote set-url origin git@github.com:YourName/my-demo.git然后用 git remote -v 检查当前地址。SSH 地址的格式是 git@github.com:OwnerName/repo.git,注意中间是冒号而不是斜杠,OwnerName 是仓库所属用户或组织,repo 是仓库名。一旦写错,推送时会报 Repository not found.。4.3 main分支改名与git push -u的含义GitHub 新建仓库默认的主分支是 main,而你本地 init 之后默认分支可能是 master,取决于 Git 全局配置。推送前统一分支名是一个好习惯:git branch -M main git push -u origin main-M 表示把当前分支强制改名为 main,即使本地已有 main 也不会冲突。最后面的 -u 参数,会把本地分支和远端 main 建立追踪关系。一次带 -u 之后,后续直接 git push 或 git pull 就能知道和哪个远端分支对应,不需要每次写全。4.4 远端已有文件时的冲突处理:不要一上来就强推如果仓库是在网页上新建且勾选了初始化 README、.gitignore,远端就存在一次提交,而你本地的首次提交与远端没有共同祖先,直接推送会被拒绝。我见过不少人的第一反应是 git push -f 强制覆盖,这在个人空仓库里可能没事,但在协作仓库里会改写远端历史,非常危险。正确流程是先把远端改动合并进来:git pull --rebase origin main git push -u origin main--rebase 的作用是把你本地的提交重新排到远端提交之后,得到一条干净的历史,而不是产生一个多余的 Merge 提交。如果合并过程中有冲突,就逐个文件解决后 git add,再 git rebase --continue,直到完成。5. 推送报错排查:我实际走过的路径和方法5.1 常见报错速查与根因对照先把最常遇到的几种报错整理成一张表,方便以后对号入座。报错关键词直接原因优先排查方向Permission denied (publickey)SSH 公钥认证未通过密钥路径、权限、GitHub 账号公钥Host key verification failedknown_hosts 中没有可信主机记录或指纹变了核对指纹,清除旧记录后重新验证Repository not found.仓库不存在或无访问权限remote URL、仓库名、私有仓库权限Could not resolve hostname github.com地址拼写或 DNS 解析问题remote -v 检查地址Connection timed out网络层无法连接 GitHub先确认基础网络连通key_load_public: invalid format把私钥文件当成了公钥使用检查 IdentityFile 路径是否指到了私钥5.2 Permission denied (publickey) 的完整排查链路遇到这个报错,我的排查顺序是固定的一套:先开调试输出:ssh -vT git@github.com输出里会列出尝试加载的密钥文件,以及实际向服务端提供的公钥,比如 Offer public key .../.ssh/id_ed25519.pub。看这里的路径和你期望的是否一致。如果输出显示尝试使用某个密钥文件但文件不存在,检查 ~/.ssh/config 里 IdentityFile 的路径,也检查 HOME 环境变量是否指向了其他地方。如果密钥文件正确、被 Offer 了,但仍然拒绝,大概率是 GitHub 账号侧的问题:公钥没保存成功、公钥被删除、或这个公钥不属于你正在推送的账户。如果权限有问题,SSH 会在更早的阶段就报 too open,所以排错时先做一次 chmod 也不亏。我记得帮 A 同学处理过一次这样的问题:他所有文件都在,权限也改好了,但 ssh -T 就是失败。最后用 ssh -G github.com 查看最终生效配置,发现 config 里末尾的空行缩进异常,IdentityFile 被解析成多了一个多余字符。这类坑很隐蔽,看到正常配置却推不上去时,先怀疑配置文件而不是加密算法。5.3 Host key verification failed 其实是个安全提醒严格来说,Host key verification failed 不是认证失败,而是 SSH 在阻止你访问一个“指纹与记忆不符”的主机。这有两种情况:一是这台机器从未连过 GitHub,known_hosts 里没有记录;二是 GitHub 的主机密钥真的变了,或者你曾经连接过某个未知主机,地址被保存在 known_hosts 中。排查方法分两步:ssh-keygen -R github.com上面这条命令会从 known_hosts 中移除 github.com 的旧记录。之后再执行 ssh -T git@github.com,重新确认指纹后 yes 即可。另外,如果 known_hosts 里有很多旧记录,不想一条条删,也可以直接定位到对应行再删。5.4 排错时提高信息量的工具:ssh -v、ssh -G、ssh-add -l日常排错我离不开三条命令:ssh -vT git@github.com:显示握手全过程,核心看它尝试了哪些密钥文件、服务端接受与否。ssh -G github.com:打印指定主机的最终生效配置,包括 HostName、User、IdentityFile。这个命令不会真的发起连接,非常适合检查 SSH config 是否被写乱。ssh-add -l:列出当前 ssh-agent 中已经加载的密钥。如果 agent 里缓存了不相关的私钥,SSH 可能在多密钥场景下用错身份。这三条命令组合起来,基本能把 80% 的推送报错定位到具体环节。遇到问题时不要上来就卸载重装,那等于跳过诊断直接做手术。6. 一台Linux机器管理多个GitHub身份6.1 什么时候需要多个密钥对一个人完全可能同时拥有个人 GitHub 账号和工作用 GitHub 账号,或者一台服务器要同时拉取两个不同组织的仓库。如果只配置一个密钥对,选择把公钥加到哪个账号都别扭;一旦私钥泄露,所有关联账号都会受影响。更清晰的做法是每个身份生成独立的密钥对,并通过 ~/.ssh/config 按别名分流。6.2 SSH config 按别名分流:把github-work映射到github.com生成第二把密钥:ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_work -C "work-account"然后把 work 公钥添加到工作账号的 SSH Keys 里。接着在 ~/.ssh/config 中追加:Host github.com HostName github.com User git IdentityFile ~/.ssh/id_ed25519 Host github-work HostName github.com User git IdentityFile ~/.ssh/id_ed25519_work关键点在于:当你用别名 github-work 作为连接目标时,SSH 会先匹配 Host github-work 这个块,实际连接 HostName 指向的 github.com 主机,并使用该块指定的私钥。于是同一台机器上,git@github.com 走个人密钥,git@github-work 走工作密钥,互不干扰。还要注意不要写一个宽泛的 Host * 块把默认身份带偏,除非你很明确自己需要它。6.3 多账号下的remote URL写法与验证写仓库 remote 时,把原地址里的 github.com 整体替换为别名:个人仓库:git@github.com:YourName/personal.git 工作仓库:git@github-work:YourCompany/private.git测试时也一样:ssh -T git@github-work成功的输出会显示工作账号的用户名,这就代表第二把密钥已经生效。这里要特别提醒:Host 块的别名必须和你 URL 里 @ 符号后面的字符串完全一致,多一个字符或少一个字符都会匹配到默认配置,然后报 Permission denied。我吃过这个亏,最后是用 ssh -G 才发现别名写错了。另外,如果 agent 里已经缓存了默认的 id_ed25519,而你当前想用 work 密钥,可以执行 ssh-add ~/.ssh/id_ed25519_work 手动加入;如果各种缓存混在一起,用 ssh-add -D 清空后重新加载需要的密钥。6.4 提交邮箱不一致导致“推送成功但记录不是你的”多账号场景下,还需要注意 git config user.name 和 user.email。SSH 只管你是谁,而提交记录的作者取决于 Git 配置。如果你用工作密钥推送,但当前仓库的 user.email 设置成了个人邮箱,提交照样能推上去,但 GitHub 会把这条提交归到个人账号名下。所以每个仓库克隆完之后,先确认本仓库配置:git config user.name git config user.email不符合预期就单独改本仓库,不要动全局配置。如果发现有历史提交用错了邮箱,GitHub 支持把多条提交重新关联到正确账号,但那比一开始就配置对要麻烦得多。6.5 迁移到新机器时的.ssh目录备份清单换电脑是不可避免的事。迁移时不要只拷一个 id_ed25519,完整的备份清单应该是:~/.ssh/id_ed25519(私钥,必须加密传输) ~/.ssh/id_ed25519.pub(公钥) ~/.ssh/config(多账号分流规则) ~/.ssh/known_hosts(已信任主机指纹,可选)新机器上恢复后,把目录权限重新设为 700/600,再用 ssh -T git@github.com 验证一次就能继续。如果你私钥设置了 passphrase,新机器需要重新输入一次并加入 ssh-agent。我在实际配置中最常看到的失败,不是命令敲错,而是权限和路径这两个容易被忽略的细节。偶尔也会遇到配置文件写得很漂亮但连接总是用错密钥的情况,这时候别急着怀疑加密算法,先跑一遍 ssh -G 看看最终生效值比什么都有用。只要你把每个环节的产物都理清楚了——本地的私钥、网页上的公钥、config 里的别名——整套 SSH 登录和推送流程就能顺畅运转起来。愿这篇教程能帮你从第一步直接走到多账号管理的阶段,而不是停留在“复制粘贴完不知道下一步怎么办”。