分支管理¶
🎯 学习目标¶
- 理解 Git 分支的本质(指针)
- 熟练掌握分支的创建、切换、合并与删除
- 理解 Fast-forward、三方合并、Rebase 的区别与适用场景
- 掌握冲突解决的实战方法
- 了解主流分支策略(Git Flow、GitHub Flow、Trunk Based)
- 学会使用 Cherry-pick 跨分支应用提交
1. 分支的本质¶
Git 的分支是极其轻量级的——它本质上只是一个指向某个提交对象的可移动指针。
- 每次
git commit,当前分支指针自动前移 HEAD是一个特殊指针,指向你当前所在的分支- 创建分支 = 创建一个新指针(几乎零成本,不复制文件)
💡 核心理解:Git 分支不是复制文件夹,它只是一个 41 字节的文件(存储 SHA-1 哈希值),所以创建分支几乎是瞬间完成。
2. 分支基本操作¶
2.1 创建与切换¶
# 查看所有分支
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 合并与删除¶
# 合并分支(先切到目标分支)
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 只需将指针前移。
合并前:
main: C0 ← C1 ← C2
\
feature: C3 ← C4
合并后(Fast-forward):
main: C0 ← C1 ← C2 ← C3 ← C4
git switch main
git merge feature/login
# Fast-forward
# 强制禁用 Fast-forward(保留分支历史)
git merge --no-ff feature/login -m "merge: 合并登录功能"
保留合并历史 --no-ff 的效果:
💡 建议:团队协作时使用
--no-ff,保留完整的功能开发历史,方便日后追溯。
3.2 三方合并(Three-way Merge)¶
当两个分支各自有新的提交(产生分叉),Git 会找到它们的共同祖先,进行三方合并并创建一个新的合并提交。
合并前:
main: C0 ← C1 ← C2 ← C5
\
feature: C3 ← C4
合并后:
main: C0 ← C1 ← C2 ← C5 ← M (merge commit)
\ /
feature: C3 ← C4
3.3 Git Merge vs Git Rebase¶
Merge(合并)¶
优点:保留完整历史,非破坏性操作 缺点:历史中会有很多合并提交,图形较复杂
Rebase(变基)¶
变基前:
main: C1 ← C2 ← C5
\
feature: C3 ← C4
变基后:
main: C1 ← C2 ← C5
\
feature: C3' ← C4' # C3、C4 被"重放"到 C5 之后
然后再合并(此时是 Fast-forward):
优点:历史线性、干净、易读 缺点:改写历史(commit hash 变了),协作时需小心
对比总结¶
| 特性 | Merge | Rebase |
|---|---|---|
| 历史 | 保留真实历史,有合并节点 | 线性历史,没有合并节点 |
| 安全性 | 非破坏性,安全 | 改写历史,有风险 |
| 使用场景 | 公共分支合并 | 私有分支更新、整理提交 |
| 冲突处理 | 一次解决所有冲突 | 逐个提交解决冲突 |
| 可读性 | 复杂项目历史可能混乱 | 历史清晰直观 |
🚫 Rebase 黄金法则¶
永远不要 rebase 已经推送到公共仓库的分支!
# ❌ 绝对不要这样做
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 冲突标记¶
4.3 手动解决冲突¶
# 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 使用工具解决冲突¶
# 使用配置的合并工具
git mergetool
# VS Code 内置冲突解决
# 打开冲突文件后会显示:
# Accept Current Change | Accept Incoming Change | Accept Both Changes | Compare Changes
VS Code 推荐设置:
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'
4.5 减少冲突的最佳实践¶
- 频繁同步:经常
git pull或git rebase保持分支最新 - 小粒度提交:每次只做一件事,减少冲突范围
- 及时合并:Feature 分支不要拖太久
- 团队沟通:修改同一模块前跟同事打个招呼
- 代码格式统一:使用 Prettier/ESLint 等工具避免格式差异导致的虚假冲突
5. 分支策略¶
5.1 Git Flow¶
适用于有明确版本发布周期的项目。
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 自身使用的简化版工作流。
流程:
- 从
main创建 Feature 分支 - 在 Feature 分支上开发并提交
- 发起 Pull Request
- Code Review + CI 检查
- 合并到
main - 部署
适用场景:Web 应用、持续部署的 SaaS 产品 优点:简单、快速 缺点:不适合需要多版本维护的项目
5.3 Trunk Based Development(主干开发)¶
所有开发者频繁向主干(main/trunk)提交小的变更。
核心原则:
- 直接向 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 可以将指定的提交"摘取"到当前分支。
# 基本用法
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
典型使用场景:
- 紧急修复移植:在
develop上修复的 bug 需要同步到release或main - 部分功能提前发布:Feature 分支中有些提交可以先合并
- 误提交到错误分支:把提交摘到正确分支后,在原分支回退
# 示例:将 develop 上的 bugfix 同步到 main
git switch main
git cherry-pick abc1234 # develop 分支上修复 bug 的 commit hash
git push origin main
⚠️ 注意:Cherry-pick 会创建新的 commit(新的 hash),不是移动原来的 commit。大量使用可能导致重复提交的困惑。
✏️ 练习题¶
基础练习¶
- 创建一个仓库,然后:
- 创建
feature/add-readme分支 - 在该分支添加 README.md 并提交
- 切回 main,执行 Fast-forward 合并
-
使用
git log --oneline --graph --all查看历史 -
练习三方合并:
- 从 main 创建两个分支
feature-A和feature-B - 在两个分支上分别修改不同文件
-
依次合并回 main,观察历史图形
-
练习冲突解决:
- 从 main 创建两个分支
- 修改同一文件的同一行
- 合并时解决冲突
进阶练习¶
- 对比 merge 和 rebase:
- 创建一个场景同时存在 main 新提交和 feature 新提交
- 分别用 merge 和 rebase 两种方式进行合并
-
对比
git log --graph的区别 -
练习 cherry-pick:
- 在
feature分支上做三次提交 -
只把其中第二个提交 cherry-pick 到 main
-
模拟 GitHub Flow 工作流(本地操作):
- 创建 Feature 分支 → 开发提交 → 合并到 main → 删除分支
📋 面试要点¶
- 分支的本质是指针,创建分支几乎零成本
- Fast-forward vs 三方合并:有无分叉决定合并方式
- Merge vs Rebase:merge 保留历史,rebase 线性化历史
- Rebase 黄金法则:不要 rebase 公共分支
- Git Flow vs GitHub Flow:根据团队规模和发布频率选择
- 冲突解决:理解冲突标记,能手动解决
- Cherry-pick:跨分支"摘取"特定提交