主题
第二阶段:Git 分支管理
一、分支基础
1.1 什么是分支?
分支(Branch)是 Git 最强大的特性之一。本质上,分支就是一个指向某个提交对象的可移动指针。
main
↓
C0 ← C1 ← C2创建新分支时,Git 只是创建了一个新的指针,不会复制任何文件,所以创建分支极其轻量和快速。
main
↓
C0 ← C1 ← C2
↑
featureHEAD 指针: Git 用一个特殊指针 HEAD 来标记"你当前在哪个分支上"。
HEAD
↓
main
↓
C0 ← C1 ← C2
↑
feature1.2 git branch — 创建、查看、删除分支
bash
# 查看本地分支(* 标记当前分支)
git branch
# 查看所有分支(含远程)
git branch -a
# 查看远程分支
git branch -r
# 查看每个分支的最后一次提交
git branch -v
# 查看已合并到当前分支的分支
git branch --merged
# 查看未合并的分支
git branch --no-merged
# 创建新分支(不切换)
git branch feature-login
# 重命名分支
git branch -m old-name new-name
# 重命名当前分支
git branch -m new-name
# 删除已合并的分支
git branch -d feature-login
# 强制删除未合并的分支
git branch -D feature-login
# 删除远程分支
git push origin --delete feature-login1.3 git checkout / git switch — 切换分支
git switch 是 Git 2.23 引入的新命令,专门用于分支切换,语义更清晰。
bash
# ——— 使用 git switch(推荐) ———
# 切换到已有分支
git switch feature-login
# 创建并切换到新分支
git switch -c feature-signup
# 基于某个提交创建并切换
git switch -c hotfix abc1234
# 切换回上一个分支
git switch -
# ——— 使用 git checkout(旧方式) ———
# 切换到已有分支
git checkout feature-login
# 创建并切换到新分支
git checkout -b feature-signup
# 基于远程分支创建本地跟踪分支
git checkout -b feature origin/feature
# 切换回上一个分支
git checkout -git switch vs git checkout 的区别:
| 功能 | git switch | git checkout |
|---|---|---|
| 切换分支 | ✅ | ✅ |
| 创建并切换 | switch -c | checkout -b |
| 恢复文件 | ❌(用 git restore) | ✅(checkout -- file) |
| 语义清晰度 | 高(只做分支切换) | 低(身兼数职) |
推荐: 使用
git switch切换分支,git restore恢复文件,职责分离更不易出错。
切换分支前的注意事项:
切换分支时,如果工作区或暂存区有未提交的修改,Git 的行为取决于这些修改是否与目标分支冲突:
- 不冲突 — 修改会被带到目标分支(这可能不是你想要的)
- 冲突 — Git 拒绝切换,提示你先处理
bash
# 安全做法:切换前先暂存工作
git stash # 暂存当前修改
git switch other # 切换分支
# ... 做完事情后 ...
git switch - # 切回来
git stash pop # 恢复暂存的修改1.4 git merge — 合并分支
将另一个分支的修改合并到当前分支。
bash
# 先切换到目标分支(通常是 main)
git switch main
# 合并 feature 分支
git merge feature-login
# 合并时创建合并提交(即使可以快速合并)
git merge --no-ff feature-login
# 合并但不自动提交(方便检查合并结果)
git merge --no-commit feature-login
# 取消正在进行的合并
git merge --abort1.5 快速合并(Fast-forward)与三方合并(3-way Merge)
Fast-forward 合并
当目标分支没有新提交时,Git 直接将指针前移,不产生合并提交。
合并前:
main
↓
C0 ← C1 ← C2 ← C3 ← C4
↑
feature
合并后(git merge feature):
main
↓
C0 ← C1 ← C2 ← C3 ← C4
↑
feature特点:历史线性,干净清爽,但看不出"曾有分支"的痕迹。
三方合并(3-way Merge)
当两个分支各自都有新提交时,Git 会找到共同祖先,创建一个新的合并提交。
合并前:
main
↓
C0 ← C1 ← C2 ← C5
↖
C3 ← C4
↑
feature
合并后(git merge feature):
main
↓
C0 ← C1 ← C2 ← C5 ← M(合并提交)
↖ ↗
C3 ← C4
↑
feature特点:保留了完整的分支历史,能看出功能是在哪个分支上开发的。
--no-ff 的意义
bash
git merge --no-ff feature-login即使可以快速合并,也强制创建一个合并提交。这样做的好处是:
- 保留"这是一个完整功能"的信息
- 方便用
git revert整体回退该功能 - 在
git log --graph中能清晰看到分支结构
# 不用 --no-ff(Fast-forward):
C0 ← C1 ← C2 ← C3 ← C4 (看不出 C3、C4 是在分支上开发的)
# 使用 --no-ff:
C0 ← C1 ← C2 ────────── M (能看出 C3、C4 来自分支)
↖ ↗
C3 ← C41.6 合并冲突的产生与解决
什么时候会产生冲突?
当两个分支修改了同一个文件的同一区域时,Git 无法自动判断应该保留哪个版本。
冲突长什么样?
<<<<<<< HEAD
这是当前分支(main)的内容
=======
这是被合并分支(feature)的内容
>>>>>>> feature三个标记的含义:
| 标记 | 含义 |
|---|---|
<<<<<<< HEAD | 冲突开始,下面是当前分支的内容 |
======= | 分隔线 |
>>>>>>> feature | 冲突结束,上面是被合并分支的内容 |
解决冲突的步骤
bash
# ① 执行合并,Git 提示冲突
git merge feature
# Auto-merging file.txt
# CONFLICT (content): Merge conflict in file.txt
# Automatic merge failed; fix conflicts and then commit the result.
# ② 查看哪些文件有冲突
git status
# both modified: file.txt
# ③ 打开冲突文件,手动编辑
# 删除冲突标记,保留你想要的内容(可以保留一方、合并双方、或写全新内容)
# ④ 标记冲突已解决
git add file.txt
# ⑤ 完成合并提交
git commit -m "merge: 合并 feature 分支,解决冲突"使用工具解决冲突
bash
# 使用图形化合并工具
git mergetool
# 配置默认合并工具
git config --global merge.tool vimdiff实际开发中: 大多数 IDE(VS Code、IntelliJ 等)都有内置的冲突解决界面,可以直观地选择保留哪些内容,比命令行方便得多。
二、分支策略
2.1 Git Flow 工作流
最经典的分支模型,适合有固定发布周期的项目。
时间轴 →
main ──●──────────────────●──────────────●── (只有发布版本)
│ ↑ ↑
│ │ │
release │ ●──●──●─┘ ●──●──●─┘ (发布准备)
│ ↑ ↑
│ │ │
develop ───●──●──●──●─┴──●──●──●──●──●─┴──●──●── (开发主线)
↑ ↑ ↑ ↑
│ │ │ │
feature ●──●──┘ ●──●──┘ (功能分支)
hotfix ──────●──●──→ main & develop (紧急修复)五种分支角色:
| 分支 | 生命周期 | 来源 | 合并到 | 用途 |
|---|---|---|---|---|
main | 永久 | — | — | 线上稳定版本,每次合并打 Tag |
develop | 永久 | main | release | 开发主线,集成所有功能 |
feature/* | 临时 | develop | develop | 开发新功能 |
release/* | 临时 | develop | main + develop | 发布前的测试和修复 |
hotfix/* | 临时 | main | main + develop | 线上紧急 Bug 修复 |
适用场景: 中大型项目、有明确版本发布计划、多团队协作。
缺点: 分支多、流程重,对于持续部署的项目过于复杂。
2.2 GitHub Flow 工作流
极简的分支模型,只有一个长期分支 main,配合 Pull Request。
main ──●──●──────●──────────●──●──
↖ ↑ ↑
●──●──┘ ●──●──┘
feature-A feature-B流程:
- 从
main创建功能分支 - 在功能分支上开发并提交
- 发起 Pull Request(PR)
- 团队 Code Review
- 通过 CI 测试
- 合并到
main,自动部署 - 删除功能分支
适用场景: 持续部署的 Web 项目、小团队、开源项目。
优点: 简单直观,配合 PR 流程质量可控。
2.3 Trunk-Based Development(主干开发)
所有开发者直接在 main(主干)上工作,或使用极短生命周期的分支。
main ──●──●──●──●──●──●──●──●──●──
↖ ↑ ↖ ↑
●─┘ ●─┘
(极短分支,通常 < 1 天)核心原则:
- 分支存活时间不超过 1-2 天
- 频繁地向主干合并(至少每天一次)
- 依赖 Feature Flag(功能开关)控制未完成功能的可见性
- 强依赖 CI/CD 和自动化测试
适用场景: 高频发布、经验丰富的团队、完善的自动化测试体系。
2.4 功能分支(Feature Branch)最佳实践
无论使用哪种工作流,功能分支的管理都遵循一些通用原则:
命名规范:
bash
# 推荐格式:类型/简短描述
feature/user-login
feature/shopping-cart
bugfix/fix-header-overflow
hotfix/security-patch
refactor/database-layer
docs/api-documentation
# 也可以加上 Issue 编号
feature/PROJ-123-user-login
bugfix/PROJ-456-fix-crash生命周期管理:
bash
# ① 创建功能分支
git switch -c feature/user-login main
# ② 开发过程中定期同步主干(避免最终合并时冲突过多)
git fetch origin
git merge origin/main
# 或使用 rebase(保持线性历史)
git rebase origin/main
# ③ 完成后合并回主干
git switch main
git merge --no-ff feature/user-login
# ④ 删除已合并的分支
git branch -d feature/user-login
git push origin --delete feature/user-login最佳实践总结:
| 原则 | 说明 |
|---|---|
| 小而专注 | 每个分支只做一件事,避免"超级大分支" |
| 短生命周期 | 分支存活越久,合并冲突越多 |
| 定期同步 | 经常从主干拉取最新代码 |
| 及时清理 | 合并后立即删除功能分支 |
| 清晰命名 | 一看就知道这个分支在做什么 |
2.5 三种工作流的选择指南
你的项目是什么类型?
│
├─ 有固定版本发布周期(如每月/每季度发版)
│ └─→ Git Flow
│
├─ 持续部署的 Web 应用
│ ├─ 团队较小(< 10 人)
│ │ └─→ GitHub Flow
│ └─ 团队较大、测试完善
│ └─→ Trunk-Based Development
│
└─ 开源项目
└─→ GitHub Flow(Fork + PR)三、实践 Demo
Demo 1:分支的创建、切换与合并
bash
# ① 初始化项目
mkdir branch-demo && cd branch-demo
git init
echo "# Branch Demo" > README.md
git add . && git commit -m "feat: 初始化项目"
# ② 创建并切换到功能分支
git switch -c feature/add-hello
# ③ 在功能分支上开发
echo "Hello from feature branch!" > hello.txt
git add . && git commit -m "feat: 添加 hello.txt"
echo "Another line" >> hello.txt
git add . && git commit -m "feat: 更新 hello.txt"
# ④ 查看分支图
git log --oneline --graph --all
# ⑤ 切回 main 合并
git switch main
git merge feature/add-hello
# ⑥ 查看合并结果
git log --oneline --graph --all
cat hello.txt
# ⑦ 删除已合并的分支
git branch -d feature/add-helloDemo 2:体验 Fast-forward 与 --no-ff 的区别
bash
# ① 准备一个干净的仓库
mkdir merge-demo && cd merge-demo
git init
echo "base" > file.txt
git add . && git commit -m "feat: 初始提交"
# ——— 场景 A:Fast-forward 合并 ———
git switch -c feature-a
echo "feature a" >> file.txt
git add . && git commit -m "feat: feature-a 修改"
git switch main
git merge feature-a # Fast-forward,没有合并提交
git log --oneline --graph --all
git branch -d feature-a
# ——— 场景 B:--no-ff 合并 ———
git switch -c feature-b
echo "feature b" >> file.txt
git add . && git commit -m "feat: feature-b 修改"
git switch main
git merge --no-ff feature-b # 强制创建合并提交
git log --oneline --graph --all
git branch -d feature-b
# 对比两种方式在 log --graph 中的区别Demo 3:模拟并解决合并冲突
bash
# ① 初始化
mkdir conflict-demo && cd conflict-demo
git init
echo "line 1: original" > story.txt
echo "line 2: original" >> story.txt
echo "line 3: original" >> story.txt
git add . && git commit -m "feat: 初始故事"
# ② 在 main 上修改第 2 行
sed -i 's/line 2: original/line 2: modified by main/' story.txt
git add . && git commit -m "feat: main 修改了第 2 行"
# ③ 创建分支(基于上一次共同祖先)
git switch -c feature HEAD~1
# ④ 在 feature 上也修改第 2 行
sed -i 's/line 2: original/line 2: modified by feature/' story.txt
git add . && git commit -m "feat: feature 修改了第 2 行"
# ⑤ 切回 main 合并 — 此时会产生冲突!
git switch main
git merge feature
# 输出:CONFLICT (content): Merge conflict in story.txt
# ⑥ 查看冲突状态
git status
# ⑦ 查看冲突内容
cat story.txt
# 你会看到 <<<<<<< ======= >>>>>>> 标记
# ⑧ 手动解决冲突(编辑文件,选择你想要的内容)
# 例如保留两个分支的修改:
cat > story.txt << 'EOF'
line 1: original
line 2: modified by main and feature
line 3: original
EOF
# ⑨ 标记已解决并提交
git add story.txt
git commit -m "merge: 合并 feature,解决第 2 行冲突"
# ⑩ 查看合并结果
git log --oneline --graph --allDemo 4:模拟 GitHub Flow 工作流
bash
# ① 初始化项目
mkdir github-flow-demo && cd github-flow-demo
git init
echo "# My App" > README.md
echo "v1.0" > version.txt
git add . && git commit -m "feat: 初始化项目 v1.0"
# ② 开发者 A:开发登录功能
git switch -c feature/user-login
echo "function login() { }" > auth.js
git add . && git commit -m "feat: 添加登录功能框架"
echo "function validateUser() { }" >> auth.js
git add . && git commit -m "feat: 添加用户验证"
# ③ 同时,main 上有了新的提交(模拟其他人的合并)
git switch main
echo "v1.1" > version.txt
git add . && git commit -m "chore: 更新版本号"
# ④ 开发者 A 先同步主干的最新代码
git switch feature/user-login
git merge main # 或 git rebase main
# ⑤ 合并功能分支到 main(模拟 PR 合并)
git switch main
git merge --no-ff feature/user-login -m "merge: 合并登录功能 (#1)"
# ⑥ 清理
git branch -d feature/user-login
# ⑦ 查看完整历史
git log --oneline --graph --allDemo 5:模拟 Git Flow 的 hotfix 流程
bash
# 基于上一个 demo 继续
# ① 发现线上 Bug,紧急修复
git switch -c hotfix/fix-auth-bug main
# ② 修复 Bug
echo "function login() { /* fixed */ }" > auth.js
git add . && git commit -m "fix: 修复登录验证漏洞"
# ③ 合并到 main(发布修复)
git switch main
git merge --no-ff hotfix/fix-auth-bug -m "merge: 紧急修复登录漏洞"
# ④ 打标签标记版本
git tag -a v1.1.1 -m "hotfix: 修复登录验证漏洞"
# ⑤ 清理 hotfix 分支
git branch -d hotfix/fix-auth-bug
# ⑥ 查看标签和历史
git tag
git log --oneline --graph --all --decorate四、常见问题 Q&A
Q1:切换分支时提示有未提交的修改,怎么办?
A: 有三种处理方式:
bash
# 方式一:暂存(推荐,最常用)
git stash # 暂存修改
git switch other-branch # 切换
# ...做完事情后...
git switch - # 切回来
git stash pop # 恢复修改
# 方式二:提交(修改已经告一段落)
git add . && git commit -m "wip: 保存进度"
git switch other-branch
# 方式三:丢弃(不要这些修改了)
git restore . # 丢弃所有工作区修改
git switch other-branchQ2:git merge 和 git rebase 有什么区别?该用哪个?
A:
| 特性 | git merge | git rebase |
|---|---|---|
| 操作方式 | 创建一个合并提交 | 将提交"移植"到目标分支末端 |
| 历史记录 | 保留真实的分支历史 | 生成线性历史 |
| 安全性 | 不改写历史,安全 | 改写历史,对已推送的分支危险 |
| 冲突处理 | 一次性解决所有冲突 | 逐个提交解决冲突 |
# merge 的结果:
main
↓
A ← B ← E ← M (合并提交)
↖ ↗
C ← D
↑
feature
# rebase 的结果:
main
↓
A ← B ← E ← C' ← D' (线性历史,C 和 D 被"重放"为 C' 和 D')选择建议:
- 个人本地分支 → 用
rebase,保持历史干净 - 公共分支 / 已推送 → 用
merge,不要 rebase - 团队约定 → 统一标准,避免混乱
黄金法则: 永远不要 rebase 已经推送到远程的公共分支。
Q3:怎么撤销一次已完成的合并?
A:
bash
# 如果合并还没推送到远程
git reset --hard HEAD~1 # 回到合并前的状态
# 如果合并已推送到远程(安全做法)
git revert -m 1 <merge-commit-hash>
# -m 1 表示保留主分支的版本Q4:误删了分支怎么恢复?
A: 只要提交还在(没有被 GC 清理),就可以恢复:
bash
# 查看所有操作记录(包括被删除的分支指向的提交)
git reflog
# 找到被删分支最后一次提交的 hash,重新创建分支
git branch recovered-branch abc1234Q5:两个分支修改了不同的文件,会产生冲突吗?
A: 不会。Git 会自动合并。只有同一文件的同一区域被两个分支同时修改才会产生冲突。
常见不会冲突的场景:
- 两个分支修改了不同的文件
- 两个分支修改了同一文件的不同区域
- 一个分支新增文件,另一个分支没动
常见会冲突的场景:
- 同一文件的同一行被不同分支修改
- 一个分支删除了文件,另一个分支修改了该文件
- 两个分支在同一位置添加了不同内容
Q6:如何查看某个分支和 main 的区别?
A:
bash
# 查看 feature 分支相比 main 新增了哪些提交
git log main..feature --oneline
# 查看文件差异
git diff main..feature
# 只看哪些文件变化了
git diff main..feature --name-only
# 查看分支分叉以来的所有提交
git log main...feature --oneline.. vs ... 的区别:
main..feature:feature 有但 main 没有的提交(单向)main...feature:main 和 feature 各自独有的提交(双向)
Q7:合并时显示 "Already up to date" 是什么意思?
A: 说明当前分支已经包含了目标分支的所有提交,没有需要合并的内容。常见原因:
- 该分支之前已经被合并过了
- 当前分支本身就是从目标分支创建的,且目标分支没有新提交
Q8:git branch -d 和 git branch -D 的区别?
A:
| 命令 | 行为 |
|---|---|
git branch -d | 安全删除,只能删除已合并到当前分支的分支 |
git branch -D | 强制删除,无论是否合并 |
bash
# 安全删除(如果未合并会报错提醒)
git branch -d feature-x
# error: The branch 'feature-x' is not fully merged.
# 强制删除(确认不需要了再用)
git branch -D feature-xQ9:多人协作时分支冲突频繁怎么办?
A: 减少冲突的策略:
- 缩短分支生命周期 — 功能尽量拆小,快速合并
- 频繁同步主干 — 每天
git pull origin main一次 - 合理划分模块 — 避免多人同时修改同一文件
- 先沟通再开发 — 涉及公共文件时提前告知
- 使用
--no-ff— 保持合并记录,方便排查问题
Q10:本地分支太多太乱怎么清理?
A:
bash
# 查看已合并的分支(可安全删除)
git branch --merged main
# 批量删除已合并的分支(排除 main 和当前分支)
git branch --merged main | grep -v "main\|^\*" | xargs git branch -d
# 清理远程已删除但本地还存在的远程跟踪分支
git fetch --prune
# 或一步到位
git remote prune origin
# 查看远程已删除的分支
git remote prune origin --dry-run五、分支操作速查表
┌──────────────────────────────────────────────────────────────┐
│ Git 分支命令速查 │
├────────────────────────────┬─────────────────────────────────┤
│ git branch │ 查看本地分支 │
│ git branch -a │ 查看所有分支(含远程) │
│ git branch <name> │ 创建分支 │
│ git branch -d <name> │ 删除已合并的分支 │
│ git branch -D <name> │ 强制删除分支 │
│ git branch -m <new> │ 重命名当前分支 │
│ git branch --merged │ 查看已合并的分支 │
│ git switch <name> │ 切换分支 │
│ git switch -c <name> │ 创建并切换分支 │
│ git switch - │ 切回上一个分支 │
│ git merge <branch> │ 合并分支 │
│ git merge --no-ff <branch> │ 合并并强制创建合并提交 │
│ git merge --abort │ 取消进行中的合并 │
│ git stash │ 暂存当前修改 │
│ git stash pop │ 恢复暂存的修改 │
│ git log --graph --all │ 查看分支图 │
│ git diff main..feature │ 比较两个分支的差异 │
│ git fetch --prune │ 清理远程已删除的分支 │
└────────────────────────────┴─────────────────────────────────┘六、学习检查清单
完成以下任务,确认你已掌握本阶段内容:
- [ ] 能说出 Git 分支的本质(指针)
- [ ] 能使用
git switch -c创建并切换到新分支 - [ ] 能区分 Fast-forward 合并和三方合并
- [ ] 能解释
--no-ff参数的作用和使用场景 - [ ] 能手动解决一个合并冲突
- [ ] 能画出 Git Flow 的分支模型图
- [ ] 能说出 Git Flow、GitHub Flow、Trunk-Based 各自适用的场景
- [ ] 能按照规范命名功能分支
- [ ] 能使用
git stash在切换分支前保存工作进度 - [ ] 能使用
git log --graph查看分支合并历史