☰
从零配置SSH密钥并推送代码到GitHub:完整实操指南
2026/9/26 3:32:18 网站建设 项目流程

1. 写在前面:这可能是你与 GitHub 之间最值得花半小时搞定的事

如果你已经受够了每次 push 代码都要输一遍用户名和密码,或者经常被 GitHub 的“Access denied”和“Permission denied (publickey)”折磨到怀疑人生,那这篇文章大概率能帮到你。我最早接触 GitHub 的时候,也是从 HTTPS 协议开始用的,那时候觉得“能跑就行”,直到某天 push 一个稍微大点的仓库,连续输入了四五次凭证,再加上网络稍微一波动就直接超时,整个人瞬间就炸了。后来换成 SSH 方式推送远程仓库,整个流程变得顺畅很多,而且不用反复输密码,配合 SSH key 的权限管理,在多个设备、多个账号之间切换也清晰得多。

这篇内容围绕一个核心场景展开:用 SSH 协议给 GitHub 远程仓库做推送。适合刚接触 Git 不久、想彻底搞懂 SSH 推送原理的入门读者,也适合已经用 HTTPS 但想换一种更稳定、更省事方式的开发者。我会先解释为什么选 SSH 而不是 HTTPS,再完整演示从生成密钥、配置 GitHub 到真正 push 代码的全过程,最后把我这些年踩过的坑、排查问题的思路一并整理出来。标题听起来不大,但里面涉及的细节其实足够写一篇很扎实的实操笔记。

2. 为什么推送远程仓库我首选 SSH

2.1 HTTPS 和 SSH 到底差在哪

很多人第一次接触 GitHub 时,默认用的就是 HTTPS 地址,形如https://github.com/用户名/仓库名.git。这种方式的好处是门槛低,不需要额外配置密钥,只要在弹出窗口里输入 GitHub 的用户名和密码、或者 Personal Access Token(个人访问令牌)就能完成认证。但它的痛点也很明显:每次 push 都要重新认证,虽然可以用缓存工具把凭证存下来,但换设备、换系统、凭证过期之后又得折腾一遍。

SSH 则完全不一样。它走的是公钥加密、私钥签名的认证机制,一次配置,长久使用。我只需要在本地生成一对密钥,把公钥放到 GitHub 账号里,之后所有 Git 操作都通过 SSH 协议完成认证,不需要输入密码,也不依赖 HTTPS 那套令牌体系。从实际体验来看,SSH 在连接稳定性上也更有优势,尤其是在某些网络环境下,HTTPS 反复握手失败的概率明显比 SSH 高。

2.2 为什么 SSH 更适合“长期主义者”

这里我不打算把 SSH 的底层原理讲得太深,毕竟你只是用它来推代码,不是来写 RFC 文档的。但有一个概念必须建立起来:SSH 认证之所以安全,是因为你手上握着私钥,GitHub 那边只保存公钥。私钥不离开你的电脑,任何想冒充你的人都拿不到它,所以即使 GitHub 的服务器被攻破,攻击者拿到的公钥也做不了什么坏事。

从实际工作流来说,SSH 还有一个好处:支持多账号管理。如果你同时有个人 GitHub 账号、公司 GitLab 账号,甚至还有自建的 Gitea 服务,完全可以通过多份 SSH 配置分别指定不同的私钥,互不干扰。HTTPS 想在多个账号之间切换就比较麻烦,虽然也能通过凭证管理器实现,但远不如 SSH 的 config 文件来得直观。

提示:如果你只是临时用一下 GitHub,HTTPS 完全够用。但如果你打算长期维护开源项目、频繁推送代码,换 SSH 是性价比最高的选择。

3. 先搞清楚 SSH 推送远程仓库的整个工作流程

3.1 一次 SSH 推送,背后发生了什么

我在最开始接触 SSH 推送时,其实并不理解为什么本地明明没有“密码”,GitHub 却能认识我。后来拆开看才明白,整个过程可以简化成三步。

第一步,本地生成密钥对。常用的工具有ssh-keygen,它会同时生成一个私钥(默认存在~/.ssh/id_ed25519)和一个公钥(~/.ssh/id_ed25519.pub)。私钥等同于你的“数字身份证”,公钥则是身份证的公开信息。

