6、Git分支管理(一):分支的概念、git branch、git checkout、git switch、git merge(快进合并与三方合并)

分支,是 Git 最核心的概念之一。说实话,我刚用 Git 那会儿,对分支的理解就是「复制一份代码出来改」。后来踩了不少坑才明白——分支不是复制,而是指针。嗯,这个认知差异,直接决定了你后面会不会把仓库搞得一团糟。

6.1 分支到底是什么?

Git 的分支,本质上就是一个指向某个提交对象的可变指针。每次你提交,这个指针就自动向前移动。默认情况下,这个指针叫 mastermain

你想想看,如果每次开新功能都复制整个项目目录,那得浪费多少磁盘空间?Git 的做法聪明得多——它只记录每次提交的差异,分支只是贴在这些提交上的「便利贴」。

核心理解:分支 = 指向提交的指针。创建分支 = 创建一个新指针。切换分支 = 把 HEAD 指向另一个指针。

我个人习惯把分支想象成「平行宇宙」。主分支是现实世界,你开一个新分支,就等于创造了一个平行宇宙。你在里面怎么折腾都行,不影响现实世界。等测试好了,再合并回来。

6.2 git branch:管理你的分支

git branch 是最基础的分支操作命令。它的用法其实很简单,但很多人只用了它 20% 的功能。

常用操作一览

命令 作用
git branch 列出本地所有分支,当前分支前面会标 *
git branch <name> 创建一个新分支(但不会切换过去)
git branch -d <name> 删除一个已合并的分支
git branch -D <name> 强制删除一个分支(即使没合并)
git branch -m <old> <new> 重命名分支
git branch -a 列出所有分支(包括远程分支)

我的习惯:每次开新分支前,先跑 git branch 看一眼当前在哪个分支上。别笑,我见过太多人在 main 上直接开改,然后一脸懵地问「我的代码去哪了」。

创建分支示例

# 查看当前分支
$ git branch
* main

# 创建一个新分支,用于开发登录功能
$ git branch feature/login

# 再次查看,分支创建成功,但还在 main 上
$ git branch
  feature/login
* main

注意,git branch feature/login 只是创建了一个新指针,它指向当前所在的提交。这时候两个指针指向同一个提交,但之后各自提交就会分道扬镳。

6.3 git checkout vs git switch:切换分支的今与昔

早期 Git 只有 git checkout 来切换分支。但这个命令身兼数职——既能切分支,又能恢复文件,还能创建分支。功能太多,反而容易用错。

我记得刚学 Git 那会儿,想切分支结果把文件给覆盖了,就是因为 git checkout 的参数写错了。后来 Git 2.23 版本引入了 git switchgit restore,把「切换分支」和「恢复文件」这两个职责分开了。

git checkout(传统方式)

# 切换到已有分支
$ git checkout feature/login

# 创建并切换到新分支(-b 参数)
$ git checkout -b feature/logout

git switch(推荐方式)

# 切换到已有分支
$ git switch feature/login

# 创建并切换到新分支(-c 参数)
$ git switch -c feature/logout

避坑指南:我曾经在团队里看到有人用 git checkout -- file 想切分支,结果把文件修改给丢弃了。所以我现在都推荐团队用 git switch 切分支,git restore 恢复文件,职责清晰,不容易手滑。

6.4 git merge:合并的艺术

合并分支,是团队协作中最频繁的操作。Git 提供了两种合并方式:快进合并和三方合并。理解它们的区别,能帮你避免很多冲突。

6.4.1 快进合并(Fast-Forward Merge)

什么时候会发生快进合并?简单说,就是你的分支是从当前分支的最新提交分出去的,而且分出去之后,当前分支没有新的提交。

说白了,就是一条直线。Git 只需要把指针往前挪一下就行,不需要创建新的合并提交。

# 场景:在 main 上创建 feature 分支,feature 有提交,main 没有
# 初始状态:
# main: A---B
# feature:     C---D

# 在 main 上合并 feature
$ git switch main
$ git merge feature

# 结果:main 指针直接移到 D
# main: A---B---C---D
# feature:     C---D

