前言: 本文将简单介绍一下Git的分支管理
1. 为什么需要分支
在 Git 中,每一次提交都会生成一个新的提交对象,提交之间通过父子关系串成一条历史线。我们平时说的"分支",本质上就是指向某个提交的可移动指针。
设想你身处于一个洪荒背景的村庄, 打算参加一场村内演武比赛从而迎娶村长女儿, 你和你的对手处于竞争关系且初始实力相近,为了获胜,你决心苦练一门武功, 但是藏经阁内武功共享且学习能力相近. 为了成功迎娶村长女儿, 你该如何抉择?

但是如果你偶见间坠崖获生, 偶然间学会了一气化三清, 你就可以进行分身操作, 这个分身就是分支, 分身从创建时刻会继承创建人的知识以及记忆,这是你就可以让分身去另外学一门武功,到时候收回分身,定要打得对方一个落花流水口呀~~

而GIT就可以起到创建这样一个和"分身"类似的分支,那分支有什么功能呢?
分支最直接的价值是隔离工作:
- 开发新功能时,可以在自己的分支上频繁提交,不影响主分支的稳定性。
- 修复 bug 时,可以从稳定分支临时拉出一个修复分支,修完再合并回去。
- 尝试实验性代码时,可以大胆修改;如果方案废弃,直接删除分支即可。
如果没有分支,试想一下你的老板突然要求为项目追加一个新功能, 如果项目已经上线了, 直接在主分支上操作的话, 一个未完成的新功能可能会污染主分支;如果迟迟不提交,又有丢失进度的风险。分支刚好解决了这两个问题:既能保存开发过程,又不影响别人或主线代码。
2. HEAD、master/main 与分支指针
理解 Git 分支,关键是理解三个概念:
commit:一次提交,代表仓库在某一时刻的快照。- 分支名:例如
master、main、dev,本质是一个指向 commit 的指针。 HEAD:指向"当前所在分支"的指针。
讲义中使用的是 master 作为主分支名。现在很多新仓库默认主分支名是 main,但原理完全一致。
可以通过下面的文件观察 Git 内部状态:
bash
cat .git/HEAD
cat .git/refs/heads/master
如果 .git/HEAD 内容类似:
text
ref: refs/heads/master
说明当前 HEAD 指向 master 分支。每次在当前分支提交后,当前分支指针都会向前移动到最新提交。

3. 创建、查看与切换分支
查看本地分支
bash
git branch
输出中带 * 的分支表示当前所在分支:
text
dev
* master
创建分支
bash
git branch dev
这会创建一个名为 dev 的分支。刚创建时,dev 和当前分支通常指向同一个提交。
需要注意:git branch dev 只创建分支,不会自动切换过去。
切换分支
bash
git checkout dev
现代 Git 也可以使用语义更清晰的写法:
bash
git switch dev
切换分支后,HEAD 会改为指向新分支,工作区文件也会切换成该分支对应提交的状态。

创建并切换分支
常用快捷命令:
bash
git checkout -b dev1
等价于:
bash
git branch dev1
git checkout dev1
现代写法是:
bash
git switch -c dev1

4. 在分支上开发并提交
切换到 dev 分支后,可以正常修改、暂存、提交:
bash
git checkout dev
vim ReadMe
git add .
git commit -m "modify ReadMe"
此时新的提交只属于 dev 分支。切回 master 后,可能会发现刚才修改的内容"不见了",这是正常现象。
原因是:
- 在
dev上提交后,dev指针向前移动。 master指针还停留在原来的提交。- 切回
master时,工作区会恢复到master所指向的版本。
这正是分支隔离的效果。

5. 合并分支
如果希望主分支获得 dev 上的修改,需要把 dev 合并到当前分支。就像想要提高本体战力,赢得比武大赛, 最终也要把化三清的分身回收, 共享功法
一般先切回目标分支,再执行合并:
bash
git checkout master
git merge dev
git merge dev 的意思是:把 dev 分支的修改合并到当前分支。
如果 master 从创建 dev 后没有产生新提交,Git 通常会执行 Fast-forward 合并。所谓快进合并,就是直接把 master 指针移动到 dev 当前指向的提交上。
快进合并的优点是简单、快速;缺点是历史记录中看不出曾经发生过一次分支合并。

