首页
/ etcd Cherry-Pick 回移实战:从 k8s-infra-cherrypick-robot 自动化流程到 scripts/cherrypick.sh 实现剖析

etcd Cherry-Pick 回移实战:从 k8s-infra-cherrypick-robot 自动化流程到 scripts/cherrypick.sh 实现剖析

2026-09-05 19:21:50作者:尤辰城Agatha

本文围绕 etcd 贡献者指南中的 cherry-pick 回移文档 展开,讲解如何把已合入 main 分支的 PR 安全回移到 release-x.y 稳定分支:先掌握推荐的 /cherry-pick 机器人自动化流程,再深入剖析兜底脚本 scripts/cherrypick.sh 的分支命名、git am -3 补丁应用、冲突处理与自动建 PR 的完整实现,读完即可独立完成一次 etcd 补丁回移(backport)。

为什么 etcd 需要专门的 cherry-pick 流程

etcd 采用滚动发布模型(rolling release model),新特性先合入 main 开发分支,之后 main 在发版时被标记为新稳定分支;所有以 release- 为前缀的分支都是稳定分支,团队对最近两个受支持的小版本持续提供向后兼容的 bug 修复,补丁发布大约每两周一次。详见 分支管理指南

在这个模型下,cherry-pick 承担了核心职责:回移文档 开篇即定义——"Cherry-picking applies changes from a PR to a release branch",即把某个 PR 的变更应用到发布分支上。发布指南 进一步给出了回移的规范要求:

  • 回移 PR 必须指向正确的发布分支(base:release-<major>-<minor>);
  • 提交的 commits 不应包含 merge commit,且内容应仅限于 bug 修复和安全补丁
  • 补丁发布经理(patch release manager)负责评审回移 PR,并从最旧的 commit 开始依次 cherry-pick 进稳定分支。

另外,安全发布流程 中也依赖同样的机制:PSC(私有安全委员会)会把安全补丁 cherry-pick 到 main 分支及所有相关的 release 分支。由于每个小版本有独立的变更日志(如 CHANGELOG-3.6.md),且补丁版本的 changelog 只记录相对上一个补丁版本的增量(规则见 CHANGELOG/README.md),所以回移操作直接决定了各 release 分支上补丁版本的变更记录。

etcd 提供了两条执行路径:首选自动化机器人,机器人失效时再落到手动脚本。

推荐流程:k8s-infra-cherrypick-robot 自动化回移

回移主要由 k8s-infra-cherrypick-robot 承担。用法非常简单:在已合入的 PR 下评论:

/cherry-pick release-3.6

机器人会自动创建一个指向目标 release 分支的 cherry-pick PR。

这个命令背后的约定与 发布指南 中的 "k8s infra cherry pick robot /cherrypick <branch> PR chatops command" 描述一致:<branch> 参数填发布分支名(如 release-3.6),机器人以该分支为 base 生成回移 PR。对贡献者来说,选择这条路径意味着:

  • 无需本地准备 fork/remote,也无需处理冲突分支的推送细节;
  • 回移 PR 的标题、描述由机器人自动生成,便于补丁经理统一评审;
  • 冲突仍需回移发起者(或评审者)在机器人生成的 PR 中解决——这与手动脚本的冲突处理本质相同。

适用前提:目标 PR 已经合入上游 main,且你有权在对应 PR 下评论。若机器人无响应、目标分支特殊或需要一次性回移多个 PR 合并成单个 PR,则使用下一节的脚本。

兜底方案:手动 cherry-pick 脚本的完整准备

回移文档scripts/cherrypick.sh 定位为 "automation 不生效时的手动兜底"。脚本头部的注释说明它派生自 Kubernetes 社区的 cherry_pick_pull.sh(见脚本第 4 行注释),工作流是:检出目标远端分支,逐个应用 PR 补丁,然后为你留下创建 PR 的一切材料。

环境变量准备

文档要求的三步准备:

export UPSTREAM_REMOTE=upstream
export FORK_REMOTE=origin
export GITHUB_USER=${github-username}
  • UPSTREAM_REMOTE:上游 etcd 仓库在你本地的 git remote 名,对应 github.com/etcd-io/etcd
  • FORK_REMOTE:你自己 fork 的 etcd 仓库对应的 remote 名(github.com/<你的用户名>/etcd)。如果还没有 fork,需要先在 GitHub 上 fork,再用 git remote add ... 在本地注册;
  • git remote -v 查看实际的 remote 名。

