远程仓库、SSH公钥、git push、git pull,这串关键词看着像基础课,可真正上手时,不少人卡在公钥配好了仍在提示输密码、仓库建好了push却失败、分支名对不上这种小地方。这篇文章就按我平时从零搭一个项目仓库的真实顺序来写:注册账号、生成并配置SSH公钥、新建远端仓库、初始化本地仓库、第一次push、日常pull。我用的是某个访问稳定的国内代码托管平台,流程和主流平台基本一致,你把其中的域名换成自己常用的那家就行。
这条链路并不长,但每两个环节之间都可能藏着一个隐蔽的问题。与其百度一条命令、复制一个结果,不如把整个流程从原理到操作完整过一遍,后面遇到报错也知道去哪看。
1. 项目标题拆解:六个关键词其实是一条完整流水线
1.1 从注册账号到git pull,依赖顺序比命令本身更重要
标题里的“注册账号、配置SSH公钥、新建远端仓库、初始化本地仓库、git push、git pull”不是六个独立动作,而是一条有先后依赖的流水线:先有平台账号,才能配置公钥;先把远端仓库建出来,本地仓库才有地方推;只有推上去过,pull才有意义。
我见过不少新手把顺序完全倒过来:先在本地git init,commit完之后才去注册账号,回来还要临时生成公钥。虽然最终也能跑通,但中间会多出很多无意义报错,比如“repository not found”“Permission denied”这类问题,有时根本不是命令写错,而是步骤提前了。
所以我建议的依赖关系是:
- 注册账号,拿到身份标识;
- 生成SSH密钥并把公钥给平台,解决免密认证;
- 在平台上新建一个空仓库,作为远端存储;
- 在本地初始化仓库并完成commit;
- 把本地仓库关联到远端;
- 用git push上传,之后再用git pull同步。
这一步一步之间有明确因果:没有公钥,push时平台不认识你;没有远端仓库,push时没有目标地址;没有本地commit,push时也没有提交内容。标题把这六个动作放到一起,本质上就是要你完整打通这套闭环。
1.2 六个动作背后要解决的三类问题
这六个关键词背后,其实只有三个问题:
- 身份问题:平台怎么确认你这台电脑是本人?靠账号加SSH公钥;
- 存储问题:代码放在哪里?远端仓库负责集中存放,本地仓库负责版本管理;
- 同步问题:如何把本地改动送上去,把远端改动拿回来?靠git push和git pull。
身份问题最容易被忽略。很多人第一次用平台时想直接用账号密码操作,觉得简单,但实际用起来很痛苦:每次push都需要输密码,遇到token过期还要重新申请。SSH公钥配置一次之后,本地电脑就成为“可信设备”,平台凭公钥辨别你,私钥则始终留在本地。
存储问题相对好理解。如果把本地仓库比作个人笔记本,远端仓库就是一个公共资料柜。你从笔记本上复印文件放到资料柜里,别人再从资料柜拿走,这就是push和pull的关系。没有远端仓库,本地做得再多也只是一个单机版本管理。
同步问题则贯穿日常开发。push是把本地新增提交上传,pull是把远端新提交抓到本地。这里容易踩坑的地方在于冲突处理,同时也是Git命令最容易报错的区域。
1.3 这篇内容适合谁看,能帮你避掉哪些坑
如果你是第一次接触代码托管平台,这篇文章可以当作一份可直接照抄的操作手册;如果你已经用过一段时间,但还在用HTTPS方式输密码,我更建议你把SSH公钥这套配好,省下的时间远比你配置花掉的时间多。
我最想帮你避开的坑有三个:公钥复制错文件、第一次push时远端有残留文件导致历史不匹配、以及分支名不一致造成的困扰。这三种问题几乎每个初学者都会遇到,而且百度出来的答案往往夹杂着各种误导操作,比如让你直接git push -f覆盖远端,这在协作项目里非常危险。
2. 为什么推荐SSH公钥而不是账号密码:原理与方案选型
2.1 SSH公钥和私钥到底怎么配合
SSH是一种登录协议,核心要解决的问题是:如何在不传输密码的情况下,证明“你是你”。它靠的是一对非对称密钥:公钥和私钥。
我习惯用一个锁和钥匙的类比来解释:公钥是一把挂在门上的锁,平台拿到你的公钥后,会在服务器端“装好这把锁”;私钥是你手里的钥匙,永远不出现在网络上。push或pull时,本地Git会用私钥对请求进行签名,服务器收到后拿你之前给的公钥去验签,通过就说明这把钥匙确实配这把锁。
这个设计最妙的地方在于,私钥全程不会离开你的电脑,服务器上也只存放公钥。即使平台某个服务器出问题,拿到的人也无法逆推出你的私钥,安全性远高于来回传输密码。
在配置SSH公钥时,你本地会生成两个文件:一个叫id_ed25519,这是私钥;另一个叫id_ed25519.pub,这是公钥。请务必记住,粘贴到平台上的永远是.pub那个文件里的内容。私钥一旦泄露,你应该立刻删除平台上的公钥配置并重新生成一对,而不是继续使用原来的私钥。
2.2 HTTPS和SSH方式怎么选
代码托管平台一般提供两种远端地址:一种以https://开头,另一种以git@开头。
HTTPS方式配置简单,浏览器能直接打开;但每次push时都需要身份验证,要么输入密码,要么配置个人访问令牌。SSH方式配置稍复杂,门槛在于生成密钥对和上传公钥,但一旦完成,之后就再也不用手动输入密码,脚本化操作更顺畅。
两者之间的取舍,我一般建议:
| 对比项 | HTTPS方式 | SSH方式 |
|---|---|---|
| 第一次配置 | 无需额外操作 | 需要生成密钥并对公钥上传 |
| 日常push | 需要密码或token | 免密 |
| 安全性 | 依赖密码强度 | 基于非对称加密 |
| 适合场景 | 偶尔操作、环境受限 | 日常开发、命令行重度用户 |
如果你只想推几个公开练习项目,HTTPS也能接受;但如果你每天都和仓库打交道,SSH几乎是必选项。登录平台后,尽量把默认远端地址改成git@开头的SSH格式。
2.3 为什么注册账号要在配置SSH之前完成
注册账号这件事本身不难,但有几个前置细节直接影响后面的体验。
首先,账号名会决定你仓库URL的一部分。很多平台上,仓库地址格式是git@域名:用户名/项目名.git,账号名起得好不好,直接影响别人记不记得住你的项目地址。其次,平台会把SSH公钥和账号绑定,不先注册,公钥无处安放。
此外我还建议注册后立刻开启两步验证。虽然SSH公钥本身已经够安全,但开启两步验证可以防止有人在网页端盗用你的账号,修改公钥配置或删除仓库。这个操作基本不花时间,收益却很高。
3. 注册账号与SSH公钥配置全流程
3.1 注册账号时的实操细节
注册流程很简单:打开平台首页,点击注册,填写用户名、邮箱、密码,然后收邮件验证。但有几个小细节我可少提醒一下:
用户名尽量用英文小写和数字组合,这样生成的仓库地址干净好记。邮箱必须是你经常使用且能正常收信的邮箱,因为之后很多通知、找回密码、验证操作都靠它。密码不要和平台之外的网站重复,Git仓库里包含的可能不只是代码,还有你无意中提交进README里的敏感信息。
注册完成之后,先别急着建仓库,而是进入个人设置页面,确认“用户名”这一栏显示的是你想要的字符。因为一旦提交了项目,改用户名会导致旧链接全部失效,虽然平台一般会做重定向,但还是提前确认更省心。
3.2 用ssh-keygen生成密钥对
生成SSH密钥对在Windows、macOS、Linux上都可以直接使用系统自带的命令行工具。我强烈建议直接用ed25519算法,而不是旧的RSA:
ssh-keygen -t ed25519 -C "me@example.com" -f ~/.ssh/id_ed25519参数作用:
-t ed25519:指定密钥算法为ed25519,比RSA短、快、安全;-C "me@example.com":注释信息,通常填你的邮箱,CPU用它识别密钥用途;-f ~/.ssh/id_ed25519:指定密钥保存路径和文件名,不填默认就是这个路径。
执行后终端会问你是否设置passphrase。这个passphrase是保护私钥的额外密码,建议设一个。如果私钥被别人拷走,没有passphrase也基本用不了。缺点是你每次使用密钥时可能要输入,不过后面可以通过ssh-add配置免输。
执行完命令后,到~/.ssh目录下看,应该能看到两个文件:id_ed25519和id_ed25519.pub。有这两个文件才能继续。
3.3 复制公钥并粘贴到平台
复制公钥时最容易出错的是把私钥内容当成公钥贴上去。请时刻记住,.pub后缀的是公钥。
查看公钥内容:
cat ~/.ssh/id_ed25519.pub在macOS上可以一键复制到剪贴板:
pbcopy < ~/.ssh/id_ed25519.pub在Windows PowerShell里则可以这样:
type $env:USERPROFILE\.ssh\id_ed25519.pub | clip登录你的代码托管平台,进入“个人设置”或“账户设置”,找到“SSH公钥”或“SSH Keys”入口。把复制到的内容粘贴到输入框里。公钥内容是一整行以ssh-ed25519开头、以注释邮箱结尾的字符串,别漏掉任何开头或结尾的字符。
公钥的“标题”或“名称”一栏可以随意填,比如“work-laptop”“home-mac”。这个名字只用于将来管理多台电脑时区分密钥,不影响连接。保存之后,平台就锁定你的公钥了。
3.4 用ssh命令验证是否配置成功
配置完公钥后,不用急着建仓库,先验证一次连接:
ssh -T git@域名这里的域名换成你注册平台对应的SSH域名。如果连接成功,你会看到类似下面这样一行提示:
Hi username! You've successfully authenticated, but XXXX does not provide shell access.看到Hi username就说明公钥生效了。如果你的终端提示需要输入密码,或者返回Permission denied (publickey),大概率是公钥没粘贴完整,或者粘贴的是私钥内容。这时需要回到平台,重新编辑公钥内容再试一次。
这里多说一句:SSH连接验证可能会弹出一条关于known_hosts的确认信息,问你是否继续连接,你要输入yes回车。很多新手在这里犹豫,以为输错了,其实按yes确认即可。
4. 新建远端仓库并初始化本地仓库
4.1 在平台页面新建远端仓库的正确姿势
平台注册好后,点击“新建仓库”或“New Project”,会进入一个表单。项目名称建议用短横线分隔的小写英文,比如my-blog、user-service,方便后续在URL里直接引用。
这里最关键的一个决定是:是否让平台自动生成README文件。
如果你打算从本地初始化仓库再推送已有代码,我建议选择“不生成README”“不添加.gitignore”“不添加license”。原因是:远端仓库若已经有一个README文件,本地仓库又是一个全新的提交历史,两个历史完全没有关联,第一次push时必然报“fetched this remote”之类的错误。让远端保持干净,后面操作会顺畅很多。
同时推荐把仓库可见性设为“私有”。很多人习惯把所有练习项目设成公开,想着以后展示方便,但公开仓库意味着任何人都能看到你的代码,有时还会被自动化工具扫描。稳妥起见,练习项目先私有,确定要开源时再改公开。
4.2 在本地初始化一个仓库
打开终端,进入准备放项目的目录。我先建一个空目录并进入:
mkdir my-blog cd my-blog然后执行:
git init执行完之后,目录里会多出一个隐藏的.git文件夹,这个文件夹就是本地仓库的版本数据库。今后所有commit、branch信息都存在里面。
接着创建项目文件。假如这里你写了一个README.md,需要先把它加入暂存区再提交:
git add README.md git commit -m "init commit"git add把文件放进暂存区,git commit把暂存区内容固化成一次版本快照。很多人会在第一次提交时忘记配置用户信息,于是Git报错,提示需要user.name和user.email。在提交前最好先检查一遍:
git config --global user.name "username" git config --global user.email "me@example.com"这里的用户名和邮箱会记录在提交历史上,尽量和平台注册信息保持一致,别人看历史时才知道作者是你。
建议在项目里提前写好.gitignore文件,把系统临时文件、日志、依赖目录等排除在版本管理之外,否则一个git add .会把大量无关文件塞进仓库,后面清理起来极其麻烦。
4.3 把本地仓库关联到远端仓库
回到平台新建好的仓库页面,页面上一般会提供两类地址:HTTPS地址和SSH地址。这里请复制git@开头的SSH地址,例如git@域名:username/my-blog.git。
在本地仓库里执行:
git remote add origin git@域名:username/my-blog.gitorigin是Git约定俗成的默认远程仓库别名,你可以改成任何名字,但我建议保持origin,因为绝大多数文档和命令默认它。
执行完后,用git remote -v确认地址是否正确:
git remote -v你应该看到两个输出:一个fetch地址和一个push地址,内容都是刚才添加的那条SSH地址。如果地址写错了,可以用git remote set-url origin 正确地址来修改,不用删了重加。
4.4 第一次push:把本地提交推上远端
关联完远端后,第一次push需要带上-u参数:
git push -u origin main这里的main是当前分支名。如果你的默认分支是master,就写成git push -u origin master。-u的作用是设置上游分支,告诉Git:本地main分支对应远端origin/main分支。设置成功之后,以后再push只需输入git push或git push origin main即可。
如果一切正常,终端会显示推送进度,最后出现类似main -> main的输出。这时刷新平台仓库页面,应该能看到你的代码了。
第一次push常见的失败有两种:一是SSH公钥问题,二是远端和本地历史不关联。如果是后者,请看第6章,不要贸然使用git push -f。
5. 日常开发中的git push与git pull
5.1 push的三步走和常用参数
配置好第一次push后,日常push不变的是“三步走”:
git add . git commit -m "commit message" git pushgit add .把当前目录所有改动加入暂存区,git commit生成一次本地提交,git push把这次提交推送到远端。这里最容易忽视的是提交信息质量。fix bug、update这种提交信息在单人项目里勉强能看,但在多人项目里会对查看历史造成极大困扰。建议用“动词+对象+原因”的格式,比如“修复登录接口空指针导致用户无法登录”。
push的参数里,我强烈建议你熟悉--force-with-lease而不是-f。git push -f会无条件覆盖远端分支,而--force-with-lease会在确认远端没有别人新提交后才执行覆盖,相当于给强制操作加了一个安全锁。需要覆盖历史时,优先用:
git push --force-with-lease origin main5.2 pull的本质是fetch加merge
很多人以为git pull是“把远端代码下载到本地”,这个说法不准确。准确地说,git pull内部做了两件事:先git fetch把远端提交拉到本地暂存区,再git merge把它合并到当前分支。
所以完全可以用两个命令替代一次pull:
git fetch origin git merge origin/main理解这点后就会明白:为什么有时git pull会产生一个“merge commit”,为什么本地会有分叉历史。如果你希望历史更干净,可以使用git pull --rebase,它不会生成额外合并节点,而是把本地提交“搬到”远端最新提交后面。不过rebase也有代价,它会改写本地提交时间线,还没有完全掌握前,先在个人分支上用。
日常操作中,我建议在pull前先确认自己没有未提交的修改。可以执行git status,如果显示工作区不干净,先commit或stash再pull,否则可能遇到“local changes would be overwritten”的报错。
5.3 多远端仓库的同步思路
有时同一个项目需要推送到两个不同的远端仓库,比如一个主仓一个备份仓。我的做法是添加新的remote名称:
git remote add backup git@域名:username/my-blog.git git push backup main这样本地仓库同时关联了origin和backup两个远端,推送时通过名字区分。也有人希望一个git push同时推送多个远端,这时可以给同一个命名添加多个URL,但日常维护容易混乱。我倒是建议不要过度自动化,显式推送两条命令,至少能让自己知道你在干什么。
6. 常见问题与排查技巧实录
6.1 Permission denied (publickey) 到底卡在哪
这是SSH方式最高频的报错。遇到这行,按以下顺序逐步排查:
ls ~/.ssh:确认有没有id_ed25519和id_ed25519.pub两个文件;cat ~/.ssh/id_ed25519.pub:确认公钥内容是以ssh-ed25519开头的一整行;- 回到平台“SSH公钥”页面:确认粘贴的字符串和本地公钥完全一致,有没有多空格、丢字符;
- 执行
ssh -T git@域名:确认是否出现Hi username提示。
还有一个常见坑是私钥未被SSH agent加载。执行:
ssh-add -l如果提示The agent has no identities,需要先把私钥加入:
ssh-add ~/.ssh/id_ed25519另外,如果你同时维护了多个SSH密钥,还要检查~/.ssh/config里的Host配置,确保当前仓库域名指向了正确的私钥文件。否则Git会默认使用第一个私钥去连接,导致平台不认识你。
6.2 第一次push提示“rejected: Remote contains work”
这个报错往往是因为平台新建仓库时勾选了“初始化README”,导致远端多了一个本地没有的提交。两个仓库就像两本平行的历史书,Git不敢自动合并。
正确做法有两种:
- 如果你确认本地已经是完整内容,可以用
git pull origin main --allow-unrelated-histories,把远端初始README合并进本地,再push; - 如果远端只是空壳仓库,里面什么都不需要保留,可以推的时候加
--force-with-lease,把远端那个孤立提交覆盖掉。
我建议先试第一种,因为它更安全。
6.3 分支名是main还是master
很多平台默认新仓库分支名为main,但早期版本和部分旧习惯仍用master,push时就会遇到src refspec main does not match any。
处理方式是在本地把分支重命名:
git branch -M main然后再push:
git push -u origin main如果远端仓库已经有了master分支,本地又不想改名,可以用:
git push origin main:master把本地main分支推送到远端master分支。这种映射写法偶尔应急可以,长期用还是建议统一分支名,减少认知负担。
6.4 每次都提示输入用户名密码,哪怕配置了SSH
如果你已经上传了公钥,push时却仍然提示输入用户名和密码,八成原因是当前仓库的remote地址还是https://格式。
执行:
git remote -v如果看到的是https://开头,说明Git走的是HTTP方式,SSH公钥当然派不上用场。改成SSH地址即可:
git remote set-url origin git@域名:username/my-blog.git改完后再次push,应该就不再询问密码了。
6.5 连接超时或无法访问
SSH连接超时有时不是密钥问题,而是网络环境因素。遇到这种问题,先检查能否用浏览器打开平台页面,如果页面也打不开,说明是网络层面的问题,先去解决网络连通性。
平台一般在SSH服务上支持443端口的连接。如果你的网络环境拦截默认22端口,可以尝试切换为ssh.github.com这类443端口地址。具体写法要根据你使用的平台而定,大致是:
ssh -T -p 443 git@ssh.平台域名很多主流平台都提供类似方案。如果实在连不上,带着HTTPS方式继续用也不是不行,总比阻塞整天强。
写在后面:我的一个习惯
最后分享一个我自己调整过的操作习惯:新建项目时,永远先在本地init并commit一次,再上平台建空仓库。
这个顺序也许和平台页面引导相反,但能逼我在上手代码前就搭好项目结构,也避免了远端自动生成README带来的历史分叉问题。等你哪天把“先看remote属性、再跑push、拉不动就先fetch单看差异”变成肌肉记忆,这套流程就算真正吃透了。