第二步,把公钥内容添加到 GitHub 账号的 SSH keys 设置页面。这一步相当于告诉 GitHub:“这把公钥对应的私钥持有者,是我本人”。GitHub 会把公钥存到它的用户配置里。

第三步,执行git push。Git 通过 SSH 协议连接 GitHub 时,GitHub 会向客户端发起挑战,客户端用私钥签名并返回响应,GitHub 用之前存储的公钥验证签名。验证通过后,GitHub 就认为你是合法用户,允许推送。

整个过程看起来很复杂,但实际操作中 Git 和 SSH 客户端早已把背后的逻辑封装好了,我们要做的只是生成密钥、添加公钥、把远程地址改成 SSH 格式。

3.2 SSH key 里那些绕不开的名词

第一次生成密钥时,命令行会提示你选择密钥类型,常见的选项包括 RSA、Ed25519 等。我个人的建议是直接选 Ed25519,因为它的密钥更短、生成速度更快,安全性也足够好。以前老教程里喜欢用 RSA 4096,只能说是历史遗留习惯,现在的新项目基本都推荐 Ed25519。

还需要理解~/.ssh/config文件的作用。这个文件可以按 Host 维度配置 SSH 连接的参数,比如指定用哪个私钥、是否开启压缩等。对于 GitHub 这种单一场景,不写 config 文件也能跑,但如果你的电脑上同时使用 GitHub、GitLab、Gitee,那配置一个清晰的 config 文件就非常有必要了。

3.3 SSH 推送和 HTTPS 推送在命令上的区别

以git remote add为例,HTTPS 和 SSH 只是 URL 的写法不同,其他 Git 命令完全一样。HTTPS 格式是https://github.com/用户名/仓库名.git,SSH 格式是git@github.com:用户名/仓库名.git。注意 SSH 格式里不是https://开头,而是git@开头,中间的github.com就是 SSH 连接的主机地址,冒号后面才是仓库路径。

已经用 HTTPS 添加过 remote 的仓库,不需要删掉重新克隆,直接修改 remote 地址即可。下面会演示具体命令。

4. 手把手实操:从零配置 SSH 并推送代码到 GitHub

4.1 检查本地是否已有 SSH key

这一步特别容易被跳过去,但我觉得值得花十秒钟看一眼。打开终端,输入:

ls -al ~/.ssh

如果这个目录下已经有id_ed25519和id_ed25519.pub这样的文件,说明你之前生成过密钥。这时候有两个选择:一是继续沿用旧密钥,二是重新生成一份新的。我建议,如果你不确定旧密钥是否安全、或者是否已经把公钥配置在了某个平台上,那就重新生成一份,避免后面排查问题时分不清是哪把钥匙在起作用。

如果目录不存在或者没有任何文件,那就直接进入下一步生成流程。

4.2 生成新的 SSH 密钥

执行下面的命令,把your_email@example.com替换成你自己的邮箱,这个邮箱仅作为注释出现在公钥信息里,方便你识别这把密钥是谁生成的,并不需要和 GitHub 账号邮箱完全一致:

ssh-keygen -t ed25519 -C "your_email@example.com"

终端会提示你选择保存位置,默认是/Users/你的用户名/.ssh/id_ed25519,直接回车即可。接着会提示输入 passphrase(口令)。这里很多人会纠结:不设口令吧,怕私钥泄露后别人直接就能用;设了口令吧,每次 push 又会多一步输入。我的选择是:在个人电脑上不设口令,在共享电脑或公司电脑上一定设。不设口令时,只要私钥文件不被别人复制走,安全风险其实可控;而如果设了口令,也强烈建议配合 ssh-agent 使用,避免每次操作都输一次。

生成完成后,可以用下面命令查看公钥内容,后面要用:

cat ~/.ssh/id_ed25519.pub

这串内容看起来像ssh-ed25519 一堆乱码... your_email@example.com,复制它的时候别加多余的空格和换行。

4.3 把公钥配置到 GitHub 账号

登录 GitHub,点右上角头像,进入 Settings,在左侧菜单里找到 SSH and GPG keys。点击 New SSH key,Title 栏可以填“我的 MacBook Pro”之类方便识别的名字,Key 栏把刚刚复制的公钥内容粘贴进去,然后保存。

