TL;DR:git cherry-pick <sha> 把任意分支上的一个提交复制到你当前分支——补丁相同,SHA 换新,历史一步不动。 多个提交就列出 SHA 或用区间(git cherry-pick A..B);注意区间不含 A,想带上就写 A^..B。如果在冲突处停下:解决,git add,然后 git cherry-pick --continue。cherry-pick 是用来搬特定那个修复的——想要对方分支的全部,请用 merge 或 rebase。
git cherry-pick 到底做了什么?
cherry-pick 取一个已有提交,把它的 diff 作为新提交应用到当前分支。原提交原地不动;副本拿到一个全新的 SHA。git 什么都没”搬”——后来纳闷为什么那个提交还出现在旧分支上的人,看到的就是这个。
“复制而非移动”这个模型决定了 cherry-pick 何时是正确的工具:
- 你现在就要 feature 分支上的某一个修复进
main,其余不想合。 - 热修复提交错了分支,要落到对的那个。
- 一个补丁必须重放到一个从不从
main合并的 release 分支上。
配套技能是知道怎么撤掉一个事后发现不对的提交——具体操作见 git 撤销最后一次提交:保留改动。
怎么从另一个分支 cherry-pick 一个提交?
找到 SHA,切到目标分支,拣选:
# 1. 在源分支上定位那个提交
git log feature/payment-fix --oneline -5
# 2. 切到应该接收它的分支
git switch main
# 3. 复制过来
git cherry-pick 1a2b3c4 两个值得一知的便利:
git cherry-pick <branch>拣选那个分支的顶端提交——顺手,但也容易因为对”顶端”的认知过期而误触。- 拣完之后,
git log -1 --stat确认落了什么。花一秒钟看一眼,省一次 revert。
那些提交留在 feature/payment-fix 上;那个分支想什么时候删就什么时候删——main 上的副本有自己的 SHA,不依赖旧的。
怎么 cherry-pick 多个提交?
三种形态,按使用频率排序:
# 1. 显式列表——按你列出的顺序依次拣选
git cherry-pick 1a2b3c4 5d6e7f8
# 2. 区间——A 之后直到 B(含 B)的所有提交
git cherry-pick A..B
# 3. 包含 A 的区间
git cherry-pick A^..B A..B 与 A^..B 的区别是经典的意外来源:A..B 不包含 A。如果你从 git log 里目测区间然后拣 oldest..newest,最老的提交就被无声跳过了。目标是”最老的那几个,按顺序”时,写 oldest^..newest,这个差一错误就消失了。
想把几次拣选合成一个提交而不是三个,用 -n / --no-commit 只暂存不提交,然后一次性提交:
git cherry-pick -n 1a2b3c4 5d6e7f8
git commit -m "Backport: payment retry fixes" 为什么 git cherry-pick 不工作?
四个真实原因,按频率排序:
1. 冲突把拣选停住了。 git 应用补丁,撞上一行两边都改过的代码,中途暂停:
# 解决冲突的文件,然后:
git add <resolved-files>
git cherry-pick --continue # 或者 --abort 回到拣选前的状态 --continue 不是可选项,也不会自动发生——不跑它,你就一直停在暂停的 cherry-pick 序列里,git status 会不停地提醒你这一点。
2. 拣选是空的(“The previous cherry-pick is now empty”)。 改动已经在这个分支上,通常来自之前一次拣选或一次 squash 合并。用 git cherry-pick --skip 跳过;真需要这个标记就用 --allow-empty 强制一个空提交。
3. 目标提交是个 merge commit。 合并提交有两个父提交,“应用这个 diff”是有歧义的——git 宁可拒绝也不猜。指明你相对哪个父提交取 diff:
git cherry-pick -m 1 <merge-sha> # parent 1 = 被合并*进*的那个分支 4. 工作树不对或处于 detached HEAD。 拣选落在 HEAD 指向的地方。拣之前跑一下 git branch --show-current;如果它什么都不打印,你在 detached HEAD 里,切走时这个提交会变成孤儿。
Cherry-pick、merge、rebase:什么场景用哪个?
| 命令 | 落到目标上的东西 | 历史 | 适用场景 |
|---|---|---|---|
git cherry-pick <sha> | 仅指定的提交 | 复制,新 SHA | 某个特定修复现在就得过去 |
git merge <branch> | 分支上的全部内容 | 合并提交或快进 | 要整个分支,且分叉可见 |
git rebase <base> | 分支上所有提交重放 | 线性,SHA 重写 | 要分支呈现为干净的线性历史 |
git revert <sha> | 某提交的逆操作 | 新增一个撤销提交 | 已上共享历史的提交必须撤销 |
一句话规则:cherry-pick 搬的是挑出来的那部分;merge 和 rebase 搬的是全部。想用 cherry-pick 去和某个分支”同步”,说明你真正想要的是 merge——如果那两个分支是你的 fork 的 main 和上游,完整流程在 同步 fork 与上游,分步指南。
一个可以跳过的习惯:长期把同一个提交拣进多个分支。源分支上往后每个修复都得再拣一次,分支们迟早漂移。向 release 分支 backport 是正常操作;一个永久的平行宇宙不是。
Cherry-pick 工作流,浓缩版
git log <source-branch> --oneline -5 # 找到 SHA
git switch <target-branch> # 站到正确的分支上
git cherry-pick A^..B # 区间、列表或单个 SHA
# 冲突时:解决 → git add → git cherry-pick --continue
git log -1 --stat # 确认落了什么 找、切、拣、验。这个命令的名声比它应得的凶——diff 要么应用成功,要么停下来告诉你为什么。唯一真正危险的错误是拣错了分支,而 push 之前看一眼 git log -1 就很难错过它。
— mrsaynothing
Get the next one by email
One email per post. No spam, no algorithms.
what is this?喜欢这些文章?我的本职工作就是这样的工程。 雇用我