深入细节:Gerrit 依赖链批量操作完全指南
2026/7/22 3:23:02 网站建设 项目流程

一、依赖链究竟是如何建立的?

要自动化处理依赖,首先要理解 Gerrit 中“父子关系”的两种表现形式:

1. 基于 Git 提交图的隐式依赖

当你本地基于某个分支工作,依次生成多个 commit:

A --- B --- C (feature-branch)

push 到 Gerrit 后,Gerrit 会为每个 commit 创建一个独立的 Change。此时:

  • Change C 的 Git 父提交是 B
  • Change B 的父提交是 A
    Gerrit内部通过提交对象的 parent 字段建立依赖关系。无需任何额外标记。

2. 基于Depends-On页脚的显式依赖(跨分支/跨项目)

如果依赖关系无法通过提交父子关系表达(例如基于不同分支的 Change 需要顺序合入),可以在 commit message 中增加:

Depends-On: I0123456789abcdef...

Gerrit 解析后会强制建立依赖。

绝大多数批量场景都是同一分支上的连续提交链,也就是第1种情况。下面的讨论均以此为主。


二、解读 Related Changes API 的返回细节

上一篇我们用了/related端点,这里看一下它的完整响应以及如何处理各种边界情况。

请求:

GET /changes/{change-id}/revisions/{revision-id}/related

响应核心字段(去除了)]}'前缀后的 JSON):

{"changes":[{"project":"my-project","change_id":"my-project~master~Ie5f45c...","commit":{"commit":"a1b2c3d4...","parents":[{"commit":"base_sha"}],"subject":"first commit"},"_change_number":111,"_revision_number":1,"_current_revision_number":1,"status":"NEW"},{"project":"my-project","change_id":"my-project~master~I3f2a19...","commit":{"commit":"b5c6d7e8...","parents":[{"commit":"a1b2c3d4..."}],"subject":"second commit"},"_change_number":112,"_revision_number":1,"_current_revision_number":1,"status":"NEW"},{"project":"my-project","change_id":"...","_change_number":113,...}]}

关键点:

  • changes数组的顺序已经是拓扑排序的(祖先在前,后代在后),可以直接遍历。
  • 数组中只包含“开放状态”或“尚未合入目标分支”的 Change。如果一个祖先已经被合入(MERGED),则不会被包含在内(但它的提交信息可能出现在parents中)。这恰好是我们想要的:我们只需要处理那些还未合入的 Change。
  • 每个元素中_change_number可用于构造/changes/{project}~{number}的 ID,_revision_number是该 Change 最新的 Patch Set 号。通常我们操作当前 patch set,可以直接用current关键字。
  • 如果依赖链很长(如超过 100 个),这个接口会一次性返回全部吗?官方实现会遍历整个相关图但有限制,通常 100 以内没问题,极端情况需考虑分页,但在日常批量合入中极少遇到。

由此 API 我们可以实现“从一个链接获取全部相关提交”,不需要再手动回溯父提交。


三、如果没有/related端点怎么办?(老版本 Gerrit <2.15)

某些老版本(或受限环境)可能没有这个端点。此时需要手动回溯,流程如下:

  1. 获取某个 Change 的当前 commit SHA

    GET /changes/{change-id}/revisions/current/commit

    返回中有commitparents

  2. 用父 SHA 搜索对应的 Change

    GET /changes/?q=commit:{parent_sha}+AND+status:open+AND+project:{project}

    注意:一个 commit 可能同时出现在多个 Change 中(如果是 cherry-pick 等),需要限定项目和状态。

  3. 递归向前追溯,直到找不到父 Change(说明已合入或不存在)。

该方式需处理分页和可能的多结果,复杂度远高于/related,此处仅作备选方案。


四、SSH 命令与 REST API 的深度对比

功能SSH 命令示例REST API 示例备注
获取相关变更无直接命令,需结合gerrit queryGET /changes/{id}/revisions/current/relatedREST 完胜
评审打分ssh ... gerrit review 123,1 --code-review +2POST /changes/{id}/revisions/current/review都可
提交合入ssh ... gerrit review 123,1 --submitPOST /changes/{id}/submit或 review 中"submit": true都可
获取 change 详情ssh ... gerrit query change:123 --format JSONGET /changes/{id}/detailSSH query 功能强大但输出难解析

建议:

  • 获取依赖关系必须用 REST。
  • 批量操作如果脚本环境已配置好 HTTP 密码,全部用 REST 更统一。
  • 如果只在命令行临时操作,SSH 更便捷(无需记 URL)。

五、顺序的绝对性:为什么必须先祖先后后代

Gerrit 在执行 Submit 时会进行“依赖检查”:

  • 对于 Change B,如果它的父提交(即 Change A 的当前 patch set)尚未合入到目标分支,Submit 操作将被拒绝,返回:
    depends on change ... which is not merged
  • 即使所有需要的 label 都已齐备,也必须等待父 Change 合入。

因此必须严格按拓扑排序执行 Submit
而 Review(打分)本身不检查依赖关系,可以任意顺序打分,但为了避免混淆,我们也按照相同的祖先优先顺序操作。


六、处理非标准标签与 Submit 策略

1. 自定义标签

你的项目可能除了Code-Review还有VerifiedGatekeeperProduct-Review等。你需要知道成功合入所需的最低标签集合

可以在 Change 页面中查看Permitted labels,或通过 API:

GET /changes/{id}/detail?o=LABELS

