6、Git分支管理(一):分支的概念、git branch、git checkout、git switch、git merge(快进合并与三方合并)
分支,是 Git 最核心的概念之一。说实话,我刚用 Git 那会儿,对分支的理解就是「复制一份代码出来改」。后来踩了不少坑才明白——分支不是复制,而是指针。嗯,这个认知差异,直接决定了你后面会不会把仓库搞得一团糟。
6.1 分支到底是什么?
Git 的分支,本质上就是一个指向某个提交对象的可变指针。每次你提交,这个指针就自动向前移动。默认情况下,这个指针叫 master 或 main。
你想想看,如果每次开新功能都复制整个项目目录,那得浪费多少磁盘空间?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 switch 和 git 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 分支管理核心逻辑图
下面这张图展示了分支的创建、切换和合并的核心流程。我特意把快进合并和三方合并放在一起对比,方便你理解它们的区别。
6.7 避坑指南与最佳实践
做分支管理这么多年,我总结了几条铁律,分享给你:
- 分支命名要有意义:别用
fix、test这种模糊的名字。我习惯用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