返回博客

Git Cherry Pick:多提交、跨分支与冲突

2026年9月12日

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..BA^..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.

self-hosted · no third parties · one-click unsubscribe

what is this?

Cron 还是 systemd timer:该用哪个?

喜欢这些文章?我的本职工作就是这样的工程。 雇用我