Git Cherry-pick — 精确摘取提交的艺术
深入理解 git 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
| git checkout develop
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 2 3 4 5
| 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
| 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
| git cherry-pick abc1234
git add 冲突文件
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
git checkout develop
git cherry-pick a1b2c3d e4f5g6h
git log --oneline
|
摘取后修改提交信息
1 2 3 4 5 6 7 8
| git cherry-pick -e a1b2c3d
|
七、
Cherry-pick vs Merge vs Rebase
| 特性 | cherry-pick | merge | rebase |
|---|
| 摘取范围 | 指定提交 | 整个分支 | 整个分支 |
| 历史影响 | 生成新提交 | 生成合并提交 | 重写历史 |
| 是否改写 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
| 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> │ └─────────────────────────────────────────────────────────┘
|