一、 引言:为什么需要分支?
想象一下这样的场景:你正在为即将发布的新版本V1.0开发一个核心功能,这个功能代码量很大,需要一周时间才能完成。与此同时,测试团队发现了一个线上V1.0版本的高危Bug,需要立即修复并发布V1.1版本。
如果没有分支,你会陷入两难:
-
如果基于未完成的V1.1代码修复Bug,那么修复代码会包含大量不稳定的半成品。
-
如果等待V1.1开发完成,线上Bug无法及时修复。
这就是分支 要解决的核心问题。分支就像是科幻电影里的平行宇宙,让你可以在同一个代码库中创建独立的开发线,互不干扰地同时进行多个任务。
二、 理解分支的本质
2.1 从时间线到平行宇宙
在之前的学习中,我们知道Git的每次提交都会形成一条时间线。在只有一条时间线的情况下,这个分支就是主分支(master/main)。
实际上,Git的分支本质上是一个指向某个提交对象的可变指针 。Git的默认分支名字叫 master。当我们进行提交时,master分支指针会自动向前移动,指向最新的提交。
HEAD指针是一个特殊的指针,它指向你当前所在的分支(也就是当前激活的"平行宇宙")。
初始状态下,整个提交历史看起来是这样的:
提交1 (cff9d1e) <-- 提交2 (14c12c3) <-- 提交3 (d95c13f) <-- master分支指针 <-- HEAD指针
2.2 分支的底层实现
让我们通过查看 .git目录来验证分支的本质:
# 查看HEAD指针指向
liu@139-159-150-152:~/gitcode$ cat .git/HEAD
ref: refs/heads/master # HEAD指向master分支
# 查看master分支指向的提交
liu@139-159-150-152:~/gitcode$ cat .git/refs/heads/master
d95c13f8a12b4e2c1f8a5e3c7b8d9a0e1f2a3b4c5 # 指向最新的提交
# 查看分支文件
liu@139-159-150-152:~/gitcode$ ls .git/refs/heads/
master # 目前只有master分支
可以看到,分支就是存储在 .git/refs/heads/目录下的文件,文件内容就是该分支指向的提交ID。
三、 分支的基本操作
3.1 查看分支
liu@139-159-150-152:~/gitcode$ git branch
* master # *号表示当前所在分支
3.2 创建分支
现在我们来创建第一个分支 dev:
liu@139-159-150-152:~/gitcode$ git branch dev # 创建dev分支
liu@139-159-150-152:~/gitcode$ git branch # 查看所有分支
dev # 新创建的dev分支
* master # 当前仍在master分支
创建分支时发生了什么?
-
Git创建了一个新的指针
dev,指向当前所在的提交 (和master指向同一个提交)。 -
HEAD指针仍然指向
master,所以你现在还在master分支上。
用图示表示当前状态:
提交1 <-- 提交2 <-- 提交3 <-- master, dev, HEAD
3.3 切换分支
要开始在 dev分支上工作,我们需要切换到该分支:
liu@139-159-150-152:~/gitcode$ git checkout dev # 切换到dev分支
Switched to branch 'dev'
liu@139-159-150-152:~/gitcode$ git branch
* dev # 现在当前分支是dev
master
一步创建并切换分支(更常用的方式):
liu@139-159-150-150-152:~/gitcode$ git checkout -b feature # 创建并切换到feature分支
Switched to a new branch 'feature'
切换分支时发生了什么?
-
HEAD指针从指向
master改为指向dev。 -
Git会用
dev分支指向的提交内容替换工作区的文件。
3.4 在新分支上开发
现在我们在 dev分支上进行开发:
# 当前在dev分支,修改ReadMe文件
liu@139-159-150-152:~/gitcode$ echo "Developing new feature on dev branch" >> ReadMe
liu@139-159-150-152:~/gitcode$ cat ReadMe
hello world - version3
Developing new feature on dev branch
# 提交修改到dev分支
liu@139-159-150-152:~/gitcode$ git add ReadMe
liu@139-159-150-152:~/gitcode$ git commit -m "add new feature on dev branch"
[dev 3740dce] add new feature on dev branch
1 file changed, 1 insertion(+)
现在分支状态变成了:
提交1 <-- 提交2 <-- 提交3 <-- master
↓
提交4 (dev分支的新提交) <-- dev <-- HEAD
3.5 切换回master分支验证
liu@139-159-150-152:~/gitcode$ git checkout master # 切换回master
Switched to branch 'master'
liu@139-159-150-152:~/gitcode$ cat ReadMe
hello world - version3 # 看不到dev分支的修改!
为什么看不到dev分支的修改?因为master分支还停留在原来的提交,而dev分支已经向前发展了。
3.6 合并分支
当 dev分支的功能开发完成后,我们需要将其合并到 master分支:
liu@139-159-150-152:~/gitcode$ git merge dev # 将dev分支合并到当前分支(master)
Updating d95c13f..3740dce
Fast-forward
ReadMe | 1 +
1 file changed, 1 insertion(+)
liu@139-159-150-152:~/gitcode$ cat ReadMe
hello world - version3
Developing new feature on dev branch # 现在master也能看到修改了
Fast-forward(快进合并) :由于 master分支是 dev分支的直接祖先,Git只是简单地将 master指针向前移动到了 dev指向的提交。
合并后的状态:
提交1 <-- 提交2 <-- 提交3 <-- 提交4 <-- master, dev
3.7 删除分支
合并完成后,dev分支的使命就完成了,可以删除:
liu@139-159-150-152:~/gitcode$ git branch -d dev # 删除dev分支
Deleted branch dev (was 3740dce)
liu@139-159-150-152:~/gitcode$ git branch
* master # 只剩master分支
四、 解决合并冲突
分支合并并不总是这么顺利。当两个分支对同一个文件的同一部分进行了不同的修改时,就会产生合并冲突。
4.1 制造冲突场景
-
创建并切换到新分支:
liu@139-159-150-152:~/gitcode$ git checkout -b dev1
Switched to a new branch 'dev1' -
在dev1分支上修改文件:
liu@139-159-150-152:~/gitcode echo "Feature developed on dev1 branch" >> ReadMe liu@139-159-150-152:~/gitcode git add ReadMe
liu@139-159-150-152:~/gitcode$ git commit -m "modify ReadMe on dev1"
[dev1 0854245] modify ReadMe on dev1
1 file changed, 1 insertion(+) -
切换回master分支并修改同一文件:
liu@139-159-150-152:~/gitcode git checkout master liu@139-159-150-152:~/gitcode echo "Feature developed on master branch" >> ReadMe
liu@139-159-150-152:~/gitcode git add ReadMe liu@139-159-150-152:~/gitcode git commit -m "modify ReadMe on master"
[master c10f6d0] modify ReadMe on master
1 file changed, 1 insertion(+)
现在两个分支的状态:
提交3 <-- 提交4 (dev1的修改) <-- dev1
↓
提交5 (master的修改) <-- master
4.2 合并并解决冲突
liu@139-159-150-152:~/gitcode$ git merge dev1
Auto-merging ReadMe
CONFLICT (content): Merge conflict in ReadMe # 检测到冲突!
Automatic merge failed; fix conflicts and then commit the result.
Git无法自动合并,需要手动解决冲突。
4.3 查看冲突文件
liu@139-159-150-152:~/gitcode$ cat ReadMe
hello world - version3
Developing new feature on dev branch
<<<<<<< HEAD
Feature developed on master branch # 当前分支(master)的内容
=======
Feature developed on dev1 branch # 要合并的分支(dev1)的内容
>>>>>>> dev1
Git用特殊标记标出了冲突部分:
-
<<<<<<< HEAD:当前分支的内容开始 -
=======:分隔符 -
>>>>>>> dev1:要合并的分支的内容结束
4.4 手动解决冲突
编辑文件,选择保留哪部分内容,或者进行修改整合:
liu@139-159-150-152:~/gitcode$ vim ReadMe
liu@139-159-150-152:~/gitcode$ cat ReadMe
hello world - version3
Developing new feature on dev branch
Feature developed on both branches - resolved conflict # 手动解决后的内容
4.5 完成合并
解决冲突后,需要将文件标记为已解决并提交:
liu@139-159-150-152:~/gitcode$ git add ReadMe # 标记冲突已解决
liu@139-159-150-152:~/gitcode$ git commit -m "merge ReadMe and resolve conflict"
[master 2976afc] merge ReadMe and resolve conflict
4.6 查看合并历史
liu@139-159-150-152:~/gitcode$ git log --graph --pretty=oneline --abbrev-commit
* 2976afc (HEAD -> master) merge ReadMe
|\
| * c594fd1 (dev1) modify ReadMe
* | c10f6d0 modify ReadMe
|/
* 3740dce add new feature on dev branch
* d95c13f add version3
* 14c12c3 add version2
* cff9d1e add version1
图形化日志清晰地显示了分支的合并历史。
五、 分支管理策略
5.1 禁用Fast-forward合并
在之前的简单合并中,我们使用了Fast-forward模式。但这种模式下,分支历史信息会丢失。为了保留完整的分支信息,我们可以使用 --no-ff参数:
liu@139-159-150-152:~/gitcode$ git checkout -b dev2
liu@139-159-150-152:~/gitcode$ echo "New feature from dev2" >> ReadMe
liu@139-159-150-152:~/gitcode$ git add ReadMe
liu@139-159-150-152:~/gitcode$ git commit -m "add feature from dev2"
liu@139-159-150-152:~/gitcode$ git checkout master
liu@139-159-150-152:~/gitcode$ git merge --no-ff -m "merge with no-ff" dev2
Merge made by the 'recursive' strategy.
ReadMe | 1 +
1 file changed, 1 insertion(+)
使用 --no-ff合并会创建一个新的合并提交,即使是可以Fast-forward的情况。
5.2 实际开发中的分支策略
在实际团队开发中,推荐使用以下分支策略:
-
master分支:始终保持稳定,仅用于发布正式版本。
-
develop分支:集成开发分支,包含要发布到下一个版本的所有功能。
-
feature分支:功能开发分支,从develop分支创建,完成后再合并回develop。
-
release分支:发布准备分支,用于测试和修复发布前的bug。
-
hotfix分支:紧急修复分支,用于快速修复线上bug。
master o---------o-----------------o----------- (发布点)
\ / /
develop o-----o---o---o---o-----o---o---o-----
/ \ /
feature o---o o---o (功能开发分支)
release o---o---o---o (发布准备)
hotfix o---o (紧急修复)
六、 分支实战技巧
6.1 保存工作现场(git stash)
当你在一个分支上开发到一半,需要紧急切换到其他分支处理问题时,可以使用 git stash保存当前工作进度:
# 在dev分支上开发到一半
liu@139-159-150-152:~/gitcode$ echo "half done work" >> file.txt
liu@139-159-150-152:~/gitcode$ git stash # 保存工作现场
Saved working directory and index state WIP on dev2: 41b082f modify ReadMe
liu@139-159-150-152:~/gitcode$ git status # 工作区变干净了
On branch dev2
nothing to commit, working tree clean
# 处理完紧急任务后恢复工作现场
liu@139-159-150-152:~/gitcode$ git stash pop # 恢复并删除stash
6.2 强制删除分支
如果要删除一个未合并的分支,需要使用 -D参数强制删除:
liu@139-159-150-152:~/gitcode$ git branch -D dev3 # 强制删除未合并的分支
Deleted branch dev3 (was cd2f149)
七、 总结
本章我们深入学习了Git分支管理的核心概念和操作:
-
分支的本质:指向提交的可变指针,创建分支几乎零成本。
-
基本分支操作:创建、切换、合并、删除。
-
合并冲突解决:识别冲突标记,手动解决后提交。
-
分支策略:Fast-forward vs --no-ff,实际项目中的分支模型。
-
实用技巧:git stash保存工作现场。
分支是Git最强大的功能之一,它让并行开发、功能隔离、版本发布变得异常简单。在下一篇中,我们将学习Git的另一个重要方面:远程仓库操作,了解如何与团队协作和代码托管平台(如Gitee、GitHub)交互。