跳转至

分支管理

🎯 学习目标

  • 理解 Git 分支的本质(指针)
  • 熟练掌握分支的创建、切换、合并与删除
  • 理解 Fast-forward、三方合并、Rebase 的区别与适用场景
  • 掌握冲突解决的实战方法
  • 了解主流分支策略(Git Flow、GitHub Flow、Trunk Based)
  • 学会使用 Cherry-pick 跨分支应用提交

1. 分支的本质

Git 的分支是极其轻量级的——它本质上只是一个指向某个提交对象的可移动指针

Text Only
                         main
  C0 ◄── C1 ◄── C2 ◄── C3
                        HEAD
  • 每次 git commit,当前分支指针自动前移
  • HEAD 是一个特殊指针,指向你当前所在的分支
  • 创建分支 = 创建一个新指针(几乎零成本,不复制文件)
Text Only
  创建并切换到 feature 分支后:

                         main
  C0 ◄── C1 ◄── C2 ◄── C3
                       feature
                        HEAD

💡 核心理解:Git 分支不是复制文件夹,它只是一个 41 字节的文件(存储 SHA-1 哈希值),所以创建分支几乎是瞬间完成。


2. 分支基本操作

2.1 创建与切换

Bash
# 查看所有分支
git branch           # 本地分支
git branch -a        # 所有分支(含远程)
git branch -v        # 查看各分支最后一次提交

# 创建分支
git branch feature/login

# 切换分支(推荐使用 switch,更语义化)
git switch feature/login        # Git 2.23+
git checkout feature/login      # 经典方式

# 创建并切换(一步到位)
git switch -c feature/login     # Git 2.23+
git checkout -b feature/login   # 经典方式

# 基于指定提交创建分支
git switch -c bugfix abc1234

# 基于远程分支创建本地分支
git switch -c feature/login origin/feature/login

2.2 合并与删除

Bash
# 合并分支(先切到目标分支)
git switch main
git merge feature/login

# 删除已合并的分支
git branch -d feature/login

# 强制删除未合并的分支(⚠️ 可能丢失提交)
git branch -D experimental

# 删除远程分支
git push origin --delete feature/login

# 重命名分支
git branch -m old-name new-name

# 查看已合并/未合并的分支
git branch --merged
git branch --no-merged

3. 合并策略详解

3.1 Fast-forward 合并

当目标分支是当前分支的直接后继(没有分叉),Git 只需将指针前移。

Text Only
  合并前:
  main: C0 ← C1 ← C2
                      \
  feature:             C3 ← C4

  合并后(Fast-forward):
  main: C0 ← C1 ← C2 ← C3 ← C4
Bash
git switch main
git merge feature/login
# Fast-forward

# 强制禁用 Fast-forward(保留分支历史)
git merge --no-ff feature/login -m "merge: 合并登录功能"

保留合并历史 --no-ff 的效果:

Text Only
  main: C0 ← C1 ← C2 ──────── M (merge commit)
                      \       /
  feature:             C3 ← C4

💡 建议:团队协作时使用 --no-ff,保留完整的功能开发历史,方便日后追溯。

3.2 三方合并(Three-way Merge)

当两个分支各自有新的提交(产生分叉),Git 会找到它们的共同祖先,进行三方合并并创建一个新的合并提交

Text Only
  合并前:
  main:    C0 ← C1 ← C2 ← C5
                      \
  feature:             C3 ← C4

  合并后:
  main:    C0 ← C1 ← C2 ← C5 ← M (merge commit)
                      \         /
  feature:             C3 ← C4
Bash
git switch main
git merge feature/login
# 如果有冲突,手动解决后:
git add .
git commit

3.3 Git Merge vs Git Rebase

Merge(合并)

Bash
git switch main
git merge feature
Text Only
  C1 ← C2 ← C5 ← M (merge commit)
        \        /
         C3 ← C4

优点:保留完整历史,非破坏性操作 缺点:历史中会有很多合并提交,图形较复杂

Rebase(变基)

