
1. 从“记不住命令”到“肌肉记忆”聊聊 Git 常用操作的核心思路很多开发者接触 Git 都是边用边查今天commit忘了加-m明天push被拒绝不知道怎么办。我当年也是这样直到把常用命令背后的逻辑理顺之后才真正告别了“复制粘贴”——遇到问题脑子里面自动会浮现出对应的命令组合效率提升非常明显。这篇文章想聊的就是 Git 日常开发中最高频的那些操作提交、分支、远程同步、撤销、查找历史还有团队协作时最容易踩坑的场景。不会把 Git 的全部命令铺开讲只讲你在真实项目里几乎每周都会用到的那些配合我实战中积累的一些经验和教训。不管你是刚接触 Git 的初学者还是已经写了几个月项目但总感觉命令用得不够顺的开发者这篇内容都能给你一些可落地的参考。当然Git 命令光看不练没有意义。我的建议是你把这篇文章当成一份“操作地图”在看的过程中随手开一个测试仓库把每条命令都敲一遍尤其是我标了“容易踩坑”的地方亲自跑一次比看十遍都管用。2. 安装配置与初始化为什么这些“一次性操作”决定了后续体验2.1 全局配置两条命令解决“我是谁”的问题先说最基础但又是很多人忽略掉的部分。刚装好 Git 或者换了新电脑之后需要做的第一件事就是配置用户信息。git config --global user.name 你的名字 git config --global user.email 你的邮箱这两条命令设置的user.name和user.email会写进每一次提交记录里。也就是说将来团队里面所有人查看git log时看到的就是这里设置的署名。很多人图省事随便填或者干脆用一个像testtest.com这样的假邮箱后面对接代码托管平台的时候就会很尴尬——你在平台上的账号和个人提交记录对不上号贡献图也统计不到。另外提一句如果你想针对某个特定仓库使用不同的用户名和邮箱可以去掉--global参数在对应仓库目录内单独执行。我自己在工作电脑上就是这样处理的个人项目用一个身份公司项目用另一个身份避免出现提交历史里混着两种身份的情况。2.2 初始化仓库的三种场景git init是创建一个新仓库的命令但实际操作中“我需要一个仓库”的场景其实分三种本地新建项目还没有任何版本管理在项目根目录执行git init从远程克隆一个已有项目git clone 仓库地址这个命令不仅会下载代码还会自动把远程仓库地址配置好并且默认创建一个本地分支跟踪远端分支已有本地代码想推送到远程先git init再添加远程地址git remote add origin 地址然后提交推送第三种场景中最容易出问题的是在已经执行过git init的目录里又执行一次。不会报错只是重复初始化而已但不建议养成这种习惯。另外每次执行git init之后建议用git status看一眼当前状态确认一下自己确实是在正确的目录里操作的。我就有过在错误的路径下执行git add .把一堆不该提交的文件全部加了进去的经历等注意到的时候已经晚了。2.3 忽略文件的正确打开方式每次初始化仓库之后第一件该做的事其实是创建.gitignore文件。很多新手忽略了这一点直到提交了一堆node_modules或者编译产物进仓库之后才开始后悔。.gitignore是一个纯文本文件里面写的是 Git 不需要跟踪的文件或目录规则。比如一个前端项目通常会忽略这些node_modules/ dist/ .env *.lognode_modules/表示忽略整个目录*.log表示忽略所有以.log结尾的文件.env这种带点号开头的文件直接写文件名就能忽略。这里有个值得注意的细节.gitignore是支持通配符的*匹配任意内容但不会跨越目录层级如果想要匹配任意层级的某个文件需要写成类似**/temp/的形式。踩坑提示如果某个文件已经被 Git 跟踪了再写进.gitignore是不生效的。也就是说.gitignore只对“尚未被跟踪”的文件有效。补救办法是先执行git rm --cached 文件名把它从暂存区移除但保留本地文件然后再提交一次。3. 提交与状态查阅让时间线清晰可读的基本功3.1 status 和 diff动手提交前一定要做的事git status大概是整个 Git 里使用频率最高的命令了。它的作用就是告诉你当前仓库的状态哪个分支、哪些文件被修改了、哪些文件暂存了、有没有未跟踪的新文件。我个人的习惯是在执行任何可能改变仓库状态的操作之前先跑一次git status尤其是在多分支切换和代码合并之前。比status更进一步的是git diff它用来查看文件具体的改动内容。直接执行git diff查看的是工作区和暂存区之间的差异——说白了就是你改了但还没git add的内容。等你执行了git add之后再想看暂存区和上一次提交之间的差异需要加一个参数git diff --cached很多新手不知道这两个查看区域的区别经常出现“我明明改了代码为什么git diff什么都没输出”的疑问其实就是文件已经被git add过了。git diff是一个非常适合用来做“提交前自查”的命令我每次打算提交时都会先看一眼改动内容防止把调试用的临时日志或者意外修改一起提交上去。3.2 add 的三种常用方式与各自的坑git add负责把工作区的改动添加到暂存区是构成 Git 工作流的关键一环。最常见的三种用法是git add 具体文件名 git add . git add -ugit add 文件名最稳妥按需添加精确控制到底哪些文件进入暂存区适合改动比较分散的时候使用。git add .是把当前目录下所有改动和新文件全部加入暂存区用起来方便但也最容易把不该提交的东西提交上去。git add -u稍微冷门一点它只会把“已经被 Git 跟踪的文件”的改动加入暂存区不会添加新文件。如果你项目里恰好有一些不想提交的新文件用这个命令会更安全。有一个实际遇到的坑想分享在仓库根目录执行的git add .确实会递归添加所有子目录的内容但如果你在某个子目录里面执行同样命令它只会添加当前目录下的内容。有些同学在项目中的某个模块目录下执行了git add .以为整个项目都提交了结果推送上去发现漏掉了一堆文件——这就是对“当前目录”这个概念理解不透彻导致的。3.3 commit 规范与 --amend 的妙用提交是 Git 操作里最核心的动作之一执行命令是git commit。最常用的两种方式git commit -m 提交信息 git commit -am 提交信息git commit -a等价于先把所有已跟踪文件的改动加入暂存区再执行提交。但它对新建文件是无效的新文件依然需要显式git add。这个细节和git add -u是一致的因为-a内部做的就是同样的事情。另一个我强烈建议养成的习惯是使用git commit -m时提交信息不要随便写。fix bug和修复了用户首次登录时头像无法加载的问题完全是两种信息密度。等到你三个月后再看这段提交记录前一种信息完全没有任何帮助。比较好的格式是“动词 修改对象 原因或效果”比如修复订单列表在低网速下重复请求的问题这样一看就明白。如果提交完之后发现信息写错了或者漏掉了一个文件不用慌张用--amend参数git commit --amend -m 修正后的提交信息这个命令会修改当前分支的最新一次提交。注意是“修改”不是“新增加一条提交记录”。如果这个提交已经推送到了远程那么--amend之后需要强制推送才能更新远端记录。在团队协作时如果没有和队友沟通好直接强制推送改写历史是有风险的——别人的本地提交可能会因此出现冲突。所以我的建议是--amend只用于处理“还在自己本地还没推送”的提交。3.4 log 与 status 的搭配使用技巧查询历史记录主要靠git log。默认的输出格式是一长串带哈希值、作者、日期、提交信息的列表信息量足够但看起来会比较密。我日常工作最常用的两个变体是git log --oneline git log --oneline --graph--oneline把每次提交压缩成一行清爽很多--graph会额外显示分支合并的关系图提交网络一目了然。想要筛选某一个人的提交或者只看最近多少条记录可以配合更多参数。还有一个组合拳我经常用git log -p 文件名这条命令会显示某个文件的完整修改历史包括每次改动前后的具体内容。查“某行代码是什么时候被改掉的”“那次改动解决了什么问题”非常方便可以说是我排查历史问题时的第一选择。4. 分支操作并行开发和风险隔离的利器4.1 分支的创建、切换与删除分支是 Git 最核心的设计之一它让你可以在不影响主代码的情况下并行开发新功能或修复问题。常用命令git branch # 查看本地分支列表 git branch name # 创建新分支 git checkout name # 切换分支 git checkout -b name # 创建并切换分支 git branch -d name # 删除分支git checkout -b是创建并切换分支的快捷写法也是我日常新建分支时最常用的命令。现在新版本的 Git 也支持git switch系列命令功能上和checkout对分支的操作是等价的但switch更专注在“切换”这个语义上不容易和checkout恢复文件的功能混淆。如果你用的 Git 版本比较新可以试试git switch -c name创建并切换分支。删除分支时需要注意一点git branch -d只能删除“已合并”的分支。如果你尝试删除一个还没合并的分支Git 会提示你使用-D强制删除。这其实是 Git 在保护你的工作成果。我有一次在一个功能分支上写了不少代码还没来得及合并进主分支因为着急清理分支用了强制删除后面发现有个功能需要参考那段代码只能靠远程分支或者git reflog勉强找回来。所以删除分支前建议三思。4.2 merge 和 rebase两种合并思路的对比当你在分支上完成了开发需要把改动合并回主分支时有两种主要方式git merge和git rebase。git merge的语义是“合并”它会创建一个新的合并提交节点保留两条分支的完整历史。优点是不改动原有提交记录操作安全直观缺点是合并次数多了以后提交历史会出现很多分叉--graph看起来像一团毛线。git rebase的语义是“变基”它会把当前分支的提交“重新播放”到目标分支的最新提交之上。执行之后提交历史会变成一条干净的直线。但代价是提交的哈希值全部会变也就是重写了历史。我的使用建议是功能分支合并回主分支用merge保留合并痕迹方便回溯功能分支从主分支拉取最新代码时用rebase让本地提交始终基于最新代码具体操作上把功能分支切换到需要并入的分支之后执行git merge 功能分支名如果发生冲突Git 会列出冲突文件你需要手动编辑解决冲突之后再执行git add和git commit完成合并。注意冲突解决完之后是不需要git merge --continue的直接提交即可。4.3 解决冲突的三个原则合并冲突是 Git 使用中最头疼的问题。出现冲突的本质是两个分支修改了同一个文件的同一个位置Git 不知道以谁为准。解决方案只有一个——“人工判断”。真实操作中冲突文件里会有这样的标记 HEAD 当前分支的内容 要合并进来的分支的内容 feature-branch你要做的就是把、、这些标记连同其中一方的内容一起删掉保留正确的代码。我处理冲突时的原则有三条不盲目保留任意一方。除非你对这块代码特别熟悉否则最好看看两边的上下文再决定涉及关键业务逻辑的冲突建议与相关开发者沟通确认解决完毕之后立即运行一次构建或测试越早暴露问题越好5. 远程仓库协作push、pull 和 fetch 的配合节奏5.1 remote 与 clone建立本地和远端的连接远程仓库是团队协作的枢纽。把一个仓库从远端复制到本地用的是git clone这个命令会自动建立本地分支对远端分支的跟踪关系。后面如果需要手动添加远程地址用的命令是git remote add origin 仓库地址 git remote -vgit remote -v用来查看当前配置的远程地址。我建议每次换电脑、换网络之后先跑一次这个命令确认地址对不对因为很多“推送失败”的问题其实根本原因是仓库地址配错了。origin是 Git 对“默认远程仓库”的约定命名可以改成其他名字但没有必要。多远程仓库的场景确实存在比如同一份代码需要推到两个不同平台但我个人觉得日常开发维护一个origin就足够了太多远程地址反而容易操作失误。5.2 push 的参数选择与常见报错推送本地提交到远程仓库是每个开发者都要频繁做的操作git push origin 分支名 git push -u origin 分支名 git push --force-with-lease origin 分支名第一次推送新分支时建议加上-u参数作用是建立本地分支与远程分支的跟踪关系。建立过跟踪关系之后后续再推送或拉取时直接执行git push或git pull就可以不需要每次都写清楚远端和分支名。推送时报错最经典的是下面这种! [rejected] feature - feature (fetch first) error: failed to push some refs这通常意味着远程分支有你本地没有的提交。正确做法是先git pull把远程更新拉到本地处理完可能的冲突再重新推送。这里提醒一下遇到这种报错千万不要直接执行git push --force强行覆盖。如果不是充分确认远程分支可以被覆盖的话强制推送可能把队友的提交历史全部冲掉后果相当严重。如果必须强推优先使用--force-with-lease它会检查远程分支是否被别人更新过相对安全得多。5.3 pull 与 fetch理解“拉取”的两个阶段git pull是一条被很多人当作“一键更新”来用的命令但它实际上做了两件事先git fetch把远程的提交记录下载到本地再执行一次merge或rebase取决于你的配置把远端更新合并到当前分支。git fetch单独执行时只是把远程的最新状态下载到本地并不会修改你当前的工作目录和分支。所以想要先看看远程改了些什么再决定怎么处理时可以先git fetch然后使用git log origin/分支名查看远程分支的提交情况再决定用git merge还是其他方式合入。如果你想直接拉取并合并默认执行git pull就行。但有些团队约定用 rebase 的方式来拉取以保持提交历史的线性可以这样设置git pull --rebase用--rebase拉取的原理是先把本地未推送的提交“暂存”到一边然后合并远端最新代码最后把你的提交再应用到最新代码之上。好处是历史干净坏处是遇到冲突时解决起来稍微复杂一点。建议在自己熟悉的项目里试验几次找到适合自己的节奏。6. 撤销与回滚从工作区到历史记录的“后悔药”6.1 撤销未提交的改动checkout 和 restore日常开发中最常出现的误操作就是“改错文件”或者“改砸了想回到原样”。如果改动还没有git add那么想撤销工作区的修改可以git checkout -- 文件名 git restore 文件名这两种写法等价作用都是丢弃工作区里对某个文件的修改恢复到暂存区或上一次提交时的状态。注意这个操作是不可逆的文件内容一旦被覆盖就找不回来。所以执行前一定要确认这个文件里没有你想保留的代码。如果改动已经执行了git add想从暂存区撤销而不动工作区的内容git restore --staged 文件名这条命令会把文件从暂存区退回到工作区但保留你当前对它的修改。经常在git add时误加了文件或者目录就可以用这个命令把它移出来。6.2 reset 的三种模式soft、mixed、hard当你要撤销“已经提交”的改动时git reset是核心工具。它有三种模式分别对应不同的“回退深度”git reset --soft HEAD~1 git reset --mixed HEAD~1 git reset --hard HEAD~1HEAD~1表示上一次提交HEAD~2表示上上次以此类推。三种模式的区别在于--soft只移动 HEAD 指针到指定提交暂存区和工作区都不变。也就是说回退之后你刚才的提交内容仍然在暂存区里可以直接重新提交。适合“提交完之后发现信息写错了想重新提交”的场景--mixed默认移动 HEAD 指针并且重置暂存区但保留工作区的内容。效果是回退之后所有改动回到工作区未暂存状态--hardHEAD、暂存区、工作区全部回退到指定提交。这是最“重”的操作会直接丢弃所有后续修改。除非当前分支完全不需要这些改动否则不要轻易使用注意了如果一个提交已经推送到了远程那么git reset只在本地有效想同步到远程还是要强制推送。这又会带来前面说过的风险所以依然建议只对“还没推送的提交”使用reset。6.3 revert安全地撤销历史提交另一种撤销提交时更“安全”的方式是git revertgit revert commit哈希值revert不是把历史回退而是“生成一个新的提交用这个新提交来抵消目标提交的改动”。也就是说历史记录里保留原提交同时多出一个反向提交。这种方式非常适合用在已经推送到远程的公共分支上——因为不改变提交历史队友拉取时不会出现不一致的问题。在多人协作的项目里撤销一个已经合并到主分支的功能我强烈建议用revert而不是reset。即使之后需要恢复这个功能也可以再revert回去整个操作过程完全可追溯。6.4 reflog最后的救命稻草git reflog是所有“后悔”操作的最后一道保险。它会记录 HEAD 指针在本地仓库中的所有变动历史包括被reset --hard丢弃的提交、被rebase改变位置的提交等等。比如不小心执行了git reset --hard发现回退错了想找回丢失的提交可以先git reflog输出里能看到每次操作对应的哈希值和操作说明找到想要恢复的那次提交的哈希值然后git reset --hard 哈希值就能恢复过去的状态。很多人以为reset之后代码就彻底丢了其实本地仓库里的对象还在reflog会保留一段时间。你得知道这个机制到了关键时刻能派大用场。7. 暂存与历史查询两个容易被低估的高频操作7.1 stash临时保存当前工作状态开发过程中常常会遇到这种情况正在一个分支上改着代码突然需要切到另一个分支处理紧急事务。如果此时直接切换分支Git 会阻止你因为有未提交的改动或者把这些改动带过去造成混乱。git stash就是专门解决这个问题的git stash # 暂存当前所有修改 git stash list # 查看暂存列表 git stash pop # 恢复最新的暂存并删除记录 git stash apply # 恢复最新的暂存但保留记录stash pop和stash apply的区别在于是否删除暂存记录。我个人的建议是如果只是应急切换分支用pop就够了恢复之后暂存列表清空免得后面忘记了自己还留着暂存。如果你怀疑暂存的代码可能还需要在多个分支间复制使用那就用apply它会保留记录可以反复恢复。和stash类似的还有一个git stash save 说明信息的写法可以给暂存内容加一句备注方便后面辨认。我在一个多任务并行时期试过同时攒了三个 stash如果没有备注提示过几天根本不知道哪个是哪个。7.2 log 的筛选组合与 blame 定位git log配合参数可以实现很多实用查询场景。比如git log --author用户名 git log --since2025-01-01 --until2025-06-01 git log -S某个关键字-S参数比较特别它用来查找“某个字符串被添加或删除”的提交。想定位某行代码是什么时候引入的时候这个参数效率很高。与定位代码来源相关的还有git blamegit blame 文件名它在每一行的前面标出该行最后一次被修改的提交哈希和作者。查“这行代码是谁写的”“上次谁改过这里”时非常实用。不过逐行查看的方式在文件行数多的时候会比较累实际使用时建议先定位到具体行号范围再用blame查看对应区段。8. 项目实战一套适合团队的 Git 工作流参考8.1 我常用的分支模型与命令组合说了这么多分散的命令最后把它们串成一整套实际可用的工作流。我日常参与的项目一般遵循这样的分支模型main或master始终可部署的稳定版本develop日常开发的主战场feature/*功能分支按功能命名fix/*修复分支实际开发流程是这样的从最新的develop拉一条功能分支git checkout develop git pull git checkout -b feature/xxx在功能分支上进行开发频繁提交git add . git commit -m 完成XX模块的接口对接开发完成后先把自己的分支 rebase 到最新的developgit checkout develop git pull git checkout feature/xxx git rebase develop回到develop分支合并功能分支git checkout develop git merge feature/xxx推送并删除功能分支git push git branch -d feature/xxx这套流程的好处是功能分支始终基于最新代码开发减少合并冲突的概率合并到develop时用merge保留合并记录方便回溯功能提交。8.2 一次完整的本地提交推送实战记录再以一个具体的场景走一遍。假设你在feature/login分支上完成了登录功能的开发准备提交推送# 查看当前状态 git status # 查看修改的具体内容 git diff # 添加需要提交的文件 git add src/pages/login.vue src/api/login.ts # 提交并写好信息 git commit -m 完成登录页面的前端校验逻辑和接口对接 # 推送前先拉取远端最新代码用 rebase 保持历史整洁 git pull --rebase # 推送 git push -u origin feature/login如果pull --rebase时出现冲突按照冲突标记解决后继续执行git add 解决完的文件 git rebase --continue如果中途想要放弃 rebase可以用git rebase --abort回到 rebase 之前的状态。8.3 快速保存修复现场的高效方式还有一种我经常遇到的情况上线后发现紧急 bug需要一个“临时补丁 干净工作区”的组合。假设你的功能分支上正做着开发工作区有一堆改动不想提交但必须立即切到fix分支去修 buggit stash save 登录功能开发中待修复上线bug后继续 git checkout fix/xxx # 修复完之后 commit push git checkout feature/login git stash popstash在这里的价值是你不用为了一次临时修复而做出一个半成品提交也不会因为切换分支把代码搞乱。项目紧急时这套操作是救命级别的体验。9. 常见问题排查与经验速查9.1 六类典型问题的判断思路与解决方案这里整理了一张速查表是我自己在实际使用中反复验证过的你可以直接收藏问题现象可能原因排查思路常用解法push 被拒绝远端有本地没有的提交先 pull 或 fetch 查看差异git pull 解决冲突后重新 pushGit 提示 “not a git repository”当前目录不在仓库内确认路径执行git statuscd到仓库根目录切换分支时提示工作区会被覆盖有未提交修改看git status判断改动范围commit 或 stash 后切换commit 之后发现漏了文件漏执行了 add补 add 并执行--amendgit commit --amend误 commit 想撤回提交未推送决定回退深度git reset --soft或--mixed误 reset 后想找回提交本地引用被重置立即执行 refloggit reset --hard 原哈希9.2 几个减少低级失误的小习惯第一提交前必看git status和git diff。哪怕只是一个小改动花十秒钟扫一眼状态能规避大量的误提交。第二大改动尽量按模块分多次提交不要一个git add .全塞进一次提交。这样出了问题定位时范围更小代码评审也更轻松。第三不确定远程分支状态时多用git fetchgit log origin/分支名来观察不要直接 pull 或 push尤其是公共分支。第四需要强制推送时永远优先--force-with-lease而不是--force。前者相当于加了安全带防止你在不经意间覆盖掉同事刚推送的提交。10. 写在最后的一点真实体会从我自己的经历来看Git 学会容易用好需要沉淀。初期总是记不住参数这太正常了。我的建议是从“先掌握一个场景的一个固定操作”开始比如先熟练git add、git commit、git push这三条形成肌肉记忆之后再逐步补上分支、合并、撤销这些进阶操作。双减到实际操作中有一个很真实的经验想分享遇到问题不要急着去搜索“Git 报错怎么解决”先冷静下来跑一遍git status再想想自己最近执行的命令有哪些。80% 的 Git 问题都可以通过搞清楚“当前状态”和“目标状态”之间的差异来解决——所谓命令只不过是从一个状态迁移到另一个状态的方式而已。这篇文章提到的那些命令和流程都是我个人反复验证过的常规做法。你完全可以根据自己团队的习惯做适当调整关键是把“用 Git 管理好每次改动的来龙去脉”这个底层逻辑记在心里思路清晰了什么命令都是工具用什么顺手、合理才是最重要的。