文章
关于rebase 'dev' onto 'main' 和 merge 'main' into 'dev'
目录
在 Git 中,rebase 'dev' onto 'main' 和 merge 'main' into 'dev' 是两种完全不同的分支整合策略,它们会产生不同的提交历史和项目结构:
1. merge 'main' into 'dev'****(将 main 合并到 dev)#
git checkout dev
git merge main
- 操作:将
main分支的最新状态合并到dev分支 - 结果:
- 创建一个新的合并提交(merge commit)
- 保留两个分支的完整历史记录
dev分支的提交历史变为非线性(有分叉)
- 历史结构:
-
合并提交 (dev) |\ | * main 的提交
-
| dev 的提交 |/
-
共同祖先
- 优点:
- 操作简单安全
- 保留完整开发历史
- 不会重写历史(适合共享分支)
- 缺点:
- 历史记录可能变得复杂
- 会产生额外的合并提交
2. rebase 'dev' onto 'main'****(将 dev 变基到 main)#
git checkout dev
git rebase main
- 操作:
- 找到
dev和main的共同祖先 - 将
dev分支上的所有新提交提取为临时补丁 - 将这些补丁重新应用到
main分支的最新提交之后
- 找到
- 结果:
- 重写历史:
dev分支的所有提交会获得新的哈希值 - 创建线性历史:所有提交按时间顺序排列
- 不会产生合并提交
- 重写历史:
- 历史结构:
- dev 的提交 C' (新哈希)
- dev 的提交 B' (新哈希)
- dev 的提交 A' (新哈希)
- main 的最新提交
- 共同祖先
- 优点:
- 创建清晰、线性的提交历史
- 避免不必要的合并提交
- 便于代码审查(变更显示为连续修改)
- 缺点:
- 重写历史:会改变提交的哈希值
- 可能导致协作问题(如果 dev 已共享)
- 解决冲突更复杂(可能需要多次解决相同冲突)
🔑 核心区别总结:#
| 特性 | `merge main into dev` | `rebase dev onto main` |
| **历史记录** | 保留原始分叉历史 | 创建线性历史 |
| **提交哈希** | 保留原始提交 | 创建新提交(哈希改变) |
| **额外提交** | 创建合并提交 | 不创建额外提交 |
| **历史复杂度** | 非线性(可能有复杂分叉) | 线性(清晰的时间线) |
| **冲突解决** | 一次性解决所有冲突 | 可能需按提交顺序多次解决冲突 |
| **适用场景** | 公共分支、已共享的分支 | 本地私有分支、准备合并前的整理 |
| **安全性** | 安全(不重写历史) | 危险(重写历史,影响协作) |
| **结果位置** | `dev` 分支包含合并结果 | `dev` 分支变成基于 `main` 的新分支 |
🚀 工作流建议:#
- 私有特性分支(尚未共享):
定期整理分支保持更新#
git checkout feature git rebase main
完成后合并到 main#
git checkout main git merge feature
```
2. 公共/共享分支: ```shell
永远不要对共享分支做 rebase#
git checkout dev git merge main # 安全合并更新
```
3. Pull Request 前整理历史: ```shell
本地整理提交历史#
git rebase -i main
然后推送到远程#
git push -f # 强制推送(仅适用于自己的分支)
```
⚠️ 黄金法则:#
永远不要对公共分支(已推送到远程仓库的分支)执行 rebase Rebase 只适用于你私有的、尚未与他人共享的分支
🌰 实际示例:#
# 初始状态
A--B--C (main)
\\
D--E (dev)
# 执行 merge main into dev 后:
A--B--C (main)
\\ \\
D--E--M (dev) # M 是合并提交
# 执行 rebase dev onto main 后:
A--B--C (main)
\\
D'--E' (dev) # 提交被重写
选择哪种方式取决于:
- 分支是否已共享
- 是否需要清晰的线性历史
- 团队的工作流程约定 两者都是有效的 Git 工作流策略,理解它们的区别能帮助你在不同场景做出最佳选择。