news 2026/9/4 16:19:52

Zed 发布补丁实践:script/cherry-pick 脚本、cherry_pick 工作流与 preview/stable 分支的手动 Cherry-Pick 流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Zed 发布补丁实践:script/cherry-pick 脚本、cherry_pick 工作流与 preview/stable 分支的手动 Cherry-Pick 流程

Zed 发布补丁实践:script/cherry-pick 脚本、cherry_pick 工作流与 preview/stable 分支的手动 Cherry-Pick 流程

【免费下载链接】zedCode at the speed of thought – Zed is a high-performance, multiplayer code editor from the creators of Atom and Tree-sitter.项目地址: https://gitcode.com/GitHub_Trending/ze/zed

Zed 的main分支上合并的修复,最终需要落到previewstable两条长期维护的发布分支上。本篇以仓库中的 runbook 文档 .agents/skills/zed-cherry-pick/SKILL.md 为主体,结合 cherry-pick 脚本 与 cherry_pick 工作流 的源码级实现,完整讲解如何在自动化的 GitHub Actions 工作流失败(几乎总是合并冲突)时,在本地手工完成 cherry-pick 并开出一个与自动化产物不可区分的 PR——读完你能掌握通道到分支的映射查询、冲突判定与解决准则、验证与收尾的全部操作。

1. 发布模型:两条长期分支与"绝不硬编码"的通道映射

Zed 从位于origin的两条长期发布分支出货(runbook 原文表述):

  • preview通道 → 形如v1.4.x的分支
  • stable通道 → 形如v1.3.x的分支

版本号随每次发布变化,因此绝不能硬编码通道与分支的映射,必须按当前仓库状态动态发现(见第 3 节)。

这一"分支名即vX.Y.x"的约定可以从版本提升流程得到印证:bump_zed_version 工作流 中明确计算preview_branch="v${major}.${minor}.x"(从当前main切出新 preview 分支),并通过script/get-released-version preview反查已发布的 preview 版本来推导stable_branch="v${stable_major}.${stable_minor}.x"。同时 release_channel 定义 中通道取值为stable | dev | nightly | preview四种,可见preview/stable是通道(channel)概念,而非分支名——这正是后文"陷阱"一节强调git fetch origin preview会失败的原因。

2. 自动化基线:读懂 script/cherry-pick 脚本

runbook 明确指出:规范流程住在 script/cherry-pick 与cherry_pick工作流里,"如果任何地方看起来不对劲,先读脚本——你的本地步骤必须产出与它相同的分支名、PR 标题和 PR 正文"。脚本签名:

script/cherry-pick <branch-name> <commit-sha> <channel>
  • <branch-name>是发布分支(如v1.4.x),不是通道名;
  • <channel>previewstable,仅用于 PR 标题/正文中的展示文本。

对照脚本源码(script/cherry-pick,全文仅 34 行),其完整行为如下:

