很多人在用Git和GitHub Desktop的时候都遇到过这个场景:用HTTPS方式clone或者push,终端里反复弹窗要输入用户名和密码,一旦开启了双重认证还得去生成Personal Access Token,粘来粘去非常麻烦;换成GitHub Desktop倒是能记住账号,但偶尔也会莫名弹出一个授权窗口,点了又报错。折腾一圈下来,仓库还没提交几次,时间全耗在认证上了。
这篇文章我把整个流程重新捋了一遍,从零开始讲怎么用SSH方式把你本地环境、Git命令行、GitHub Desktop三者串起来。配置完成后,clone、pull、push全程免密,不用再反复输入账号密码,也不用手动去填token。文章适合刚接触Git的初学者,也适合已经用了HTTPS一段时间、想彻底切换到SSH的老手。我会把每一步为什么要这么做、底层发生了什么、踩过的坑是什么都交代清楚,你照着做基本一次就能通。
1. 先说清楚:SSH到底是什么,为什么值得折腾
1.1 HTTPS和SSH两种方式的区别
Git在访问远程仓库的时候,官方支持两种主流的传输协议,一个是HTTPS,一个是SSH。很多人在本地clone仓库的时候直接复制了网页上的HTTPS链接,默认就走到了用户名密码认证的路子上。
HTTPS的认证逻辑在本地环境里是一个“每次都要验证身份”的模式。如果你是普通账号,每次push都要输入用户名和密码;如果你开启了双重认证,密码还不好使,得去GitHub设置页面生成一个token,把这个token当成密码来用。这个token有过期时间,到期了又得重新生成。GitHub Desktop稍微聪明一点,它能帮你记住token,所以在桌面上操作感觉没那么烦。但只要你在终端里用git命令,或者在另一台电脑上操作,麻烦就全回来了。
SSH的方式完全不一样。它不靠用户名密码,而是靠一对密钥:私钥留在你本机,公钥放到GitHub账号里。本地请求连接的时候,GitHub根据公钥生成一个挑战,只有持有对应私钥的电脑才能解开。这个机制说白了一句话:只要你的电脑上有那把对的私钥,服务器就认你,全程不需要再输密码。
打个比方可能更好理解。HTTPS就是每次进小区大门都要掏身份证登记,保安一眼一眼地查;SSH是你去物业办了一张门禁卡,卡里写着你家的楼栋和门牌号,以后刷卡直接就进。第一次办卡稍微麻烦一点,但之后每次进出都省事。
1.2 为什么推荐SSH而不是继续用HTTPS
我个人的实际体验主要有三个理由。
第一是免密。配置好之后,Git命令行里的fetch、pull、push全程不弹窗。GitHub Desktop同样受益,它调用的是系统里的SSH能力,不需要再额外管理token。
第二是安全。私钥始终保存在你本地,不会在网络上传输入密码,也不会因为token在某个第三方工具里泄露而被人拿去乱推代码。你可以在GitHub账号设置里随时删除某台电脑的公钥,相当于吊销了那台设备的访问权限。
第三是跨平台一致。SSH的配置方式在Windows、macOS、Linux上都差不多。你在这台电脑上配置好了,换一台电脑,只要把私钥拿过去(或者重新生成一对),流程完全一样。而token在不同系统上的体验差异就比较明显,尤其Windows的终端复制粘贴token有时候还会多出换行符,导致认证失败。
当然,SSH也不是没有门槛。它的密钥生成、权限设置、ssh-agent管理这些概念,第一次接触的人容易懵。这篇文章后续的内容就是把这一步一步拆开,每一句命令都解释清楚。
1.3 准备工作:Git与GitHub Desktop的安装确认
在开始生成密钥之前,先确认你本地的环境是完整的,不然到后面经常会出现“命令找不到”这类低级问题。
如果你用的是Windows,强烈建议直接安装Git for Windows。这个安装包自带了一个Git Bash终端,后面的所有命令我都在Git Bash里执行。为什么不用系统自带的cmd或者PowerShell?因为Git Bash里模拟了Linux环境,很多命令(比如cat、ssh-keygen、ssh-agent)的路径和参数都是处理好的,直接能用,不用额外配环境变量。下载地址就是Git官网或者国内镜像站,安装的时候一路Next就行,唯一要注意的是安装过程中有一个选择编辑器、调整PATH环境变量的步骤,保持默认选项即可。
安装完之后,打开Git Bash,输入:
git --version能看到版本号就说明Git本体没问题。这个输出结果的版本信息后面排查问题的时候有用,比如有些老版本的Git在SSH密钥算法支持上会有差异,如果版本太旧(比如2.x早期版本),建议升级到最新稳定版。
GitHub Desktop的安装更简单,直接去GitHub Desktop官网下载对应平台的安装包。Windows版本装完之后会自动检测系统里的Git,不需要你手动指定路径。装好之后先用你的GitHub账号登录一次,这一步会引导你授权Desktop访问账号信息。需要提醒的是,Desktop登录用的是OAuth授权,和你后面配置的SSH密钥是两套东西,互不冲突,别混在一起理解。
另外,Windows用户还需要提前确认一个服务是否在运行,这个服务叫OpenSSH Authentication Agent,也就是ssh-agent。很多人在配置完密钥之后还是被要求输入密码,或者GitHub Desktop一直报错,问题就出在这个服务没启动。我在后面章节会专门讲它的启动方法,这里先有一个印象:SSH私钥不是直接裸给Git用的,它要先注册到ssh-agent这个“钥匙串”里,系统才能在你发起请求的时候自动取出私钥去完成认证。
2. 生成SSH密钥:这一步是整个配置的地基
2.1 用ssh-keygen生成你的专属密钥对
打开Git Bash,输入下面这条命令,然后一路回车:
ssh-keygen -t ed25519 -C "你的邮箱"注意,这里的邮箱建议写你注册GitHub账号时用的邮箱,这样密钥和账号之间有一个明确的对应关系,方便以后在GitHub后台辨认。不过严格来说,邮箱只是密钥的备注信息,写不写、写什么都没关系,它不会参与认证逻辑。
如果之前没有生成过SSH密钥,系统会提示你选择保存位置,默认路径是~/.ssh/id_ed25519。这里直接回车确认就行,不要自己改路径。改路径之后,后续的ssh-agent查找、Git的默认配置都要跟着改,徒增麻烦。如果之前生成过,系统会提示文件已存在,问你要不要覆盖。这时候要小心,如果你确实需要保留原来的密钥,就先把这个文件重命名备份,再生成新的。
接下来有一个设置passphrase的步骤,就是给私钥加一层口令保护。这里我建议初学者直接留空,按回车跳过。原因很简单:如果你设置了passphrase,每次使用私钥时都需要输入这个口令,虽然ssh-agent可以帮你记住一次,但在一些特殊场景(比如GitHub Desktop调用密钥)还是会弹出来,体验反而更繁琐。等你自己对SSH机制足够熟悉了,再考虑给私钥加口令也不迟。
生成成功之后,你会看到类似这样的输出,里面有一个像涂鸦一样的图案,那叫randomart image,是密钥指纹的图形化表示,用来让你快速辨认密钥是否被改动过,不用去解读它。
2.2 私钥和公钥:哪个能给别人,哪个绝对不能
很多新手在这一步就懵了:生成完之后~/.ssh目录下多了两个文件,一个叫id_ed25519,一个叫id_ed25519.pub,这俩到底有什么区别?
简单来说,id_ed25519是私钥,绝对不能泄露给任何人。它在文件系统里的权限应该是只有你当前用户能读写,Windows上Git Bash会自动处理,macOS和Linux上需要你手动检查一下权限是否符合要求。如果私钥被别有用心的人拿到,就相当于他拿到了你所有配置了对应公钥的服务器、GitHub账号的访问权。
id_ed25519.pub是公钥,这个就是要放到GitHub服务器上的内容。公钥可以放心给别人看,它的作用只是让服务器验证“你确实持有那把对应的私钥”,本身不含任何敏感信息。
用小区门禁来类比:公钥就是门禁系统里登记的你那张卡片的ID,私钥就是你口袋里的那张卡片。别人就算知道你的卡片ID,没有卡也刷不进来。而门禁系统验证的时候,并不是把整个卡号输过来对一遍,而是通过一系列数学运算,确认你手里的卡对应着登记过的那个ID。
2.3 把私钥注册到ssh-agent
密钥生成之后,还需要让本机的SSH客户端认识它。这个过程在Windows上分两步。
第一步,确保系统中OpenSSH Authentication Agent服务正在运行。在开始菜单里搜索“服务”,打开服务管理器,找到OpenSSH Authentication Agent,如果状态不是“正在运行”,就右键启动,并且把启动类型改为“自动”,省得每次开机都要手动来一遍。也可以通过PowerShell管理员模式执行下面的命令:
Get-Service ssh-agent Set-Service -Name ssh-agent -StartupType 'Automatic' Start-Service ssh-agent第二步,回到Git Bash,把私钥加载进agent:
eval "$(ssh-agent -s)" ssh-add ~/.ssh/id_ed25519第一条命令的作用是启动一个ssh-agent进程,并且把环境变量设置好。第二条命令把私钥加入agent的钥匙串里。如果你在前面生成密钥的时候设置了passphrase,这一步会要求你输入一次,之后就不用再输入了。
这里有个细节值得说一下:eval "$(ssh-agent -s)"中的eval是让shell执行命令的输出结果。ssh-agent启动后输出的是一堆环境变量赋值语句,比如SSH_AUTH_SOCK=...,通过eval才能让这些变量在当前终端会话中生效。如果你直接运行ssh-agent -s,你会看到一堆输出,但这些输出不会保留在你当前的shell环境里,后面ssh-add就找不到这个agent进程了。
配置完成之后,可以用下面的命令确认私钥已经加进agent里了:
ssh-add -l如果输出里有256 SHA256:...之类的指纹信息,就说明私钥已经在钥匙串里了。这一步是后面所有免密操作的前提,也是最多人忽略的一步。
3. 把公钥交给GitHub:给账号配上“门禁卡”
3.1 在GitHub网页端添加SSH公钥
到这一步,你本地的密钥对已经准备好了,接下来要做的就是把公钥内容贴到GitHub账号的对应位置。
先在Git Bash里查看公钥内容:
cat ~/.ssh/id_ed25519.pub输出是一行以ssh-ed25519开头的字符串,后面跟着一串编码,最后是你生成密钥时写的邮箱备注。把这整行内容完整复制下来。注意要复制完整,不要换行,不要缺字符,很多人卡在这一步是因为只复制了一部分。
然后登录GitHub网站,点击右上角头像,进入Settings。在左侧边栏找到SSH and GPG keys,点击右上角的New SSH key按钮。Title栏随便填一个能让你记住这台设备的名字,比如“My Windows Laptop”,Key栏粘贴刚才复制的公钥内容,最后点击Add SSH key。如果GitHub要求你确认密码,输入账号密码即可。
这里有一个小技巧:如果你有多台设备,每台设备单独生成一对密钥,然后给每台设备的公钥取一个容易辨认的名字。这样以后你在GitHub后台看到某个key异常,可以快速定位是那台机器出了问题,直接删除对应公钥,就能吊销那台设备的访问权限。
3.2 命令行验证:确认免密连接已经打通
公钥添加完成之后,先别急着clone仓库,先在终端里验证一下SSH连接是否正常。在Git Bash里执行:
ssh -T git@github.com如果你是第一次连接GitHub,终端会显示一条host key验证提示,大意是“无法确认github.com的真实性,是否仍要继续连接”。输入yes回车。这里输入的yes会记录在~/.ssh/known_hosts文件里,下次再连接就不会问了。
连接成功之后,GitHub会返回一段欢迎信息,类似下面这样:
Hi your-username! You've successfully authenticated, but GitHub does not provide shell access.看到这个提示,就说明本地的私钥和GitHub上的公钥已经匹配成功,你本地这台设备已经获得了GitHub账号的访问许可。
如果这个过程中报错了,最常见的报错就是Permission denied (publickey)。这个错误表示GitHub没有认可你提供的公钥信息。排查思路很简单:第一,确认刚才把公钥贴到了GitHub的Settings里,而不是某个独立仓库的设置里;第二,确认你贴的是.pub文件里的公钥,而不是id_ed25519私钥的内容;第三,确认ssh-agent里确实加载了私钥,用ssh-add -l再检查一遍;第四,确认你不是用sudo或者管理员身份运行Git Bash——有时候权限过高反而会让ssh找不到正确的文件路径。
4. Git仓库的SSH配置与下载上传实操
4.1 设置Git的用户名和邮箱
SSH连接打通了,接下来就是正式用Git操作仓库了。但先别急,在第一次提交之前,还要给Git配置最基本的身份信息。
执行下面两条命令:
git config --global user.name "你的名字" git config --global user.email "你的邮箱"这里的user.name和user.email会写入你每一次提交(commit)的元数据里,远端仓库的提交记录上会显示这个信息。它不是认证信息,不参与账号校验,但如果你用的是GitHub,建议把邮箱写成GitHub注册邮箱,这样你的提交可以直接关联到你的GitHub头像和主页。如果你不想暴露私人邮箱,可以在GitHub后台开启Keep my email addresses private功能,然后把生成的@users.noreply.github.com邮箱填在这里。
4.2 用SSH地址clone仓库:从这里开始就不需要密码了
现在找一个仓库来测试。打开GitHub上任意一个仓库页面,点击绿色Code按钮,再点击SSH标签页,你会看到仓库的SSH地址,格式是git@github.com:用户名/仓库名.git。复制这个地址。
在Git Bash里找一个合适的目录,执行clone命令:
git clone git@github.com:用户名/仓库名.git命令执行后,如果一切正常,你会看到终端开始下载对象,并且全程没有任何密码提示。这就是SSH生效的直接体现。
这里顺便说明一下,如果你之前已经用HTTPS方式clone过一个仓库,现在想切换到SSH,不需要重新clone一遍,只需要修改远程地址就行。进入那个仓库目录,执行:
git remote set-url origin git@github.com:用户名/仓库名.git然后可以用git remote -v确认一下当前远程地址,看到的是git@github.com:开头就说明切换成功了。
4.3 上传修改的完整流程:从add到push
clone下来之后,你可以在本地修改文件。为了演示完整流程,我以一个简单的场景为例:在仓库根目录创建一个新文件,然后把这个文件提交到远端。
第一步,查看仓库状态:
git status这一步我建议每次都先执行。它能让你看清当前仓库处于什么分支、哪些文件被修改过、哪些文件还没被跟踪。在终端输出里,红色文件名代表已修改还没暂存,绿色文件名代表已暂存还没提交。
第二步,把文件加入暂存区:
git add 文件名如果你有很多文件要提交,可以用git add .把所有修改都加入暂存区。但我不推荐add .,尤其是在多人协作的仓库里,很容易把一些本不该提交的文件(比如本地的配置文件、log文件)一起加进去。手动指定文件名更稳妥。
第三步,提交:
git commit -m "提交说明"这里-m后面的内容是这次提交的说明文字。好的提交说明应该清楚描述这次改动做了什么,比如“修复登录页面的按钮样式”或者“新增导出Excel功能”,不要写“更新”或者“修改”这种没有信息量的话。
如果在commit的时候报错,提示Please tell me who you are,说明你跳过了第4.1小节的用户名和邮箱配置,回去设置一下再commit就行。
第四步,推送:
git push origin main这里的origin是远程仓库的默认别名,main是分支名。如果你的默认分支叫master,那就把最后的main改成master,或者在仓库里用git branch查看当前分支名。
推送成功后,你会在终端看到类似这样的输出:
Enumerating objects: 5, done. Writing objects: 100% (3/3), 323 bytes | 323.00 KiB/s, done. Total 3 (delta 0), reused 0 (delta 0), reused 0... To github.com:用户名/仓库名.git abc1234..def5678 main -> main看到main -> main,说明本地的提交已经成功推送到GitHub远程仓库。
再补充一个场景:如果你编辑的是仓库里已经存在的文件,流程也是一样,git status查状态,git add暂存,git commit提交,git push推送。区别只是文件内容被修改保存后,git status会显示这个文件是modified状态,而不是untracked。
5. GitHub Desktop中的SSH配置与使用细节
5.1 GitHub Desktop与系统SSH的关系
有人可能会问:既然Git命令行已经配置好SSH了,GitHub Desktop还需要单独配置吗?
答案是:不需要单独配置,但前提是系统层面的ssh-agent必须正常运行。
GitHub Desktop的做法是,它不自带一套独立的SSH密钥管理系统,而是直接调用操作系统底层的SSH组件。在Windows上,它调用的是OpenSSH以及系统服务里的ssh-agent;在macOS上,它调用的是系统自带的SSH。这也就是说,只要你在命令行里已经把SSH配好了,Desktop就能天然复用这同一套密钥,不需要在Desktop的设置里做任何额外操作。
但这里有一个非常典型的坑:如果Windows的ssh-agent服务没有启动,你会发现终端里的git命令用起来一切正常,但在GitHub Desktop里clone或者push却一直转圈,最后报错说认证失败。原因就是你在某个终端里手动执行过eval "$(ssh-agent -s)",那个agent进程只在那一个终端会话里有效,Desktop启动之后根本找不到这个agent进程,自然也就拿不到私钥。解决方式就是我前面提过的,把Windows系统的OpenSSH Authentication Agent服务设为自动启动,让系统在开机时就运行一个全局的agent,这样不管是终端还是Desktop都能正常访问。
5.2 用GitHub Desktop完成clone和push操作
在Desktop里clone一个SSH仓库,有两种方式。
第一种,在GitHub网页上打开仓库,点击Code,选择SSH标签页,复制git@github.com:用户名/仓库名.git这个地址,然后回到Desktop,点击File菜单,选择Clone repository,在弹出的窗口里点击URL标签页,把地址粘贴进去,选择本地存储路径,点击Clone。Desktop会直接按照SSH地址克隆仓库到本地。
第二种方式更直接,在Desktop的主界面左侧点击Your repositories,列表里会显示你账号下所有仓库。但要注意,这个列表是通过GitHub API拉取的,它和SSH没太大关系,Desktop会在克隆的时候自动选择用HTTPS还是SSH。如果你发现用这种方式clone下来之后,Desktop里显示仓库正常,但你想在终端里push却需要密码,那说明仓库的remote地址被设成了HTTPS格式。这时候可以在终端进到那个仓库目录,手动把remote地址改成SSH格式,方法就是第4.2小节里说的git remote set-url。
clone完成之后,你在Desktop里对仓库的任何操作,本质上就是调用git命令。修改本地文件后,Desktop会自动检测到变化,左侧会显示文件列表和改动摘要。输入commit message,点击Commit to main,再点击Push origin,内容就上传到GitHub了。整个过程中,你完全不会感觉到SSH的存在,因为一切都是自动完成的。如果push失败了,Desktop会弹出错误提示窗口,点开详细日志,里面会有具体的报错信息,方便排查。
5.3 Desktop里的常见坑
第一个坑是密码短语问题。如果你在生成密钥时设置了passphrase,那么在GitHub Desktop首次使用SSH密钥时,可能会有两种情况:一种是Desktop直接弹窗要你输入这个密码短语;另一种是静默失败,Desktop一直转圈但没有任何提示。我遇到过的情况是Windows系统会在后台弹出一个类似“Enter passphrase”的小窗口,但焦点不在上面,用户根本没注意到。这个问题的根治办法就是前面说的,在生成密钥时留空passphrase,或者你已经设置了,就把私钥从ssh-agent里移除后重新添加,让它记住一次。
第二个坑是known_hosts文件权限问题。如果你在Windows上用过WSL或者虚拟机,或者手动复制过~/.ssh目录到别的电脑,known_hosts文件的权限可能不对。这会导致SSH连接时提示“Host key verification failed”。解决办法是删除~/.ssh/known_hosts中对应GitHub的那一行(或者直接删掉整个文件),下次连接时会重新询问并写入。
第三个坑是Desktop的默认仓库拉取方式。你通过GitHub Desktop的Your repositories克隆下来的仓库,它的remote地址不一定是你期望的SSH地址。去终端跑一下git remote -v看一下子最清楚。如果显示的是https://github.com/...,那说明Desktop用的是HTTPS。虽然Desktop内部有自己的凭证管理,你在Desktop里push可能是正常的,但终端里就会要求你输入账号。这种情况就是我上面说的,手动改一下remote地址,让终端和Desktop行为统一。
6. 常见问题与排查技巧实录
6.1 高频报错速查表
这里把我在配置过程中遇到过的、以及身边同事经常碰到的问题整理成一张速查表。
| 报错信息 | 原因 | 解决方法 |
|---|---|---|
| Permission denied (publickey) | GitHub没识别到你的公钥,或者私钥没加载进agent | 确认公钥已添加到GitHub;确认ssh-add -l能看到私钥;确认用的是.pub公钥内容 |
| Host key verification failed | known_hosts里没有对应主机的指纹记录,或指纹不匹配 | 输入yes确认连接;如果反复出现,删除known_hosts中对应行 |
| connect to host github.com port 22: Connection timed out | 22端口被防火墙屏蔽,或者网络环境不允许 | 用443端口方案,配置~/.ssh/config |
| Repository not found | 仓库不存在,或你没有访问权限 | 确认仓库地址正确;确认账号有访问权限;如果用公司网络,可能是端口问题 |
| Authentication failed | 远程地址还是HTTPS,或者token失效 | 修改remote地址为SSH格式;或改用SSH方式认证 |
6.2 22端口被封锁时,如何切换到443端口方案
有些网络环境(比如公司防火墙)会封掉SSH默认的22端口,这时候你会发现ssh -T git@github.com一直卡住或者直接超时。GitHub官方其实提供了一个备用方案:通过443端口访问SSH服务。
方法是创建或编辑~/.ssh/config文件,加入以下内容:
Host github.com HostName ssh.github.com Port 443 User git保存之后,测试一下连接:
ssh -T git@github.com如果走443能成功,后续的所有git操作都会自动走这个配置,不需要再改remote地址。这里有一个需要注意的地方:配置完这个之后,git@github.com:用户名/仓库名.git这种地址依然有效,因为Git会根据Host段匹配到config里对应的HostName和Port,实际连的是ssh.github.com的443端口,而不是直接连github.com的22端口。这个方案实测在很多受限网络环境下都能生效。
6.3 多账号、多密钥的config管理
有些人工作账号和个人账号分开,在两个GitHub账号下都有仓库;还有人同时使用GitHub、GitLab、Gitee,每个平台一套密钥。这时候如果还是用一个默认的id_ed25519私钥,就会发生冲突:你在甲平台上传了公钥,用乙平台的时候Git默认还是会拿同一把私钥去尝试,当然会被拒绝。
解决办法是多生成几对密钥,然后通过~/.ssh/config文件做区分。比如生成一个专门给工作用的密钥:
ssh-keygen -t ed25519 -C "工作邮箱" -f ~/.ssh/id_ed25519_work生成之后,在~/.ssh/config文件里增加这样的配置:
Host github-work HostName github.com User git IdentityFile ~/.ssh/id_ed25519_work配置完成之后,你在clone仓库的时候,如果用默认的git@github.com:用户名/仓库名.git会走默认密钥,如果用git@github-work:用户名/仓库名.git就会走id_ed25519_work这把私钥。注意这里Host部分改成自定义别名之后,Git会按照config里的HostName去寻找真实地址。
这个方案初看有点绕,但实际用起来很方便。它让你在一台电脑上同时管理多个账号的多个仓库,互不干扰,也不用来回更换密钥。
最后分享一个我自己的小习惯:每次配置完新机器,我都先跑一遍ssh -T git@github.com,确认返回的是successfully authenticated之后,再试着clone一个仓库测试全流程。如果卡在哪一步,就只排查那一步,不要东一下西一下地改配置。SSH的整个链路其实很清晰:本地私钥、agent注册、远程公钥、网络连通、仓库地址,五个环节逐一排除,问题总能定位到。按这个思路走,绝大多数配置问题能在十分钟内解决。