第一次把代码推上 GitHub,你大概率会撞上这几行红字
准备把本地项目推送到 GitHub,流程看起来就三步:git init、git add .、git push,结果刚敲完git push -u origin main,终端直接给你甩了两行报错:
error: src refspec main does not match any error: failed to push some refs to ...说真的,这不怪你。Git 的报错风格是出了名的“告诉你出事了,但不告诉你哪出事了”。我在带新人时,十个人里面至少有七个会卡在同一条报错上,而且很多人在此之前连“暂存区”“远程仓库”这些概念都没搞清,全靠复制粘贴网上的命令。这篇教程就是干这个的——从安装 Git 开始,到真正把代码推送上去,再到最头疼的报错排查,一条龙过一遍。
文章很啰嗦,但每一个命令我都会解释它“为什么存在”,而不是让你无脑抄。搞定之后,你不但能把项目推上去,还能顺手理解 Git 到底在背后做了什么。
1. 动手前必看:安装 Git 并完成身份配置
1.1 Windows / macOS / Linux 三平台安装 Git 的完整步骤
先说 Windows。新手最省事的方案是直接下载 Git for Windows 的安装包,它自带一个 Git Bash 终端,长得很像 Linux 命令行,后面所有 git 命令都建议在这个终端里敲,而不是在系统自带的 CMD 里敲。
安装过程一路 Next 基本不会出错,但有一步要留神,就是调整 PATH 环境变量的那个选项,建议选“Use Git from the Windows Command Prompt”。它的意思是把 git 命令注册到系统环境变量里,将来你在 VS Code、IDEA 这些编辑器里也能直接调 Git 命令。如果这里选错了,后面在编辑器里提交代码经常会报“git 不是内部或外部命令”的错。
macOS 最简单,在终端里敲git --version,系统通常会弹窗提示安装 Xcode Command Line Tools,按提示装完就有 Git 了。如果你习惯用 Homebrew,也可以执行:
brew install gitLinux 用户没什么好说的,Debian/Ubuntu 系就:
sudo apt install gitCentOS/RHEL 系就:
sudo yum install git装完之后记得验证一下版本:
git --version能看到版本号,说明安装成功了。如果提示找不到命令,Windows 用户重新打开一下终端,或者检查一下系统环境变量里有没有 git 的安装路径。
1.2 把用户名和邮箱配好,否则连提交都做不了
Git 装好之后,第一件正事不是建仓库,而是配置身份信息。这一步新手最容易漏,因为不配置的时候 Git 也不会提示你,只有等你第一次执行git commit的时候,它才会狠狠教育你:
Author identity unknown *** Please tell me who you are.配置就两条命令:
git config --global user.name "你的名字" git config --global user.email "你的邮箱"这里我想多解释一句:这个用户名和邮箱是 Git 的“署名”,会写进你的每一次提交记录里。将来你推送到 GitHub 后,GitHub 会读取这个邮箱来判断某条提交是不是你做的,然后把它关联到你的 GitHub 账号上。所以,邮箱最好填你注册 GitHub 用的那个邮箱,这样提交历史里的小绿点和头像渲染才正常。
--global这个参数的意思是“全局生效”,也就是这台电脑上所有 Git 仓库都默认用这个身份。如果你只有个人项目,用全局配置就够了。如果是给公司电脑配的,建议去掉--global,直接在具体项目里配置,避免将来给私人仓库提交时署上公司邮箱。
想确认配置是否生效,可以执行:
git config --global --list1.3 配置 SSH 密钥,免去每次输入密码的麻烦
身份配置好之后,建议直接顺手把 SSH 密钥也配了。GitHub 提供了 HTTPS 和 SSH 两种代码传输协议,HTTPS 推送代码时每次都要验证账号密码,虽然可以靠缓存解决,但远不如 SSH 一把钥匙走天下。
生成密钥的命令:
ssh-keygen -t ed25519 -C "你的邮箱"-t指定算法类型,ed25519是目前比较推荐的加密算法,密钥短、安全性高。如果你的系统太老不支持,可以退一步用rsa -b 4096。
命令执行后会问你要保存路径,直接回车用默认路径~/.ssh/id_ed25519就行,然后会问你设置 passphrase(密钥密码),嫌烦就直接回车留空。生成结束后,执行:
cat ~/.ssh/id_ed25519.pub把输出的内容从头到尾复制,那一长串以ssh-ed25519开头、以你的邮箱结尾的字符串,就是你的公钥。
接着登录 GitHub,按以下路径进入:右上角头像 -> Settings -> SSH and GPG keys -> New SSH key。标题随便填,比如填“我的电脑”,把公钥粘贴进 Key 文本框里,点保存。
验证是否配置成功:
ssh -T git@github.com如果看到类似Hi 用户名! You've successfully authenticated的提示,说明 SSH 通道已经通了。注意提示里的用户名是你 GitHub 账号名,不是你自己设置的身份信息。
提示:SSH 密钥是一把“钥匙”,公钥放在 GitHub 上,全世界都能看到;私钥留在本机的
~/.ssh/目录下,千万不能泄露,也不要提交到代码仓库里。
2. 把本地项目初始化成 Git 仓库,并做好第一次提交
2.1 git init 初始化仓库,README 为什么不建议省
假设你现在有一个本地项目文件夹,比如叫my-first-project,里面已经有一些文件了。想要让 Git 管理这个目录,就要在这个目录里初始化一个仓库:
cd my-first-project git init执行之后,目录下会多出一个隐藏的.git文件夹。别动它,里面装的是这个项目完整的版本历史、分支信息、配置参数。可以这么理解:.git文件夹就是 Git 的“记忆库”,所有提交记录都以对象形式存在这里。
接下来建议先写一个 README 文件。很多新手不理解 README 的意义,觉得“我的代码写得那么清楚,别人自己看代码不就行了?”——并不是。README 是仓库的门面,GitHub 会默认把它渲染在仓库首页最显眼的位置。别人点进你的项目,第一眼看的不是代码,而是 README。哪怕只是简单写一句“这是一个学习 Git 的示例项目”,也好过让访客对着空文件列表发愣。
创建方式很随便,在项目根目录新建README.md,写点内容。我习惯的格式是:
# my-first-project 这是一个用于学习 Git 推送流程的示例项目。2.2 用 .gitignore 拦掉垃圾文件,避免仓库膨胀
不少新手第一次提交时图省事,上来就是一个git add .,然后把自己的 node_modules、target、.idea 之类的几百 MB 文件一股脑推上去了。结果仓库体积瞬间膨胀,克隆项目的时候等半天,协作的同事恨不得顺着网线过来找你。
这些文件全部应该用.gitignore拦住。.gitignore是 Git 的“屏蔽名单”,里面写的是你不想被 Git 跟踪的文件或目录名。它的作用范围包含当前目录和所有子目录。
我随便写一个通用模板,十几行就够覆盖大多数场景:
# Node.js node_modules/ dist/ build/ # Java target/ *.class # IDE .idea/ .vscode/ *.iml # 系统文件 .DS_Store Thumbs.db # 日志/临时文件 *.log *.tmp重点说一句:.gitignore要写得早,最好在第一次git add之前就写。因为已经提交过的文件,就算你事后把它加进.gitignore,Git 依然会继续跟踪它。真要处理这种误提交,需要先执行git rm -r --cached node_modules把文件从索引里移除,这会多出一堆无谓的麻烦。
2.3 第一次 git add 与 git commit:先弄懂“暂存区”再动手
好,项目目录准备好了,.gitignore也写了,现在终于可以准备提交了。但在敲命令之前,我强烈建议你先把三者的关系搞清楚:
- 工作区:就是你电脑里看得见摸得着的项目文件。
- 暂存区:Git 内部的一个临时区域,用来放你“本次准备提交”的文件。
- 本地仓库:提交完成后,文件快照会永久记录在这里。
用生活化的类比就是:工作区是你逛超市时手里的购物车,暂存区是你已经挑好、放进袋子的商品,本地仓库是在收银台付完钱打包好的包裹。git add是把商品装进袋子,git commit是付钱打包,git push才是把这包东西寄出去。
按这个逻辑,第一批文件就该这么加:
git add .git add .的意思是添加当前目录下所有未忽略的文件到暂存区。如果你想精准添加,也可以git add README.md。然后看一下当前状态:
git status如果显示类似Changes to be committed的列表,说明文件已经进入暂存区了。接着执行提交:
git commit -m "初始化项目结构"-m后面跟的是本次提交说明。提交信息的质量和你的 Git 水平直接挂钩,新手最容易犯两个毛病:一是空话连篇,比如 “update” “fix”;二是把一堆改动塞到一条提交里。正确的姿势是:动词开头、简洁、说清做了什么。比如 “修复登录页面按钮无法点击的问题”,别人一眼就懂。
提交完可以看一眼提交历史:
git log --oneline如果这里能看到你的提交记录,说明本地仓库已经万事俱备,只差“推送”这临门一脚了。
3. 在 GitHub 上创建远程仓库,并和本地建立连接
3.1 网页端快速创建仓库,这 3 个选项别选错
本地仓库就绪后,翻到 GitHub 网页端,右上角找到那个“+”号,点击 New repository,填写信息。
第一个要填的是仓库名(Repository name)。命名建议全部小写,单词之间用连字符-分隔,比如my-first-project。大写字母和长下划线虽然在技术上合法,但社区约定俗成不这么用。
第二个是可见性(Visibility)。Public 是公开的,所有人都能看;Private 是私有的,只有你能看。新手如果只是练手,选 Private 完全没问题,以后想公开随时可以改。
第三个关键是网页会自动让你选是否创建 README 文件、.gitignore 或 License。请你一定、一定、一定不要勾选Add a README file。原因很简单:你本地已经有了一个 README.md,如果远程再自动生成一个,两个仓库的提交历史就对不上了,推送时必然报failed to push some refs。我们宁可等代码推上去之后再在网页端补文件,也不要在这里埋雷。
创建完成后,GitHub 会展示一个纯命令行引导页,里面有当前仓库的远程地址。这个地址至关重要,它有两种形式:
- HTTPS:
https://github.com/你的用户名/仓库名.git - SSH:
git@github.com:你的用户名/仓库名.git
我们在前面已经配好了 SSH 密钥,所以这里直接选 SSH 地址。
3.2 git remote add 建立关联,HTTPS 与 SSH 怎么选
远程仓库创建好了,现在需要把本地仓库和它关联起来。执行:
git remote add origin git@github.com:你的用户名/仓库名.git这里解释两个事情。
第一,origin是什么?“origin”本身没有任何魔法含义,它只是 Git 给远程仓库起的默认别名,约定俗成大家都叫它 origin,但你可以起任何名字。之所以用 origin,纯粹是习惯问题,因为官方文档和市面上所有教程都这么写,你也就沿用了。
第二,为什么推荐 SSH 而不是 HTTPS?这不是情怀问题,是实际体验差异。HTTPS 每次git push都要输入 GitHub 的用户名和个人访问令牌(token),虽然 Git 有凭据管理工具帮你记住,但配置麻烦,而且早期很多人被那个“输入密码”卡得怀疑人生——因为现在 GitHub 不让你填登录密码,必须填 token。相比之下,SSH 配好之后什么都不用输,直接推就能通。唯一要注意的是 SSH 依赖网络环境,某些网络下 22 端口不通,这个问题我们后面在报错排查部分详细展开。
关联好之后确认一下:
git remote -v能看到你的 origin 指向的拉取和推送地址,说明本地和远程的关系已经建立了。
注意:如果你之前操作过、误打误撞配了不对的地址,执行
git remote add origin ...时会报fatal: remote origin already exists。这时候先执行git remote remove origin,再重新 add 一次即可。
4. git push 核心流程:一句话命令拆开讲
4.1 推送前的分支整理:git branch -M main 是干嘛的
先解释一个概念问题:分支。Git 仓库默认会有一个分支,你把代码提交在这个分支上。这个分支叫什么名字,Git 本身不管,但 GitHub 默认把主分支命名为main。
以前 Git 的默认分支名是master,很多老教程还在这么写。后来社区考虑到master/slave这套叫法在行业里有争议,GitHub 把默认主分支改名成了main,现在新建的仓库主分支都叫 main。
问题来了:你本地的 Git 版本如果比较老,git init后处于master分支;而你在 GitHub 创建的仓库主分支是main。两边分支名对不上,推送时就会很绕。所以我们要在推送前把本地分支改名:
git branch -M main-M是强制重命名的意思,大小写敏感。执行后再用git branch查看当前分支,应该会显示:
* main从 master 改到 main,这一步只影响本地,不涉及远程,所以随便改。
4.2 git push -u origin main 到底做了什么
接下来这句就是整个教程的核心了:
git push -u origin main拆开看:
git push:推送命令,把本地提交推送到远程。origin:目标远程仓库的别名,指的是你 add 时配置的那个地址。main:你要推送的本地分支名。-u:全称--set-upstream,意思是把本地 main 分支和远程 main 分支绑定,建立起“上游”跟踪关系。绑定之后,你以后就可以直接敲git push不带任何参数,Git 也会自动推送当前分支到它对应的远程分支。
如果你已经执行了git branch -M main,并且本地至少有一次 commit,推送通常会很顺利,结束后终端会显示类似:
Enumerating objects: 3, done. Counting objects: 100% (3/3), done. Writing objects: 100% (3/3), 482 bytes | 482.00 KiB/s, done. Total 3 (delta 0), reused 0 (delta 0) To github.com:你的用户名/仓库名.git * [new branch] main -> main branch 'main' set up to track 'origin/main'.看到[new branch] main -> main,你就可以关掉终端,回 GitHub 网页刷新一下仓库首页了。你的文件此刻应该整齐地躺在那里。
4.3 推送后的检查习惯
推送完成不代表事情结束了。我要求自己每次推送后都做两个检查,你也养成这个习惯,能省掉后面很多沟通成本:
第一,刷新 GitHub 仓库首页,确认文件列表确实和本地一致。有时候你以为推上去了,实际只是 commit 了,还没 push,网页上一片空白。网页是人类友好的验证方式,比看命令行输出更直观。
第二,查看提交历史。在仓库页面点击 commit 图标,确认最近提交的说明文字和你git commit -m写的一致。如果这里显示的是未知用户(Unknown user)或者头像没渲染出来,多半是你第 1.2 节里配置的邮箱和 GitHub 账号不匹配,去改一下邮箱再提交一次就好了。
5. 高频报错与卡顿排查:新手必看的救急手册
5.1 error: src refspec main does not match any error
这是我在开头提到的那条报错,也是新手遇到频率最高的一条。完整的报错一般是:
error: src refspec main does not match any error: failed to push some refs to ...翻译成人话就是:Git 在本地找不到一个叫 main 的分支,或者这个分支上根本没有提交。
排查顺序是固定的,我建议你按这个顺序来:
第一步,确认你有没有执行过 commit:
git log --oneline如果提示fatal: your current branch 'main' does not have any commits yet,那就说明本地仓库是空的,Git 没什么可推的。解决办法很简单:回到第 2.3 节,把git add .和git commit -m "..."完成,然后再执行 push。
第二步,如果 log 能显示提交记录,那再看一下分支名:
git branch如果输出的是master而不是main,说明你的本地分支名和推送命令里的分支名对不上,执行一下git branch -M main再重新推送。
第三步,还有一种冷门情况:你人在一个非主分支上,比如dev分支上做了提交,却用它来推main。这时要不就切回 main 合并提交,要不就把git push命令里的分支名改成当前分支名。新手初期不用搞太复杂,统一在主分支上操作就好。
5.2 error: failed to push some refs to ...
第二条高频报错长这样:
error: failed to push some refs to 'https://github.com/xxx/xxx.git' hint: Updates were rejected because the remote contains work that you do not have locally.后面通常还会跟着一句建议:
hint: You may want to first integrate the remote changes.原因几乎千篇一律:远程仓库里有本地没有的提交。最常见的是你在创建 GitHub 仓库时勾选了生成 README 或 License,GitHub 自动帮你提交了一个文件;而你本地也做了一次提交。两边提交历史互不认识,Git 直接拒绝覆盖式推送,保护远程数据不被你一个人在本地哼哧哼哧搞出来的旧历史覆盖。
解决办法就是提示里那个“first integrate the remote changes”:先把远程的改动拉下来合并,再推送。我推荐用 rebase 方式合并:
git pull --rebase origin main--rebase的意思是:把你本地的提交“重放”到远程提交的后面,形成一条干净的线性历史。用大白话解释就是,你在电影院买好了座位,但前面已经有人坐下了,rebase是让工作人员把你安排到那人后面,而不是连人带座一起掀掉。
如果运气好,没有冲突,pull 结束之后直接重新推送:
git push -u origin main如果提示冲突(CONFLICT),不要慌,Git 会在文件里用<<<<<<<、=======、>>>>>>>标出冲突区域。你想保留哪个版本就把另一个删掉,然后重新git add冲突文件,再执行:
git rebase --continue最后再 push 一次。
5.3 git push 卡住、超时、一直提交不上去怎么办
“git push 卡住”是这几年最常被问到的问题。具体表现是:命令敲下去了,终端就一直转圈,几分钟甚至十几分钟没反应,最后要么超时(connection timed out),要么报Could not connect to GitHub。
这里我先说一句负责任的提醒:GitHub 本身托管在国外,国内网络环境下访问它偶尔不稳定是正常现象,跟你的代码、Git 配置都没什么关系。但是在处理这个问题的时候,千万不要去网上随便找一个所谓的“加速工具”或“镜像站”拿来就用——尤其是镜像站,那些来路不明的站点很有可能会把你输入的账号密码、仓库代码全部截走。人民日报都点名提示过不要用不明来源的第三方加速通道,这类工具的安全风险远远大于它带来的方便。
正确的处理顺序应该是:
第一,确认问题在哪一层。你自己电脑上能不能正常访问 GitHub 网页?如果能访问网页,说明网络基本通,问题可能出在 Git 使用的传输协议上。如果网页都打不开,那就先解决网络问题,或者换个网络环境试试,比如手机开热点。
第二,尝试切换传输协议。如果你用的是 HTTPS 仓库地址,改成 SSH;如果是 SSH 但连接超时,可以试着走 GitHub 官方提供的 443 端口 SSH 方案。GitHub 官方文档里明确支持这种做法,它不需要任何第三方软件:
ssh -T -p 443 git@ssh.github.com如果这个命令能显示认证成功,那就在你的~/.ssh/config文件里加一段配置,后续 SSH 连接都走 443 端口:
Host github.com Hostname ssh.github.com Port 443 User git如果是公司内网,可能还要配合代理才能访问外网,这种情况直接咨询公司网络管理员是最高效的,别自己折腾半天。
第三,如果怎么折腾都不通,那就不要死磕 GitHub。代码托管平台不止一家,国内就有 Gitee(码云),体验上跟 GitHub 高度相似,你可以把代码推送到 Gitee 上,或者用两个远程地址同时维护。等将来网络条件允许了再往 GitHub 同步,也不吃亏。
提示:不要把大量时间花在和网络对抗上。Git 的核心能力是“本地版本管理”,推送只是锦上添花,代码在本地安全地提交着,就永远不会丢。
5.4 常见报错速查表
把上面提到的和另外一些常见报错整理成一张表,建议大家收藏,遇到问题先来查一遍:
| 报错信息 | 原因 | 解决办法 |
|---|---|---|
error: src refspec main does not match any error | 本地没有 main 分支,或还没有 commit | git log检查提交,git branch -M main改名 |
error: failed to push some refs to | 远程有本地没有的提交 | 执行git pull --rebase origin main后重推 |
fatal: remote origin already exists | 已经配置过一次远程地址 | git remote remove origin后重新 add |
Please tell me who you are | 没配置用户名和邮箱 | 执行 1.2 节的git config命令 |
Permission denied (publickey) | SSH 密钥未配置或不对 | 检查~/.ssh/id_ed25519.pub是否已添加到 GitHub |
Repository not found | 仓库地址不对,或没有访问权限 | 检查远程地址、仓库是否私有、是否有权限 |
Connection timed out | 网络无法连接 GitHub | 切换网络、换 SSH 端口,或换代码托管平台 |
git push 一直卡住无反应 | 网络原因占绝大多数 | 先排除网络问题,再检查 Git 代理配置 |
6. 从日常更新到克隆仓库:再养成 3 个 Git 好习惯
6.1 日常开发循环:编辑 -> add -> commit -> pull -> push
推送成功只是入门,日常开发中你还会反复跟 Git 打交道。一个标准的更新循环是这样的:
- 修改项目文件。
git add <文件>或git add .把改动放进暂存区。git status快速确认哪些文件被改动了,顺便检查有没有误加文件。git commit -m "描述本次改动"在本地提交。git pull --rebase origin main拉取远程最新提交,防止你 push 时才发现别人已经改了代码。git push推送。
这里我想重点强调一下第 5 步和第 6 步的顺序感。很多新手习惯直接git push,等发现远程有别人提交时再回来 pull,这时候就被迫处理合并和冲突,效率很低。如果你每次推送前主动 pull 一次,大概率都能轻松合并,甚至连冲突都不会碰到。
6.2 git clone 从零拉取一个远程仓库
前面讲的都是本地已有项目的情况,但还有一种场景很常见:你想把 GitHub 上别人的项目拿下来跑一跑。这时候不用init,用clone:
git clone git@github.com:用户名/仓库名.gitgit clone会把远程仓库完整复制到本地,包括全部提交历史、所有分支和远程地址配置。克隆完的仓库已经和远程关联好了,你不需要 init,也不需要 remote add,直接可以 add、commit、push。
顺带一提,clone和init的区别一句话就能讲清楚:init是把一个已有的本地目录变成 Git 仓库,clone是把一个已有的远程仓库复制到本地。
6.3 三个避坑技巧
最后分享三个我踩过坑之后总结的避坑技巧,每一个都对应一段血泪史。
第一,提交信息里不要写任何敏感信息,比如账号密码、API Token。GitHub 会对公开仓库做扫描,一旦发现明显的密钥格式,它会自动告警,甚至禁用相关 key。更麻烦的是,就算你删掉文件重新提交,这些信息依然躺在 Git 历史记录里,别人翻 history 照样看得到。新手的处理原则很粗暴:密码、密钥、token 统统不要写进代码里,更不要写进 commit 信息里。
第二,大文件不要直接 push。Git 本身适合管文本类代码,不适合管二进制大文件。单文件超过 50MB 时 GitHub 会警告,超过 100MB 会直接拒绝。如果你确实需要管理大文件,去学一下 Git LFS(Large File Storage),它能用轻量指针代替大文件本体,不拖累仓库性能。
第三,误 commit 之后,分清楚“已推送”和“未推送”两种情况。如果只是本地 commit 错了,可以用git reset --soft HEAD~1撤销提交,改动还在,重新整理后再次提交;但如果已经 push 到远程了,就不要再用 reset 强行回头,因为远程其他人可能已经拉过你的提交了,这时候应该用git revert创造一个新提交来“抵消”错误提交。这个习惯能帮你避免很多协作事故。
坦白说,Git 的入门门槛不在命令多,而在概念多、报错翻译能力差。我能给你最大的建议就是:每次报错先复制报错文本去搜索引擎查,然后回到本地用git status和git log自己排查一遍。排查多了,你会慢慢形成一种感觉——Git 只不过是在管理文件快照,一切操作都有迹可循。这套流程你完整走通一次之后,再碰到类似的问题,自己就能举一反三了。