Bash
git switch feature
git rebase main
Text Only
  变基前:
  main:    C1 ← C2 ← C5
                \
  feature:       C3 ← C4

  变基后:
  main:    C1 ← C2 ← C5
                       \
  feature:              C3' ← C4'   # C3、C4 被"重放"到 C5 之后

然后再合并(此时是 Fast-forward):

Bash
git switch main
git merge feature  # Fast-forward
# 结果:C1 ← C2 ← C5 ← C3' ← C4'

优点:历史线性、干净、易读 缺点:改写历史(commit hash 变了),协作时需小心

对比总结

特性 Merge Rebase
历史 保留真实历史,有合并节点 线性历史,没有合并节点
安全性 非破坏性,安全 改写历史,有风险
使用场景 公共分支合并 私有分支更新、整理提交
冲突处理 一次解决所有冲突 逐个提交解决冲突
可读性 复杂项目历史可能混乱 历史清晰直观

🚫 Rebase 黄金法则

永远不要 rebase 已经推送到公共仓库的分支!

Bash
# ❌ 绝对不要这样做
git switch main
git rebase feature    # 不要 rebase 公共分支!

# ✅ 正确做法:rebase 自己的特性分支
git switch feature
git rebase main       # 将自己的分支变基到 main 最新位置

原因:Rebase 会改写 commit 历史(改变 hash),如果你 rebase 了一个其他人也在使用的分支,其他人拉取后会遇到历史冲突的噩梦。


4. 冲突解决实战

4.1 什么时候会产生冲突

  • 两个分支修改了同一文件的同一位置
  • Git 无法自动判断应该保留哪个修改

4.2 冲突标记

Text Only
<<<<<<< HEAD
这是当前分支(main)的内容
=======
这是合并分支(feature)的内容
>>>>>>> feature

4.3 手动解决冲突

Bash
# 1. 尝试合并
git merge feature
# CONFLICT (content): Merge conflict in file.txt

# 2. 打开冲突文件,编辑为期望的内容
# 删除冲突标记 <<<<<<<、=======、>>>>>>>

# 3. 标记为已解决
git add file.txt

# 4. 完成合并
git commit   # Git 会自动生成合并提交信息

# 如果想放弃合并
git merge --abort

4.4 使用工具解决冲突

Bash
# 使用配置的合并工具
git mergetool

# VS Code 内置冲突解决
# 打开冲突文件后会显示:
# Accept Current Change | Accept Incoming Change | Accept Both Changes | Compare Changes

VS Code 推荐设置:

Bash
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'

4.5 减少冲突的最佳实践

  1. 频繁同步:经常 git pullgit rebase 保持分支最新
  2. 小粒度提交:每次只做一件事,减少冲突范围
  3. 及时合并:Feature 分支不要拖太久
  4. 团队沟通:修改同一模块前跟同事打个招呼
  5. 代码格式统一:使用 Prettier/ESLint 等工具避免格式差异导致的虚假冲突

5. 分支策略

5.1 Git Flow

适用于有明确版本发布周期的项目。

Text Only
main       ──●─────────────────●───────────────●──
              \               / \             /
release        \        release   \     release
                \        /   \     \      /
develop    ──●───●──●──●──●───●──●──●──●──●────
               \  /     \  /        \  /
feature     feat-A    feat-B      feat-C
                                    \
