
只要你写过几天代码就应该听过Git那句经典口诀add、commit、push。很多教程把它叫作“向仓库提交代码三步走”听起来简单到不能再简单可真到了实际项目里我见过太多人在这一步上栽跟头。有人习惯性git add .把密钥、日志、node_modules一股脑塞进仓库有人 commit 信息随手写个“update”三个月后翻 log 完全看不懂当时改了什么还有人一到 push 就被no upstream branch之类的报错卡住现场百度半天也搞不明白问题出在哪。这篇文章我就围绕 add、commit、push 三个命令把各自背后的设计逻辑、标准用法、高频报错和实操习惯一次性说清楚。适合刚接触 Git 的初学者把底子打牢也适合用了一段时间但总被各种小问题折腾的开发者查漏补缺。文章里涉及的命令我都会解释“为什么要这么写”不绕弯子都是实际工作里能直接用上的东西。1. 为什么说 add、commit、push 是Git的黄金三步1.1 先搞懂Git的四个核心区域很多人学 Git 学不明白根源不在命令背不下来而是脑子里没有一个清晰的“区域模型”。Git 处理代码的方式本质上是文件在四个区域之间流转工作区Working Directory、暂存区Staging Area / Index、本地版本库Local Repository、远程仓库Remote Repository。工作区是你编辑器里正在改动的真实文件目录你写代码、删文件、改配置影响的全是工作区。暂存区则是 Git 给你开辟的一块临时缓冲区用来收集你打算一起打包提交的文件快照。本地版本库在你机器的.git文件夹里保存着项目从初始化到现在的所有提交历史。远程仓库则托管在服务器上比如 Gitee、GitHub 或者你们公司内部的 GitLab是团队协作时大家共享代码的地方。如果觉得抽象可以把它想象成投稿一本书的流程。工作区是你电脑里正在写的 Word 草稿暂存区是你桌子上那个“准备寄出”的文件袋本地版本库是你自己的档案柜里面整齐摆着每一版的定稿远程仓库是出版社总部的资料库。add 就是把草稿放进文件袋commit 是把文件袋里的内容正式归档到自己的档案柜push 是派快递把档案寄到出版社总部。这个类比基本能覆盖三步操作的全部含义。1.2 为什么顺序必须是 add → commit → push这三个命令的顺序不是随便定的每一步都对应一个区域间的迁移跨不过去也跳不了级。add 负责把工作区的改动放入暂存区commit 负责把暂存区的内容固化成一次本地提交push 负责把本地提交同步到远程仓库。为什么不能跳过 add 直接 commit因为 Git 需要你明确告诉它这次提交到底包含哪些改动。比如你一个下午改了五个文件其中三个是登录功能的两个是购物车功能的你希望分成两次逻辑清晰的提交就得先 add 那三个文件commit 一次再 add 剩下两个commit 一次。如果没有暂存区这个设计Git 就只能把工作区所有改动一股脑打成一次提交想控制提交粒度就完全没戏了。为什么不能跳过 commit 直接 push因为 push 的本质是“把本地版本库中的某些提交传输到远程仓库”。本地如果没有生成任何 commit那就没有东西可以推送。有些新手以为 push 是把工作区文件直接传上去这是误解。push 的起点必须是 commitcommit 的起点必须是 add这条链是环环相扣的。1.3 暂存区到底带来了什么价值暂存区这个设计是 Git 比很多早期版本管理工具高明的地方。它给了你一个“反悔”和“挑选”的空间。你在工作区改了一堆文件经过一天的高强度编码脑子已经不清醒了这时候如果直接全部提交很容易把临时调试代码、无用日志、甚至密钥文件一起提交上去。有了暂存区你可以先git status看看到底有哪些改动再决定哪些进入本次提交。暂存区也让“分阶段提交”成为可能。一个文件里改了两处逻辑一处是修 bug一处是加功能使用git add -p可以交互式地选择到底暂存哪一块。这种精细控制对代码 review 和后期排查非常有帮助。另外误 add 了文件也有后悔药git restore --staged file就能把文件从暂存区撤回到工作区内容不会丢。2. 环境准备装好Git才能动手2.1 Git安装和初始化配置工欲善其事必先利其器。先确保你的电脑上装好了 Git。Windows 用户可以直接去 Git 官网下载安装包一路默认安装即可也可以用包管理器比如winget install --id Git.Git。macOS 用户执行brew install gitLinux 用户根据发行版执行sudo apt install git或sudo yum install git。有些朋友比较习惯图形界面可以装一个 TortoiseGit也就是常说的“小乌龟”装完以后在资源管理器里右键就能完成大部分 Git 操作对新手很友好。安装完 Git 之后第一件必须做的事是设置用户名和邮箱。这一步不是可选项因为每次 commit 都会把作者信息写入提交记录团队协作时全靠它定位“这段代码是谁写的”。git config --global user.name Tomy git config --global user.email tomyexample.com--global表示对当前系统用户全局生效。如果某个仓库需要单独的身份信息可以在那个仓库目录下不加--global设置仓库级配置会覆盖全局配置。查看当前配置用git config user.name和git config user.email。2.2 SSH密钥配置与免密提交如果你不想每次 push 都输一遍账号密码SSH 免密是必须做的。原理很简单你在本地生成一对公私钥把公钥放到代码托管平台的账户里之后 Git 通过 SSH 协议连接服务器时会自动完成身份验证不再需要输入密码。生成密钥最推荐用 ed25519 算法密钥短、安全性高新版 OpenSSH 都原生支持ssh-keygen -t ed25519 -C tomyexample.com一路回车默认保存到~/.ssh/id_ed25519。然后查看公钥内容cat ~/.ssh/id_ed25519.pub把输出的一整行内容复制登录 Gitee 或 GitHub在设置里找到“SSH Keys”或“SSH公钥”入口粘贴保存。验证是否配置成功执行ssh -T gitgitee.com看到类似Hi Tomy! Youve successfully authenticated的输出说明免密已经生效。这一步对应很多人在网上搜的“git免密”“git配置gitee密钥”操作一次就能解决。注意clone 仓库时要用 SSH 地址也就是gitgitee.com:xxx/xxx.git这种格式而不是https://开头的地址否则仍然会走密码登录。如果 SSH 密钥失效或者换了电脑常见报错是Permission denied (publickey)。排查方向就两个一是看本地密钥有没有加载ssh-add -l二是看托管平台上的公钥是否和本地私钥匹配。把这两个点检查一遍基本都能解决。3. 核心操作add、commit、push一步步来3.1 git add的多种姿势与选择git add的核心作用是把改动放入暂存区但它有多个变体用法不同影响范围也不同我按实用频率给你梳理一遍。最常用的是git add .表示把当前目录下的所有改动加入暂存区包括修改和新增文件。注意它不会处理已被删除的文件这个细节后面说。git add -A比.更彻底会把整个工作区的所有改动包括删除操作都纳入暂存区。git add -u则只更新已被 Git 跟踪的文件新文件不会加进来适合你只想提交已有文件的修改时用。如果只是想提交某个或某几个文件直接写文件名git add src/login.js docs/readme.md。还有一个进阶神器git add -p交互式暂存。你改了一个大文件里面既有重构代码又有新功能可以用它一块一块地选择哪些 hunk 进入暂存区。它会逐段问你Stage this hunk?按 y 暂存、按 n 跳过、按 s 拆分更细的块。这个命令我几乎每天都会用到对控制提交粒度帮助极大。注意不要把git add .当成肌肉记忆。我见过有开发者把.env文件一并提交到仓库里面写着数据库密码和第三方密钥直接把内部系统暴露了。正确的做法是先看好git status输出确认没有敏感文件混入再执行 add。配合.gitignore文件能拦截掉绝大多数不该入库的内容。项目根目录新建.gitignore把常见忽略项写进去.DS_Store node_modules/ dist/ *.log .env .env.local这样就算你手滑执行git add .这些文件也不会被加入暂存区相当于多了一道保险。3.2 git commit把暂存区固化成一次提交add 完成之后下一步是 commit。基本命令是git commit -m feat(login): 增加手机号登录接口-m后面跟提交信息。提交信息不是随便写写就行的我强烈推荐使用 Conventional Commits 规范格式是type(scope): subject。type 表示提交类型最常用的包括 feat新功能、fix修复bug、docs文档改动、style格式调整不影响逻辑、refactor重构、perf性能优化、test测试相关、chore构建或工具链变动。scope 是这个改动影响的范围比如 login、cart、api。subject 是一句清晰的描述说清楚“做了什么”必要时可以再加正文说明“为什么这么做”。例如git commit -m fix(cart): 修复购物车数量为0时页面报错的问题 git commit -m refactor(auth): 抽取token校验逻辑为公共方法为什么提交信息要这么讲究因为 Git 的每个提交都是项目历史的“日志”。出 bug 的时候你会打开git log --oneline查看最近几次提交如果每一条都是“update”“fix”“aa”你根本分不清哪个提交引入了问题。反之规范的提交信息配合git blame能快速定位到具体行是哪次提交改的找责任人、做回滚都会非常高效。git commit还有一些实用参数。git commit -a可以跳过 add直接提交所有已跟踪文件的改动但注意它不会包含新文件所以新文件仍然要先 add。git commit --amend可以修改上一次提交比如漏提交了文件或者信息写错了都可以用它补救。git commit --allow-empty允许生成一次空白提交偶尔用于触发 CI 流程时会用到。3.3 git push把提交同步到远程仓库commit 完成之后代码还只存在你本地队友看不到云端的备份也没有。这一步就要靠git push把本地提交推送到远程仓库。最基本的用法是git push origin master这里origin是远程仓库的默认别名master是你当前的分支名也可能是main具体看仓库初始化时的默认分支设置。如果是第一次往一个远程仓库推代码很可能会遇到下面这条经典报错fatal: The current branch master has no upstream branch. To push the current branch and set the remote tracking branch, use git push --set-upstream origin master意思是当前分支还没有和远程分支建立追踪关系Git 不知道它该对应远程的哪个分支。解决方案就是按提示执行git push -u origin master-u等价于--set-upstream它会把当前分支的上游分支设置为origin/master从今往后你在这个分支上直接执行git push就够了不用每次都写完整命令。如果是自己创建的新仓库还需要先关联远程地址方法是git remote add origin gitgitee.com:用户名/仓库名.git git remote -v第二条命令用来确认关联是否成功。push 的时候如果远程分支和本地分支名字不同可以显式指定git push origin dev这样会把本地的 dev 分支推送到远程的 dev 分支。注意git push --force会强制覆盖远程分支的历史非常危险。团队协作时千万不要随意使用。如果确实需要覆盖优先用--force-with-lease它会在覆盖前检查远程分支是否发生了变化比裸--force安全得多。push 过程中经常会遇到! [rejected] master - master (fetch first)的提示这说明远程仓库有本地没有的提交你的 push 被拒绝了。正确的处理方式是把远端改动先拉下来合并完成后再推送具体步骤在下一章的冲突处理里详细讲。4. 常见报错与排查技巧实录4.1 fatal: not a git repository 的几种可能这个报错大概是 Git 新手最先遇到的一个。报错信息是fatal: not a git repository (or any of the parent directories): .git含义很简单当前目录不是 Git 仓库或者从当前目录往上找都找不到包含.git的仓库目录。解决办法也直白如果你是想在现有项目里用 Git先确认路径对不对是不是真的进入了项目目录如果你是想把当前目录初始化为 Git 仓库执行git init如果项目是从远程 clone 下来的正常情况不会出现这个错出现的话多半是.git文件夹被误删了那就只能重新git init但历史记录也就随之丢失了。我在实际工作中还碰到一种情况在仓库的子目录里操作 Git 没问题但切到系统用户目录或者别的无关目录执行 Git 命令就会报这个错。记住一个判断原则能执行git status且不报错说明你处于某个仓库范围内报错了优先考虑目录层级和.git是否存在。4.2 commit提交后发现写错了怎么办这个问题出现频率极高解决办法也很成熟。如果你的提交还没 push 到远程用git commit --amend直接修改即可。只想改上次的提交信息git commit --amend -m feat(order): 修正订单超时判断逻辑上次提交漏了文件先 add 再 amendgit add 漏掉的文件 git commit --amend不写-m时会自动打开编辑器保留上一次的信息你只需要确认或者修改一下保存退出就行。amend 的本质是生成一个新的提交来替换原来的提交所以它只适合“还没有推送远端”的场景。如果该提交已经被 push 并且队友已经拉取就不要再 amend 了否则会导致你们两个人的提交历史分叉后续每次 pull/push 都是冲突。如果实在必须改改完之后需要git push --force-with-lease并且一定要提前和团队成员打招呼。还有一个比较隐蔽的坑commit 之后发现提交里包含了大文件或者被 A 忽略的文件此时 amend 只能改信息没法把文件从历史里真正抹掉。想彻底删除历史里的大文件需要用到git filter-branch或者 BFG Repo-Cleaner这属于高级操作日常开发中如果遇到建议先备份仓库再动手。4.3 push被拒绝与冲突处理push 被拒绝是团队协作中很难避免的情况。报错信息大概长这样! [rejected] master - master (fetch first) error: failed to push some refs to gitgitee.com:xxx/xxx.git hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing hint: to the same ref.核心原因就是远程仓库有本地没有的提交如果 Git 允许你直接 push会把队友的提交覆盖掉所以 Git 会直接拒绝。解决方案是先拉取远程改动合并后再推送。我个人推荐的做法是git pull --rebase origin master--rebase的意思是把你本地的提交临时摘下来等远程的新提交应用完成后再把你的提交放到最前面。这样得到的历史是一条干净的线性记录没有多余的 merge 提交git log --graph看起来会非常清爽。如果不用--rebase默认会生成一个 merge commit长此以往提交历史会越来越乱review 代码时非常痛苦。pull 的过程中如果两边改了同一个文件的同一个位置Git 就会提示冲突。打开冲突文件你会看到类似这样的内容 HEAD 当前分支或远程的代码 你本地的代码 feature/xxx手动整理成最终想要的代码删掉、、这些标记行然后执行git add 冲突解决后的文件 git rebase --continue最后再git push问题就解决了。冲突本身不可怕它只是 Git 在保护大家的代码不被互相覆盖。养成“push 前先 pull --rebase”的习惯遇到冲突的概率会小很多。4.4 其他几个高频报错速查我整理了一个高频报错速查表可以直接收藏备用报错信息大致原因解决办法fatal: not a git repository当前目录不是Git仓库git init或切换到项目目录fatal: The current branch master has no upstream branch分支未关联远程追踪分支git push -u origin master! [rejected] (fetch first)远程有本地没有的提交git pull --rebase后重新 pushPermission denied (publickey)SSH密钥未配置或未加载检查本地密钥和托管平台公钥failed to push some refs通常是上一行的具体原因根据上方提示定位具体拒绝原因could not read Username for https://github.com使用HTTPS协议但未配置凭据改用SSH地址或配置credential helper另外提醒一下线上项目如果部署到服务器时把.git目录暴露在了 web 根目录下攻击者可以直接访问/.git/config获取仓库元数据甚至可能下载到完整源码。这是很多安全事故的源头。排查方法是在 Nginx 配置里对/.git路径做访问限制或者干脆部署时把.git目录从项目目录里隔离出去。作为开发者更重要的是守好.gitignore敏感文件坚决不进版本库。5. 进阶技巧与实操心得5.1 让大模型帮你生成规范commit信息现在写代码用上大模型已经是很常态的操作了连 commit 信息也能让模型代劳。我的习惯是先把已暂存的改动内容取出来再丢给大模型去生成符合规范的提交信息。命令如下git diff --cached把输出内容复制给大模型附加一句提示“请根据以下 diff 内容按照 Conventional Commits 规范生成一条简洁的 commit 信息包含 type、scope 和 subject。”模型会返回类似feat(user): 增加用户昵称修改接口这样的结果自己校验一遍没有问题再执行 commit。如果是大型重构或者涉及 Vue 项目里多个组件同时改动的情况diff 输出往往非常长一股脑全塞给模型既浪费 token 又容易让它抓不住重点。我建议先执行git diff --cached --stat看哪些文件改动量最大再挑出核心文件的具体 diff 给模型分析。这样做下来的 commit 信息往往比随手敲的update、fix强太多至少它能把这次改动的“背景”写清楚回头看的时候能省很多时间。5.2 提交前的自检清单踩过太多次“手滑”的坑之后我现在每次提交前都会有一整套自检流程熟练之后也就一两分钟但能避免掉绝大多数低级事故。第一步git status看当前工作区的状态有哪些文件被修改、哪些文件是新增的。第二步git diff看未暂存的改动内容重点确认没有把调试代码、临时日志、本地环境相关的改动混进去。第三步git diff --cached看即将被提交到暂存区的内容这一步相当于提交前的最终确认。第四步检查 commit 信息是否写清楚了“做了什么”和“为什么”而不是单纯描述文件路径。第五步判断这次提交的粒度是否合适。一次提交最好只做一件逻辑完整的事如果你发现这次提交里既有登录功能又有购物车修复强烈建议拆分。我印象最深的一次事故是急着下班执行git add .之后没有看状态就直接 commit push结果把一个本地测试用的临时配置文件带上去了同事 pull 下来之后整个项目启动报错。那天晚上我在群里公开道歉从此以后“push 前检查”就成了肌肉记忆。5.3 git stash 与 git worktree 的妙用在“三步走”的基础上还有两个命令非常值得掌握它们解决的是“手头工作没做完但又不得不切分支”的窘境。git stash可以把当前工作区的改动暂时保存起来让工作区回到干净状态。比如你正在 dev 分支开发功能线上突然报了个紧急 bug需要立刻切换到 master 分支修复。直接切分支会报错因为工作区有未提交的改动。这个时候执行git stash切分支、修 bug、提交、push再切回 dev 分支执行git stash pop之前没做完的工作就原样回来了。git worktree更厉害它允许一个仓库同时存在多个工作目录。同样上面那个场景不想打断当前开发进度可以直接执行git worktree add ../hotfix -b hotfix/xxx然后在新目录里切换分支、修 bug和原来的工作区互不干扰。这个命令对“需要同时维护多个分支”的场景特别实用。等 bug 修完git worktree remove ../hotfix就能清理掉多余的工作目录。5.4 分支策略与提交习惯的个人建议最后聊点软性的东西。add、commit、push 是命令层面的基本功但放在真实项目里还需要一套分支策略和提交习惯来支撑。我的个人建议是main/master 分支始终保持可用任何未经测试的新功能都不要直接推到主分支新功能在单独的分支上开发合并回主分支前至少自己在本地跑一遍测试合并时优先用--no-ff保留一个合并记录方便以后知道哪些功能合入了主分支。写到这里我发现很多所谓“Git 很难”的恐惧其实来自对底层的无知和对报错的不理解。把 add、commit、push 吃透再配合git status、git diff、git log这三个查看命令日常开发 90% 的场景你都能游刃有余。真正决定一个开发者 Git 水平的不是熟练用了多少高级技巧而是能不能在每一次提交时都保持“这个提交是清晰、完整、可追溯的”这样一种习惯。代码写得快不算本事写出来的东西别人能接得住、查得清那才是真功夫。