merge
和 rebase
是 Git 中合并分支的两种核心策略,它们都能将一个分支的更改整合到另一个分支,但工作原理、提交历史和适用场景有本质区别。
一、核心区别(原理层面)
特性 | merge (合并) |
rebase (变基) |
---|---|---|
工作原理 | 创建一个新的合并提交(merge commit) ,将两个分支的历史连接起来。 | 将当前分支的提交**"重新播放"到目标分支的最新提交之上,形成一条线性历史**。 |
提交历史 | 保留完整的分支拓扑,历史真实反映开发过程。 | 历史更"干净"、线性,但重写了提交历史。 |
提交哈希(Hash) | 原有提交的哈希值不变。 | 原有提交的哈希值会改变(因为父提交变了)。 |
分支结构 | 保留分支的"分叉"痕迹。 | 消除分叉,看起来像一条直线开发。 |
二、图解对比
假设有如下历史:
css
A --- B --- C ← main
\
D --- E ← feature
1. git merge feature
(在 main
分支上执行)
css
git checkout main
git merge feature
结果:
css
A --- B --- C -------- F ← main
\ /
D --------- E -- ← feature
- F 是一个新的合并提交(merge commit) 。
- 它有两个父提交:
C
和E
。 - 历史保留了
feature
分支的存在。
2. git rebase main
(在 feature
分支上执行)
css
git checkout feature
git rebase main
结果:
css
A --- B --- C --- D' --- E' ← feature (更新)
\
D --- E ← (原 feature 提交,将被丢弃)
然后切换到 main
并合并(此时是快进):
bash
git checkout main
git merge feature # 快进合并
最终:
css
A --- B --- C --- D' --- E' ← main, feature
D
和E
被"重放"为D'
和E'
,它们的内容相同但哈希值不同。- 历史是线性的,没有合并提交。
三、使用场景对比
场景 | 推荐策略 | 原因 |
---|---|---|
将功能分支合并到主分支(如 main /master ) |
✅ merge |
- 保留完整的历史记录,便于追踪问题。 - 合并提交明确标记了功能集成的时间点。 - 安全,不重写已公开的历史。 |
同步主分支的最新更改到你的功能分支 | ✅ rebase |
- 保持功能分支基于最新的主分支。 - 避免不必要的合并提交,保持历史清晰。 - 减少最终合并时的冲突。 |
团队协作,分支已推送到远程仓库 | ✅ merge |
- rebase 会重写历史,如果分支已被他人拉取,会导致混乱。 - merge 是安全的,不会破坏他人工作。 |
个人功能分支,尚未推送到远程 | ✅ rebase |
- 可以自由重写历史,保持提交记录整洁。 - 最终合并时可以用 --ff-only 快进,得到线性历史。 |
需要清晰的线性历史(如某些团队规范) | ✅ rebase + merge --ff-only |
先用 rebase 整理分支,再用快进合并,得到干净的历史。 |
需要保留分支的上下文和合并信息 | ✅ merge |
合并提交可以包含审查信息、Jira 链接等,是重要的元数据。 |
四、优缺点总结
merge
的优缺点
优点 | 缺点 |
---|---|
✅ 历史真实、完整,反映实际开发流程。 | ❌ 会产生额外的"合并提交",历史可能显得杂乱。 |
✅ 安全,不改变已有提交,适合共享分支。 | ❌ 分支拓扑复杂时,历史图谱可能难以阅读。 |
✅ 支持非快进合并,保留集成点信息。 |
rebase
的优缺点
优点 | 缺点 |
---|---|
✅ 提交历史线性、整洁,易于 git bisect 调试。 |
❌ 重写历史,禁止对已推送的公共分支使用。 |
✅ 避免不必要的合并提交。 | ❌ 丢失了"分支从哪里分出"的上下文信息。 |
✅ 减少冲突,便于集成。 | ❌ 如果操作失误,恢复较复杂。 |
五、最佳实践建议
-
pull
时使用rebase
:arduinogit config --global pull.rebase true
这样
git pull
会先rebase
而不是merge
,避免本地产生不必要的合并提交。 -
功能分支开发流程:
bash# 开发中定期同步主分支 git checkout feature git rebase main # 保持 feature 基于最新的 main # 完成后,合并到 main git checkout main git merge feature # 使用 merge 保留集成记录
-
追求线性历史的团队:
css# 在 feature 分支上 git rebase main # 切换到 main git checkout main git merge feature --ff-only # 强制快进,如果不能快进则报错
-
永远不要
rebase
已推送的公共分支 (如main
,develop
或他人正在使用的分支)。
总结
一句话概括 | |
---|---|
merge 是"记录历史",rebase 是"重写历史" 。 |
|
merge 保真,rebase 求洁。 |
|
共享用 merge ,私有用 rebase 。 |