git 仅在目标分支最新提交是待合并分支的直接祖先、且目标分支自上次同步后无新提交时自动执行 fast-forward 合并,此时仅移动指针不生成新提交,但删除分支后合并上下文将丢失。

什么时候 Git 会自动走 Fast-Forward?
Git 只在满足两个硬性条件时才触发 Fast-Forward:git merge 目标分支(如 main)的最新提交,必须是待合并分支(如 feature)的**直接祖先**;且目标分支自上次同步后**没有新提交**。
换句话说:你切出 feature 后,main 没动过,所有新工作都在 feature 上——这时 git merge feature 就是快进。Git 不会创建新提交,只把 main 的指针挪到 feature 最后一个提交上。
常见错误现象:
- 你以为是快进,但执行后却出现冲突提示——大概率是
main被别人 push 过,或者你本地没git pull同步 -
git log --oneline看不到 merge 提交,但git log --graph显示分叉——说明实际走了非快进,不是你预期的线性历史
Fast-Forward 的隐藏代价:删分支后历史就没了
快进合并看起来干净,但代价很实在:它不记录“这是一次合并”。一旦你 git branch -d feature,那个分支的所有上下文——起因、范围、负责人、甚至 PR 关联——全靠外部系统(如 GitHub)维系,Git 历史里只剩一串孤立提交。
典型场景:
- 线上出问题,你想回滚上周某个功能,但
git log里找不到对应 merge 提交,只能靠 commit message 模糊匹配 - 审计时需要确认某次发布是否包含特定分支,而
git show <commit></commit>查不到父分支信息 - 新人看历史,分不清哪些提交是独立开发,哪些是合并进来的
这不是理论风险——它每天发生在没加 --no-ff 的团队里。
用 --no-ff 强制生成合并提交的实操要点
git merge --no-ff feature 是最常用也最稳妥的补救方式。但它不是无脑加参数就能跑通:
- 必须在目标分支(如
main)上执行,且该分支已 checkout - 强制非快进时,Git 要求你提供合并说明,否则报错:
fatal: refusing to merge unrelated histories或提示 missing-m - 正确写法:
git merge --no-ff -m "merge feature/user-profile into main" - 如果省略
-m,Git 会打开编辑器让你手写,容易卡住 CI 流程
这个合并提交有两个父提交:main 当前 HEAD 和 feature 的 tip,git log --graph 会清晰显示分叉再汇合的结构。
要不要全局禁用 Fast-Forward?
可以,但不推荐一刀切。Git 允许配置:git config --global merge.ff false,此后所有 git merge 默认走 --no-ff。
问题在于:它会影响所有合并,包括那些本该是快进的本地小分支(比如临时修复分支),徒增无意义的合并节点,污染线性历史。
更务实的做法:
- 对集成分支(
main/release/*)强制--no-ff,确保每次发布都有可追溯的合并点 - 对开发分支间合并(
dev → staging)视团队规范决定,但建议统一 - CI/CD 脚本里显式写死
git merge --no-ff -m "$CI_COMMIT_MESSAGE",避免人肉遗漏
真正容易被忽略的点不是命令怎么写,而是:**合并策略必须和分支生命周期绑定**。你删不删 feature 分支、要不要保留它的元信息、是否依赖 Git 历史做自动化回滚——这些决策,比选哪个 merge 参数更重要。











