Skip to content

拣选 (Cherry-pick)

概述

Git Cherry-pick 允许你选择性地将某个提交应用到当前分支。这在需要从其他分支获取特定修改时非常有用,而不需要合并整个分支。

什么是 Cherry-pick

Cherry-pick 就像是"摘樱桃"——从一棵树(分支)上挑选你需要的果实(提交),移植到另一棵树上。它可以将任意提交的修改复制到当前分支。

Cherry-pick 的特点

  • 选择性合并:只获取需要的提交
  • 精确控制:避免引入不需要的修改
  • 灵活性高:可以在任意分支间操作
  • 创建新提交:产生新的提交哈希

git cherry-pick 用法

基本用法

bash
# 拣选单个提交
git cherry-pick <commit-hash>

# 拣选多个提交
git cherry-pick <commit1> <commit2> <commit3>

# 拣选提交范围
git cherry-pick <start-commit>..<end-commit>

# 拣选范围(不包含起始提交)
git cherry-pick <start-commit>..<end-commit>

# 拣选范围(包含起始提交)
git cherry-pick ^<start-commit>..<end-commit>

常用选项

bash
# 只应用修改,不自动提交
git cherry-pick -n <commit-hash>
git cherry-pick --no-commit <commit-hash>

# 编辑提交信息
git cherry-pick -e <commit-hash>
git cherry-pick --edit <commit-hash>

# 使用原始提交信息
git cherry-pick -x <commit-hash>

# 保留原始作者信息
git cherry-pick --keep-redundant-commits

# 自动提交(默认行为)
git cherry-pick --commit

# 处理冲突时使用我们的版本
git cherry-pick --ours

# 处理冲突时使用他们的版本
git cherry-pick --theirs

高级用法

bash
# 拣选并修改提交信息
git cherry-pick -e <commit-hash>

# 拣选合并提交
git cherry-pick -m <parent-number> <merge-commit>

# 拣选时保留原始提交时间
git cherry-pick --allow-empty

# 从另一个仓库拣选
git cherry-pick --strategy=recursive -X theirs <commit>

选择性合并提交

场景一:从开发分支拣选 Bug 修复

bash
# 1. 查看开发分支的提交历史
git log --oneline feature/development

# 输出示例:
# a1b2c3d 添加用户登录功能
# e4f5g6h 修复密码验证 Bug
# i7j8k9l 优化数据库查询
# m0n1o2p 修复 XSS 漏洞

# 2. 切换到主分支
git checkout main

# 3. 只拣选 Bug 修复
git cherry-pick e4f5g6h
git cherry-pick m0n1o2p

# 4. 推送到远程
git push origin main

场景二:批量拣选提交

bash
# 1. 查看需要拣选的提交范围
git log --oneline feature-branch

# 输出:
# commit-d (HEAD) 最新提交
# commit-c
# commit-b
# commit-a 起始提交

# 2. 拣选从 commit-a 到 commit-d 的所有提交
git cherry-pick commit-a..commit-d

# 或者使用 ^ 包含起始提交
git cherry-pick ^commit-a~1..commit-d

场景三:拣选合并提交

bash
# 合并提交有两个父提交,需要指定使用哪个
git cherry-pick -m 1 <merge-commit>

# -m 1 表示使用第一个父提交(主分支)
# -m 2 表示使用第二个父提交(合并进来的分支)

使用场景

1. 热修复发布

bash
# 开发分支修复了紧急 Bug
# feature-branch: fix-critical-bug

# 发布分支需要这个修复
git checkout release/v1.2
git cherry-pick fix-critical-bug

# 推送发布
git push origin release/v1.2

2. 回退错误的提交

bash
# 如果拣选了错误的提交,可以回退
git cherry-pick --abort

# 或者重置到拣选前的状态
git reset --hard HEAD~1

3. 重建提交历史

bash
# 创建新分支,拣选需要的提交
git checkout -b new-feature

# 按顺序拣选提交
git cherry-pick commit-a
git cherry-pick commit-b
git cherry-pick commit-c

4. 团队协作中的选择性合并

bash
# 团队成员的分支有多个功能
# 只需要其中一个功能

# 查看提交
git log --oneline teammate-branch

# 拣选特定功能的提交
git cherry-pick feature-specific-commit

处理冲突

冲突发生时的处理

bash
# 当拣选发生冲突时
git cherry-pick <commit>
# error: could not apply <commit>... 
# hint: after resolving the conflicts, mark the corrected paths

