git merge 的 --no-ff 参数有什么作用
--no-ff 禁止快进合并:即使可以直接快进,也强制创建一个合并提交。作用是保留"这里曾经合并过一个功能分支"的历史痕迹,让分支拓扑清晰可见,回滚整组改动更容易。
默认情况下,如果目标分支没有新提交,merge 会"快进"(fast-forward)——分支指针直接前移,不产生合并提交,历史上看不出这里曾有一条功能分支。 --no-ff 强制生成一个合并提交,把"这次合并"作为一个可见的节点留在历史里。
图解
默认快进(--ff):
main: A---B---C---D (D 原属于 feature,看不出分支存在过)
--no-ff:
main: A---B-----------M (M 是合并提交,"Merge branch 'feature-x'")
\ /
feature-x: C---D
为什么团队喜欢 --no-ff
- 特性边界清晰:
git log --first-parent main看到的就是一份"合并历史",每个合并点对应一个特性/PR; - 回滚整组改动容易:
git revert -m 1 <合并提交>一次撤销整个特性; - 配合分支删除:分支删了,拓扑仍在。
配置默认行为
git config --global merge.ff false # 本机所有合并默认 --no-ff
# 只对特定分支(如 main)
git config branch.main.mergeoptions "--no-ff"
常见问题(FAQ)
Q:--no-ff 和 squash merge 怎么选?
--no-ff 保留功能分支的每个提交+合并节点(历史详尽);squash 压成一个提交(最干净)。要细节选前者,要简洁选后者。
Q:Git Flow 必须用 --no-ff 吗?
Git Flow 的分支模型依赖合并提交来标记特性边界,所以是它的惯例配置。GitHub Flow 下按团队喜好。
Q:--ff-only 又是什么?
相反方向:只允许快进,不能快进就报错。用于"我不想产生合并提交"的线性历史策略,常与 rebase 工作流搭配。