很多开发者第一次接触 GitLab 的 Cherry-pick 时都会有一个疑问如果团队开发足够规范一个 MRMerge Request不是应该只完成一个功能或一个 Bug 修复吗既然如此为什么还需要 Cherry-pick这是一个非常典型的误区。实际上Cherry-pick 的价值并不是解决 MR 粒度的问题而是解决代码发布的问题。一个优秀的 MR 应该是什么样大多数团队都会遵循一个原则One MR, One Purpose一个 MR只完成一个目标例如MR1修复登录 Bug MR2新增 OAuth 登录 MR3重构登录模块 MR4优化登录性能而不是MR ✓ 修复登录 Bug ✓ 新增 OAuth 登录 ✓ 修改数据库 ✓ 重构代码 ✓ 优化 UI这样做有很多好处Code Review 更容易只需要关注一个目标。CI 出问题时更容易定位原因。回滚更加简单。Git 历史更加清晰。每个 MR 都可以独立验证。所以一个成熟团队通常都会尽量保持MR 足够小、职责单一。那为什么还需要 Cherry-pick关键在于Cherry-pick 不是在 MR 中选择而是在分支中选择。很多人误以为 Cherry-pick 是「这个 MR 里面既有 Bug又有新功能所以我要挑一个出来。」事实上在规范团队里这种情况反而很少发生。真正经常发生的是下面这种情况。开发分支和发布分支并不是同一个分支假设团队有这样几个分支main 线上 release/2.1 待发布 develop 日常开发开发人员所有工作都提交到develop。例如最近开发了三个 MRMR1 修复库存计算 Bug MR2 新增 AI Reply MR3 重构邮件模块这些 MR 都已经合并到了develop。此时develop MR1 MR2 MR3但是公司准备发布 2.1 版本。问题来了产品经理决定这次版本只上线 Bug 修复不上线新功能。于是需要得到这样的结果release/2.1 ✓ MR1 ✗ MR2 ✗ MR3这时候就不能直接Merge develop → release因为这样会把 AI Reply、新功能、重构全部带过去。正确的方法就是Cherry-pick MR1 ↓ release/2.1整个发布过程变成develop MR1 MR2 MR3 │ │ Cherry-pick ▼ release/2.1 MR1可以看到Cherry-pick 选择的是「哪个 MR 进入哪个分支」而不是「MR 里面选择哪些 Commit」。为什么不直接把 MR 合并到 Release因为不同分支承担着不同职责。例如develop意味着最新开发成果。可能包含新功能实验功能重构Bug 修复而release/2.1意味着即将上线的稳定版本。通常只允许Bug 修复安全修复极少量稳定改动因此Release 分支必须尽可能保持稳定。Cherry-pick 就成为了连接这两个分支的桥梁。热修复Hotfix也是一样还有一种更常见的情况。线上运行的是main开发人员发现一个严重 Bug。修复之后代码首先进入develop但是线上用户已经受到影响。这时候不能等待下一次版本发布而需要立刻修复线上。于是MR Fix login bug │ ├────────► develop │ └─Cherry-pick──► main这样开发分支继续正常开发。线上立即获得 Bug 修复。新功能不会提前发布。这就是 Hotfix 最经典的流程。为什么大家会误解 Cherry-pick原因在于很多教程都会举这样的例子MR Commit1 修 Bug Commit2 新功能然后Cherry-pick Commit1虽然 Git 的确支持这种操作但这并不是 Cherry-pick 最重要的使用场景。如果一个团队经常需要从一个 MR 中挑 Commit反而说明MR 拆分得不够合理。真正优秀的开发流程应该是MR1 Fix Login Bug MR2 Add OAuth MR3 Refactor Login这样 Cherry-pick 时直接选择整个 MR 即可不需要再从里面挑 Commit。总结很多人认为一个 MR 应该只完成一件事所以 Cherry-pick 没什么意义。实际上这两件事情并不冲突。MR 的职责保证一次开发只解决一个问题方便 Review、测试和回滚。Cherry-pick 的职责决定哪些修改进入哪个分支方便版本发布和热修复。换句话说MR 是开发维度的管理工具而 Cherry-pick 是发布维度的管理工具。开发阶段我们追求的是小而独立的 MR发布阶段我们追求的是只把需要的修改发布到目标分支。正因为 MR 足够独立Cherry-pick 才能发挥最大的价值精准地将一个已经验证完成的修改同步到需要它的分支而不会夹带任何无关代码。