Flutter 发布分支 Cherry-pick 流程全解析:自动与手动请求、审批生命周期与实操指南
本文围绕 Flutter/flutter 仓库的 Flutter-Cherrypick-Process.md 展开,系统讲解当问题已经随 beta/stable 渠道发布后,如何把 master 上的修复合入发布候选分支(release candidate branch)的完整流程。你将掌握自动 cherry-pick 请求的触发方式、冲突时的手动操作与 PR 描述填写规范、从请求到合入的完整生命周期,以及依赖升级、热修复等配套实践。
为什么要 Cherry-pick:稳定性优先的修复通道
Flutter 采用多渠道发布模型,仓库内 Flutter-build-release-channels.md 给出了清晰的稳定性递增结构:master(tip-of-tree,最新但不保证全量测试通过)→ beta(每月初从 master 切出并进入"稳定化"阶段)→ stable(每三个 beta 中大约一个被提升而来,面向生产发布推荐)。
一旦代码进入 beta 或 stable 渠道,就面向了大量开发者与生产应用。因此对已发布软件(beta 和 stable 渠道)中暴露的问题,cherry-picking 是首选的修复手段,其核心原则是:
- 发布稳定性是压倒性目标,因此只有高影响、关键级别的 cherry-pick 才会在 Dart 与 Flutter 范围内被批准;
- 该流程只针对"上一个版本引入的回归"或"当前版本本身带来的严重 bug";
- 功能开发(feature work)不在 cherry-pick 考量范围内,必须等待下一个正式版本。
重要提示:如果你的目的是在当前 release 尚未 ship 之前就把修复提前带进去(例如想把某提交合入 3.21 beta,但当前 beta 还是 3.20),则属于"发行前操作",不能走自动流程,需要直接跳到下文的手动 cherry-pick 部分。
Cherry-pick 的目标分支:release candidate branch 从哪找
整个 cherry-pick 流程都围绕一个关键问题:把修复合入哪条分支? 答案是 beta/stable 各自的 candidate(候选)分支。
- 每条候选分支的命名遵循模式
flutter-X.XX-candidate.X,其中X.XX是目标 release 版本号; - 分支内通过
bin/internal/release-candidate-branch.version文件标记当前候选版本; - 对于发行前的预发布分支,需要自己根据该命名模式找到正确的候选分支。
本仓库的 content_aware_hash.sh 与 content_aware_hash.ps1 直接印证了这一命名约定——脚本用 flutter-*-candidate.* 通配符判断"当前分支是否为 release candidate 分支":
# bin/internal/content_aware_hash.sh
# 3. The current branch is a release candidate branch.
if ... && \
"$CURRENT_BRANCH" != "flutter-"*"-candidate."* ; then
而 last_engine_commit.sh 则展示了候选分支与 master 的差异如何被用于确定引擎版本:它先定位 release-candidate-branch.version 文件的最后一次提交作为参考点,再搜索 REFERENCE_COMMIT..HEAD 区间内改动 DEPS 或 engine/ 的提交,从而确定该分支应当使用的引擎 commit。这说明候选分支是被"持续稳定化"维护的分支,cherry-pick 正是在该窗口期把 master 的高影响修复搬运进来。
注意:该标记文件存在于 beta/stable 等发布分支上;在本仓库当前快照的
bin/internal/目录中并不包含release-candidate-branch.version(仅能看到引用它的脚本),需要切到对应 beta/stable 分支才能查看实际版本号。原文档中通过外部链接直接查看 beta/stable 分支上该文件的内容,本文不再列出外部地址。
自动创建 Cherry-pick 请求
对于已经发布的 release,最省力的方式是让机器人帮你自动创建 cherry-pick PR。完整步骤如下:
- 给 master 上的 PR 打标签:在 flutter/flutter master 仓库中,给"包含修复的那个 PR"添加
cp: beta或cp: stable标签,取决于你希望修复进入哪条候选分支; - 等待约 30 秒,让自动化机器人捕获到该标签;
- 自动成功:若没有合并冲突,机器人会自动创建新的 cherry-pick PR,并发送邮件通知你。此时你需要编辑生成 PR 描述中的 cherry-pick 细节(即下述五个字段),随后 release engineer 会跟进该请求;
- 自动失败:机器人会在原 PR 上留言说明失败原因。这种情况通常意味着存在合并冲突,你需要按下文"手动创建 Cherry-pick 请求"的方式自行创建 PR。
如果因为任何原因自动 cherry-pick 无法应用,都请走手动流程。自动化的边界很清晰:它只负责"无冲突地搬运提交并生成 PR",而"评审、补全信息、最终合入"仍然依赖人工作业。
手动创建 Cherry-pick 请求
当自动流程失败(典型原因是合并冲突),或你需要合入到尚未 ship 的预发布分支时,手动创建步骤如下:
1. 创建指向目标分支的 cherry-pick PR
先确定目标候选分支(命名模式 flutter-X.XX-candidate.X,参考上文)。然后在本地基于该分支创建你的提交分支并推送,最终在 GitHub 上向该候选分支发起 PR(具体 git 操作见下节)。
2. 修改 PR 标题
PR 标题必须以渠道前缀开头:[beta] 或 [stable],方便 release 团队在队列中快速识别请求类型。
3. 填写 PR 描述
描述必须包含以下五个字段,每一项都有明确的"需要交代的信息"要求:
| 字段 | 含义 | 填写示例 |
|---|---|---|
| Impacted Users | 大约哪些人会命中此问题 | 所有 Flutter 开发者 / Windows 开发者 / 所有终端用户 / 使用 X framework 特性的应用 |
| Impact Description | 影响是什么 | 三星手机上视觉卡顿;应用崩溃;无法发布 iOS 应用。同时说明影响层面:是影响开发(如装了 Android Studio 后 flutter doctor 崩溃)还是影响生产应用(如应用启动即崩溃) |
| Workaround | 是否存在绕过的解决办法 | 是否存在规避方案 |
| Risk | 本次 cherry-pick 的风险级别 | 评估改动对已发布分支的风险 |
| Test Coverage | 是否有信心该修复已被自动化测试充分覆盖 | 说明配套的测试情况 |
| Validation Steps | 验证该修复有效的步骤 | 具体可执行的验证步骤 |
4. 添加 cp: review 标签
打完标签后,请求即进入 release 团队的评审队列。
第一次做 Cherry-pick?一条完整的 git 操作链
如果你是从零开始,一次典型的 cherry-pick 请求遵循以下命令序列。注意:用 < > 包裹的是变量,需要替换成你场景中的实际值(例如 <your commit hash> 应换成真实提交哈希)。
# 1. 回到 master/main 并确保最新
git checkout <master/main>
git fetch
git pull # 确保 master/main 上的所有改动都已拉取
# 2. 切换到你想合入的候选分支,并基于它创建本地工作分支
git checkout <candidate branch you want to cherry-pick to>
git checkout -b <your local branch name for cherry-picking>
# 3. 把目标提交搬运过来
git cherry-pick <your commit hash>
# 4. 推送并建立上游关联
git push --set-upstream origin <your branch name>
推送成功后即可在 GitHub 上向目标候选分支发起 PR,并按照上文规范补全标题与描述。
仓库内 git-worktrees.md 补充了一个与 cherry-pick 强相关的工程实践:在频繁变动的 Flutter 仓库中,用 rebase 而不是 merge 整理自己的提交,在冲突解决时保持你的每个离散提交原子且功能完整,这样它们将来被 cherry-pick 时才不会粘连进无关变更。这条建议直接服务于本流程——一个干净、单主题的提交是后续能够被顺利搬进发布分支的前提。
Cherry-pick 的生命周期:从请求到合入
一个请求的完整流转过程如下:
- 发起:请求方打开指向 beta/stable candidate 分支的 cherry-pick PR;
- 分派评审:release engineering 团队收到队列通知,指派一名改动影响领域内的专家作为 cherry-pick 评审人,评审该 issue 与关联的 cherry-pick PR;
- 打标:release 团队为请求应用
merge-to-beta或merge-to-stable标签; - 进入终态判定,二者必居其一:
- Approved(批准):评审人批准 cherry-pick 及其 PR 后,release 团队合并 PR,并在 cherry-pick issue 上打
cp: merged标签; - Denied(拒绝):评审人会在 cherry-pick issue 上留言说明拒绝原因,release 团队随后关闭该 issue 与关联的 cherry-pick PR;
- Approved(批准):评审人批准 cherry-pick 及其 PR 后,release 团队合并 PR,并在 cherry-pick issue 上打
- 被批准的修复会在下一个发布周期中被纳入;
- 一旦修复已进入某个 release,release 团队关闭该 cherry-pick issue。
由此可见,与开发主流程不同,cherry-pick 的最终合入权归 release engineering 团队,而非 PR 作者本人;普通贡献者负责的是"发起请求、补全信息、解决冲突"。
高频问题 FAQ
谁能请求 cherry-pick?
任何人都可以。该流程不设贡献者门槛,社区开发者同样可以为自己修复的严重 bug 发起请求。
什么时候该请求 cherry-pick?
- 当你在 master 上定位到一个 commit,修复的问题在 beta 或 stable 分支上依然存在;
- 当需要更新某个 pub 依赖以修复 beta/stable 上存在的问题时(详见下文"依赖更新"小节)。
谁评审并批准?
release engineering 团队会指派一位该改动影响领域的专家作为 cherry-pick 评审人。
为什么我的 cherry-pick 被拒绝了?
官方明确列举了(但不限于)以下原因:
- PR 信息填写不完整或不恰当(例如五个描述字段缺失);
- 试图 cherry-pick 的不是"修复"而是其他内容(例如新功能);
- 等等其他情形。
问题出现在"更早的 stable"上怎么办?
如果发现的问题出现在 X 版本、且该版本已不在 stable 渠道上,仍然可以为其打 hotfix。对于 stable 渠道,官方更倾向于这么做,因为 stable 是绝大多数 Flutter 开发者实际在用的版本。尤其是当 stable 刚发布不久、大量开发者尚未迁移时,团队会优先考虑将修复 backport 到旧 stable。
beta 与 stable 之间如何取舍?
节奏上,Flutter 大约每三个 beta 会提升一个到 stable,因此"即将成为 stable"的那批 beta 分支会被优先修复。在某个 stable 临近发布的最后几周,团队可能选择只向 beta 渠道发布 hotfix 而不是同时给 stable。原文档还提到计划在 2023 年底前后加强发布自动化,以便轻松向两个渠道同时发布 hotfix,从而弱化这一权衡。
两类典型 Cherry-pick 场景的扩展实践
场景一:为 cherry-pick 更新单个 pub 依赖
有些问题靠源码提交修复,有些则依赖升级某个 pub 包。仓库内 Updating-dependencies-in-Flutter.md 专门说明了这种情况,不要手动去改 pubspec.yaml,而是使用 flutter_tools 的 update-packages 命令(其实现位于 update_packages.dart):
# 通用形式:可一次指定多个包
flutter update-packages --cherry-pick=[pub package name]:[pub package version],[pub package2 name]:[pub package2 version]
# 实际示例:升级 test 系列依赖
flutter update-packages --cherry-pick=test_api:0.7.6,test_core:0.6.10,test:1.26.1
如果平时希望阻止某个依赖被 flutter update-packages --force-upgrade 整体升级,官方做法是先把它钉死在 update_packages_pins.dart 的 kManuallyPinnedDependencies 中并附上解钉 issue 链接。
场景二:为 cherry-pick 写一份"可消费"的 hotfix 说明
hotfix 面向的是生产客户,他们真正关心的是"这个修复是否影响我、有多大风险、要不要尽快升级"。因此仓库内 Hotfix-Documentation-Best-Practices.md 要求每条 hotfix 摘要做到:非贡献者也能看懂、一行内可扫读、尽量说清场景、受影响平台、可能出现的上下文、发生概率。
推荐的描述模板是以修复前状态来表述问题:
"When $scenario [on $platform], $problem_description"(当 $场景 [在 $平台] 发生时,会出现 $问题描述)
一个符合该公式的优秀示例是:"在极少数情况下,iOS 与 macOS 上应用终止过程中 engine 可能崩溃";而"Don't remove overlay views when the rasterizer is being torn down"这类面向实现细节的描述则被官方点名批评——读者无法判断它如何影响自己的应用。这条规范与本 cherry-pick 流程中的 "Impact Description" 字段一脉相承:请求与发布沟通都要求以"用户可感知的影响"为中心。
与 Cherry-pick 耦合的仓库内约束
理解以下仓库约束,能帮你更好地预判自己的提交是否"可被 cherry-pick":
- Dart SDK 版本策略:Bumping-the-Dart-SDK-version.md 指出,Flutter 频繁需要把修复从 master 合入 stable/beta 分支,因此 master 上的代码不允许使用比 stable SDK 更新的语言特性或依赖,否则这些提交将无法被干净地搬回发布分支;同时 Dart 版本升级常触发全仓库格式化与 lint 迁移,干扰持续 rolling 的发布节奏。这是从源头保障"提交可移植性"的关键制度。
- CI 配置可移植性:Ci-Best-Practices.md 提醒,在 tip-of-tree 删除某个测试分片会导致它在所有 release candidate 分支中被静默跳过,届时需要把
.ci.yaml文件本身也 cherry-pick 进每个候选分支才能恢复测试覆盖——即 cherry-pick 的对象不限于功能代码,也包括 CI 配置。 - 真实案例时间线:仓库内 Postmortem-Flutter-3.32.3-Release.md 记录了一次真实的多 commit hotfix 发布:三个 cherry-pick(Impeller 内存泄漏修复、Android app bundle 构建失败修复、Navigation 组件视觉异常的回滚)于 2024 年 6 月 4 日被接受并合入分支,随后在发布阶段又因引擎改动未签名等工程问题追加修复提交。它直观展示了本流程"评审通过 → 合入候选分支 → 下一发布周期纳入 → 关闭 issue"的完整闭环。
结语与自查清单
Cherry-pick 是 Flutter 修复"已发布代码"的唯一官方通道,其核心可归纳为一句话:功能等下一个版本,严重修复走 candidate 分支。动手前请对照以下清单:
- [ ] 确认这是回归或严重 bug,而非功能开发;
- [ ] 已为已发布版本打上
cp: beta/cp: stable标签(自动流程),或确认需要走手动流程; - [ ] 目标分支正确(
flutter-X.XX-candidate.X,必要时查看对应分支上的bin/internal/release-candidate-branch.version); - [ ] PR 标题以
[beta]或[stable]开头; - [ ] PR 描述完整包含 Impacted Users / Impact Description / Workaround / Risk / Test Coverage / Validation Steps 六项内容;
- [ ] 已添加
cp: review标签进入评审队列; - [ ] 合并冲突已在本地自行解决,提交保持单主题、可原子搬移;
- [ ] 耐心等待 release 团队指派领域专家评审,并在合入后由团队关闭 issue。
如果你的请求被拒绝,通常是因为信息不完整或试图搬运非修复内容——补全信息、回到问题本质,是对评审团队最好的尊重,也是让高影响修复更快抵达每一位 Flutter 开发者的关键。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00