在多人协作的 Git 仓库里,经常遇到一种需求:别的分支上已经有了一个修复或功能提交,但当前分支不需要合并整个分支的改动。git cherry-pick 就是为这种场景设计的——把指定的提交提取到当前分支。
不过,cherry-pick 并不只是执行一条命令那么简单。它本质上是一次三方合并,把源提交相对其父提交的差异重新应用一遍,因此会涉及到冲突处理、提交信息保留、以及来源分支后续合并时的重复改动问题。实际工作中,用错 cherry-pick 的情况不少:带着脏工作区执行、冲突发生后盲目 --continue、或者选错了提交范围。下面按场景拆开说。
什么时候适合用 cherry-pick
cherry-pick 的典型场景是修补发布分支。比如线上版本需要同步一个 hotfix,而这个 hotfix 只存在于开发分支上,你希望把这次修复提取到 release 分支,同时不带走其他未发布的特性。
另一个常见场景是提交位置放错了。比如开发时误在 feature-A 分支上提交了属于 feature-B 的改动,可以用 cherry-pick 把这次提交复制到 feature-B,再在 feature-A 分支上通过 revert 或 reset 清理干净。
还有一类场景是临时集成测试:把某个独立提交移植到另一个分支,验证改动在那个分支上的兼容性,不打算长期保留。如果之后发现改动不兼容,直接 revert 掉即可,不会污染来源分支的提交历史。
不太适合的场景也必须说清楚。如果两个分支已经分叉很久,同一个文件的改动很多,cherry-pick 的冲突成本可能比直接 merge 更高。另外,如果一次要提取几十个提交,且这些提交之间存在依赖关系,建议优先评估 rebase --onto 或 format-patch 加 git am 的组合方式,而不是逐个 cherry-pick。
基础操作:找到提交并执行
首先要定位目标提交。在来源分支上运行 git log --oneline -10 可以快速看最近提交,如果提交比较早,可以用 git log --all --oneline --grep='关键词' 按提交信息搜索。确定哈希后,切换到目标分支,执行:
git checkout release-2.1
git cherry-pick 3f4a2c9d
Git 会把 3f4a2c9d 的改动应用到当前分支,并自动创建新提交。这个新提交的哈希与源提交不同,提交信息默认沿用源提交信息,末尾会追加一行来源说明,通常形如:
cherry-pick 3f4a2c9d
这行说明对后续追溯很有用,建议保留,不要在执行 --continue 时清理掉。
执行后先用 git log -1 看新提交的改动范围,再用 git show --stat HEAD 确认涉及的文件列表与源提交一致。如果目标分支上恰好有相同文件的改动,cherry-pick 执行时就会进入冲突状态。
冲突处理:--continue 与 --abort
cherry-pick 发生冲突时,Git 会进入待处理状态,工作区出现冲突标记(<<<<<<<、=======、>>>>>>>)。用 git status 查看冲突文件,逐一解决后,git add 标记为已解决,再执行:
git cherry-pick --continue
--continue 会沿用源提交信息,打开编辑器让你确认。要注意,这个命令必须在所有冲突都已 git add 之后执行。如果冲突范围太大,或者某个文件怎么改都不确定,可以用 git cherry-pick --abort 回到执行前的状态,已产生的改动会被丢弃。
另一个容易踩的情况是:所有冲突解决并 git add 后,直接 git commit 而不是 git cherry-pick --continue。这样做虽然也能生成提交并结束暂停状态,但提交信息不会自动追加来源说明,后续追溯时会少一条线索,团队如果依赖这段说明,回溯成本会变高。
容易被忽略的检查点
cherry-pick 默认保留源提交的作者(Author)和作者日期(AuthorDate),但提交者日期(CommitDate)会更新为当前时间。用 git log 查看时,如果 CommitDate 和 AuthorDate 差异很大,就会形成一条“提交了很久但刚合入”的记录。团队通常不关心这个问题,但如果需要精确保持作者信息,可以提前确认 user.name 和 user.email 配置正确,或者用 git commit --amend --reset-author 修改。
第二个检查点是工作区状态。在 cherry-pick 之前,最好确认当前工作区是干净的。如果有未提交的改动,cherry-pick 会把改动叠加到上面,冲突判断会变得复杂。可以先 git stash,执行完 cherry-pick 后再 git stash pop 恢复。
第三个检查点与后续合并有关。cherry-pick 生成的提交和源提交在 Git 中是两个不同的提交,因为哈希不同。如果之后把来源分支合并到目标分支,Git 会尝试再次应用相同的补丁,可能出现重复修改或冲突。规避方式没有统一答案:如果源提交已经不需要,可以在来源分支上 revert 或 reset 掉;如果源提交仍需保留,则要在合并时手动协调,或者用 git rerere 记录此前解决过的冲突。
多个提交、范围提取与远程推送
一次提取多个提交时,如果提交在历史上是连续的,可以用范围写法:
git cherry-pick A..B
这个范围表示从 A 之后到 B 之间的所有提交,Git 会按提交顺序从旧到新逐个应用。如果提交之间本身有依赖,范围提取通常能保持顺序,冲突一般在依赖边界附近出现。但要注意,A..B 不包含 A 本身;如果需要包含 A,写成 A^..B。
如果想挑几个不连续的提交,可以逐个列出哈希,顺序就是应用顺序:
git cherry-pick 3f4a2c9d e7b8f1a2
如果只想先把改动放进工作区、暂不生成提交,可以用 -n 参数。git cherry-pick -n 3f4a2c9d 会把改动只应用到暂存区,让你在本地查看效果,确认无误后再手动 git commit。但用 -n 时要格外小心:改动的文件在 git status 里显示为暂存状态,如果之后又做了其他分支操作或重新 checkout,很容易混入无关改动,建议用完立即提交或重置。
关于远程推送,cherry-pick 只改变本地目标分支。如果之前已经推送过该分支,且你在 cherry-pick 后又对该提交做了 amend 或 rebase,推送时就需要考虑 --force-with-lease。这个选项会先检查远程分支是否与本地预期一致,比盲目 --force 更稳妥,但前提是团队明确允许强制推送。如果目标分支是受保护分支,通常需要在代码评审通过后才能推送,这类规则限制不在 Git 命令本身,尽早确认能避免白忙一圈。
最后一个建议是:在执行前用 git status 确认工作区干净,用 git log 确认源提交哈希正确,再用 git show --stat 快速看一遍源提交的改动范围。这三步做完,通常能避免大部分低级问题。cherry-pick 本身不难,难的是预见它作为一次三方合并会带来什么边界情况——这个判断能力,比记住命令参数更值得积累。