引言:保持同步的挑战

大型公司在为其特定使用场景定制开源项目时,通常面临的一个主要挑战是如何与上游变更保持一致。在 PlanetScale,我们在管理开源 Vitess 项目的持续演进与我们的私有定制修改时深刻体会到了这一问题。


初期阶段:手动处理

起初,当我们的私有更改还相对较小时,我们采用了一种简单的方式来维护这些差异。每周会有一个基于 GitHub Action 的定时任务运行,它会将所有私有更改 cherry-pick 到开源项目主分支的最新代码上。虽然这种方法在开始时可以工作,但随着 PlanetScale 的私有差异随着需求扩展而变得复杂,这种方法迅速变得不可持续。
特别是当我们决定与 Vitess 稳定发布版本保持同步,而非主分支最新代码时,挑战更为严重。这又新增了一项任务:不仅要维护主分支,还需维护多个发布分支的私有差异。


第一次尝试:Git-replay

在处理私有提交到多个发布分支时,我们频繁遇到相同的冲突。为了应对这一问题,我们开发了一种工具,可以按顺序处理所有相关提交,并在开源分支上重放这些提交。这一工具被命名为 **git-replay**,它能够记录在 cherry-pick 冲突中指定的解决方式,并在未来遇到类似冲突时复用这些信息。
虽然 git-replay 是对我们手动流程的一大提升,但它仍有局限性:

  • 仍然需要人工运行工具,解决未识别的冲突并验证所有代码是否正确地被 cherry-pick。
  • 为确保没有提交遗漏,需生成开源分支和私有分支的代码差异,然后由代码所有者对仓库的不同部分进行令人费神的手动审查。

尽管我们在多个版本中使用了这个工具,但随着私有差异集规模进一步增长,git-replay 的缺点变得越来越明显。我们意识到需要一种更全面的解决方案。这一需求促使我们开发了 Vitess Cherry-Pick Bot,实现管理私有差异集的重大改进。


新方法的开始:需求分析

随着私有差异集持续增长,我们认识到需要一个更连续、更加高效的流程。我们希望不再每次需要新版本时进行大量的 cherry-pick,而是建立一个系统,使开源项目的变更能无缝流向私有分支。因此,我们定义了一个模型:

  • 持续跟踪开源 Vitess 主分支(OSS main)的变更,并使其与我们私有分支中的对应 “upstream” 分支保持同步;
  • 设置 Vitess 发布分支的私有版本,如 release-x.0 对应 latest-x.0 分支;
  • 这些私有分支还需包含我们的私有差异集。
开源分支私有对应分支
mainupstream
release-22.0latest-22.0
release-x.0latest-x.0

关键需求

为实现目标,我们制定了以下需求:

  • 任何合并到开源主分支的 PR 应被 cherry-pick 到私有 upstream 中;
  • 开源主分支的 PR 被回溯到发布分支 release-x.0 时,应触发对应的 PR 被回溯到私有发布分支 latest-x.0
  • 私有更改继续进入 upstream,并根据需要被 cherry-pick 到各私有最新分支;
  • 每当开源发布分支从主分支创建,应对应生成一个私有分支;
  • 尽量自动化整个过程,减少人工操作。

自动化的价值

新流程带来了以下显著益处:

  • 不再需要显式维护私有差异集;
  • 开源 PR 会持续流入私有分支,确保其保持同步。

因此,我们开始开发一个自动化的 bot,以尽可能优化和简化这一工作流,这便是 Vitess Cherry-Pick Bot 的诞生。


Vitess Cherry-Pick Bot 的设计

在定义需求后,我们开始探索如何开发 bot。过程中我们遇到了几个关键性设计问题:

  • 是否应该将 bot 部署在专用服务器上,还是使用 GitHub Actions 与应用集成?
  • bot 是否应该是无状态的,将所有信息存储在 PR 中,还是有状态的,并使用专用数据库?

经过深入讨论,我们选择使用 GitHub Actions 的时间触发功能,并将状态存储在 PlanetScale 数据库中。bot 每小时运行一次,任务分为两大核心模块:**Cherry-Picking** 和 **Backporting**。


Cherry-Picking 模块

  1. 识别需要 Cherry-Picking 的 PR
    1. bot 使用 go-github 库与 GitHub API 交互,从 Vitess 仓库获取最近关闭的 PR。
    2. 过滤未合并即关闭的 PR,仅保留合并的内容并插入数据库。
    3. bot 停止历史回溯的时间点为数据库中现有的最早 PR 日期。
  2. 执行 Cherry-Picking
    1. cherry-pick 和 PR 创建完全在工作流内完成,bot 使用 GitHub Token 验证并操作私有仓库。
  3. 处理冲突
    1. 如果出现冲突,工作流仍会创建一个草稿 PR(status为do not merge),并在 PR 中标明冲突详细信息。
    2. bot 会注释冲突文件并提醒原始 PR 作者解决问题。
  4. 完成 Cherry-Picking
    1. 流程完成后,bot 将 PR 标记为已 cherry-picked。

Backporting 模块

Backporting 流程类似于 Cherry-Picking,主要区别是:

  • Backports 不会自动触发,而是依赖于 vitess-private 仓库中的标签,例如 Backport to: latest-x.0
  • 一旦标签被应用,bot 按 Cherry-Pick 的流程执行 Backporting 操作。

构建可靠性

为了确保流程的可靠性,我们为 bot 添加了完整性检查,每周运行一次。这些检查生成差异性摘要,供人工干预。

每周完整性检查

  1. 上游同步检查
    1. 确保 upstream 分支与开源 Vitess 变更保持一致;
    2. 标记以下问题:
      • 未合并到 upstream 的 PR;
      • 未被 cherry-pick 的内容;
      • 不正确直接合并到最新分支的 PR。
  2. 最新分支检查
    1. 检查最新分支(例如latest-21.0)的一致性;
    2. 标记以下问题:
      • 最新分支未完成的 Backport PR;
      • 合并到最新分支但未通过 Backport 的 PR;
      • 未同步到所有相关最新分支的 Backport PR。

结果与未来

有了这些保障措施后,我们谨慎地推出了新流程,并开始使用 Vitess Cherry-Pick Bot。经过一年多的运行,结果令人满意:

  • bot 节省了开发团队大量时间,使我们能够专注于为用户构建创新功能,而不是繁琐的手动 cherry-pick。

Vitess Cherry-Pick Bot 的自动化流程,让我们能够高效管理私有分支与开源项目的同步,成为工作流优化的典范。



自动化处理开源与私有分支间的 Cherry-Pick插图

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

本文链接:https://choupangxia.com/2025/09/14/cherry-pick/