hotfix                            hotfix──
分支 用途 从哪来 合并到哪
main 生产环境代码 - -
develop 开发集成分支 main release → main
feature/* 新功能开发 develop develop
release/* 发布准备 develop main + develop
hotfix/* 紧急修复 main main + develop

适用场景:移动App、桌面软件等有版本号的产品 缺点:流程较重,分支多,不适合快速迭代

5.2 GitHub Flow

GitHub 自身使用的简化版工作流。

Text Only
main    ──●──────●──────────●──────●──
            \   /     \    /        \
feature   feat-A    feat-B        feat-C

流程:

  1. main 创建 Feature 分支
  2. 在 Feature 分支上开发并提交
  3. 发起 Pull Request
  4. Code Review + CI 检查
  5. 合并到 main
  6. 部署

适用场景:Web 应用、持续部署的 SaaS 产品 优点:简单、快速 缺点:不适合需要多版本维护的项目

5.3 Trunk Based Development(主干开发)

所有开发者频繁向主干(main/trunk)提交小的变更。

Text Only
main    ──●──●──●──●──●──●──●──●──●──
           │     │     │        │
           小改动  小改动  小改动    小改动

核心原则:

  • 直接向 main 提交,或使用非常短期的 Feature 分支(< 1天)
  • 使用 Feature Flags 控制未完成功能的可见性
  • 强依赖 CI/CD 和自动化测试

适用场景:高频部署的大型团队(Google、Facebook) 优点:减少合并冲突、促进持续集成 缺点:需要强大的基础设施支持

5.4 策略选择建议

团队规模 发布频率 推荐策略
1-3人 随时 GitHub Flow
3-10人 每周/双周 GitHub Flow 或简化版 Git Flow
10+ 人 固定版本周期 Git Flow
大型团队 持续部署 Trunk Based Development
开源项目 不定期 Fork + PR(基于 GitHub Flow)

6. Cherry-pick 使用场景

Cherry-pick 可以将指定的提交"摘取"到当前分支。

Bash
# 基本用法
git cherry-pick <commit-hash>

# 摘取多个提交
git cherry-pick <hash1> <hash2> <hash3>

# 摘取一段连续提交(不含 A,含 B)
git cherry-pick A..B

# 不自动提交(先查看效果)
git cherry-pick --no-commit <hash>

# 冲突时放弃
git cherry-pick --abort

# 冲突时跳过当前提交
git cherry-pick --skip

典型使用场景:

  1. 紧急修复移植:在 develop 上修复的 bug 需要同步到 releasemain
  2. 部分功能提前发布:Feature 分支中有些提交可以先合并
  3. 误提交到错误分支:把提交摘到正确分支后,在原分支回退
Bash
# 示例:将 develop 上的 bugfix 同步到 main
git switch main
git cherry-pick abc1234     # develop 分支上修复 bug 的 commit hash
git push origin main

⚠️ 注意:Cherry-pick 会创建新的 commit(新的 hash),不是移动原来的 commit。大量使用可能导致重复提交的困惑。


✏️ 练习题

基础练习

  1. 创建一个仓库,然后:
  2. 创建 feature/add-readme 分支
  3. 在该分支添加 README.md 并提交
  4. 切回 main,执行 Fast-forward 合并
  5. 使用 git log --oneline --graph --all 查看历史

  6. 练习三方合并:

  7. 从 main 创建两个分支 feature-Afeature-B
  8. 在两个分支上分别修改不同文件
  9. 依次合并回 main,观察历史图形

  10. 练习冲突解决:

  11. 从 main 创建两个分支
  12. 修改同一文件的同一行
  13. 合并时解决冲突

进阶练习

  1. 对比 merge 和 rebase:
  2. 创建一个场景同时存在 main 新提交和 feature 新提交
  3. 分别用 merge 和 rebase 两种方式进行合并
  4. 对比 git log --graph 的区别

  5. 练习 cherry-pick:

  6. feature 分支上做三次提交
  7. 只把其中第二个提交 cherry-pick 到 main

  8. 模拟 GitHub Flow 工作流(本地操作):

  9. 创建 Feature 分支 → 开发提交 → 合并到 main → 删除分支

📋 面试要点

  • 分支的本质是指针,创建分支几乎零成本
  • Fast-forward vs 三方合并:有无分叉决定合并方式
  • Merge vs Rebase:merge 保留历史,rebase 线性化历史
  • Rebase 黄金法则:不要 rebase 公共分支
  • Git Flow vs GitHub Flow:根据团队规模和发布频率选择
  • 冲突解决:理解冲突标记,能手动解决
  • Cherry-pick:跨分支"摘取"特定提交

← 上一章:Git基础 | 下一章:远程协作 →