git cherry-pick 是一个非常有用的 Git 命令,它的主要作用是将**某一个或多个特定的提交(commit)**应用到当前分支上,而不是合并整个分支。
你可以把它想象成从一棵樱桃树(其他分支)上,只摘下你喜欢的几颗"樱桃"(特定的提交),然后放到你的篮子里(当前分支)。
核心概念
- 作用:复制一个或多个提交,并在当前分支上生成内容相同的新提交。
- 来源 :这些提交可以来自:
- 同一个仓库的其他分支
- 同一个分支的更早位置(通过 commit hash)
- 甚至远程仓库的分支
- 结果 :在当前分支会产生新的提交记录,其内容与原始提交相同,但 commit hash 会改变。
基本语法
bash
# 1. 挑选单个提交
git cherry-pick <commit-hash>
# 2. 挑选多个不连续的提交
git cherry-pick <commit-hash-1> <commit-hash-2>
# 3. 挑选一个连续的提交范围 (注意:不包含 start-commit)
git cherry-pick <start-commit>..<end-commit>
# 4. 挑选一个连续的提交范围 (包含 start-commit)
git cherry-pick <start-commit>^..<end-commit>
常用场景与示例
假设我们有以下提交历史,有两个分支 main 和 feature:
C---D---E feature
/
A---B---F---G main
- 提交
C,D,E在feature分支上。 - 你当前在
main分支(HEAD指向G)。
场景1:只想要功能分支上的一个修复(提交 D)
你觉得提交 D 是一个很重要的 bug 修复,需要立刻应用到 main 分支,但还不想要 feature 分支的完整功能(提交 C 和 E)。
- 找到提交
D的 hash(例如a1b2c3d)。 - 确保你在
main分支:git checkout main - 执行 cherry-pick:
bash
git cherry-pick a1b2c3d
执行后,历史会变成:
C---D---E feature
/
A---B---F---G---D' main
注意:D' 是一个新的提交,它引入了和 D 完全相同的更改,但拥有一个新的 hash。
场景2:想要功能分支上的某几个连续功能(提交 C 和 D)
bash
# 方法1:逐个挑选
git cherry-pick <hash-of-C>
git cherry-pick <hash-of-D>
# 方法2:使用范围 (注意 B 是 C 的父提交,所以要用 ^ 来包含 C)
git cherry-pick B^..D
常用选项
| 选项 | 全称 | 说明 |
|---|---|---|
-e |
--edit |
在应用提交前,允许你编辑提交信息。默认情况下,会使用原提交的信息。 |
-n |
--no-commit |
只应用代码变更,但不创建新的提交。这会将变更放入暂存区和工作区,允许你手动修改后一次性提交。非常有用! |
-x |
在提交信息中追加一行 (cherry picked from commit ...),说明这个提交是从哪里挑选来的。在团队协作中推荐使用,便于追溯。 |
|
-s |
--signoff |
在提交信息末尾添加 Signed-off-by 行。 |
--abort |
当 cherry-pick 发生冲突并中断后,使用此命令可以放弃整个 cherry-pick 过程,恢复到操作前的状态。 | |
--continue |
当你解决完冲突并 git add 了相关文件后,使用此命令继续完成 cherry-pick 过程。 |
|
-m 1 |
当挑选一个合并提交(merge commit)时,必须指定使用哪个父提交的diff。通常 -m 1 表示第一个父分支(被合并进去的分支)。 |
处理冲突
和 merge 或 rebase 一样,cherry-pick 也可能发生冲突。
- 冲突发生:如果 cherry-pick 过程中发生冲突,Git 会停止操作,并告诉你需要解决冲突。
- 解决冲突:手动编辑标记为冲突的文件,解决冲突。
- 标记为已解决 :使用
git add <file-name>将解决后的文件添加到暂存区。 - 继续操作 :使用
git cherry-pick --continue来完成操作。 - 放弃操作 :如果解决不了,可以用
git cherry-pick --abort回到操作前的状态。
最佳实践与注意事项
- 慎用于公共分支 :因为 cherry-pick 会创建新的提交,如果对已经推送到远程的公共分支进行 cherry-pick,可能会造成历史混乱。它更适用于将特定提交从功能分支应用到主分支,或者在自己的分支间同步代码。
- 可能破坏上下文:提交之间可能有依赖关系。只挑选其中一个而忽略其他,可能会导致代码不完整或编译失败。
- 与
merge和rebase的区别 :merge:合并整个分支的所有更改。rebase:移动或重新应用整个分支的一系列提交。cherry-pick:有选择地复制单个或少量提交。
- 优先考虑其他方法 :如果需要一个分支的所有更改,优先使用
merge;如果想保持线性历史,可以考虑rebase。cherry-pick是当你只需要特定提交时的精准工具。
总结
git cherry-pick 是一个强大的"外科手术"工具,用于精准地获取特定的提交。理解其工作原理和使用场景,可以让你在复杂的开发工作流中游刃有余。