🔑 为什么 Git 要有 fast-forward merge
很多人第一次看到 fast-forward merge,会觉得它“像没合并过一样”,因为 Git 只是把分支指针往前挪了一下,没有新建 merge commit。这个设计不是偷懒,而是在承认一个事实:如果目标分支在你切出新分支后根本没动过,那么历史里其实不存在“分叉又汇合”这件事,只是主线继续往前走了而已。Git 不额外造一个 commit,是为了让历史只记录真正发生过的结构,而不是为了形式上看起来“像一次合并”。
这件事的好处很直接。第一,提交历史更干净,
真正容易踩坑的地方是
🧪 假设
💡 易混淆点:fast-forward merge 不是“没合并”,而是“这次合并不需要额外的合并节点”;只有当两边都各自前进过,merge commit 才是在表达真实分叉。
#CS
很多人第一次看到 fast-forward merge,会觉得它“像没合并过一样”,因为 Git 只是把分支指针往前挪了一下,没有新建 merge commit。这个设计不是偷懒,而是在承认一个事实:如果目标分支在你切出新分支后根本没动过,那么历史里其实不存在“分叉又汇合”这件事,只是主线继续往前走了而已。Git 不额外造一个 commit,是为了让历史只记录真正发生过的结构,而不是为了形式上看起来“像一次合并”。
这件事的好处很直接。第一,提交历史更干净,
git log 不会充满“Merge branch 'feature'”这种信息噪音。第二,CI/CD 和回滚更容易看,因为主线上的每个 commit 都是实打实的代码状态,不夹杂一堆只表示“操作动作”的空壳合并点。但坑也在这里:有些团队以为“看不到 merge commit 就等于没人审过”,这就把“代码历史”和“流程记录”混成一件事了。Git 解决的是版本图结构,不负责替你证明流程是否严谨;那个应该靠 PR、review 记录和分支保护规则,不该靠制造无意义的 merge commit 硬凑。真正容易踩坑的地方是
--no-ff。它会强制创建 merge commit,就算本来能 fast-forward。这样做不是错,但你得知道自己在换什么:你得到的是更明显的“功能分支边界”,代价是主线历史变胖,阅读成本上升。所以问题从来不是“要不要统一用 fast-forward”,而是你想让历史表达什么。如果你在维护一个长期复杂分支,保留 merge commit 可能有价值;如果只是一个小修复,硬造合并点通常只是噪音。🧪 假设
main 指向提交 A,你切出 feature 后做了两个提交 B、C,而这期间 main 没有新提交。此时把 feature 合并回 main,默认情况下 Git 会发生什么?为什么?mainmainfeature💡 易混淆点:fast-forward merge 不是“没合并”,而是“这次合并不需要额外的合并节点”;只有当两边都各自前进过,merge commit 才是在表达真实分叉。
#CS