步骤源码行为说明
参数校验set -euxo pipefail+ 参数数检查(L2-L7)任一命令失败立即退出,且逐行回显命令,便于定位失败点
构造分支名SHORT_SHA="${COMMIT_SHA:0:8}"NEW_BRANCH="cherry-pick-${BRANCH_NAME}-${SHORT_SHA}"(L13-L14)短 SHA 取提交前 8 个字符;分支名必须严格遵循cherry-pick-<branch-name>-<short-sha>约定
拉取git fetch --depth 2 origin +${COMMIT_SHA} ${BRANCH_NAME}(L15)从源码结构看,--depth 2浅拉取提交及其父提交,为 cherry-pick 提供完整 diff 上下文
建本地分支git checkout --force "origin/$BRANCH_NAME" -B "$NEW_BRANCH"(L16)以发布分支远端引用为基线强制重建本地分支
拣选git cherry-pick "$COMMIT_SHA"(L18)冲突时在此中止
推送git push origin -f "$NEW_BRANCH"(L20)强制推送
生成 PR 文本提取%s/%b,正则匹配标题尾部(#数字)(L21-L30)见下文标题/正文格式
开 PRgh pr create --base ... --head ... --title ... --body ...(L33)使用 GitHub App token

PR 标题(脚本 L33 与 runbook 一致):

<original commit subject> (cherry-pick to <channel>)

原始提交的主题通常已以(#<original_pr_number>)结尾(如 squash merge 的提交),这一后缀必须保留。

PR 正文:runbook 描述的是正常情况(原始提交标题以(#<N>)结尾):

Cherry-pick of #<original_pr_number> to <channel> ---- <original commit body, verbatim>

脚本源码还揭示了 runbook 未展开的兜底分支(script/cherry-pick):若标题(#N)结尾(如直接拣选裸提交),正文首行退化为Cherry-pick of <commit-sha> to <channel>,其余格式不变。

3. cherry_pick 工作流与通道-分支映射的现查现用

3.1 工作流长什么样

cherry_pick.yml 是手动触发(workflow_dispatch)的工作流,接收四个必填字符串输入:commitbranchchannelpr_number。其执行链为(见 cherry_pick.yml):

  1. steps::authenticate_as_zippy—— 通过actions/create-github-app-token为机器人zed-zippy[bot]生成 token,授予 contents/workflows/pull-requests 三项写权限;
  2. steps::checkout_repo—— 使用上述 token 检出仓库;
  3. cherry_pick::run_cherry_pick::cherry_pick—— 执行./script/cherry-pick "$BRANCH" "$COMMIT" "$CHANNEL",其中BRANCH/COMMIT/CHANNEL三个环境变量直接来自工作流输入(L46-L51),提交者身份被固定为 zed-zippy[bot](L52-L55)。

该文件头两行注明Generated from xtask::workflows::cherry_pick,即它是代码生成的产物,生成器位于 tooling/xtask/src/tasks/workflows/cherry_pick.rs,可用cargo xtask workflows重建。pr_number输入只用于 run-name 展示(cherry_pick to ${{ inputs.channel }} #${{ inputs.pr_number }}),并不参与脚本执行。

3.2 现查通道→分支的当前映射

runbook 给出的标准做法是检查最近的cherry_pick工作流运行记录:

gh run list --workflow=cherry_pick.yml --limit 30 --json displayTitle,databaseId # pick a recent run for the channel you want, then: gh run view <id> --log 2>&1 | grep -E "BRANCH:|CHANNEL:"

一次成功的运行会在日志中打印BRANCH:CHANNEL:两个环境变量;这就是当前的通道→分支映射。

4. 手动完成 Cherry-Pick 的标准流程

4.1 第一步:收集上下文

需要三要素:merge 提交 SHA目标分支通道名

  • 用户给出多个 PR/提交时,先收集齐全部元数据,再按它们落到main的先后顺序(旧到新)依次拣选:PR 按mergedAt排序,裸提交按其在main上的顺序(不可得时按提交日期)。runbook 的解释是:后续改动可能依赖前序改动,按序拣选可减少不必要的冲突,但当发布分支已分叉时并不保证无冲突。
gh pr view <PR_NUMBER> --json title,number,mergeCommit,mergedAt,url
  • 用户提到"工作流失败了"时,拉取失败日志,看清究竟哪条命令失败、哪个文件冲突:
gh run list --workflow=cherry_pick.yml --limit 10 --json databaseId,displayTitle,status,conclusion gh run view <failed_run_id> --log-failed

失败日志还会顺带确认工作流实际使用的BRANCHCOMMIT——存在歧义时这是可靠依据(对应上文工作流中BRANCH/COMMIT/CHANNEL环境变量的回显)。

4.2 第二步:本地复现脚本的准备工作

仓库目录可能是 git worktree(检查.git:若它是一个文件,说明当前是 worktree,指向共享的 gitdir)。这没有问题,照常操作即可。

git --no-pager fetch origin <branch-name> <commit-sha> git checkout --force origin/<branch-name> -B cherry-pick-<branch-name>-<short-sha> git cherry-pick <commit-sha>

分支名必须与cherry-pick-<branch-name>-<short-sha>完全一致(脚本约定;评审人与工具链都依赖它)。这三条命令与 script/cherry-pick 中的fetch/checkout/cherry-pick一一对应,差异仅在于本地场景不需要--depth 2

4.3 第三步:先查"缺失的前置 cherry-pick",不要急着手工解冲突

runbook 在此设置了一个关键闸门:cherry-pick 若冲突,不要立即手工解决

先判断冲突是否很可能由"main上已存在、但发布分支缺失"的其他 PR/提交引起。若是,向用户指出这些候选前置 PR/提交(附 PR 链接),并给出两个选项:手工解决冲突,或先让 GitHub cherry-pick 工作流把这些前置提交拣过去。若用户选择先跑工作流补齐前置,到此停止——这往往能让后续 cherry-pick 保持干净、并有资格获得自动批准(automated approval)。

只有满足以下其一,才进入手工解决:

  • 未发现可能存在缺失的前置;或
  • 用户明确选择手工解决而非先拣选前置。

4.4 第四步:手工解决冲突

仅在完成前置检查后进行。runbook 的准则:

  • grep -n '<<<<<<<\|>>>>>>>\|=======' <path>定位每个冲突文件中的标记;
  • 冲突通常是diff3风格,含三段:HEAD(发布分支侧)、||||||| parent of <sha>(在main上的合并基)、以及传入的改动;
  • 先读原始提交git --no-pager show <commit-sha> -- <path>)理解作者意图,然后选择一种能"在发布分支上产出等价终态"的解法;
  • 不要顺手把main上恰好位于冲突区旁边的无关改动一并带进来——保持 cherry-pick 最小化。

4.5 第五步:验证

在继续 cherry-pick 之前,必须构建(在合理时并测试)受影响的 crate:

cargo check -p <affected_crate> cargo test -p <affected_crate>

验证失败就修解决方案,绝不让构建处于破损状态继续。如果无法达到干净状态,用git cherry-pick --abort中止并向用户回报。

4.6 第六步:完成 cherry-pick

git cherry-pick --continue默认会打开编辑器,非交互环境下必须屏蔽:

git add <resolved_files> GIT_EDITOR=true git cherry-pick --continue

这样做会逐字保留原始提交信息——与脚本的行为一致。

4.7 第七步:推送并创建 PR

git push origin -f cherry-pick-<branch-name>-<short-sha>

然后用gh pr create创建 PR,标题与正文格式必须与 script/cherry-pick 的产物完全一致,使手动 PR 与自动化 PR 不可区分:

  • 标题<commit subject> (cherry-pick to <channel>)(原始主题的(#<N>)后缀保留);
  • 正文(原始提交标题以(#<N>)结尾的正常情形):
Cherry-pick of #<original_pr_number> to <channel> ---- <original commit body, verbatim>

runbook 建议把正文写入临时文件以保持格式:

git --no-pager log -1 --pretty=format:"%b" > /tmp/cp-body-tail.md printf 'Cherry-pick of #%s to %s\n\n----\n' <PR_NUMBER> <channel> | cat - /tmp/cp-body-tail.md > /tmp/cp-body.md gh pr create --base <branch-name> --head cherry-pick-<branch-name>-<short-sha> \ --title "<commit subject> (cherry-pick to <channel>)" \ --body-file /tmp/cp-body.md

两条明确的"不要做":

  • 不要添加Release Notes:段——原始提交正文里已经有一个(或已写N/A),重复添加会造成冗余;
  • 标题不匹配(#N)时,正文首行使用Cherry-pick of <commit-sha> to <channel>(脚本 L28-L30 的兜底行为)。

5. 收尾:给用户的最终报告

runbook 规定完成后必须向用户交代四件事:

  1. 新 PR 的 URL("When Finished"一节强调:最后一步永远是给出已开 PR 的链接);
  2. 冲突及解决方式的一句话总结;
  3. 运行了哪些验证(命令 + 结果);
  4. 本地分支当前停在cherry-pick-<branch-name>-<short-sha>上,以便用户需要时切回。

6. 常见陷阱(Gotchas)

runbook 单独列出四条,全部有明确的工程原因:

  1. --no-pagerGIT_EDITOR=true:本环境中非交互 git 的硬性要求;cherry-pick --continue漏掉GIT_EDITOR=true会挂起终端。
  2. worktree 的索引锁:若前一条 git 命令被中断,可能遇到index.lock错误;worktree 场景下锁位于<gitdir>/index.lock<gitdir>cat .git所指向的路径。仅确认没有 git 进程在运行时才可删除。
  3. 不要扩大 cherry-pick 的范围:解冲突时绝不因为无关改动恰好位于冲突区旁边就从main把它们拉进来。PR 应当是"在发布分支上复现原始提交意图的最小 diff"。
  4. 通道分支不叫preview/stable:不要尝试git fetch origin preview,先查出真实的vX.Y.x分支名再操作。

7. 相关文件速查

文件作用
.agents/skills/zed-cherry-pick/SKILL.md本 runbook 的原始文档(何时使用、七步流程、陷阱清单)
script/cherry-pick规范脚本:分支命名、拣选、强推、PR 标题/正文生成
.github/workflows/cherry_pick.yml自动化工作流:四个输入、zippy 鉴权、脚本调用与环境变量
tooling/xtask/src/tasks/workflows/cherry_pick.rs工作流的 xtask 生成器(cargo xtask workflows重建)
.github/workflows/bump_zed_version.yml版本提升流程,展示vX.Y.x分支名的派生规则
crates/release_channel/src/lib.rs通道枚举定义:stable / dev / nightly / preview

适用前提说明:上述流程依赖仓库具备可用的ghCLI 与对origin的写权限,且目标仓库启用 GitHub App(zed-zippy[bot])自动化;对于仅本地查看的镜像仓库,本文档的价值在于理解发布分支的补丁规范与分支/PR 命名约定,所有命令均可照原文复制执行。

【免费下载链接】zedCode at the speed of thought – Zed is a high-performance, multiplayer code editor from the creators of Atom and Tree-sitter.项目地址: https://gitcode.com/GitHub_Trending/ze/zed

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 16:18:23

【NebulaGraph】NebulaGraph 的核心定位是什么?它的主要设计目标和优势有哪些?

NebulaGraph 的核心定位与设计哲学:为超大规模实时图计算而生的分布式引擎 用户问题原文:NebulaGraph 的核心定位是什么?它的主要设计目标和优势有哪些? 本文将深入剖析 NebulaGraph 3.8.0 的核心定位,揭示其作为一款云原生、高性能、强扩展性的分布式图数据库,是如何通过…

作者头像 李华
网站建设 2026/9/4 16:17:35

C语言编程核心:从内存视角理解字面量与变量的本质区别

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/4 16:16:57

BlenderMCP 上手指南:让 Claude 替你搭 3D 场景

BlenderMCP 上手指南&#xff1a;让 Claude 替你搭 3D 场景 【免费下载链接】blender-mcp Community plugin to control Blender 3D with any LLM of your choice 项目地址: https://gitcode.com/GitHub_Trending/bl/blender-mcp 你在 AI 对话框里敲下"做一个地牢&…

作者头像 李华
网站建设 2026/9/4 16:15:32

基于Hacker News API的竞品分析:从992条帖子精准定位7个真对手

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/4 16:12:46

基于STM32F407的工程级智能鱼缸系统设计

简介&#xff1a;本资源是一套面向嵌入式初学者与物联网项目开发者的完整智能鱼缸控制系统方案&#xff0c;聚焦STM32平台与OneNet云平台联动&#xff0c;解决宠物鱼远程监控、环境参数采集&#xff08;水温、pH等&#xff09;及自动化投喂、增氧、加热等核心需求。压缩包共401…

作者头像 李华