此外还需安装 hub 命令行工具(脚本用它创建 PR)。这些要求都能在源码中得到印证——脚本在入口处做硬性校验:

  • GITHUB_USER 未设置时直接退出并提示 Please export GITHUB_USER=<your-user>cherrypick.sh);
  • PATH 中找不到 hub 时退出并提示安装(cherrypick.sh);
  • UPSTREAM_REMOTEFORK_REMOTE 若未设置,默认值分别为 upstreamorigincherrypick.sh),所以文档中的 export 更多是显式声明,方便本地 remote 命名与默认值不一致时覆盖。

基本用法

把 PR 12345 回移到 release-3.6 并自动以 PR 形式提出:

./scripts/cherrypick.sh ${UPSTREAM_REMOTE}/release-3.6 12345

把 12345 和 56789 两个 PR 依次回移并合并为一个 PR 提出:

./scripts/cherrypick.sh ${UPSTREAM_REMOTE}/release-3.6 12345 56789

脚本自身的帮助信息(参数不足 2 个时打印,cherrypick.sh)还额外说明了两个文档未展开的环境变量,值得了解:

环境变量 作用
DRY_RUN 跳过 git push 和 PR 创建,只在你本地保留一个包含已回移 commits 的分支。适合"只想给 release 分支做补丁、不打算开 PR"的场景
REGENERATE_DOCS 在回移 commit 后为目标分支重新生成文档,适用于回移了涉及 API 文档变更的 commit 的情况
UPSTREAM_REMOTE / FORK_REMOTE 覆盖默认的 upstream / origin remote 名

脚本实现剖析:从补丁下载到 PR 创建

下面按执行顺序拆解 cherrypick.sh 的关键机制,帮助你在脚本行为不符合预期时快速定位原因。

前置检查:拒绝在脏树上工作

脚本开头启用了 set -o errexit / nounset / pipefail,并强制切换到 etcd 仓库根目录(cherrypick.sh)。随后做两项安全检查:

  • 工作树必须干净git status --porcelain --untracked=no 非空即报 !!! Dirty tree. Clean up and try again.cherrypick.sh);
  • 不允许有进行中的 rebase/am:检查 .git/rebase-apply 目录是否存在,存在则提示先清理(cherrypick.shL60-L63)。

脚本同时记录你执行前所在的分支 STARTINGBRANCHgit symbolic-ref --short HEAD),这是后面"善后返回"的落点。

分支命名规则

目标分支即命令行第一个参数(如 upstream/release-3.6),其余参数全部视为 PR 编号(cherrypick.sh)。脚本据此构造回移分支名:

automated-cherry-pick-of-#12345-#56789-upstream-release-3.6-<unix时间戳>

由三段拼接而成(cherrypick.sh):

  1. automated-cherry-pick-of-#12345-#56789——固定前缀加 # 号连接的 PR 列表,注释里说明这是给工具识别的 "Required" 部分;
  2. 目标分支名中的 / 替换为 -upstream-release-3.6);
  3. date +%s 时间戳,保证重复执行不冲突。

#12345 #56789 形式的 PULLSUBJ 则会用于最终 PR 的标题与描述。

补丁应用:curl 下载 + git am -3

对每个 PR 编号,脚本执行以下循环(cherrypick.sh):

  1. 下载 patchcurl 强制 HTTPS 下载该 PR 的 .patch 文件到 /tmp/<pr>.patch,并明确提示"万一需要重来,这是你再次使用的材料";
  2. 应用 patchgit am -3 /tmp/<pr>.patch,即带三方合并能力的 git am,这是脚本"cherry-pick"的实际执行手段(并非 git cherry-pick 命令);
  3. 冲突处理循环:若 git am 失败,脚本检查 git status --porcelain | grep ^U 中是否有未合并文件,有则打印冲突文件清单,提示你在另一个窗口解决冲突并执行 git add / git am --continue,然后交互式询问是否继续(非 y 即中止,cherrypick.sh)。同时它也给出手动重放提示:git am -3 /tmp/<pr>.patch
  4. 提取 commit 主题:从 patch 文件的 Subject: 行解析出 PR 标题,累积进 SUBJECTS 数组(cherrypick.sh),最终写入 PR 描述。