特点:没有额外的合并提交,历史是一条直线,非常干净。

6.4.2 三方合并(Three-Way Merge)

如果两个分支都有各自的提交,Git 就不能简单地挪指针了。它需要找到两个分支的「共同祖先」,然后合并两个分支的修改。

为什么叫三方?因为涉及三个提交:共同祖先、当前分支的最新提交、要合并的分支的最新提交。Git 会对比这三个版本,自动合并代码。

# 场景:main 和 feature 都有新提交
# 初始状态:
# main: A---B---C
# feature: A---D---E

# 在 main 上合并 feature
$ git switch main
$ git merge feature

# 结果:生成一个新的合并提交 M
# main: A---B---C---M
# feature: A---D---E---/
# M 是合并提交,有两个父提交:C 和 E

我的经验:三方合并时,如果两个分支改了同一个文件的同一行,就会产生冲突。这时候 Git 会停下来,让你手动解决。我曾经在合并一个大型功能时,遇到了 30 多个冲突文件,一个一个解决花了一整个下午。从那以后,我养成了「小步提交、频繁合并」的习惯。

6.5 分支合并的完整流程示例

咱们走一遍完整的流程,从创建分支到合并,看看每一步发生了什么。

# 1. 在 main 上创建并切换到新分支
$ git switch -c feature/payment

# 2. 在 feature/payment 上做开发
$ echo "payment module" > payment.txt
$ git add payment.txt
$ git commit -m "add payment module"

# 3. 切回 main,发现 main 也有新提交
$ git switch main
$ echo "hotfix" > hotfix.txt
$ git add hotfix.txt
$ git commit -m "hotfix"

# 4. 合并 feature/payment 到 main
$ git merge feature/payment

# 如果两个分支都改了同一个文件,会提示冲突
# 手动解决冲突后:
$ git add .
$ git commit -m "merge feature/payment"

# 5. 删除已合并的分支
$ git branch -d feature/payment

6.6 分支管理核心逻辑图

下面这张图展示了分支的创建、切换和合并的核心流程。我特意把快进合并和三方合并放在一起对比,方便你理解它们的区别。

Git 分支核心操作流程 ① 创建分支 git branch feature → 创建指针,指向当前提交 ② 切换分支 git switch feature → HEAD 指向 feature ③ 合并分支 快进合并 直线历史,无合并提交 三方合并 生成合并提交,有分叉 条件: 快进:目标分支无新提交 三方:双方都有新提交 ④ 结果 分支合并完成,可删除源分支:git branch -d feature

6.7 避坑指南与最佳实践

做分支管理这么多年,我总结了几条铁律,分享给你:

  • 分支命名要有意义:别用 fixtest 这种模糊的名字。我习惯用 feature/功能名bugfix/问题编号hotfix/紧急修复 这种格式。
  • 合并前先更新:合并之前,先 git switch main 然后 git pull,确保本地 main 是最新的。我曾经因为本地 main 太旧,合并后产生了一堆莫名其妙的冲突。
  • 小步提交,频繁合并:一个分支不要活太久。超过一周的分支,合并时大概率会痛不欲生。
  • 合并后及时删除分支:已经合并的分支留着干嘛?占地方不说,还容易混淆。用 git branch -d 清理掉。
  • 不要强制删除未合并的分支:git branch -D 是核武器,用了就找不回来了。除非你确定那个分支的代码永远不需要了。

我曾经踩过的坑:有一次在 main 上直接 git merge feature,结果冲突了。我手动解决冲突后,忘了 git add 就直接 git commit,导致冲突标记留在了代码里。编译直接挂了。从那以后,我解决冲突后一定会先 git diff --check 检查一下有没有残留的冲突标记。

分支管理是 Git 的精髓。掌握了分支,你就掌握了团队协作的钥匙。这一章我们讲了分支的概念和基本操作,下一章会深入分支策略和远程分支的管理。嗯,先把这些基础打牢,后面的内容才能用得顺手。


公众号:蓝海资料掘金营,微信deep3321