搜索 K
主题
git cherry-pick 是 Git 中用于将指定提交应用到当前分支的命令。它会复制一个提交的修改内容,在当前分支上创建一个新的提交。与 merge 和 rebase 不同,cherry-pick 只选取特定的提交,而非整个分支的所有提交。
cherry-pick 就像「从其他分支摘一颗樱桃」——只拿你想要的那个提交,不影响其他。
git cherry-pick <commit>…
git cherry-pick --no-commit <commit>…
git cherry-pick --continue
git cherry-pick --abort
git cherry-pick --quitgit cherry-pick 会提取指定提交引入的变更(diff),然后在当前分支上重新应用这些变更,创建一个新的提交(新的 SHA)。
C5' 和 C5 的代码修改内容相同,但 SHA 不同(因为父提交不同)。cherry-pick 不会移动或删除原始提交。
| 对比项 | cherry-pick | merge | rebase |
|---|---|---|---|
| 选取范围 | 指定的一个或多个提交 | 整个分支的所有提交 | 整个分支的所有提交 |
| 新提交 | 创建新提交(新 SHA) | 创建合并提交 | 重写所有提交(新 SHA) |
| 原始提交 | 保留不变 | 保留不变 | 原始提交变为悬空对象 |
| 历史关系 | 无父子关系 | 保留分支合并关系 | 线性历史 |
| 适用场景 | 跨分支移植少量提交 | 合并整个分支 | 整理历史 / 同步上游 |
# 将 feature 分支上的某个提交应用到当前分支
git cherry-pick abc1234
# 不打开编辑器,使用原始提交信息
git cherry-pick --no-edit abc1234# 摘取多个不连续的提交(按顺序应用)
git cherry-pick abc1234 def5678 ghi9012
# 摘取一个范围(不含起始提交,含结束提交)
git cherry-pick abc1234..ghi9012
# 摘取一个范围(含起始提交)
git cherry-pick abc1234^..ghi9012A..B — 从 A(不含)到 B(含),即 A 之后到 B 的所有提交A^..B — 从 A(含)到 B(含),即 A 到 B 的所有提交# C1 → C2 → C3 → C4 → C5
git cherry-pick C2..C5 # 摘取 C3, C4, C5
git cherry-pick C2^..C5 # 摘取 C2, C3, C4, C5--no-commit:只应用修改不提交 # 将修改应用到暂存区,但不自动创建提交
git cherry-pick --no-commit abc1234
# 可以继续修改后手动提交
git commit -m "feat: 从 feature 分支移植的功能"--no-commit# cherry-pick 默认会保留原始提交信息
# 但会在末尾添加 "(cherry picked from commit xxx)"
# 添加 -x 选项,在提交信息中记录原始 SHA
git cherry-pick -x abc1234
# 提交信息会包含:(cherry picked from commit abc1234)
# 不添加 -x 时,提交信息不包含原始引用
git cherry-pick abc1234-x-x 添加的原始 SHA 引用让你以后能追溯这个提交的来源,特别是在多分支维护的项目中非常有用。
cherry-pick 时如果目标分支和源分支的代码有冲突,需要手动解决。
# 1. cherry-pick 遇到冲突
git cherry-pick abc1234
# CONFLICT (content): Merge conflict in src/main.ts
# 2. 解决冲突
# 编辑冲突文件,删除冲突标记
# 3. 标记已解决
git add src/main.ts
# 4. 继续
git cherry-pick --continue
# 或完全放弃
git cherry-pick --abort--abort vs --quit vs --continue | 命令 | 效果 |
|---|---|
git cherry-pick --continue | 冲突解决后继续处理下一个提交 |
git cherry-pick --abort | 完全取消,恢复到 cherry-pick 前的状态 |
git cherry-pick --quit | 退出但保留已成功 cherry-pick 的提交 |
# 冲突时自动采用「当前分支」的版本
git cherry-pick -X ours abc1234
# 冲突时自动采用「被 cherry-pick 的提交」的版本
git cherry-pick -X theirs abc1234-X ours / -X theirs 的含义在 cherry-pick 中:
ours = 当前分支(你正在工作的分支)theirs = 被 cherry-pick 的提交(来源提交)这与 merge 中的含义一致,但与 rebase 中的含义相反。
与 git revert 类似,cherry-pick 合并提交需要指定 --mainline(-m)参数:
# 合并提交有两个父提交
# -m 1:保留第一个父提交的差异(通常是目标分支)
# -m 2:保留第二个父提交的差异(通常是被合并的分支)
git cherry-pick -m 1 <merge-commit-sha>-m 1大多数情况下,你想 cherry-pick 的是「被合并进来的分支引入的修改」,这对应 -m 1(相对于第一个父提交的差异)。
最经典的 cherry-pick 场景——在 develop 上修复了 bug,需要把修复移植到 release 分支:
# 1. 在 develop 上修复 bug 并提交
git checkout develop
# ... 修复代码 ...
git commit -m "fix: 修复用户登录超时问题 (#789)"
# 2. 将修复 cherry-pick 到 release 分支
git checkout release/v2.0
git cherry-pick -x <fix-commit-sha>
# 3. 推送
git push origin release/v2.0同时维护 v1.x 和 v2.x 版本线时,安全补丁需要应用到所有维护中的版本:
# 安全修复提交在 main 上
git log --oneline -1
# abc1234 fix: 修复 SQL 注入漏洞 (CVE-2026-xxxx)
# 依次移植到所有维护版本
git checkout release/v2.0
git cherry-pick -x abc1234
git checkout release/v1.5
git cherry-pick -x abc1234
git checkout release/v1.4
git cherry-pick -x abc1234
# 注意:版本越旧,冲突的可能性越大功能分支被废弃了,但其中某些提交仍然有用:
# 查看废弃分支的提交历史
git log --oneline abandoned-feature
# abc1234 feat: 有用的工具函数
# def5678 WIP: 半成品功能
# ghi9012 feat: 另一个有用的改动
# 只摘取有价值的提交
git checkout main
git cherry-pick -x abc1234 ghi9012一个大 PR 中只有部分提交已经通过审查,想先合入已审查的部分:
# PR 包含 5 个提交,前 3 个已审查通过
git log --oneline feature-branch
# e5f6a7b feat: 功能 C(未审查)
# c3d4e5f feat: 功能 B(未审查)
# a1b2c3d feat: 功能 A(已通过)
# 9e8f7a6 refactor: 代码重构(已通过)
# 7c6d5e4 fix: 修复 bug(已通过)
# 只 cherry-pick 已审查通过的提交
git checkout main
git cherry-pick -x 7c6d5e4 9e8f7a6 a1b2c3d一些团队使用标签或评论触发自动 cherry-pick:
# GitHub Actions 示例:当 PR 有 "backport-v2" 标签时自动 cherry-pick
# .github/workflows/backport.yml
# 当 PR 合并后,自动将合并提交 cherry-pick 到 release/v2 分支
# 手动触发的脚本
#!/bin/bash
COMMIT=$1
TARGET_BRANCH=$2
git fetch origin
git checkout $TARGET_BRANCH
git cherry-pick -x $COMMIT
if [ $? -ne 0 ]; then
echo "Cherry-pick 冲突,需要手动处理"
git cherry-pick --abort
exit 1
fi
git push origin $TARGET_BRANCH在灰度发布或多环境部署中,某个修复只需要部署到特定环境:
# staging 环境发现 bug,需要紧急修复
# 但不想把 develop 的其他未完成功能一起部署
# 1. 在 develop 上修复
git checkout develop
git commit -m "fix: 修复支付金额计算错误"
# 2. 只将修复 cherry-pick 到 staging 分支
git checkout staging
git cherry-pick -x <fix-sha>
git push origin staging
# staging 环境自动部署,只包含这个修复# 将多个提交合并为一个
git cherry-pick --no-commit abc1234 def5678 ghi9012
git commit -m "feat: 从 feature 分支移植的核心功能"# 查看提交修改了什么
git show abc1234
# 只看文件列表
git show --name-only abc1234
# 查看统计信息
git show --stat abc1234
# 模拟 cherry-pick(不实际执行)
git cherry-pick --no-commit abc1234
git diff --cached --stat
git cherry-pick --abort# 查看 feature 有但 main 没有的提交
git log --oneline main..feature
# 查看 main 有但 feature 没有的提交
git log --oneline feature..main
# 查看两个分支的完整差异
git log --oneline --left-right main...feature
# < 开头的是 main 独有的
# > 开头的是 feature 独有的# 查看某个提交是否已经被 cherry-pick 过
# 方式 1:使用 git log 搜索提交信息
git log --grep="cherry picked from commit abc1234" --oneline
# 方式 2:使用 git log 搜索原始提交信息
git log --grep="原始提交信息的关键字" --oneline
# 方式 3:使用 patch-id 对比
git show abc1234 | git patch-id
# 对比两个提交的 patch-id 是否相同# 在 ~/.gitconfig 中添加
[alias]
# 快速 cherry-pick(带 -x 记录来源)
cp = cherry-pick -x
# cherry-pick 不提交
cpn = cherry-pick --no-commit
# 继续 / 放弃
cpc = cherry-pick --continue
cpa = cherry-pick --abort
# 使用
git cp abc1234 # cherry-pick 并记录来源
git cpn abc1234 def5678 # 多个 cherry-pick 不提交# cherry-pick 后对比两个提交的 diff 是否一致
git diff abc1234 HEAD
# 如果输出为空,说明 cherry-pick 完全一致
# 如果有输出,说明有冲突解决导致的差异
# 运行测试确认
npm test大型开源项目(如 Node.js、Linux 内核)使用标签驱动的 backport 流程:
# 1. 修复合入 main(最新开发分支)
git checkout main
git commit -m "fix: 修复内存泄漏 (#1234)"
# 2. 在 PR/Issue 上添加标签 "backport-v18.x"
# 3. 手动(或 bot 自动)将修复 cherry-pick 到旧版本分支
git checkout v18.x-staging
git cherry-pick -x <commit-sha>
# 4. 如果有冲突,手动适配旧版本的 API
# 旧版本可能没有新版本中引入的辅助函数
# 需要手动调整代码使其兼容很多项目使用 Mergify 或 github-backport 等工具自动执行 cherry-pick backport。当 PR 合并且包含特定标签时,bot 自动创建 backport PR。
不小心把修改提交到了错误的分支:
# 场景:本该提交到 feature-payment 的代码,提交到了 main
git log --oneline -3
# abc1234 (HEAD -> main) feat: 支付模块逻辑 ← 提交错了!
# def5678 fix: 之前的修复
# ghi9012 上一个正式提交
# 1. 先 cherry-pick 到正确的分支
git checkout feature-payment
git cherry-pick abc1234
# 2. 回到 main 撤销错误的提交
git checkout main
git reset --hard HEAD~1
# 现在修改在正确的 feature-payment 分支上了每个开发者都会偶尔提交到错误分支。记住这个 cherry-pick + reset 的组合技。
在开源贡献中,经常需要从上游仓库 cherry-pick 修复到自己的 fork:
# 1. 添加上游仓库为 remote
git remote add upstream https://github.com/original/repo.git
# 2. 获取上游的提交
git fetch upstream
# 3. 找到需要的提交
git log --oneline upstream/main -10
# 4. cherry-pick 到自己的分支
git checkout my-feature
git cherry-pick -x <upstream-commit-sha>一个巨大的提交包含了多个不相关的修改,需要拆分到不同分支:
# 原始提交 abc1234 包含了 3 个功能的修改
git show --stat abc1234
# src/auth/login.ts | 50 +
# src/payment/checkout.ts | 30 +
# src/ui/button.css | 10 +
# 对每个功能分别 cherry-pick 并只保留相关文件
# 分支 1:auth 功能
git checkout -b feature-auth main
git cherry-pick --no-commit abc1234
git checkout HEAD -- src/payment/ src/ui/
git commit -m "feat: 登录功能"
# 分支 2:payment 功能
git checkout -b feature-payment main
git cherry-pick --no-commit abc1234
git checkout HEAD -- src/auth/ src/ui/
git commit -m "feat: 支付功能"
# 分支 3:UI 修改
git checkout -b feature-ui main
git cherry-pick --no-commit abc1234
git checkout HEAD -- src/auth/ src/payment/
git commit -m "style: 按钮样式更新"当需要为 bug 报告创建最小复现时:
# 1. 创建一个干净的分支
git checkout -b reproduce/bug-123 v2.0.0
# 2. 只 cherry-pick 与 bug 相关的最少提交
git cherry-pick abc1234 # 引入了有问题的依赖
git cherry-pick def5678 # 修改了配置
# 3. 现在有了一个只包含必要修改的最小复现环境
npm test
# 确认 bug 可以在这个最小环境中复现# 模式 1:上下文偏移(最常见)
# 原因:cherry-pick 的提交周围的代码在目标分支中已被修改
# 解决:检查冲突标记,通常只需要保留正确的上下文
# 模式 2:已删除的文件
# 原因:cherry-pick 修改了一个在目标分支中已被删除的文件
git cherry-pick abc1234
# CONFLICT (modify/delete): src/old-file.ts deleted in HEAD
# 解决:决定是否需要这个文件
git rm src/old-file.ts # 确认删除
# 或
git add src/old-file.ts # 保留文件
# 模式 3:重命名冲突
# 原因:文件在两个分支中被重命名为不同的名字
git cherry-pick abc1234
# CONFLICT (rename/rename): src/utils.ts renamed to...
# 解决:选择正确的文件名
git add src/correct-name.ts
git rm src/wrong-name.ts# cherry-pick 冲突解决后,执行以下检查
# 1. 确认没有遗留的冲突标记
grep -rn "<<<<<<" src/
grep -rn "======" src/
grep -rn ">>>>>>" src/
# 2. 对比与原始提交的差异
git diff --cached # 查看暂存区变更
git show <original-sha> --stat # 对比原始提交的文件列表
# 3. 运行测试
npm test
# 4. 继续
git cherry-pick --continue同一个提交被 cherry-pick 两次,虽然不会报错(因为 SHA 不同),但代码中会有重复的修改:
# ❌ 不要这样做
git cherry-pick abc1234 # 第一次
git cherry-pick abc1234 # 第二次 → 可能产生冲突或重复代码
# ✅ cherry-pick 前先检查
git log --grep="cherry picked from commit abc1234"如果你 cherry-pick 了 feature 的提交到 main,之后再 merge feature 到 main,Git 通常能正确处理(不会重复应用),但在某些情况下可能产生冲突。
解决方案:如果计划之后要 merge 整个分支,考虑是否真的需要 cherry-pick。
如果提交 B 依赖于提交 A(例如 B 使用了 A 中定义的函数),只 cherry-pick B 会导致编译错误。
# A: 定义了 processPayment() 函数
# B: 调用了 processPayment() 函数
# ❌ 只 cherry-pick B → 编译错误(找不到 processPayment)
git cherry-pick B
# ✅ 同时 cherry-pick A 和 B
git cherry-pick A B# 报错
git cherry-pick <merge-commit>
# error: commit is a merge but no -m option was given
# 正确
git cherry-pick -m 1 <merge-commit>cherry-pick 会保留原始提交的作者信息和提交信息,并且使用 Git 的合并引擎来应用修改,能正确处理文件重命名、上下文偏移等情况。手动复制容易出错且没有追溯性。
不会。cherry-pick 只是复制修改内容创建新提交,原始提交在其所在分支上完全不受影响。
技术上可以(比如 cherry-pick 一个已被 reset 掉的提交),但通常没有意义。如果你想恢复被撤销的提交,使用 git reflog + git reset 更合适。
冲突的提交会暂停,你有三个选择:
git cherry-pick --continue(继续处理后续提交)git cherry-pick --abort(完全取消,已成功的也回滚)git cherry-pick --quit(退出但保留已成功的提交)git format-patch + git am 有什么区别?功能类似,但适用场景不同:
| 对比项 | cherry-pick | format-patch + am |
|---|---|---|
| 适用场景 | 同一仓库的不同分支 | 跨仓库移植 |
| 使用方式 | 直接引用 SHA | 生成 patch 文件再应用 |
| 网络需求 | 需要在同一仓库中 | 可通过邮件/文件传输 patch |
| 保留信息 | 作者、时间、提交信息 | 作者、时间、提交信息 |
在严格管控的团队中,cherry-pick 到 release 分支需要审批:
# 1. 开发者:创建 cherry-pick PR 而非直接操作
git checkout -b cherry-pick/fix-1234 release/v2.0
git cherry-pick -x abc1234
git push origin cherry-pick/fix-1234
# 创建 PR:cherry-pick/fix-1234 → release/v2.0
# 2. 审查者:确认修改内容和适用性
# - 这个修复是否适用于 v2.0?
# - 是否有 API 不兼容问题?
# - 测试是否覆盖?
# 3. 合并后打补丁标签
git checkout release/v2.0
git tag -a v2.0.1 -m "Hotfix v2.0.1"# 查看所有 cherry-pick 过来的提交
git log --grep="cherry picked from" --oneline
# 统计每个分支被 cherry-pick 的次数
git log --grep="cherry picked from" --format="%s" | \
grep -oP 'cherry picked from commit \K[a-f0-9]+' | \
while read sha; do
branch=$(git branch --contains $sha --format='%(refname:short)' 2>/dev/null | head -1)
echo "$sha → $branch"
done | sort | uniq -c | sort -rn
# 检查某个修复是否已经 cherry-pick 到所有需要的分支
FIX_SHA=abc1234
for branch in release/v1.0 release/v2.0 release/v3.0; do
if git log $branch --grep="cherry picked from commit $FIX_SHA" --oneline | grep -q .; then
echo "✅ $branch 已包含修复"
else
echo "❌ $branch 缺少修复"
fi
done#!/bin/bash
# backport.sh — 自动 cherry-pick 到多个 release 分支
# 用法:./backport.sh <commit-sha> release/v1.0 release/v2.0 release/v3.0
COMMIT=$1
shift
BRANCHES="$@"
if [ -z "$COMMIT" ] || [ -z "$BRANCHES" ]; then
echo "用法: $0 <commit-sha> <branch1> [branch2] ..."
exit 1
fi
echo "准备将 $COMMIT 移植到以下分支:"
for branch in $BRANCHES; do
echo " - $branch"
done
echo ""
ORIGINAL_BRANCH=$(git branch --show-current)
FAILED=""
for branch in $BRANCHES; do
echo "=== 移植到 $branch ==="
git checkout $branch 2>/dev/null
if [ $? -ne 0 ]; then
echo "❌ 无法切换到 $branch"
FAILED="$FAILED $branch"
continue
fi
git pull origin $branch
git cherry-pick -x $COMMIT
if [ $? -ne 0 ]; then
echo "❌ $branch 有冲突,需要手动解决"
git cherry-pick --abort
FAILED="$FAILED $branch"
else
echo "✅ $branch 移植成功"
git push origin $branch
fi
echo ""
done
git checkout $ORIGINAL_BRANCH
if [ -n "$FAILED" ]; then
echo "⚠️ 以下分支需要手动处理:$FAILED"
exit 1
fi
echo "🎉 全部移植完成"# .github/workflows/backport.yml
name: Auto Backport
on:
pull_request:
types: [closed]
jobs:
backport:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Backport
uses: sqren/backport-github-action@v9
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
auto_backport_label_prefix: backport-# cherry-pick 本质上等价于:
git diff abc1234^..abc1234 | git apply --3way
# 然后创建一个新提交,保留原始作者信息| 对比项 | cherry-pick | rebase |
|---|---|---|
| 操作粒度 | 选择性地挑选提交 | 整个分支的所有提交 |
| 基准点 | 当前 HEAD | 目标分支的最新提交 |
| 提交关系 | 新提交与原始提交无 Git 关联 | 新提交替代原始提交 |
| 原始分支 | 不受影响 | 原始提交被「抛弃」 |
| 适用场景 | 跨分支移植少量提交 | 同一分支上整理历史 |
| 目标 | 命令 |
|---|---|
| 摘取单个提交 | git cherry-pick <sha> |
| 摘取并记录来源 | git cherry-pick -x <sha> |
| 摘取多个提交 | git cherry-pick <sha1> <sha2> |
| 摘取范围(不含起始) | git cherry-pick <A>..<B> |
| 摘取范围(含起始) | git cherry-pick <A>^..<B> |
| 只应用不提交 | git cherry-pick --no-commit <sha> |
| 摘取合并提交 | git cherry-pick -m 1 <merge-sha> |
| 冲突时用当前分支版本 | git cherry-pick -X ours <sha> |
| 冲突时用来源提交版本 | git cherry-pick -X theirs <sha> |
| 冲突解决后继续 | git cherry-pick --continue |
| 放弃 cherry-pick | git cherry-pick --abort |
| 保留已成功的退出 | git cherry-pick --quit |
git format-patch / git am — 通过 patch 文件跨仓库移植提交