关于 merge 与 rebase 的争论

Git 用户对于 merge commit、rebase 还是 squash merge 哪种方式最能产出有用的项目历史,依然存在分歧。支持 rebase 和 squash 工作流的人看重一条线性、经过“整理”的主分支,因为这能简化 `git bisect`、回滚和对变更的推理,尤其是在大规模或基于 trunk 的开发中。批评者则认为,重写历史脆弱、会遮蔽真实发生的过程,而且对经验不足的团队来说可能过于容易出错;他们更倾向于基于 merge 或基于 patch 的工作流,在那里混乱但准确的历史得以保留,而复杂性由工具而不是开发者来承担。

Merge vs. Rebase(以及 Bisect 可用性)

  • 许多人认为,由 rebase 管理的线性历史会让 git bisect 和对改动的推理容易得多;而大量使用 merge 的历史常被形容为“毛线团”,经常会把 bisect 结果缩减为“就在这个大 merge 的某个地方”。
  • 也有人反驳说,git log --first-parent 和 bisect 选项可以缓解 merge 的复杂性,而且结构良好的 merge commit 可以和 squash 过的提交一样易于理解。
  • 还有人强调,在活跃、多人协作的仓库里,大型、冲突很多的 rebase 很痛苦,而且容易出错。

Squash Merge 与提交粒度

  • 支持 squash:main/trunk 应该包含原子化、可回滚的单元(PR 或变更集),而不是“wip/修 typo”这类噪音;在提交卫生状况较差的地方,squash 被视为保持历史整洁的最简单方式。
  • 反对 squash:squash 可能会把重要的逻辑步骤(例如 helper 的引入、机械性重构、棘手的边界情况修改)埋进一个巨大的 diff 中,让 blame、review 和未来理解都更困难。
  • 也有不少人采用混合策略:对噪音较多的 PR 进行 squash,而通过 rebase+ff merge 保留经过深思熟虑、结构清晰的提交。

工作流与分支模型

  • 示例工作流:
    • 将功能分支 squash 到 staging,然后在发布时把 staging → master 合并。
    • 基于 trunk 的开发,或生命周期很短、频繁部署的分支 vs 2–3 周的功能分支。
  • 有些团队重视一致、简单的规则(例如“永远 squash”)来降低认知负担;另一些团队更偏好“视情况而定”,按每个 PR 单独选择。

工具与替代方案

  • Sapling 和 git-absorb 等工具因让堆叠 diff 和提交清理更容易而受到称赞(例如自动把编辑 amend 回正确的提交)。
  • 有些人认为 Git 的模型(分支只是指针、merge 历史别扭)存在根本性限制;文中还提到 Mercurial 或基于补丁/堆叠的工作流,在概念上更干净。

理念:“准确”历史 vs “整理过的”历史

  • 一派希望历史反映实际发生过的事情,包括试错和混乱的提交,认为重写历史是在“说谎”,并且会妨碍审计或调试。
  • 另一派则把历史视为一份经过整理的逻辑变更叙事,而不是不可更改的实验记录本;他们更看重可读性、更小的逻辑步骤,以及更容易做考古,而不是记录每一个失误。
  • 双方都同意:提交规范和共享的“git 风格指南”比任何单一规则都更重要。