这里有个非常常见的坑:公钥粘贴时绝对不能有额外的换行,开头和结尾的空格也要留意。曾经有个朋友把公钥粘到 GitHub 上之后一直认证失败,我远程帮他一核对,发现末尾多了一串看不见的换行符,GitHub 在校验时自然就把整个 key 当作无效了。另外,如果你有多个设备,可以给每台设备的公钥分别命名,这样以后在 GitHub 后台看到列表时,能一眼看出哪台设备对应哪个 key。

4.4 先做一次连接测试,别急着 push

配置完成后,可以在终端执行:

ssh -T git@github.com

如果看到类似下面的输出,说明认证已经通过了:

Hi yourusername! You've successfully authenticated, but GitHub does not provide shell access.

这句提示的意思是“认证成功,但 GitHub 不提供 shell 访问权限”,这是正常现象,并不意味着出错。假如你看到Permission denied (publickey),就要回到前面的步骤检查公钥配置是否正确,或者看下本地是否用了正确的私钥。

我自己习惯把这一步当作标配动作:不管在哪个新环境配置完 SSH,都会先执行一次ssh -T做连通性测试。这样能尽早暴露绝大多数配置问题,节省后面真正 push 时的等待时间。

4.5 修改远程仓库地址为 SSH 格式

如果你手里的仓库是用 HTTPS 克隆的,不需要重新克隆,直接执行:

git remote -v

查看当前 remote 地址。如果是https://github.com/...这类格式,就执行:

git remote set-url origin git@github.com:用户名/仓库名.git

把 origin 换成 SSH 格式。如果你是先创建好 GitHub 远程仓库、本地还没初始化,那标准的初始化流程是:

git init git remote add origin git@github.com:用户名/仓库名.git

之后正常执行git add .、git commit -m "first commit"、git push -u origin main就行了。注意现在的 GitHub 默认分支名是main,不再是以前的master。如果 push 时提示分支名不匹配,用git branch -M main把当前分支改名即可。

4.6 完整的推送命令序列参考

我没有刻意把命令复杂化,但很多人第一次操作时会搞混顺序,这里贴一份完整的最小化操作序列,方便你直接对照执行:

# 1. 生成密钥 ssh-keygen -t ed25519 -C "your_email@example.com" # 2. 查看公钥 cat ~/.ssh/id_ed25519.pub # 3. 把公钥复制到 GitHub -> Settings -> SSH and GPG keys # 4. 测试连接 ssh -T git@github.com # 5. 初始化本地仓库并关联远程 git init git remote add origin git@github.com:用户名/仓库名.git # 6. 推送 git add . git commit -m "init project" git push -u origin main

如果你是从 HTTPS 仓库迁移到 SSH,只需把第 5 步替换为:

git remote set-url origin git@github.com:用户名/仓库名.git

这里有一个容易被忽略的点:git push -u origin main中的-u表示建立本地分支与远程分支的跟踪关系,之后直接git push就能推送,不需要每次再带完整的参数。

5. 多设备多账号场景下的 config 文件管理

5.1 为什么你迟早需要一份 SSH config

当电脑上只有一把私钥、只连接一个 GitHub 账号时,SSH 的默认逻辑足够用。但现实往往更复杂。比如我自己,家里有一台 Linux 服务器,公司配的 MacBook 上还连着 GitLab,另外我还有一个专门维护开源项目的 GitHub 小号。这种情况下,如果每个平台都用默认的id_ed25519,Git 连接时就会拿同一把私钥去撞所有平台,GitHub 可能认出了你,GitLab 却可能直接拒掉,甚至报错。

解决方式就是在~/.ssh/config里为不同的 Host 分配不同的私钥和用户。这个文件不需要额外安装工具,纯手动创建即可。

5.2 一份可复制的多账号配置示例

下面是一份很经典的配置模板,假设我要让 GitHub 主号使用id_ed25519_github_main,GitHub 小号使用id_ed25519_github_work,GitLab 使用id_ed25519_gitlab:

