在复杂的软件开发过程中,我们经常需要在不同的开发分支之间同步特定的、独立的小功能或紧急修复(Hotfix)。如果我们使用传统的 git merge,则会把整个分支的历史记录和所有提交都带入目标分支,这往往不是我们想要的。这时,强大的 git cherry-pick 命令就派上用场了。
git cherry-pick 的作用是:将某个提交(Commit)所引入的更改应用到当前所在的分支上,就像重新提交了一次该更改一样。
1. 为什么使用 cherry-pick?
想象一下,你正在 feature-A 分支上开发,突然发现了一个核心库的 Bug 并迅速修复了它(提交 C2)。这个 Bug 同样影响了 feature-B 分支。与其把整个 feature-A 合并到 feature-B(可能 feature-A 尚未完成),不如只把包含 C2 修复的那个提交同步过去。
2. 操作实战:同步单个提交
我们将演示如何在一个示例仓库中,将 feature-A 上的一个提交同步到 feature-B。
步骤 2.1: 准备示例仓库
首先,创建一个示例 Git 仓库,并创建两个独立的功能分支。
git init cherry-pick-demo
cd cherry-pick-demo
# 初始提交
echo "Project setup" > README.md
git add .
git commit -m "C0: Initial setup"
# 创建并切换到 feature-A
git branch feature-A
git branch feature-B
步骤 2.2: 在源分支(feature-A)上创建目标提交
我们在 feature-A 上进行了一些开发,并创建了一个紧急修复 C2。
git checkout feature-A
echo "Main feature logic" > component.js
git add .
git commit -m "C1: Added initial component logic"
# C2:这是我们希望同步到 feature-B 的关键提交
sed -i '' 's/logic/logic, bug fixed/' component.js # macOS sed syntax
# 如果是 Linux,请使用: sed -i 's/logic/logic, bug fixed/' component.js
git add component.js
git commit -m "C2: Critical bug fix for component (Target commit)"
步骤 2.3: 识别提交 Hash
我们需要获取 C2 提交的完整 Hash 值(或其前几位)。
git log --oneline
假设我们得到了 C2 的 Hash 为 1a2b3c4。
步骤 2.4: 执行 cherry-pick
现在切换到目标分支 feature-B,并应用该提交。
git checkout feature-B
# 确认当前分支没有 component.js 文件 (因为 feature-B 还在 C0 状态)
ls
# 执行 cherry-pick,应用 C2 的更改
git cherry-pick 1a2b3c4
# 检查结果:feature-B 现在拥有 component.js 及其内容
cat component.js
# 检查日志:一个全新的提交历史出现在 feature-B 上,但内容与 C2 一致。
git log --oneline
通过查看日志,你会发现 feature-B 现在多了一个新的提交,其内容和 C2 完全相同,但 Hash 值是不同的,因为它是针对 feature-B 历史的一个新提交。
3. 处理冲突
如果被挑选的提交(Commit)与目标分支(Target Branch)上已有的代码发生冲突,Git 会暂停操作并提示你解决冲突。这是一个非常常见的场景。
解决冲突的流程:
- 手动解决文件中的冲突标记(<<<<<<<, =======, >>>>>>>)。
- 暂存已解决的文件: git add .
- 继续 cherry-pick 流程: git cherry-pick –continue
如果希望中止操作,可以使用 git cherry-pick –abort,这将回到执行 cherry-pick 之前的状态。
4. 同步多个连续提交
如果你需要同步一系列连续的提交,可以使用范围语法。
假设你有提交 C3, C4, C5,你想同步 C4 和 C5。Git 的范围语法是 start..end,但需要注意,start 提交本身是不包含的。
要包含 C4 到 C5,你需要指定 C4 的父提交(C3)作为起点,C5 作为终点:
# 假设 C3_HASH 是 C4 的父提交,C5_HASH 是最后一个提交
git cherry-pick C3_HASH^..C5_HASH
# 注意:C3_HASH^ 表示 C3_HASH 的父提交,但如果 C3_HASH 就是第一个要挑选的,
# 且 C3_HASH 是 C4 的父提交,则使用 C3_HASH..C5_HASH 即可。
# 最精确且常用的写法是使用范围:
# git cherry-pick C_start_parent..C_end
记住,git cherry-pick 是一个强大的工具,但应该谨慎使用,因为它会创建一个新的提交,稍微修改了历史,如果被挑选的提交依赖于目标分支上不存在的上下文,它可能会引入难以预料的 Bug。
汤不热吧