一个 MR 不应该只做一件事吗?为什么还需要 Cherry-pick?
很多开发者第一次接触 GitLab 的 Cherry-pick 时,都会有一个疑问:
如果团队开发足够规范,一个 MR(Merge 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。
例如最近开发了三个 MR:
MR1 修复库存计算 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 才能发挥最大的价值:精准地将一个已经验证完成的修改,同步到需要它的分支,而不会夹带任何无关代码。