返回的labels字段中,每个 label 有all列表,包含每个用户允许打分的范围。自动化脚本需要根据项目配置传入相应的 labels 字典。

示例:需要 Code-Review +2 且 Verified +1

{"labels":{"Code-Review":2,"Verified":1}}

2. Submit 策略的影响

Gerrit 项目可以设置不同的 Submit 策略(Submit Type):

  • Merge If Necessary:默认,会创建一个 merge commit。
  • Fast Forward Only:要求 Change 的 base 恰好是目标分支的当前 HEAD,不能有分叉。如果依赖链中两个 Change 之间有其他人合入了无关变更,FF 策略可能会失败。
  • Rebase If Necessary:提交前会先 rebase。
  • Cherry Pick:直接拣选提交,不创建合并提交。

如果是Fast Forward Only,你可能需要在 Submit 前确保没有冲突或分叉,否则 Submit 会失败。批量处理时,如果中间某个 Change 因为冲突合入失败,整个链条会中断,需要人工介入。


七、增强版自动化脚本设计要点

基于上一篇的脚本,我们可以增加以下鲁棒性设计:

7.1 预检查:确保所有 Change 可合入

在执行 Submit 前,可以先逐个检查每个 Change 的mergeable状态。

GET /changes/{id}/revisions/current/mergeable

如果mergeable为 false,说明存在冲突,应提前报错并停止流程,避免部分合入后卡住。

7.2 处理 CI 投票延迟

如果你的项目依赖 CI 给出Verified+1,那么在 +2 之后,可能需要等待 CI 完成。脚本可以加入轮询:

importtime max_wait=300# 5分钟whileTrue:detail=requests.get(...,params={"o":"LABELS"}).json()verified=detail['labels']['Verified'].get('approved',{})ifverified.get('value',0)>=1:breaktime.sleep(5)

7.3 处理部分 Change 已合入的情况

/related接口已经不返回已合入的 Change,但如果我们在操作过程中手动合入了某个祖先,脚本中再对该 Change 执行 Submit 会得到 409 Conflict。此时应捕获状态码并跳过(或确认已合入)。

7.4 完整的 Python 脚本升级框架

defwait_for_label(change_full_id,label_name,required_value,timeout=300):# 轮询指定 label 达到 required_value...defprocess_chain(change_url):change_id=get_change_id(change_url)chain=get_related_changes(change_id)# 检查可合并性forchinchain:ifnotis_mergeable(ch):raiseException(f"{ch['project']}~{ch['_change_number']}存在冲突")# 按序打分forchinchain:review_change(ch['project'],ch['_change_number'])# 按序合入,合入前等待必要标签forchinchain:full_id=f"{ch['project']}~{ch['_change_number']}"wait_for_label(full_id,'Verified',1)# 如需要submit_change(ch['project'],ch['_change_number'])

八、认证与权限的实操细节

REST API 认证

  1. 在 Gerrit 网页端进入Settings → HTTP Password,生成一个密码(或获取)。
  2. 使用 HTTP Basic Auth 时,Authorization 头为Basic base64(user:pass)。curl 的-u选项自动处理。
  3. 注意 Gerrit 的 REST API 前缀是/a/,所有请求都需包含。例如/a/changes/...。如果遗漏,请求可能被重定向或拒绝。

SSH 认证

  • 确保本地~/.ssh/id_rsa.pub已上传到 Gerrit 的SSH Keys中。
  • SSH 端口通常为 29418,可通过ssh -p 29418 user@host gerrit version测试连通性。

权限

批量评审合入的账号至少需要:

  • 对应 refs/heads/* 的Label: Code-Review范围允许 -2…+2,且具备 +2 权限。
  • Submit权限(一般是Submit访问控制)。
  • 若需跳过某些标签的等待,可能需要Forge Committer或管理员权限等,一般不必要。

九、常见陷阱排查

  1. Change-Id 与 Change Number 混淆

    • URL 中的数字(如12345)是 Change Number(简短易变,不同项目可重复)。
    • change_id形如project~branch~I...是全局唯一字符串。
      REST API 中查询单个 Change 时,两者都可用,但推荐使用project~number三元组形式(仅在同一 Gerrit 实例内有效)。
  2. 跨项目依赖
    /related端点可以跨越项目边界(如果依赖的确跨项目)。此时changes数组中project字段可能变化,需要正确处理。上面的脚本已经根据每个元素的project来构建 ID,可以自然支持。

  3. Change 有多个 Patch Set
    如果某 Change 被多次 push 过,_revision_number会大于 1。通常我们想操作最新的 patch set,也就是current。我们一直用current是安全的。

  4. Submit 操作幂等性
    对已经合入的 Change 再次调用 Submit,会返回 200 且不进行任何操作(或返回change already closed的 409)。可在脚本中捕获并继续。


十、总结

通过深入解析依赖链的本质、Related API 的细节、顺序合入的原因以及各种异常处理,我们可以构建一个非常稳健的“一键合入链”工具。核心要点:

  • 使用/related端点获取按序排列的所有相关未合入 Change。
  • 严格按照祖先优先的顺序执行 Submit。
  • 考虑项目特定标签、CI 等待和合并冲突等现实问题,增强脚本的容错和等待逻辑。

把这一套流程封装为企业内部的 ChatOps 命令或 CI 流水线中的一个步骤,能够极大地提升多提交依赖场景下的研发效率,减少因手动操作顺序错误导致的反复回滚或等待。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询