深入理解 git cherry-pick 的用法,学会从任意分支精确摘取特定提交,解决跨分支同步修复、找回丢失代码等实际问题。

Cherry-pick — 从别的分支上摘樱桃

一句话解释:从别的分支上拿一个特定的修改过来,不合并整个分支。

如果说 merge 是把整棵树搬过来,那 cherry-pick 就是只摘上面的几颗樱桃


一、通俗比喻

就像抄作业

  • 小明写了 5 道题(5 个提交)
  • 你只需要抄第 2、3 题(cherry-pick)
  • 不需要把他整本作业都拿过来(不 merge)

对比理解

操作比喻效果
merge把整本作业都抄过来合并整个分支的所有提交
cherry-pick只抄第 2、3 题只摘取指定的提交
rebase把你的作业接在别人作业后面重新排列提交顺序

二、三个最常见使用场景

场景 1:跨分支同步 Bug 修复

你在 main 分支上修了一个紧急 Bug,但 develop 分支也需要同步这个修复:

1
2
main:    A → B → C → D (fix: 修复登录崩溃)
develop: A → B → E → F
1
2
3
4
5
# 切换到 develop 分支
git checkout develop

# 把 main 上的修复提交摘过来
git cherry-pick D的hash

结果:

1
2
main:    A → B → C → D (fix: 修复登录崩溃)
develop: A → B → E → F → D' (fix: 修复登录崩溃)

提示D' 是一个全新的提交(不同的 hash),但代码改动和 D 完全一样。

场景 2:找回被误删的代码

你不小心用 git reset --hard 丢掉了某个功能的代码,但同事的分支上还有:

1
2
3
4
5
# 找到同事分支上的提交
git log feature/同事分支 --oneline

# 把那个提交摘过来
git cherry-pick <commit_hash>

场景 3:只挑选部分功能

同事在他的分支上开发了 5 个功能,你只需要其中 2 个:

1
git cherry-pick <功能A的hash> <功能B的hash>

三、基本语法

摘取单个提交

1
git cherry-pick <commit_hash>

示例:

1
git cherry-pick abc1234

摘取多个提交

1
2
3
4
5
# 空格分隔多个 hash
git cherry-pick abc1234 def5678

# 或用提交范围(不包含起始提交)
git cherry-pick abc1234..def5678

范围语法abc1234..def5678 表示从 abc1234 之后到 def5678 的所有提交,但不包含 abc1234 本身。如果想包含起始提交,用 abc1234^..def5678


四、常用选项

–no-commit(-n):只摘取代码,不自动提交

1
2
3
4
# 代码改动会进入暂存区,但不会生成提交
git cherry-pick --no-commit abc1234
# 简写
git cherry-pick -n abc1234

适用场景:你想在提交前检查或修改摘取过来的代码。

–edit(-e):摘取时修改提交信息

1
2
3
4
# 摘取时打开编辑器,让你修改提交信息
git cherry-pick --edit abc1234
# 简写
git cherry-pick -e abc1234

–signoff(-s):添加签名

1
2
# 在提交信息末尾添加 Signed-off-by
git cherry-pick --signoff abc1234

–continue / –abort:处理冲突

1
2
3
4
5
6
# 遇到冲突,解决后继续
git add 冲突文件
git cherry-pick --continue

# 冲突太多,放弃
git cherry-pick --abort

选项速查表

选项缩写作用
--no-commit-n只暂存代码,不自动提交
--edit-e摘取时修改提交信息
--signoff-s添加签名
--continue解决冲突后继续
--abort放弃 cherry-pick
--quit退出当前,但保留已完成的

五、冲突处理

cherry-pick 和 merge 一样可能产生冲突。当两个提交修改了同一文件的同一区域时就会冲突。

冲突解决流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. 执行 cherry-pick
git cherry-pick abc1234

# 2. 如果冲突,Git 会提示
# error: could not apply abc1234... feat: xxx

# 3. 打开冲突文件,解决冲突标记
# <<<<<<< HEAD
# 你的代码
# =======
# 摘取过来的代码
# >>>>>>> abc1234

# 4. 标记为已解决
git add 冲突文件

# 5. 继续
git cherry-pick --continue
小技巧

如果只想保留某一方的代码,可以直接编辑文件删除冲突标记,不需要走完整流程。


