Git 分支管理实战:从创建到合并的完整指南
学会了 commit 之后,你掌握了 Git 的"存档"功能。分支则是 Git 的"平行宇宙"功能——在同一个项目里同时推进多条互不干扰的开发线,最后再合并到一起。
为什么需要分支
假设你在做一个网站,现在要加一个用户登录功能。这个功能需要三天时间,期间你要改好几个文件。
如果没有分支,你改了一半的代码就留在工作区里。这时候老板说有个紧急 bug 要修,你怎么办?把登录功能的代码先删掉,修完 bug 再重新写一遍?太痛苦了。
有了分支,你可以新建一个 login 分支,在上面安心做登录功能。修 bug 切回 main 分支,两边互不干扰。做完了再合并回去。这就是分支的日常价值。
Git 的分支实现得非常高效——创建一个分支只是创建一个41字节的指针文件,指向某个提交。几乎不占空间,不需要复制任何文件。这也是 Git 和其他版本控制系统(如SVN)最大的区别之一。SVN 的分支需要复制整个目录,又慢又占空间;Git 的分支则是"秒建"。
基本操作
查看当前有哪些分支:
git branch
输出中当前分支前面有个星号标记,绿色高亮。
创建新分支:
git branch login
这只是在本地创建了一个叫 login 的分支,远程仓库上还没有。
切换到新分支:
git checkout login
注意:切换分支前确保当前工作区是干净的(改动都已提交或 stash),否则未提交的改动会被带到新分支上。
创建并切换到新分支(常用,比上面两步快):
git checkout -b login
用新语法(Git 2.23+ 推荐):
git switch -c login
在 login 分支上正常修改、提交,这些操作只影响 login 分支,main 分支的提交历史完全不受影响。这就是"平行宇宙"的含义——两个分支各自独立推进,互不知道对方的存在。
做完了,切回 main 分支:
git checkout main
把 login 分支合并进来:
git merge login
合并之后,main 分支就有了 login 分支的所有改动。如果合并顺利,Git 会打开编辑器让你写一条合并提交说明,保存退出就完成了。
合并冲突
合并并不总是顺利的。如果 main 分支和 login 分支都改了同一个文件的同一行代码,Git 就不知道该听谁的,这时候会提示"冲突"(CONFLICT)。
打开冲突文件,你会看到这样的标记:
<<<<<<< HEAD
这是 main 分支的内容
=======
这是 login 分支的内容
>>>>>>> login
<<<<<<< HEAD 和 ======= 之间是当前分支(main)的内容,======= 和 >>>>>>> login 之间是要合并分支(login)的内容。你需要手动决定保留哪部分,或者把两部分都保留/删掉任意一方。解决方式:
- 手动编辑文件,删掉冲突标记,保留你想要的内容
- 保存文件
git add 冲突文件git commit
冲突就解决了。
减少冲突的实用技巧
冲突是 Git 里最让人头疼的部分。但仔细想想,冲突其实是好事——它说明 Git 发现了潜在的问题,让你自己决定而不是随便选一个。减少冲突的几个技巧:
- 频繁合并:如果你一周才合并一次,积累的改动多,冲突概率就高。每天或每半天从 main 拉一次最新代码,冲突小步解决比集中大爆发容易处理
- 分工明确:团队内避免多个人同时修改同一个文件的同一区域
- 善用 rebase:在提交 PR 前先
git rebase main,把 main 的最新改动提前消化掉 - 用 mergetool:如果冲突很复杂,可以用可视化合并工具(如 VSCode 自带的对比界面、Beyond Compare 等),比纯文本标记直观得多
merge vs rebase
合并分支有两种主要方式,merge 和 rebase,它们代表了两种不同的历史哲学。
merge 是保留完整的历史记录。它创建一个新的"合并提交"来连接两条分支线。优点:历史真实,保持了代码实际发展的时间线。缺点:如果分支很多,历史图中会有大量交叉的合并线,看起来很复杂。
main: A -- B -- M (merge commit)
\ /
login: C -- D
rebase 是把你当前分支的提交"移植"到目标分支的最新提交之后。优点:最终历史是一条直线,非常干净、易读。缺点:改写了提交历史(重新计算了提交ID),在公共分支上 rebase 可能导致其他人的工作基础对不上。
main: A -- B
\
login: C' -- D' (C和D被"重播"到B之后)
黄金法则:自己的开发分支用 rebase 保持干净,合并到 main 分支用 merge 保留清晰的合并记录。永远不要在公共分支(main、develop)上执行 rebase——那会让所有基于这些提交工作的人的历史记录全部错位。
rebase 操作示例
# 假设你在 login 分支,main 分支有了新提交
git checkout login
git rebase main
这会把 login 分支上的提交逐个"重播"到 main 最新提交的后面。如果在重播某个提交时遇到冲突,解决后 git rebase --continue 继续即可。如果中途想放弃,git rebase --abort 回到操作前的状态。
交互式 rebase 还可以在重播过程中修改、合并、重排、删除提交——这在整理零散提交后用。
删除分支
合并完之后,login 分支就没用了,删掉它:
git branch -d login
-d 是安全删除——如果分支还没合并(改动还没被纳入 main),Git 会提醒你并拒绝删除。如果想强制删除(确定不要了),用 -D:
git branch -D login
删除本地分支后,如果远程仓库上有同名分支,也需要手动删除:
git push origin --delete login
养成及时清理已合并分支的习惯,否则分支列表会越来越长。一个实用的工作流是:完成一个功能的合并后立刻删掉对应分支。
实际开发中的分支策略
实际项目中,一般不是随便建分支,而是有一套命名和管理规则。
最简单的策略(适合个人或两人小项目)
main分支永远是稳定的,可随时部署到生产环境- 开发新功能就建
feature/功能名分支,做完合并回 main 并删掉 - 修 bug 就建
hotfix/问题描述分支,修完合并回 main 并删掉
Git Flow(适合项目版本节奏明确的中大型团队)
main(生产环境)
└── develop(每日开发基线)
├── feature/*(功能开发)
├── release/*(发版准备)
└── hotfix/*(线上紧急修复)
main:生产环境代码,只接受来自 release 或 hotfix 的合并develop:每日开发基线,所有功能的起点和终点feature/*:从 develop 出发,做完合并回 developrelease/*:从 develop 出发准备发版,只修 bug 不加功能,发版后同时合并到 main 和 develophotfix/*:从 main 出发修线上 bug,修完合并回 main 和 develop
GitHub Flow(适合持续交付的敏捷团队)
比 Git Flow 更简单:
main分支永远可部署- 新功能从
main拉出分支 - 开发完成后提 PR,经过 code review 后合并到 main
一条核心规则
永远不要直接在 main 分支上改代码。main 分支只接受来自其他分支的合并,不接受直接提交。这样 main 永远是可部署的、稳定的。如果每个人都顺手在 main 上改两行,main 很快就变成了一堆没有经过 review 的草稿代码。
一个实用的 stash 技巧
有时候你正在写代码,突然要切换到另一个分支(比如同事让你帮忙看一个 main 分支的问题),但当前分支的修改还不想提交(因为还没做完、commit 不完整)。
这时候用 git stash:
git stash
它会把当前工作区的改动和暂存区的改动"藏"起来(存到一个栈结构里),工作区恢复到干净状态。你可以放心切到别的分支工作。
切回来之后恢复改动:
git stash pop
改动就回来了——并且从 stash 栈中弹出(pop 弹出,peek 只查看不弹出)。如果你存了多个 stash,用 git stash list 查看列表,git stash apply stash@{2} 恢复特定的那一条。
一个实用的 stash 场景:写了一半功能,突然要紧急修复线上 bug。先 stash 当前工作 → 切 main 分支修 bug → 修完提交 → 切回原分支 → stash pop 恢复。整个过程不超过两分钟,不用担心工序混乱。
最后
分支是 Git 最核心的功能,也是团队协作的基础。理解了分支,你就理解了 Git 设计哲学的一半。
刚开始用分支可能会觉得麻烦,尤其是冲突解决。但用熟了之后你会发现,没有分支根本没法做稍微复杂一点的项目。建议从今天开始,每接触一项新 Git 命令或操作,都先从 git log --oneline --graph --all 查看当前分支的历史图,直观感受每一次操作对分支历史的影响。
一句话总结:分支是平行宇宙,合并是宇宙交汇。用好了平行宇宙,你的开发效率会上一个台阶。