1. 分支到底是什么——先想明白再动手我见过不少刚接触 Git 的同学第一步就跑去敲git branch xxx但其实连“分支”这个概念都没吃透。这不怪大家因为 Git 的分支和 SVN 的分支完全是两码事。SVN 的分支是目录的物理拷贝建一个分支等于把整个项目复制一份而 Git 的分支本质上就是一个指向提交对象的可变指针创建分支只是新建一个 41 字节的文件里面存了一个 commit 的哈希值。你创建一百个分支仓库体积几乎不变。理解这个底层机制有什么用直接决定了你操作时的心态。既然分支只是指针那么切换分支、删除分支、创建分支都是瞬间完成的根本不用像 SVN 那样等半天。而且 Git 鼓励你“多建分支、勤建分支”因为它的成本极低这也是 Git 工作流能玩出各种花样的根基。举个例子你在main分支上开发到一半突然要临时修一个线上 bug。SVN 时代你得先提交半成品或者 stash 半天Git 时代你只需要git checkout -b hotfix切出去修完合回来再切回原来的分支继续干全程干净利落。另外要提醒一点很多教程上来就让你git init然后直接敲命令但我觉得在动手敲键盘之前先用一张图把仓库、暂存区、工作区、提交对象、分支指针这几个概念的关系搞清楚比记住一百条命令都管用。你把“分支是指针”这句话刻在脑子里后面遇到的 80% 的困惑都会迎刃而解。2. 创建与切换分支一天用几十次的三个命令2.1 切分支到底切的是什么很多人用git checkout切分支以为只是“换个文件夹里的文件”。其实切换分支时 Git 做了三件事更新工作目录的文件内容、更新暂存区、把HEAD指针移动到目标分支的最新提交上。听起来简单但实际操作中经常有人在切分支时报错说工作区有未提交的修改不让切。这个报错的底层逻辑是什么Git 为了保证你的修改不丢失凡是会与目标分支产生冲突的未提交更改都会阻止你切换。比如你在dev分支改了config.js的第三行而main分支上config.js的第三行内容不同那 Git 就不让你切因为它不知道你想保留哪个版本。这时候有三条路把改动提交到当前分支git commit用git stash暂存改动切过去再git stash pop恢复如果你确认这些改动不要了git checkout -- .放弃。我个人的建议是养成好习惯切分支之前看一眼工作区状态。git status这个命令看似基础但能帮你挡住绝大多数事故。踩过几次“改了半天发现改错分支”的坑之后你就知道这条有多值钱了。2.2 分支创建的正确姿势创建分支的命令很简单git branch 分支名只是在当前位置创建指针并不会切过去git checkout -b 分支名是创建并切换一步到位。我开发时几乎只用第二种因为创建完分支的第一件事大概率就是开始写代码。但这里有个容易忽视的细节你从哪个提交上创建分支直接决定了新分支的起点。如果从main的最新提交拉分支那新分支天然包含main上所有已提交的代码如果你在某个旧提交上用git checkout 哈希值切过去再建分支那就是一条“历史支线”。团队协作时大家还要约定分支命名规范。我的习惯是功能分支用feat/xxxbug 修复用fix/xxx发布用release/xxx紧急修补用hotfix/xxx。这样只看分支名就能知道这分支是干嘛的配合上git log --oneline --graph --all看历史时整个图特别清晰。2.3 切换分支后归属感注意你的 HEAD还有个新手特别容易懵的场景用 IDEA 或 VSCode 这类 IDE 时右下角可以看到当前分支名但很多人不知道在纯命令行环境下怎么看自己在哪里。git branch -vv能看到所有本地分支以及当前分支当前分支前面会带个星号git status第一行也会显示当前分支名和与远程的领先/落后状态。我习惯用git status判断当前状态而不是猜。有些人切完分支之后忘了自己在哪直接在错误分支上提交了代码再想挪过来就得用 cherry-pick 或者 reset麻烦得很。一句话切分支之前git status切完分支之后git status两次两秒钟能省你两小时的返工。3. 合并分支实战不只是“点一下按钮”那么轻松3.1 深入了解 Fast-forward 和 “真合并”合并分支的核心命令是git merge 分支名。但很多新手不知道merge其实有两种完全不同的走向理解它们的区别才能看懂 Git 的历史图。第一种叫Fast-forward快进合并。当main分支从拉出dev分支之后一个提交都没有新增时Git 会把main的指针直接“快进”到dev的最新提交上不对准确说是把dev的提交串直接添加到main的指针之前整个过程不产生新的合并提交。历史是一条笔直的线干净利落。第二种叫三方合并3-way merge。当两个分支在各自的道路上都有新提交时Git 必须做真正的合并它找到两个分支的“共同祖先”提交再对比当前分支和目标分支的差异然后生成一个全新的merge commit。这个提交有两个父提交历史图上会看到分叉再汇合的结构。我在实际项目中看到很多团队根本不看合并类型一律默认。结果就是历史图乱成一团。如果你想让主干保持线性可以用git merge --ff-only强制只允许快进合并一旦发现会生成合并提交就直接报错逼着你先rebase再合并。如果相反你想保留功能分支的完整合并痕迹用git merge --no-ff即使能快进也强制生成一个合并提交保留“这里合并过一条功能分支”的记录。3.2 合并时选择哪种工具命令行还是 IDE合并分支时很多人直接在 IDEA 或 VSCode 里点 Merge 按钮。说实话我日常也这么干因为 IDE 的图形化对比界面确实直观。但有几种场景我会乖乖回到命令行合并之后需要精细控制-m提交信息时命令行更顺手需要做--no-ff强制合并这种操作命令行更明确脚本化或 CI/CD 场景命令行是必然选择。推荐的做法是理解命令行的底层机制再结合 IDE 的图形界面辅助查看。比如在命令行执行git merge dev然后打开 IDEA 的 Git Log 面板看分支图比对合并结果效率非常高。千万别只会点按钮命令行是根IDE 是叶。另外多说一嘴合并前的准备工作也很重要。我用 VSCode 和 IDEA 多年发现一个王者习惯——合并且推送之前先跑一遍测试用例。很多项目 CI 会拦着但本地开发时养成习惯更好因为合错了再回滚远程分支的代价远高于本地多跑几分钟。3.3 合并远程分支别忽略 origin 的视角团队协作中我最常被问的一个问题是“为什么我本地分支合并了 mainpush 上去却提示冲突”这背后的原因通常是本地落后于远程。远端origin/main被别人推了新提交你本地main还停留在旧状态你基于旧main开发完毕想回合自然会产生冲突。处理思路是先git fetch origin拉取远程最新状态然后git merge origin/main或git rebase origin/main把远程的新提交并进来解决冲突后再git push。记住一句话在 push 之前先把远程的最新代码拉下来合并一遍你会少解决很多莫名其妙的冲突。如果你用了git pull它本质上是fetch merge的组合命令。但这里有个细节git pull默认会用你当前的合并策略如果你没配好可能导致生成的合并提交不符合预期。我个人的习惯是用git fetch代替git pull分步看变化可控性好很多。这不是道德绑架就是纯经验建议。4. 冲突解决的完整链路从恐慌到从容4.1 一个典型冲突长什么样冲突不可怕可怕的是不知道怎么解决。我见过太多人一看到冲突提示就慌了其实冲突是 Git 的正常保护机制它不会偷偷改你的代码而是把决定权交还给你。所谓冲突就是同一文件的同一区域在两个分支上被改成了不同内容 Git 不知道你最终想要哪一种。触发合并时Git 会明确告诉你“CONFLICT (content): Merge conflict in xxx.java”。这时候打开冲突文件你会看到这样的特殊标记 HEAD 这是 main 分支上的内容 这是 dev 分支上的内容 dev HEAD和之间是当前所在分支HEAD的内容和 dev之间是待合并分支的内容。你的任务就是删掉这些标记保留你要的内容可以是其中一边、两边都保留或者写出第三版。注意标记符号一定要删干净否则文件会语法报错这个低级错误我见过不少次。4.2 解决冲突的两种姿势第一种是纯手工修改。打开冲突文件看标记决定保留什么然后删标记、保存文件。删除标记后执行git add 文件告诉 Git “这个冲突我解决了”最后git commit完成合并。这里的关键点在于解决完所有冲突后必须用git add把文件标记为已解决然后提交合并没有自动完成等你这一步。第二种是借助 IDE 的可视化三路合并工具。IDEA 里点 Git - Resolve Conflicts会弹一个面板左边是本地版本右边是远程版本中间是合并结果区你甚至可以逐行点击箭头选择保留哪边内容。这个工具比手工改标记直观十倍尤其适合不熟悉标记符号的新手。我的建议是小冲突手工解决大冲突用工具。三五行的冲突手工删标记可能更快涉及多个文件的复杂冲突一定要用合并工具逐一过一遍每个文件防止漏改。另外解决冲突时严禁全局“接受左边/右边”一定要仔细看上下文判断代码逻辑是否仍然成立。4.3 冲突解决后的一系列连锁动作解决完冲突、提交合并之后事情还没结束。你需要跑一遍构建、跑一遍受影响模块的测试确认没有引入新的问题。前几年我吃过一次亏解决 CSS 冲突时选择保留 A 分支的样式结果 B 分支配套的 HTML 结构没改页面直接乱掉。所以冲突解决不仅是“文本层面的合并”更是“逻辑层面的整合”。如果这次合并不是最终目标你还要考虑推送到远程后是否影响其他人。我的经验是冲突解决后尽快推送到远程避免本地停留太久产生新的分叉。当然推送前最好让相关同事 review 一下合并结果尤其是逻辑复杂的部分。这里列一个冲突解决的速查清单步骤操作说明1git merge 分支名开始合并观察冲突提示2git status查看哪些文件冲突3打开冲突文件看标记判断保留内容4git add 文件标记该文件冲突已解决5重复 3-4直到所有冲突都解决6git commit完成合并提交7构建 测试验证逻辑完整性8git push推送到远程4.4 用 abort 和 reflog 兜底也有一种情况冲突太复杂或者你发现合错分支了想反悔。这时候别硬着头皮解决Git 给了你后悔药——git merge --abort。执行后Git 会把工作区恢复到合并之前的状态当这次合并完全没发生过一样。但注意这个命令只能在合并过程中、没有手动提交之前生效。如果你已经提交了合并结果甚至 push 到远程了那就得另想办法。本地可以用git reset --hard 合并前的提交哈希回滚远程可以用git revert生成反向提交。这里我特别强调推送到远程的分支不要用 force push 回滚除非你确认这个分支只有你一个人在用否则会搞乱别人的本地仓库。用git revert才是安全的选择虽然历史里多一条记录但至少不伤人。还有一个兜底神器git reflog它记录了你本地仓库所有的 HEAD 移动记录。就算你 reset 错了、rebase 搞砸了只要 reflog 还在就能找到操作前的哈希把“丢了”的提交捞回来。我把 reflog 当成 Git 的时光机每次遇到“哎呀我是不是把代码搞丢了”的危机时先想想它。5. 进阶Rebase 和 Merge 怎么选5.1 Rebase 的底层逻辑和操作合并分支除了merge还有rebase。git rebase 主分支做的事情是把你当前分支的每个提交“摘下来”然后按顺序“重新放映”到主分支的最新提交之上。结果就是历史变成一条直线没有分叉没有多余的 merge commit。操作上切到你的功能分支git rebase main。如果中途有冲突解决后不要急着 commit而是git add 文件然后git rebase --continue让 rebase 继续播放剩下的提交。如果发现这条路走不通git rebase --abort可以完整地回到 rebase 之前的状态。但 rebase 有个众所周知的禁忌不要对已经推送到远程、且别人正在基于它工作的分支做 rebase。因为 rebase 会改写提交哈希别人基于旧提交拉的分支会和你产生“幽灵冲突”排查起来极其痛苦。一句话原则公共分支用 merge私有分支可以用 rebase 保持整洁。5.2 何时用 Merge、何时用 Rebase我把选择标准总结成三句话功能分支拉取主干最新代码时如果不想在功能分支上留下一堆“merge from main”的提交用rebase把功能分支合回主干时为了保留功能开发的完整版本历史用merge --no-ff如果你根本不在乎历史是否线性只想省事那默认merge就好。实际项目中很多团队会把“拉最新的 main 用 rebase、合回主干用 merge”组合起来用。效果是主干历史相对整洁但功能分支内的提交被“重演”过哈希变了。这个模式我强烈推荐给小组自用因为它兼顾了可读性和安全性。至于 Git Flow、GitHub Flow 之类的完整工作流都是在这基础上定制的你可以慢慢摸索。6. 常见问题与排查技巧实录6.1 为什么我创建的分支“不见了”新手经常干一件事创建一个分支切过去写代码commit觉得很稳。过了几天发现这个分支在哪怎么找不到其实大概率是分支还在只是你忘了它叫什么名字。git branch -a可以看到所有本地和远程分支。如果你在别的分支上刚才那个分支自然不会高亮吓自己一跳而已。还有一种可能是把分支合进主干后用git branch -d xxx删掉了本地分支。想找回如果你已经 push 过远程git checkout -b xxx origin/xxx拿回来就行如果你没 push 就删了用git reflog找到对应提交的哈希再git branch xxx 哈希重建分支。6.2 IntelliJ IDEA 里切换分支时的坑IDEA 右下角的分支菜单确实方便但很多人遇到过这种情况在 IDEA 里切分支代码没变、报错信息看不懂。我排查过几次后发现最普遍的原因是文件被 IDE 缓存住了尤其是有未保存的修改时。所以用 IDEA 操作 Git 分支前先CtrlS保存所有文件必要时File - Invalidate Caches清理缓存。另一个 IDEA 相关的坑是项目用了 Maven 或 Gradle切换分支后依赖版本变了但 IDE 没自动刷新导致一堆红色报错。解决办法是切完分支后手动刷新依赖。花两分钟刷新比对着红色波浪线和 IDEA 生一肚子气强多了。6.3 如何查看分支是从哪拉出来的团队协作时经常要回答“这个分支从哪个分支拉出来的”这种问题。直接看分支名看不出来时可以用git reflog show 分支名查看这个分支的所有引用变动记录最早的记录一般就是创建时的位置。或者直接git merge-base --fork-point main 你的分支它能找到这个分支是从 main 的哪个提交分叉出去的。这个信息对排查代码漂移、了解功能分支的基线特别有用。我用一个土办法每个功能分支创建时在分支描述或者 PR 描述里写清楚“基于哪个分支创建”省得事后翻 reflog。6.4 远程分支和本地分支的 “不同步恐惧”本地删了一个分支远程却还在Push 时它又跑出来远程有人删了分支你本地却还在保留这些常见不同步。解决方法也不难查看本地和远程分支对应关系git branch -vv清理本地“幽灵分支”远程已删除的本地引用git remote prune origin或者git fetch --prune删除远程分支git push origin --delete 分支名。还有一种情况是IDEA 里删了分支但菜单里还能选。这多半是 IDEA 的 Git 面板还缓存着旧引用。File - Reload All from Disk或者重启 IDE 就能解决。别问我为什么知道我花十分钟排查过这种“灵异事件”。6.5 解决冲突后提交信息里的 “Merge branch” 乱入用merge时每次合并都会生成默认的提交信息类似 “Merge branch dev into main”。如果你觉得信息不够直观可以用git merge --no-ff dev -m 你的自定义信息。如果你想让历史更优雅可以在合并之前先 rebase 功能分支到 main 上然后快进合并这样就不会出现“Merge branch”记录。有些团队用 pull requestPR或 merge requestMR的方式来合代码这时 CI 平台还会生成额外的合并提交。如果你在本地 merge 了再 push到远程平台上一看它又生成一个合并提交变成“双重合并”。正确的做法是如果项目走 PR/MR 流程本地就只 rebase不手动 merge把合并这步交给平台。6.6 误提交到 main 分支怎么撤场景很常见本来想在feature/login上开发结果忘了切分支直接在main上提交了一个 commit。还没 push怎么撤销git reset --soft HEAD~1撤销提交但保留改动在暂存区git reset --mixed HEAD~1默认撤销提交保留改动在工作区git reset --hard HEAD~1撤销提交也丢弃改动慎用。如果已经 push 了那就用git revert 哈希生成一个反向提交再 push这是最安全的方式。用 reset 强推会造成团队成员本地仓库混乱这不是技术问题是沟通问题。我经常跟团队说凡是要改动公共分支历史先问自己一句别人有没有基于它干活有就别 force push。7. 一体化场景实操从拉分支到合回主干的全记录最后以一个完整场景串一遍。假设你接到一个新需求给后台管理系统加一个导出功能。你所在项目的默认分支是main远程名是origin。第一步把本地main更新到最新git checkout main git pull origin main第二步从最新main创建功能分支并切换过去git checkout -b feat/export-excel第三步开发中按常规流程提交代码git add . git commit -m feat: add export excel logic第四步开发中后期主分支被别人推了新代码你不想落后于是合并主分支git fetch origin git rebase origin/main如果 rebase 过程有冲突解决后git add 冲突文件 git rebase --continue第五步功能开发完毕合并回主干git checkout main git pull origin main git merge feat/export-excel --no-ff -m merge feat/export-excel into main第六步推送并清理git push origin main git branch -d feat/export-excel整个过程看起来平平无奇但每一步都藏着前面讲过的细节切分支前看状态、合并前拉最新、提交信息写清楚、合并类型心里有数。按照这个节奏走绝大多数合并冲突都能避免就算真的碰上了解决起来也不慌。我在实际项目里就是一直用这套流程身边带过的新同学跟着跑几遍基本都能独立上手。分支管理这件事说到底不是背命令而是建立一套肌肉记忆什么时候该建分支、什么时候该合并、冲突来了怎么决策。多练几次形成习惯你会发现 Git 是少有的“用得越熟、越觉得它强大”的工具。