六、实战演示

完整操作流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 查看远程分支的提交历史
git log origin/feature/login --oneline

# 输出:
# a1b2c3d fix: 修复密码加密问题 ← 要这个
# e4f5g6h feat: 添加记住密码功能 ← 要这个
# i7j8k9l style: 调整输入框样式 ← 不要

# 切换到目标分支
git checkout develop

# 只摘取需要的两个提交
git cherry-pick a1b2c3d e4f5g6h

# 查看结果
git log --oneline
# 输出:
# x1y2z3d feat: 添加记住密码功能 ← 新 hash,但代码一样
# m3n4o5d fix: 修复密码加密问题 ← 新 hash,但代码一样
# p6q7r8s ...之前的提交

摘取后修改提交信息

1
2
3
4
5
6
7
8
# 用 -e 选项,摘取时自动打开编辑器
git cherry-pick -e a1b2c3d

# 编辑器中会显示:
# fix: 修复密码加密问题
#
# cherry picked from commit a1b2c3d
# 你可以修改上面的提交信息

七、Cherry-pick vs Merge vs Rebase

特性cherry-pickmergerebase
摘取范围指定提交整个分支整个分支
历史影响生成新提交生成合并提交重写历史
是否改写 hash是(新提交)
适用场景只需要同步部分提交合并整个分支整理提交历史
冲突处理逐个提交处理一次性处理逐个提交处理

什么时候选哪个?

1
2
3
4
5
6
7
8
需要同步另一个分支上的 1-2 个特定修复?
└── cherry-pick

要把整个功能分支合并到主线?
└── merge

要整理提交历史,让历史更干净?
└── rebase

八、注意事项

1. Cherry-pick 会产生新提交

摘取过来的提交和原始提交的hash 不同,虽然代码改动一样。这意味着 Git 会认为它们是两个不同的提交。

1
2
原始: a1b2c3d fix: 修复密码加密问题
摘取: x1y2z3d fix: 修复密码加密问题 ← 代码一样,hash 不同

2. 避免重复 cherry-pick

如果你对同一个提交执行两次 cherry-pick,Git 会尝试应用两次相同的改动,可能产生冲突或重复代码。

3. 公共分支慎用

如果目标分支是多人协作的公共分支,cherry-pick 前最好和团队沟通,避免产生混乱的历史。

4. 提交信息会保留来源

默认情况下,cherry-pick 的提交信息末尾会包含一行注释:

1
2
3
fix: 修复密码加密问题

(cherry picked from commit a1b2c3d...)

这有助于追溯代码来源。


九、进阶用法

从远程仓库摘取

如果提交在远程分支上,先 fetch 再 cherry-pick:

1
2
3
4
5
# 拉取远程最新
git fetch origin

# 从远程分支摘取
git cherry-pick origin/feature/login~3

摘取合并提交中的改动

合并提交有两个父提交,cherry-pick 时需要用 -m 指定保留哪个父提交的内容:

1
2
# -m 1 表示保留主分支(被合入的分支)的内容
git cherry-pick -m 1 <merge_commit_hash>

批量摘取 + 不自动提交

适合摘取多个提交后统一检查:

1
2
3
4
5
6
7
8
9
# 只暂存代码,不生成提交
git cherry-pick -n abc1234 def5678 ghi9012

# 检查代码
git status
git diff --cached

# 确认无误后一次性提交
git commit -m "feat: 同步远程分支的多个修复"

总结:Cherry-pick 速查卡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌─────────────────────────────────────────────────────────┐
│ Git Cherry-pick 速查 │
├─────────────────────────────────────────────────────────┤
│ 摘取单个提交 │ git cherry-pick <hash> │
│ 摘取多个提交 │ git cherry-pick <hash1> <hash2> │
│ 摘取范围 │ git cherry-pick <start>..<end> │
├─────────────────────────────────────────────────────────┤
│ 只暂存不提交 │ git cherry-pick -n <hash> │
│ 修改提交信息 │ git cherry-pick -e <hash> │
│ 解决冲突后继续 │ git add → git cherry-pick --continue │
│ 放弃操作 │ git cherry-pick --abort │
├─────────────────────────────────────────────────────────┤
│ 合并提交 │ git cherry-pick -m 1 <merge_hash> │
└─────────────────────────────────────────────────────────┘