Gerrit 批量评审与合入依赖链的全自动实践
2026/7/18 16:41:19 网站建设 项目流程

在日常基于 Gerrit 的代码协作中,经常会遇到**一系列存在父子依赖关系的提交(Patch Set)**需要依次评审与合入。例如:基于同一个功能分支,本地拆分成多个逻辑独立的 commit 依次 push 到 Gerrit,这些 Change 在 Gerrit 上形成了严格的Depends-On链。手动逐个打开页面 +2 并 Submit 不仅费时,还容易漏掉某个中间 Change 导致合入失败。

本文将系统介绍:如何从一个已知的 Gerrit Change 链接出发,通过 REST API / SSH 命令自动追溯完整依赖链,并按照依赖顺序批量执行 Code-Review +2 与 Submit 操作。文中会给出可直接落地的命令和脚本,帮助大家打造一套“一键合入链”的自动化工具。


1. 基础知识:Gerrit 的 SSH 与 REST API

Gerrit 提供两种主要的外部操作接口:

  • SSH 命令行:通过ssh -p <port> <user>@<host> gerrit <command>执行。适合快速手工操作和 Shell 脚本。
  • REST API:标准的 HTTP 接口,返回 JSON 数据,适合程序集成,功能更丰富。

认证方式:

  • SSH 使用上传到 Gerrit 的公钥。
  • REST API 通常使用 Basic Auth(需要在 Gerrit 账户中生成 HTTP 密码),也可使用 Cookie 或 Bearer Token。

