第四章 分支 —— Git 的杀手级特性
说到Git,最令人惊叹的功能莫过于它的分支管理模型。很多初学者初次接触时或许会觉得有些抽象,但实质上,分支不过是一个指向特定提交对象的可移动指针。Git默认的分支名称通常是master或main,但这只是一种惯例,你完全可以根据喜好自由命名。
想象一下,当你用Git记录项目历史时的典型场景:
master 分支
v
C0 --- C1 --- C2 --- C3 (HEAD->master)
当你创建一个新分支时,实际发生了什么?其实就是生成了一个新指针,指向最近的提交。操作就是这么简单:
C0 --- C1 --- C2 --- C3 (HEAD->master, develop)
切换到哪个分支,工作目录就会自动更新为那个分支的最新快照。因此,切换分支时你能实时看到文件内容发生变化,这正是Git设计的精妙之处。
基本分支操作
在日常开发中,以下Git分支命令几乎每天都会用到:
# 列出所有本地分支(当前分支前带 *)
git branch
# 列出远程分支
git branch -r
# 列出所有分支(本地 + 远程)
git branch -a
# 创建新分支(但不会自动切换)
git branch feature-login
# 切换到一个分支
git checkout feature-login
# 或使用新命令
git switch feature-login
# 创建并切换到新分支(一步完成)
git checkout -b feature-login
git switch -c feature-login
# 删除分支(必须确保当前不在该分支上)
git branch -d feature-login # 已合并的分支
git branch -D feature-login # 强制删除未合并的分支
# 重命名当前分支
git branch -m new-name
# 重命名任意分支
git branch -m old-name new-name
这里有一个重要细节:git branch -d只能删除已经合并过的分支;如果你试图删除一个尚未合并的分支,Git会友好地提醒你;如果确实要强制删除,请使用-D参数。
合并(Merge)—— 整合分支的核心操作
合并操作是Git分支管理中的关键环节,但具体操作其实非常直观:
# 首先切换到目标分支(例如 master)
git checkout master
# 将另一个分支(例如 feature)合并到当前分支
git merge feature
# 合并时强制创建合并提交(非快进模式)
git merge --no-ff feature
合并通常分为两种常见场景:
快进合并(Fast-forward):当目标分支直接是源分支的后继时,指针直接向前移动即可:
Before:
master → C1 → C2
feature → C3 (基于C2)
After (fast-forward):
master → C1 → C2 → C3 (master 指向 C3)
三方合并(3-way merge):当两个分支出现分叉时,Git会创建一个新的合并提交:
Before:
C1 → C2 → C4 (master)
→ C3 (feature)
After:
C1 → C2 → C4 → C5 (merge commit)
→ C3 ---
关键区别在于:快进合并不会产生额外的合并提交,历史呈现为一条直线;而三方合并会生成一个合并提交,清晰标明两个分支在此交汇。
合并冲突解决
说到合并,就不得不提冲突。当两个分支修改了同一文件的同一行,或者一个分支删除了另一个分支修改过的文件时,Git无法自动合并。冲突发生时的典型提示如下:
$ git merge feature
Auto-merging hello.py
CONFLICT (content): Merge conflict in hello.py
Automatic merge failed; fix conflicts and then commit the result.
冲突标记在文件中看起来像这样:
<<<<<<< HEAD
print("Hello from master")
=======
print("Hello from feature")
>>>>>>> feature
解决冲突的步骤其实很简单:
- 手动编辑冲突文件,删除冲突标记,保留最终需要的代码
- 执行
git add标记文件为已解决 - 执行
git commit完成合并提交
还有两个实用的辅助命令:
# 使用外部工具解决冲突
git mergetool # 会打开配置好的合并工具(如 vimdiff, kdiff3)
# 取消本次合并,恢复到合并前状态
git merge --abort
想要模拟冲突场景进行练习?试试这个示例:
# 创建并切换到 feature 分支
git checkout -b feature
echo "Feature line" > test.txt
git add test.txt && git commit -m "feature: add test.txt"
# 切回 master,修改同一文件
git checkout master
echo "Master line" > test.txt
git add test.txt && git commit -m "master: add test.txt"
# 合并时产生冲突
git merge feature
# 解决冲突后提交
变基(Rebase)—— 另一种整合方式
如果你觉得合并提交的历史记录不够干净,可以尝试变基操作。Git rebase会将一系列提交按顺序“复制”到另一个分支的顶端,使历史保持线性:
# 在 feature 分支上执行
git checkout feature
git rebase master
# 效果:将 feature 分支中从 master 分叉后的所有提交,依次应用到 master 的最新提交之后
分叉的情况:
C1 --- C2 (master)
C3 --- C4 (feature)
执行 git rebase master 后:
C1 --- C2 (master)
C3' --- C4' (feature) # C3' 和 C4' 是全新的提交对象
然后切换到 master 执行快进合并:
git checkout master
git merge feature # 现在变成快进合并

这里有一个黄金法则必须牢记:不要对已经推送到远程仓库的分支执行 rebase! 否则会导致其他协作者基于旧分支的工作陷入混乱,后果可能很严重。
交互式变基(Interactive Rebase)—— 灵活修改历史
如果你需要整理提交历史,交互式变基是一个非常强大的工具:
# 对最近 3 个提交进行交互式变基
git rebase -i HEAD~3
执行后会打开编辑器,列出待处理的提交列表,你可以对每个提交执行不同的操作:
pick 7c6a5b8 Add login feature
pick 3a7c8f9 Fix typo in login
pick 2b9d4e1 Add logout feature
# Rebase 2b9d4e1..7c6a5b8 onto 2b9d4e1
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# d, drop = remove commit
几个常用的操作:
- squash:将当前提交合并到前一个提交中
- reword:修改提交信息
- edit:修改提交内容(例如添加遗漏的文件)
- drop:直接删除提交
比如,你想把最后两个提交合并成一个:
pick abc123 Add feature
squash def456 Fix typo
保存退出后,你可以编辑合并后的提交信息。经过这样整理后的提交历史会显得非常清晰有序。