6. 删除分支
当功能分支已经合并完成后,可以删除它:
bash
git branch -d dev
不能删除当前所在分支,所以删除前通常要先切回其他分支:
bash
git checkout master
git branch -d dev
-d 是安全删除:如果该分支还有未合并的提交,Git 会拒绝删除,防止误删工作成果。
7. 合并冲突
实际开发中,两个分支可能修改了同一个文件的同一部分。例如:
dev1把某一行改成bbbmaster把同一行改成ccc
这时执行:
bash
git merge dev1
Git 无法判断应该保留哪一份修改,就会产生冲突:
text
CONFLICT (content): Merge conflict in ReadMe
Automatic merge failed; fix conflicts and then commit the result.
可以用 git status 查看冲突文件:
bash
git status
冲突文件中会出现类似标记:
text
<<<<<<< HEAD
当前分支的内容
=======
被合并分支的内容
>>>>>>> dev1
解决冲突的标准流程是:
- 打开冲突文件,手动编辑成最终想要的结果。
- 删除
<<<<<<<、=======、>>>>>>>这些冲突标记。 - 重新暂存文件。
- 提交一次合并结果。
对应命令:
bash
vim ReadMe
git add .
git commit -m "merge ReadMe"
这里的最后一次提交很重要。冲突解决不是只改文件就结束,必须提交,Git 才会认为这次合并完成。
可以用下面的命令查看分支合并历史:
bash
git log --graph --pretty=oneline --abbrev-commit

8. Fast-forward 与 --no-ff 合并策略
默认情况下,只要条件允许,Git 会使用 Fast-forward 模式合并。
例如:
bash
git checkout master
git merge dev
如果 master 没有分叉,Git 只是把 master 指针移动到 dev 的最新提交上。这样历史很简洁,但删除 dev 后,从日志里不容易看出这个提交曾经来自一个独立分支。
这该如何 理解呢? 最开始我也有这样的困惑, 既然都创建出了dev分支,那么master 不是一定会分叉吗, 使用快进合并为什么会看不出来提交的来源呢?
其实这里我们要弄清楚一点,我们创建出一个dev分支这个操作,虽然在现实里我们可能会将其理解为从一个树干上产生分支 ,但是实际上只是创建了一个dev标签,当我们创建出dev分支切换到dev中,进行了一系列修改,add. commit 操作之后. 再回到master分支, 由于master分支在此期间没有任何修改操作, 以上在dev分支上的操作就像是和master分支在同一直线之上,并未产生分叉这样的场景, 既然是dev和master都位于同一直线之上, 即使我们在master主分支上进行了merge 合并操作, 就像是把一条直线上的dev这个节点给抹除掉, 虽然master会新增一些从dev中合并得到的内容,但是master无法得知是从哪合并过来的

以上情况是在合并前,master 没有修改操作时,使用的默认快进合并, 但是如果master有commit操作呢 ,那基于master的commit,会有两种情况产生
-
- master修改的文件和dev中修改文件不一致, 则在分支树就就像是master在不同的方向延伸了一个树枝,最后在merge合并时,会自动把这两个不同修改杂糅在一块,dev重新汇拢到master上
-
- master 修改的文件和dev中相同,则会发生冲突, 具体表现就是出现上文图片中的的==== , <<<< ,>>>>这样的符号,需要我们手动处理冲突
如果希望保留"发生过一次合并"的历史,可以禁用快进模式:
bash
git merge --no-ff -m "merge with no-ff" dev
--no-ff 会强制生成一个新的 merge commit。就像是强行留痕. 这样做的好处是分支脉络更清楚,适合团队协作和需要保留功能开发痕迹的项目。