# 个人 GitHub 主号 Host github.com HostName github.com User git IdentityFile ~/.ssh/id_ed25519_github_main # 工作用 GitHub 小号 Host github-work HostName github.com User git IdentityFile ~/.ssh/id_ed25519_github_work # 公司 GitLab Host gitlab.company.com HostName gitlab.company.com User git IdentityFile ~/.ssh/id_ed25519_gitlab

注意第二个配置的 Host 是github-work,这是一个别名,实际连接的 HostName 仍然是github.com。那么在使用这个别名时,远程地址要写成:

git remote add origin git@github-work:用户名/仓库名.git

也就是说,SSH 会先拿github-work去匹配 config 里的 Host 配置,找到对应的 IdentityFile,再用这个私钥去连接真正的github.com。

这里最容易被误导的点是:很多人以为 Host 必须写成真实域名,其实不是。Host 只是本地标识,真正决定连到哪台服务器的是 HostName。

5.3 使用 ssh-agent 减少口令输入

如果给私钥设置了 passphrase,每次连接都会询问口令,这确实很烦。推荐用 ssh-agent 把密钥加进内存。macOS 和 Linux 下执行:

eval "$(ssh-agent -s)" ssh-add ~/.ssh/id_ed25519

Windows 下如果使用 Git Bash,也可以执行类似命令。做了这一步之后,当前终端会话内就不再需要反复输入口令了。需要说明的是,这种便利是建立在“信任当前登录用户”的前提下,所以如果是公用电脑,我不建议把密钥加到 agent 里长期驻留。

6. 推送过程中的常见报错与排查思路

6.1 Permission denied (publickey)

这是最经典、也最容易踩的报错。出现这个提示时,先别慌,按顺序排查以下四件事:

一是确认公钥是否真的添加到了 GitHub 对应账号。有人可能把公钥加到 A 账号,却用 B 账号的邮箱或名称去提交,结果当然是认证失败。二是确认当前 SSH 是否使用了正确的私钥。如果有多个私钥文件,很有可能 SSH 默认拿了一把不是 GitHub 用的私钥去连接。这时可以加-v参数输出调试信息:

ssh -vT git@github.com

在输出的日志里能找到Offering public key和对应的密钥路径,看看是不是你预期的那把。三是检查文件权限。在 Linux 和 macOS 下,私钥文件权限过宽会被 SSH 拒绝使用,比如id_ed25519的权限如果是 644 就可能报Permissions too open。需要改成 600:

chmod 600 ~/.ssh/id_ed25519

四是检查~/.ssh/config里有没有互相冲突的规则。如果你配置了泛匹配的 Host,比如把 Host 写成*,它可能干扰后续规则,导致 GitHub 用了错误的私钥。

6.2 Bad owner or permissions on ~/.ssh/config

在 Windows 上使用 OpenSSH 时,我见过很多人报这个错。原因通常是 Windows 对 SSH 配置文件的权限管理方式和 Unix 不一样,当前用户对C:\Users\xxx\.ssh\config文件没有足够的控制权,或者权限被继承给了其他账户。解决办法是右键 config 文件,进入属性 -> 安全 -> 高级,把所有者改为当前用户,然后删除多余的授权项,只保留当前用户完全控制权限。也可以用 Git Bash 执行icacls命令来处理,但我在实践中发现直接在 GUI 里改更直观,不容易误操作。

6.3 Repository not found

这个报错也常见。它不一定代表仓库不存在,更可能是 SSH 没有权限访问该仓库。典型情况是:使用小号密钥推送到主号名下仓库,或者新创建的私有仓库还没有把对应的账号加为 collaborator。如果是私有仓库,记得检查仓库的 Settings -> Collaborators 是否包含当前认证账号。

6.4 git push 时总是提示输入用户名和密码

如果你已经按上面的步骤配置好 SSH,但 push 时仍然弹用户名密码,要立刻回头看一眼远程地址。大概率你还在用 HTTPS 地址,命令git remote -v会立刻暴露这个问题。把 HTTPS 地址改成 SSH 地址即可。

类似的还有另一种情况:remote 地址是对的,但 Git 配置里设置了url.*.insteadOf强制改写 URL,比如某些工具会自动把 SSH 地址改写成 HTTPS 地址,导致 push 时走了错误协议。遇到这种“明明地址没问题却总是走 HTTPS”的诡异情况,检查全局 Git 配置:

git config --global --list

看到url."https://github.com/".insteadOf一类的规则时,删掉或跳过即可。

6.5 排查思路的正确打开方式

我自己排查这类问题时会遵循“由近到远”的顺序:先看本地密钥和权限,再看 SSH 连接测试,最后看 remote 地址和 GitHub 侧配置。这样一步步排除,比直接搜一个报错然后按别人的代码抄要靠谱得多。很多时候同一个报错信息可能有完全不同的成因,生搬硬套可能越改越乱。

排查时可参考下面这个速查表:

现象最可能原因第一排查动作
Permission denied (publickey)公钥未添加或私钥不对ssh -T git@github.com查看认证结果
Bad owner or permissionsSSH 配置文件权限过宽检查并调整 config 文件权限
Repository not found无仓库访问权限确认账号与仓库权限关系
仍提示输入密码远程地址还是 HTTPSgit remote -v检查
连接超时或卡住网络问题或端口被封尝试ssh -p 443 git@ssh.github.com

关于最后一行,这里额外说一句:如果默认的 22 端口在你的网络环境下不通,GitHub 还支持通过 443 端口走 SSH。把.ssh/config里加一段:

Host github.com HostName ssh.github.com Port 443 User git

很多网络环境下的连接问题能就此解决,这也算是 SSH 推送时一个很实用的后手方案。

7. 从 HTTPS 迁移到 SSH 时的老仓库处理细节

7.1 迁移时千万别忘了本地分支跟踪关系

有些仓库用 HTTPS 已经用了很久,远程分支跟踪关系已经建立好了。改成 SSH 地址后,git push依然能正常工作,因为git remote set-url只改动远程地址,不影响 branch 的 upstream 配置。但有一点需要注意:如果你之前在 HTTPS 模式下设置过credential.helper,改成 SSH 后这个配置虽然无用,但也不会影响 SSH 认证,为了保持整洁可以执行:

git config --unset credential.helper

7.2 迁移完之后建议验证一下

改完地址后,我建议执行一次:

git fetch

确保能正常拉取。然后再做一次小改动,git push验证推送。不要在改动一堆代码后才发现推送不了,那种情况下的排查成本会成倍上升。

7.3 为什么我不推荐删掉旧仓库重新克隆

很多人图省事,直接rm -rf整个目录再git cloneSSH 地址。这样操作虽然也能达到目的,但如果本地有未提交的改动、stash 内容、本地分支或者标签,这些都会丢得一干二净。用git remote set-url切换只需要 10 秒,没有任何副作用,所以别用那种“推倒重来”的方法。

8. 一些关于 SSH 推送的细节习惯和避坑心得

8.1 提交信息里的账号不归 SSH 管

这里要特别提醒一个容易误解的点:SSH 只负责传输和认证,GitHub 上最终显示的 commit 作者,取决于你本地 Git 配置的user.name和user.email,跟 SSH key 没有直接关系。也就是说,即使你用 A 账号的密钥提交,只要本地 Git 配置写的是 B 的邮箱,commit 在 GitHub 上也会显示成 B 提交。

这会导致一个很反直觉的结果:你明明用私钥 A 通过了 SSH 认证,但 push 上去的 commit 却可能以 B 的身份展示。原因在于 GitHub 通过 commit 里的邮箱来关联用户。如果邮箱没有关联到 GitHub 账号,commit 会显示成独立的小头像。解决方式是在每个仓库或全局配置里设置期望的邮箱:

git config --global user.email "你的邮箱" git config --global user.name "你的昵称"

或者使用 GitHub 后台提供的noreply邮箱来避免暴露真实邮箱,GitHub 在 Settings -> Emails 页面能看到这个地址。

8.2 不要随意分享你的公钥文件

公钥本身是可以在网络上公开的,但很多教程会把公钥文件内容直接贴到论坛里,这种做法在绝大多数情况下没有实质风险,却容易误导别人认为“公钥都不能给别人看”。真正需要严格保密的是私钥文件,也就是~/.ssh/id_ed25519这个没有.pub后缀的文件。如果哪天怀疑私钥泄露,立即在 GitHub 后台删除对应公钥并重新生成密钥对,这是最稳妥的处理办法。

