Appearance
Merge vs Rebase
概述
合并(Merge)和变基(Rebase)是 Git 中整合分支修改的两种主要方式。理解它们的区别和适用场景对于选择正确的策略至关重要。
两者区别
工作原理
Merge(合并)
# 合并保留分支历史,创建合并提交
A --- B --- C --- M (main)
\ /
D --- E (feature)
# M 是合并提交,有两个父提交Rebase(变基)
# 变基重写历史,线性提交
A --- B --- C --- D' --- E' (main, feature)
# D' 和 E' 是新的提交,哈希值已改变历史记录
bash
# 合并后的历史
git log --oneline --graph
* a1b2c3d (HEAD -> main) Merge branch 'feature'
|\
| * e5f6g7h (feature) Add feature
* | d4e5f6g Update main
|/
* b2c3d4e Initial commit
# 变基后的历史
git log --oneline --graph
* f6g7h8i (HEAD -> feature) Add feature
* e5f6g7h Update main
* d4e5f6g Add feature
* b2c3d4e Initial commit操作可逆性
bash
# 合并:可以轻松撤销
git merge feature
git reset --hard HEAD~1 # 撤销合并
# 变基:较难撤销(需要 reflog)
git rebase main
git reset --hard ORIG_HEAD # 使用 ORIG_HEAD各自优缺点
Merge 优点
| 优点 | 说明 |
|---|---|
| 保留完整历史 | 可以看到分支的创建和合并过程 |
| 操作安全 | 不改变已有提交,不会丢失代码 |
| 易于理解 | 团队成员容易理解分支结构 |
| 可撤销 | 可以轻松撤销合并 |
| 适合公共分支 | 不会影响其他开发者的工作 |
Merge 缺点
| 缺点 | 说明 |
|---|---|
| 历史复杂 | 大量合并提交使历史难以阅读 |
| 提交分散 | 同一功能的提交可能分散在不同分支 |
| bisect 困难 | 使用 git bisact 时可能遇到合并提交 |
Rebase 优点
| 优点 | 说明 |
|---|---|
| 历史整洁 | 线性历史,易于阅读和理解 |
| 提交有序 | 同一功能的提交连续排列 |
| 易于调试 | git bisect 更有效 |
| 无合并提交 | 减少不必要的合并提交 |
Rebase 缺点
| 缺点 | 说明 |
|---|---|
| 改变历史 | 提交哈希改变,可能影响他人 |
| 风险较高 | 操作不当可能丢失提交 |
| 不适合公共分支 | 会造成协作问题 |
| 冲突处理复杂 | 每个提交可能都需要解决冲突 |
使用场景选择
选择 Merge 的场景
bash
# 1. 合并公共分支
git checkout main
git merge feature
# 2. 保留分支历史
git merge --no-ff feature
# 3. 团队协作
# 多人同时在分支上工作时
# 4. 发布分支合并
git checkout release
git merge develop选择 Rebase 的场景
bash
# 1. 本地功能分支同步
git checkout feature
git rebase main
# 2. 清理提交历史
git rebase -i main
# 3. 提交前整理
# 将多个小提交合并为一个有意义的提交
# 4. 个人开发分支
# 只有自己在使用的分支决策流程图
开始
│
├─ 分支是否已推送到远程?
│ │
│ ├─ 是 ──→ 使用 Merge
│ │
│ └─ 否 ──→ 分支是否只有你一个人使用?
│ │
│ ├─ 是 ──→ 可以使用 Rebase
│ │
│ └─ 否 ──→ 使用 Merge
│
└─ 是否需要保留分支历史?
│
├─ 是 ──→ 使用 Merge (--no-ff)
│
└─ 否 ──→ 可以使用 Rebase最佳实践建议
团队协作规范
bash
# 规则 1:公共分支只接受合并
# main、develop 等公共分支
# 规则 2:功能分支可以变基
# 在合并前整理提交历史
# 规则 3:已推送的分支谨慎变基
# 如果必须变基,需要通知团队
# 规则 4:使用 --force-with-lease
git push --force-with-lease origin feature功能分支工作流
bash
# 推荐的混合使用方式
# 1. 创建功能分支
git checkout -b feature main
# 2. 开发并提交
git add .
git commit -m "WIP: Add feature"
# 3. 定期同步主分支(使用 merge)
git fetch origin
git merge origin/main
# 或使用 rebase(如果分支未推送)
git rebase origin/main
# 4. 完成功能后整理提交
git rebase -i main
# 合并 WIP 提交,修改提交信息
# 5. 合并到主分支
git checkout main
git merge --no-ff feature
# 或使用 squash
git merge --squash featurePull Request 工作流
bash
# GitHub/GitLab PR 工作流
# 1. 创建功能分支并推送
git checkout -b feature main
git push -u origin feature
# 2. 创建 Pull Request
# 3. 代码审查期间同步主分支
git fetch origin
git merge origin/main
# 或
git rebase origin/main
git push --force-with-lease origin feature
# 4. 审查通过后合并
# 通过 Web 界面选择合并方式:
# - Create a merge commit
# - Squash and merge
# - Rebase and merge合并策略选择
bash
# 功能分支合并到 develop
git checkout develop
git merge --no-ff feature # 保留分支历史
# 发布分支合并到 main
git checkout main
git merge --no-ff release # 保留分支历史
# 热修复分支合并
git checkout main
git merge --no-ff hotfix # 保留分支历史
git checkout develop
git merge --no-ff hotfix # 同步到开发分支
# 本地分支整理
git checkout feature
git rebase -i main # 清理提交历史常见问题
变基后推送失败
bash
# 错误:远程有新提交
! [rejected] feature -> feature (non-fast-forward)
# 解决方案 1:先合并远程变更
git pull --rebase origin feature
git push origin feature
# 解决方案 2:强制推送(谨慎使用)
git push --force-with-lease origin feature合并后想改用变基
bash
# 撤销合并
git reset --hard ORIG_HEAD
# 改用变基
git checkout feature
git rebase main变基后想改用合并
bash
# 使用 reflog 找到变基前的状态
git reflog
# 恢复到变基前
git reset --hard HEAD@{n}
# 改用合并
git checkout main
git merge feature总结
| 特性 | Merge | Rebase |
|---|---|---|
| 历史记录 | 保留分支历史 | 线性历史 |
| 提交哈希 | 不改变 | 改变 |
| 安全性 | 高 | 中等 |
| 公共分支 | 适合 | 不适合 |
| 提交整理 | 不支持 | 支持 |
| 冲突处理 | 一次性 | 可能多次 |
选择建议
- 公共分支:始终使用 Merge
- 个人分支:可以使用 Rebase
- 已推送分支:优先使用 Merge
- 提交整理:使用交互式 Rebase
- 团队协作:制定明确的规范
下一步
学习完 Merge vs Rebase 后,建议继续学习:
