4、分支管理策略:Git分支的本质,创建与切换分支,合并分支(Fast-forward与Three-way merge),解决合并冲突

分支,是Git里最核心的概念之一。说实话,我见过不少团队,Git用得挺溜,但分支管理一塌糊涂。今天咱们就把分支这件事彻底讲透。

4.1 Git分支的本质:一个指针而已

很多人觉得分支很神秘。其实说白了,Git的分支就是一个指向某个提交对象的指针。就这么简单。

你想想看,每次你用git commit,Git就会创建一个提交对象,里面包含作者、时间、快照、以及指向上一个提交的指针。而分支,就是贴在这些提交上的标签。

核心理解:

  • master/main:默认分支,指向最新的提交
  • HEAD:指向当前所在的分支
  • 其他分支:就是指向某个提交的指针,可以随时移动

我记得刚带团队时,有个同事问我:「我创建了10个分支,是不是内存爆炸了?」我笑了。每个分支就是一个40字节的SHA-1哈希值,存1000个分支也就40KB。Git的设计就是这么轻量。

Git分支本质:指针指向提交 C1 C2 C3 C4 C5 feature master HEAD 分支 = 指向提交的指针 | HEAD = 指向当前分支的指针 创建分支 = 新建一个指针,成本极低

4.2 创建与切换分支

创建分支的命令很简单:

# 创建分支(不切换)
git branch feature-login

# 创建并切换
git checkout -b feature-login

# 或者用新语法
git switch -c feature-login

我个人习惯用git switch -c,语义更清晰。不过老项目里很多人还在用checkout -b,也没毛病。

我的习惯:

分支命名用类型/功能格式,比如feature/login、bugfix/issue-123、hotfix/critical-patch。这样一看就知道这个分支是干嘛的。

切换分支时,Git会做三件事:

  1. 把工作目录恢复到目标分支的快照状态
  2. 把HEAD指向目标分支
  3. 更新暂存区

注意:切换分支前,确保工作区是干净的。如果有未提交的修改,Git会拒绝切换,或者把修改带到目标分支——这往往不是你想要的。

我曾经有一次,在master上改了文件忘了提交,直接切到feature分支。结果那个修改也跟着过去了,提交后才发现把master的改动混进了feature分支。嗯,从那以后我养成了git status检查的习惯。

4.3 合并分支:Fast-forward vs Three-way merge

合并分支,就是把两个分支的工作成果整合到一起。Git提供了两种合并方式。

4.3.1 Fast-forward 合并

这是最简单的情况。当目标分支(比如master)从创建feature分支后没有新的提交,那么Git只需要把master指针直接向前移动到feature的位置。

# 在 master 分支上执行
git merge feature-login

# 输出类似:
# Updating a1b2c3d..e4f5g6h
# Fast-forward

说白了,就是「你走在我前面,我直接追上来」。没有分叉,没有冲突,干净利落。

4.3.2 Three-way merge(三方合并)

当两个分支都有各自的提交历史时,Fast-forward就不够用了。Git需要做三方合并:

  • 共同祖先:两个分支分叉前的那个提交
  • 当前分支:你所在的分支的最新提交
  • 目标分支:你要合并进来的分支的最新提交

Git会比较这三个版本,自动合并差异。如果同一行代码在两个分支上都被修改了,就会产生冲突。

Three-way Merge 示意图 祖先 M1 M2 F1 F2 合并 master feature 三方合并的三方: ① 共同祖先(base)→ ② master最新(ours)→ ③ feature最新(theirs) Git 自动比较三者的差异,尝试合并 同一行两边都改 → 冲突,需要手动解决

4.4 解决合并冲突

冲突不可怕,可怕的是不知道怎么解决。我见过新手看到冲突提示就慌了,直接git merge --abort。其实冲突是日常。

当冲突发生时,Git会在文件中插入冲突标记:

<<<<<<< HEAD
这是当前分支的代码
int result = a + b;
=======
这是要合并进来的代码
int result = calculate(a, b);
>>>>>>> feature-login

你需要手动编辑这个文件,决定保留哪一段,或者重新写一段。然后:

# 标记冲突已解决
git add resolved-file.c

# 完成合并
git commit

解决冲突的小技巧:

  • 用git status查看哪些文件有冲突
  • 用git diff查看冲突的具体内容
  • 用IDE的合并工具(VS Code、IntelliJ都自带)可视化解决
  • 解决后一定要git add标记,否则Git认为冲突还在

我曾经在一个大型重构项目中,合并了200多个文件,冲突有30多个。当时用了git mergetool配合Meld工具,一个一个过。说实话,那次之后我对项目代码的理解深了很多——因为每个冲突都逼着我去理解两边的代码逻辑。

4.5 合并策略对比

特性 Fast-forward Three-way merge
历史是否分叉 否,线性历史 是,有分叉点
是否需要共同祖先 不需要 需要
冲突概率 几乎为零 可能产生冲突
适用场景 短周期、单线开发 多人并行、长周期分支
合并提交 不产生额外提交 产生一个合并提交

我的建议:

团队协作时,我倾向于用--no-ff强制走三方合并。虽然会产生一个合并提交,但能保留分支的完整历史。你回头看git log时,能清楚看到「哦,这里合并了一个feature分支」。线性历史虽然干净,但丢失了分支的语义信息。

# 强制走三方合并
git merge --no-ff feature-login

嗯,分支管理就这些核心内容。记住:分支是廉价的,多用分支,勤合并,小步提交。这样你的项目历史才会清晰可控。