简单对比:
| 合并方式 | 特点 | 适合场景 |
|---|---|---|
Fast-forward |
历史线性、简洁 | 小改动、个人开发、临时分支 |
--no-ff |
保留合并节点,能看出分支历史 | 团队协作、功能分支、发布前合并 |
9. 常见分支策略
一种常见的分支管理方式是:
master或main:稳定分支,只放可发布版本。dev:开发主线,日常开发结果先合并到这里。feature/*:功能分支,用来开发具体需求。fix/*或bugfix/*:修复分支,用来处理线上或主分支 bug。
典型流程:
bash
# 从 dev 拉出功能分支
git checkout dev
git checkout -b feature-login
# 开发并提交
git add .
git commit -m "add login feature"
# 回到 dev 合并功能
git checkout dev
git merge --no-ff -m "merge feature-login" feature-login
# 删除功能分支
git branch -d feature-login
主分支应该尽量保持稳定,不建议把日常半成品直接提交到主分支。
10. 使用 stash 临时保存工作现场
开发中经常遇到一种情况:当前功能写到一半,还不能提交,但突然需要切换分支去修 bug。
如果工作区有未提交修改,直接切换分支可能会失败,或者把半成品带到另一个分支中。此时可以使用 git stash 临时保存工作现场。
bash
git stash
执行后,当前工作区会变干净:
bash
git status
然后就可以切换到主分支,创建修复分支:
bash
git checkout master
git checkout -b fix_bug
修复完成后合并回主分支:
bash
git add .
git commit -m "fix bug"
git checkout master
git merge --no-ff -m "merge fix_bug branch" fix_bug
git branch -d fix_bug
回到原来的开发分支后,可以查看 stash 列表:
bash
git stash list
恢复现场并删除 stash:
bash
git stash pop
如果只想恢复但保留 stash 记录,可以用:
bash
git stash apply
之后再手动删除:
bash
git stash drop
多个 stash 可以指定恢复:
bash
git stash apply stash@{0}
11. 先在开发分支合并主分支,降低主分支风险
当 master 修复了 bug,而 dev 还在继续开发时,dev 可能看不到 master 上的新修复。
最终当然可以让 master 直接合并 dev,但如果冲突很复杂,就会在主分支上解决冲突,风险较高。
更稳妥的做法是:先在自己的开发分支上合并主分支,把冲突解决并测试通过,再让主分支合并开发分支。
流程如下:
bash
# 在开发分支上吸收 master 的最新修改
git checkout dev2
git merge master
# 如果有冲突,先在 dev2 上解决并提交
git add .
git commit -m "merge master"
# 测试通过后,再回到 master 合并 dev2
git checkout master
git merge dev2
这样即使冲突解决过程出错,也主要影响开发分支,不会直接污染稳定主分支。
12. 强制删除未合并分支
如果某个功能分支已经开发了一部分,但需求被取消了,这个分支可能没有合并回主分支。
此时使用安全删除:
bash
git branch -d dev3
Git 会提示该分支尚未完全合并,拒绝删除。
如果确认这个分支不再需要,可以强制删除:
bash
git branch -D dev3
-D 要谨慎使用,因为它会丢弃这个分支上尚未合并的提交引用。除非确认代码不要了,否则优先使用 -d。

13. 分支管理命令速查
| 目标 | 命令 |
|---|---|
| 查看本地分支 | git branch |
| 创建分支 | git branch dev |
| 切换分支 | git checkout dev / git switch dev |
| 创建并切换分支 | git checkout -b dev / git switch -c dev |
| 合并分支到当前分支 | git merge dev |
| 禁用快进合并 | git merge --no-ff -m "message" dev |
| 安全删除分支 | git branch -d dev |
| 强制删除分支 | git branch -D dev |
| 查看状态 | git status |
| 查看图形化提交历史 | git log --graph --pretty=oneline --abbrev-commit |
| 暂存工作现场 | git stash |
| 查看 stash | git stash list |
| 恢复并删除 stash | git stash pop |
| 恢复但保留 stash | git stash apply |
| 删除 stash | git stash drop |
14. 一个推荐的日常工作流
个人或小团队可以先掌握下面这套流程:
bash
# 1. 从开发分支创建功能分支
git checkout dev
git checkout -b feature-demo
# 2. 在功能分支上开发
git add .
git commit -m "add demo feature"
# 3. 开发过程中同步 dev 的最新变化
git checkout feature-demo
git merge dev
# 4. 解决冲突并测试通过后,合并回 dev
git checkout dev
git merge --no-ff -m "merge feature-demo" feature-demo
# 5. 删除功能分支
git branch -d feature-demo
如果中途要临时修主分支 bug:
bash
git stash
git checkout master
git checkout -b fix-bug
# 修复并提交
git add .
git commit -m "fix bug"
git checkout master
git merge --no-ff -m "merge fix-bug" fix-bug
git branch -d fix-bug
# 回到原开发分支继续工作
git checkout feature-demo
git stash pop
15. 小结
Git 分支管理的核心不是背命令,而是理解"分支是指针,HEAD 指向当前分支"这个模型。
创建分支,就是新建一个指针;切换分支,就是让 HEAD 指向另一个分支;提交时,当前分支指针向前移动;合并时,Git 尝试把另一条提交线整合到当前分支。
实际开发中需要重点掌握:
- 用分支隔离新功能和实验代码。
- 合并前确认自己在哪个分支上。
- 遇到冲突时,手动解决后必须重新提交。
--no-ff可以保留分支合并历史。git stash适合临时保存未完成工作。- 删除分支优先用
-d,确认废弃后才用-D。
一句话概括:分支让 Git 不只是"保存历史",还可以"组织开发过程"。掌握分支管理后,Git 才真正成为团队协作和复杂项目开发中的高效工具。