8.3 用好 SSH key 的备注信息

生成密钥时-C参数后面写的注释会跟随公钥一起出现在 GitHub 的 key 列表里。如果当时没写清楚,后面在列表里看到一堆“无意义注释”的 key,很难分辨哪台设备在用。我建议把注释写成“设备名+用途+时间”的组合,比如mbp-2025-personal-main。这样管理多个密钥的时候会省很多心。

8.4 连接超时不一定是你配置的问题

有一类问题是“配置完全正确,但就是连不上”。如果你的ssh -T git@github.com卡了很久才超时,那多半不是配置问题,而是网络原因。这时候可以试试 443 端口的方案,我在 6.5 小节里已经写过,这里就不再重复。另外,在某些公司网络环境下,也会对 SSH 连接做限制,这种情况只能换网络环境或者走 HTTPS 推送,没有太多技巧。

8.5 避免在 Windows 上使用不完整的 SSH 环境

Windows 上的 Git 安装包通常会自带 OpenSSH 组件,但如果你的系统 PATH 里同时存在多个 SSH 版本,可能出现签名算法不匹配、密钥路径不对等诡异问题。解决方式是用git config --global core.sshCommand明确指定要使用的 SSH 命令路径,例如:

git config --global core.sshCommand "C:/Windows/System32/OpenSSH/ssh.exe"

这个问题不常见,但一旦出现很难排查,提前指定 SSH 实现可以省去很多麻烦。

9. 推送完成后,这些扩展操作值得尝试

9.1 用 SSH 直接拉取私有仓库到服务器

SSH 配置好之后,不只适用于 GitHub,也适用于其他托管平台,比如 Gitee、GitLab、Gitea 等。只要把公钥添加到对应平台,就能实现免密克隆。实际开发中,我经常在云服务器上执行git clone git@github.com:xxx/xxx.git,整个过程不需要在服务器上保存任何明文密码,安全性好得多。

9.2 结合 git hooks 做提交前检查

SSH 推送本身不涉及 hooks,但推送前后可以借助 Git 的 pre-push 钩子做检查,比如单元测试、代码格式化、禁止直接推送到 main 分支等。这也算是“推送”这个动作的延伸价值。我目前的一个小型项目里,就用 pre-push 钩子自动跑ruff的代码检查,如果不过就拒绝推送,帮助我避免把低质量代码推到远程。

9.3 给不同目录配置不同的签名密钥

进阶用户还可以考虑用 Git 2.x 以上的includeIf配置,按目录自动切换 Git 的user.email和 SSH 签名设置。例如:

[includeIf "gitdir:~/work/"] path = ~/.gitconfig-work

这样只要在~/work目录下操作,Git 就会自动加载工作用的配置,推送时也不会弄混身份。这一招特别适合工作和个人项目在同一台电脑上管理的情况。

10. 最后再分享一个小技巧

不要只在遇到问题的时候才检查 SSH 配置。我建议每隔一段时间,比如一个月,主动执行一次ssh -T git@github.com,确认认证状态正常。如果输出里出现了陌生的用户名,说明本地 SSH 配置可能被改过,或者有多个密钥在互相干扰,尽早发现能避免后面 commit 归属错乱的麻烦。

还有一个小细节:很多人喜欢把私钥文件直接放到~/.ssh/下但忘了设置权限,这在 Linux 和 macOS 上会导致 SSH 直接拒绝加载。所以每次新生成密钥后,记得执行chmod 600 ~/.ssh/id_ed25519。Windows 上虽然没有这个报错,但在使用 Git Bash 时同样建议手动收一下权限,防止其他软件意外读取私钥。

从我个人的实际操作体会来看,SSH 推送远程仓库这件事,真正难的不是那几条命令,而是对整套认证机制建立清晰的认知框架。一旦理解了“私钥签名、公钥验证”这个核心逻辑,之后遇到各种奇怪的认证报错,都能沿着方向一步步排查,而不是盯着错误提示干着急。希望这篇内容能帮你省下几个小时的折腾时间,下次在新环境配置 GitHub 时,直接照着步骤十分钟搞定。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询