# 查看冲突文件
git status

# 手动解决冲突
# 编辑冲突文件...

# 标记为已解决
git add <resolved-files>

# 继续 cherry-pick
git cherry-pick --continue

# 或者放弃本次操作
git cherry-pick --abort

# 或者跳过这个提交
git cherry-pick --skip

自动解决冲突策略

bash
# 使用我们的版本(当前分支)
git cherry-pick -X ours <commit>

# 使用他们的版本(被拣选的提交)
git cherry-pick -X theirs <commit>

# 忽略空白变化
git cherry-pick -X ignore-space-change <commit>

Cherry-pick 工作流程示例

完整的发布修复流程

bash
# 1. 在开发分支修复 Bug
git checkout develop
git commit -m "修复: 用户登录超时问题"
# commit-hash: abc1234

# 2. 拣选到发布分支
git checkout release/v1.0
git cherry-pick abc1234

# 3. 拣选到主分支
git checkout main
git cherry-pick abc1234

# 4. 推送所有分支
git push origin develop release/v1.0 main

批量拣选脚本

bash
#!/bin/bash

# 从 feature 分支拣选特定提交到 main

COMMITS=(
    "abc1234"
    "def5678"
    "ghi9012"
)

git checkout main

for commit in "${COMMITS[@]}"; do
    echo "Cherry-picking $commit..."
    if git cherry-pick "$commit"; then
        echo "Success: $commit"
    else
        echo "Conflict in $commit, please resolve manually"
        exit 1
    fi
done

echo "All commits cherry-picked successfully!"

Cherry-pick vs Merge vs Rebase

操作特点适用场景
Cherry-pick选择性获取提交只需要特定修改
Merge合并整个分支完整功能集成
Rebase变基重新应用提交保持线性历史

对比示例

bash
# Merge:合并所有修改
git merge feature-branch

# Rebase:变基所有提交
git rebase feature-branch

# Cherry-pick:只获取需要的提交
git cherry-pick <specific-commit>

最佳实践

1. 使用 -x 选项记录来源

bash
# 在提交信息中添加来源
git cherry-pick -x <commit>

# 提交信息会包含:
# (cherry picked from commit abc1234...)

2. 避免重复拣选

bash
# 检查提交是否已经被拣选
git log --oneline --grep="cherry picked from commit abc1234"

# 或者使用 git log 的 --cherry 选项
git log --cherry --oneline main...feature-branch

3. 小批量拣选

bash
# 不推荐:一次拣选太多提交
git cherry-pick commit-a..commit-z

# 推荐:分批拣选,便于处理冲突
git cherry-pick commit-a..commit-f
git cherry-pick commit-g..commit-l

4. 在功能分支上使用

bash
# 创建专门的功能分支进行拣选
git checkout -b hotfix/login-bug
git cherry-pick <bug-fix-commit>

# 测试通过后合并
git checkout main
git merge hotfix/login-bug

常见问题

Q: Cherry-pick 后提交哈希变了?

这是正常现象。Cherry-pick 会创建新的提交,提交哈希会改变,但内容相同。

bash
# 原提交
abc1234 修复登录 Bug

# Cherry-pick 后的新提交
def5678 修复登录 Bug

Q: 如何查看哪些提交已被拣选?

bash
# 使用 git log 查看带有 cherry-pick 标记的提交
git log --oneline --grep="cherry picked from"

# 使用 git cherry 查看未合并的提交
git cherry -v main feature-branch

Q: Cherry-pick 会影响原分支吗?

不会。Cherry-pick 只是复制提交内容,原分支保持不变。

Q: 如何撤销 Cherry-pick?

bash
# 如果 cherry-pick 还在进行中
git cherry-pick --abort

# 如果已经完成,使用 reset
git reset --hard HEAD~1

# 或者创建反向提交
git revert HEAD

总结

Git Cherry-pick 是精确控制代码合并的利器:

功能命令说明
拣选提交git cherry-pick <commit>应用指定提交
拣选范围git cherry-pick a..b应用提交范围
不自动提交git cherry-pick -n只应用修改
记录来源git cherry-pick -x添加来源标记
继续操作git cherry-pick --continue解决冲突后继续
放弃操作git cherry-pick --abort取消拣选

使用建议

  • 用于选择性获取特定修改
  • 使用 -x 选项记录提交来源
  • 避免大量拣选,分批进行
  • 解决冲突后及时测试