注意一个边界细节:如果 git am 失败但没有未合并文件,脚本会判定为"可能有未完成的 git amgit rebase"并直接退出(cherrypick.sh)——这对应前置检查之外运行时产生的残留状态。

PR 描述模板与 hub 建 PR

所有 PR 应用完成后,make-a-pr 函数(cherrypick.sh)生成如下描述的 PR 文本并调用 hub pull-request 创建 PR:

Automated cherry pick of #12345 #56789

Cherry pick of #12345 #56789 on release-3.6.

#12345: <PR 1 的标题>
#56789: <PR 2 的标题>

这里有两个从源码可见的细节需要留意:

  • PR 的 head 是 <GITHUB_USER>:<回移分支>,base 拼写为 coreos:<release分支名>cherrypick.sh)。coreos 是 etcd 旧组织名的历史遗留前缀,若你的远端组织名已不是 coreos,自动建 PR 这一步可能需要人工修正 base;
  • 描述文本先写入 mktemp 临时文件再交给 hub,注释解释这是为了规避 hub 直接读 heredoc stdin 时的崩溃问题(cherrypick.sh)。

推送前先做一次防御:如果 FORK_REMOTE 恰好配置成了上游 etcd/etcd.git 本身(cherrypick.sh),脚本认为"这不正常",改为打印手工 git push REMOTE <回移分支> 指令让你自行推送。正常路径则是打印将要执行的 git push <FORK_REMOTE> <带时间戳分支>:<规范回移分支>,交互式确认后执行,再调用 make-a-prcherrypick.sh)。

DRY_RUN 与善后清理

设置 DRY_RUN 后,脚本完成全部 cherry-pick 后停在回移分支上,打印两条收尾指令:git checkout <起始分支> 返回、git branch -D <回移分支> 删除分支(cherrypick.sh)。

未设置 DRY_RUN 时,脚本注册了 trap return_to_kansas EXITcherrypick.sh):无论正常结束还是中途失败,都会自动 git am --abort(若 am 未完成)、强制切回起始分支、删除临时回移分支并清理 PR 描述临时文件。这意味着手动解决冲突期间脚本进程保持挂起,你解决完冲突执行 git am --continue 后脚本会继续循环检查,直到没有未合并文件为止。

实践要点与常见问题

结合文档与源码,实际操作时建议关注以下几点:

  • 回移顺序:一次脚本调用中多个 PR 按命令行给出的先后顺序依次 git am,与 发布指南 "从最旧的 commit 开始 cherry-pick" 的要求一致——请自行保证参数顺序即新旧顺序;
  • patch 留档:每个 PR 的 patch 先落在 /tmp/<pr>.patch 供重试(cherrypick.sh),循环末尾才清理;如果中途退出,可手动 git am -3 重新应用;
  • 文档再生成REGENERATE_DOCS 分支会调用 hack/generate-docs.shcherrypick.sh);从当前仓库结构看,hack/ 目录下并没有该文件,因此该功能在当前版本更多是面向旧流程的保留项,回移普通 bug 修复时无需设置;
  • 内容红线:无论走机器人还是手动脚本,回移内容都应限于 bug 修复与安全补丁、不含 merge commit,并在回移 PR 中等待补丁经理评审——这是 发布指南 对 patch release 的硬性要求,每个补丁版本都应"严格优于"它的上一个版本;
  • 分支命名勿改automated-cherry-pick-of-#... 前缀被标注为 "Required portion for tools"(cherrypick.sh),自动化工具依赖该命名做识别,手动操作时不要擅自简写。

小结

etcd 的 cherry-pick 机制是"文档流程 + 可审计脚本"的组合:日常回移优先用 k8s-infra-cherrypick-robot 的 /cherry-pick release-x.y 评论一键完成;遇到机器人不可用、多 PR 合并回移或需要本地 DRY_RUN 演练时,scripts/cherrypick.sh 提供了从补丁下载、git am -3 冲突处理、规范分支命名到自动推送建 PR 的完整兜底能力。理解其分支命名约定、patch 留档路径与 trap 清理机制后,你就能在任意 release 分支上安全、可重复地完成一次补丁回移。

登录后查看全文
热门项目推荐
相关项目推荐