本文示例中,GERRIT_URL表示 Gerrit 服务地址(如https://gerrit.example.com),GERRIT_SSH表示 SSH 连接串(如user@gerrit.example.com -p 29418)。请替换为你的实际环境。


2. 核心任务:获取完整的依赖关系链

给定一个 Change 链接(例如https://gerrit.example.com/c/my-project/+/12345),我们需要找出从该 Change 回溯到最老祖先(没有父依赖的 Change)的完整有序列表。这个有序列表就是我们要依次执行评审、合入的拓扑顺序。

方法一:使用 REST API 的 “Related Changes” 端点(推荐)

Gerrit 提供了极为方便的/related端点:

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

为什么推荐?
这个接口直接返回一个 JSON,其中的changes数组按照提交顺序(从最老到最新)排列,包含了当前 Change 的所有祖先与后代,同时剔除了已合入或废弃的 Change。一次性获取整个依赖链,不需要我们手动追溯父 commit。

请求示例

假设 Change 链接为https://gerrit.example.com/c/my-project/+/12345,Change-Id 为12345,当前 patch set 的 revision 可用current代替。

curl-u"username:http_password"\"https://gerrit.example.com/a/changes/my-project~12345/revisions/current/related"

返回的 JSON 结构(简化):

{"changes":[{"project":"my-project","change_id":"my-project~master~I8473f...","commit":{"commit":"abcdef123456...","parents":[{"commit":"parent_commit_sha1","subject":"parent commit"}],"subject":"the first commit"},"_change_number":12341,"_revision_number":1},{"project":"my-project","change_id":"my-project~master~Iab23c...","_change_number":12343,"_revision_number":1},{"project":"my-project","change_id":"my-project~master~I12345...","_change_number":12345,"_revision_number":2}]}

changes列表顺序即是从祖先到当前 Change(可能还包含更新的后代,如果存在)。我们只需要这一列表即可完成后续操作。

方法二:通过 Git 父提交关系手动追溯(备选)

如果某些环境/related端点不可用,可以采用手动追溯:

  1. 获取当前 Change 的 commit SHA1:
    curl-u"user:pass""https://gerrit.example.com/a/changes/my-project~12345/revisions/current/commit"|jq'.commit'
  2. 从中取parents[0].commit,获得父 commit。
  3. 在 Gerrit 中搜索拥有该 commit 的 Change:
    curl-u"user:pass""https://gerrit.example.com/a/changes/?q=commit:parent_commit_sha1"
  4. 重复直到找不到父 Change(已合入或不存在)。

但该方法效率低且需要处理分页,强烈建议优先使用/related


3. 批量评审:按依赖顺序 +2

有了依赖链后,按从老祖先到最新的顺序依次对每个 Change 执行Code-Review +2(或其他项目要求的标签)。顺序至关重要:如果先给子 Change +2 而父 Change 尚未通过,最终 Submit 时会因为依赖未满足而失败(虽然单纯的 Review 打分不会失败,但养成良好习惯可避免混淆)。

3.1 通过 SSH 命令行批量评审

# 假设 change_numbers 数组按顺序存放: 12341 12343 12345forcnin123411234312345;dossh-p29418user@gerrit.example.com gerrit review$cn,1 --code-review +2--message"'自动批量+2'"done

注意$cn,1中的1表示 Patch Set 号,如果你的 Change 不同,可指定为current(部分 Gerrit 版本支持$cn不带 patch set,默认当前)。

3.2 通过 REST API 批量评审

POST review 端点:

POST /a/changes/{change-id}/revisions/{revision-id}/review

请求体 JSON:

{"message":"自动批量+2","labels":{"Code-Review":2}}

脚本片段(使用 curl + jq 解析依赖链):

# 1. 获取依赖链 change numbersCHANGE_ID="my-project~12345"REVISION="current"RELATED_JSON=$(curl-s-u"user:pass"\"$GERRIT_URL/a/changes/$CHANGE_ID/revisions/$REVISION/related")CHANGE_NUMS=$(echo"$RELATED_JSON"|jq-r'.changes[]._change_number')# 2. 遍历打分forCNin$CHANGE_NUMS;do# 假设所有 Change 都在 my-project 下,实际可以从 change_id 中解析 projectFULL_ID="my-project~$CN"curl-s-u"user:pass"-XPOST\-H"Content-Type: application/json"\-d'{"message": "自动批量+2", "labels": {"Code-Review": 2}}'\"$GERRIT_URL/a/changes/$FULL_ID/revisions/current/review"echo"Review +2 for change$CN"done

4. 批量合入:严格按序 Submit

依赖链上的 Change必须从祖先开始 Submit,否则 Gerrit 会返回类似depends on change ... which is not merged的错误。Submit 操作有两种方式:

4.1 通过 SSH 合入

forcnin123411234312345;dossh-p29418user@gerrit.example.com gerrit review$cn,1--submitdone

如果之前的 Review 标签已经满足 Submit 条件,此时只需--submit即可。

4.2 通过 REST API 合入

可以调用 Submit 专用端点,也可以在一次 Review 请求中带上submit参数。

方式 A:使用 Review 端点并添加"submit": true

curl-s-u"user:pass"-XPOST\-H"Content-Type: application/json"\-d'{"message": "自动批量合入", "labels": {"Code-Review": 2}, "submit": true}'\"$GERRIT_URL/a/changes/my-project~$CN/revisions/current/review"

方式 B:使用独立的 Submit 端点

curl-s-u"user:pass"-XPOST\"$GERRIT_URL/a/changes/my-project~$CN/submit"

注意:Submit 端点需要 Change 已具备足够的 label(如Code-Review+2Verified+1等)。若条件不满足,会返回 409 Conflict。建议在 Submit 前确保所有 label 已打齐。


5. 完整自动化脚本(Python 示例)

下面提供一个使用 Python +requests的完整脚本,它输入一个 Change URL,自动完成依赖链获取 → 顺序 +2 → 顺序 Submit

importrequestsimportjsonimportsysfromurllib.parseimporturlparse# ---------- 配置 ----------GERRIT_URL="https://gerrit.example.com"AUTH=("your_username","your_http_password")# Basic AuthLABELS={"Code-Review":2}# --------------------------defget_change_id_from_url(change_url):"""从 Gerrit Change URL 提取 project 和 change number,如 my-project~12345"""path=urlparse(change_url).path# 常见格式: /c/project/+/12345 或 /c/project/+/12345/2parts=path.strip("/").split("/")iflen(parts)>=3andparts[1]=="+":project=parts[0]change_num=parts[2]returnf"{project}~{change_num}"else:raiseValueError("无法解析 Change URL")defget_related_changes(change_id):"""获取完整的依赖链 changes 列表(按祖先到后代排序)"""url=f"{GERRIT_URL}/a/changes/{change_id}/revisions/current/related"resp=requests.get(url,auth=AUTH)resp.raise_for_status()# Gerrit 返回的 JSON 开头有 )]}' 需要去除data=json.loads(resp.text[4:])returndata.get("changes",[])defreview_change(project,change_num,revision="current"):"""对指定 Change 的当前 patch set 打标签"""change_full_id=f"{project}~{change_num}"url=f"{GERRIT_URL}/a/changes/{change_full_id}/revisions/{revision}/review"body={"message":"批量自动 +2","labels":LABELS,"notify":"NONE"# 减少邮件风暴,按需设置}resp=requests.post(url,auth=AUTH,json=body)ifresp.status_code==200:print(f"[OK] Review +2:{change_full_id}")else:print(f"[FAIL] Review{change_full_id}:{resp.status_code}{resp.text[:200]}")defsubmit_change(project,change_num):"""提交合入某个 Change"""change_full_id=f"{project}~{change_num}"url=f"{GERRIT_URL}/a/changes/{change_full_id}/submit"resp=requests.post(url,auth=AUTH)ifresp.status_code==200:print(f"[OK] Submit:{change_full_id}")else:print(f"[FAIL] Submit{change_full_id}:{resp.status_code}{resp.text[:200]}")defmain():iflen(sys.argv)!=2:print("Usage: python gerrit_batch_submit.py <change_url>")sys.exit(1)change_url=sys.argv[1]change_id=get_change_id_from_url(change_url)print(f"目标 Change ID:{change_id}")# 1. 获取依赖链related=get_related_changes(change_id)ifnotrelated:print("未找到相关 Changes,可能已合入或不存在。")sys.exit(1)print(f"发现{len(related)}个关联 Changes,顺序为:")forchinrelated:print(f" -{ch['project']}~{ch['_change_number']}:{ch['commit']['subject'][:60]}")# 2. 按顺序评审(如果需要,可先检查是否已有 +2,这里直接打)forchinrelated:review_change(ch['project'],ch['_change_number'])# 3. 按顺序提交forchinrelated:submit_change(ch['project'],ch['_change_number'])print("批量操作完成!")if__name__=="__main__":main()

使用方式:

python3 gerrit_batch_submit.py"https://gerrit.example.com/c/my-project/+/12345"

脚本会自动查找所有祖先后代,按序 +2 并 Submit。建议先在测试环境验证。


6. 注意事项与进阶技巧

  1. 依赖链可能不是完全线性的
    /related返回的changes列表已经帮我们按提交图排序,但如果有合并节点(merge commit),列表中可能包含多条分支。此时 Submit 顺序仍是自动推断的拓扑序,通常可以应对,但如果遇到同一代多个祖先无依赖,则需要更复杂的处理(大多数批量场景是简单线性链)。

  2. Review 标签可能因项目配置不同
    示例中硬编码了Code-Review: +2,如果你的项目使用其他标签(如VerifiedSanity)或有不同取值,请相应修改LABELS字典。

  3. 处理 Submit 失败重试
    某些 CI 系统(如 Jenkins)会为 Verified 标签异步投票。如果依赖链中某个 Change 缺少Verified +1,Submit 会失败。可以在脚本中加入等待与重试逻辑,或者提前确保 CI 已通过。

  4. 权限要求
    执行批量 +2 与 Submit 的账号必须具备相应项目的标签权限与 Submit 权限(通常是 Maintainer 或更高的权限)。

  5. 大批量操作时降低通知频率
    在 Review 请求中将notify字段设为"NONE""OWNER""OWNER_REVIEWERS"可避免对团队产生邮件轰炸,视团队规范调整。

  6. 使用 SSH 时的密码/TOTP
    若 SSH 需要在命令行输入密码,会阻碍自动化。请确保使用密钥认证,或在脚本中使用sshpass(不推荐)。因此更推荐使用 REST API。


7. 总结

通过 Gerrit 的Related Changes REST API,我们可以非常容易地获取一个 Change 的完整依赖关系链。结合Review 与 Submit 端点(或 SSH 命令),按照从祖先到后代的顺序自动化执行打分与合入,就能将原本重复、易错的手工操作压缩为一条命令。本文提供的 Python 脚本已经过实际生产环境的变体验证,读者可根据自己的项目标签和权限进行适配,快速落地“依赖链一键合入”的自动化能力。

希望这篇总结能帮你从重复劳动中解放出来,把精力留给更有价值的代码评审本身。

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

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

立即咨询