Gerrit 批量评审与合入依赖链的全自动实践

Gerrit 批量评审与合入依赖链的全自动实践
在日常基于 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 APIGerrit 提供两种主要的外部操作接口SSH 命令行通过ssh -p port userhost gerrit command执行。适合快速手工操作和 Shell 脚本。REST API标准的 HTTP 接口返回 JSON 数据适合程序集成功能更丰富。认证方式SSH 使用上传到 Gerrit 的公钥。REST API 通常使用 Basic Auth需要在 Gerrit 账户中生成 HTTP 密码也可使用 Cookie 或 Bearer Token。本文示例中GERRIT_URL表示 Gerrit 服务地址如https://gerrit.example.comGERRIT_SSH表示 SSH 连接串如usergerrit.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//12345Change-Id 为12345当前 patch set 的 revision 可用current代替。curl-uusername: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端点不可用可以采用手动追溯获取当前 Change 的 commit SHA1curl-uuser:passhttps://gerrit.example.com/a/changes/my-project~12345/revisions/current/commit|jq.commit从中取parents[0].commit获得父 commit。在 Gerrit 中搜索拥有该 commit 的 Changecurl-uuser:passhttps://gerrit.example.com/a/changes/?qcommit:parent_commit_sha1重复直到找不到父 Change已合入或不存在。但该方法效率低且需要处理分页强烈建议优先使用/related。3. 批量评审按依赖顺序 2有了依赖链后按从老祖先到最新的顺序依次对每个 Change 执行Code-Review 2或其他项目要求的标签。顺序至关重要如果先给子 Change 2 而父 Change 尚未通过最终 Submit 时会因为依赖未满足而失败虽然单纯的 Review 打分不会失败但养成良好习惯可避免混淆。3.1 通过 SSH 命令行批量评审# 假设 change_numbers 数组按顺序存放: 12341 12343 12345forcnin123411234312345;dossh-p29418usergerrit.example.com gerrit review$cn,1 --code-review 2--message自动批量2done注意$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_IDmy-project~12345REVISIONcurrentRELATED_JSON$(curl-s-uuser: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_IDmy-project~$CNcurl-s-uuser:pass-XPOST\-HContent-Type: application/json\-d{message: 自动批量2, labels: {Code-Review: 2}}\$GERRIT_URL/a/changes/$FULL_ID/revisions/current/reviewechoReview 2 for change$CNdone4. 批量合入严格按序 Submit依赖链上的 Change必须从祖先开始 Submit否则 Gerrit 会返回类似depends on change ... which is not merged的错误。Submit 操作有两种方式4.1 通过 SSH 合入forcnin123411234312345;dossh-p29418usergerrit.example.com gerrit review$cn,1--submitdone如果之前的 Review 标签已经满足 Submit 条件此时只需--submit即可。4.2 通过 REST API 合入可以调用 Submit 专用端点也可以在一次 Review 请求中带上submit参数。方式 A使用 Review 端点并添加submit: truecurl-s-uuser:pass-XPOST\-HContent-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-uuser:pass-XPOST\$GERRIT_URL/a/changes/my-project~$CN/submit注意Submit 端点需要 Change 已具备足够的 label如Code-Review2和Verified1等。若条件不满足会返回 409 Conflict。建议在 Submit 前确保所有 label 已打齐。5. 完整自动化脚本Python 示例下面提供一个使用 Python requests的完整脚本它输入一个 Change URL自动完成依赖链获取 → 顺序 2 → 顺序 Submit。importrequestsimportjsonimportsysfromurllib.parseimporturlparse# ---------- 配置 ----------GERRIT_URLhttps://gerrit.example.comAUTH(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~12345pathurlparse(change_url).path# 常见格式: /c/project//12345 或 /c/project//12345/2partspath.strip(/).split(/)iflen(parts)3andparts[1]:projectparts[0]change_numparts[2]returnf{project}~{change_num}else:raiseValueError(无法解析 Change URL)defget_related_changes(change_id):获取完整的依赖链 changes 列表按祖先到后代排序urlf{GERRIT_URL}/a/changes/{change_id}/revisions/current/relatedresprequests.get(url,authAUTH)resp.raise_for_status()# Gerrit 返回的 JSON 开头有 )]} 需要去除datajson.loads(resp.text[4:])returndata.get(changes,[])defreview_change(project,change_num,revisioncurrent):对指定 Change 的当前 patch set 打标签change_full_idf{project}~{change_num}urlf{GERRIT_URL}/a/changes/{change_full_id}/revisions/{revision}/reviewbody{message:批量自动 2,labels:LABELS,notify:NONE# 减少邮件风暴按需设置}resprequests.post(url,authAUTH,jsonbody)ifresp.status_code200: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):提交合入某个 Changechange_full_idf{project}~{change_num}urlf{GERRIT_URL}/a/changes/{change_full_id}/submitresprequests.post(url,authAUTH)ifresp.status_code200: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_urlsys.argv[1]change_idget_change_id_from_url(change_url)print(f目标 Change ID:{change_id})# 1. 获取依赖链relatedget_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.pyhttps://gerrit.example.com/c/my-project//12345脚本会自动查找所有祖先后代按序 2 并 Submit。建议先在测试环境验证。6. 注意事项与进阶技巧依赖链可能不是完全线性的/related返回的changes列表已经帮我们按提交图排序但如果有合并节点merge commit列表中可能包含多条分支。此时 Submit 顺序仍是自动推断的拓扑序通常可以应对但如果遇到同一代多个祖先无依赖则需要更复杂的处理大多数批量场景是简单线性链。Review 标签可能因项目配置不同示例中硬编码了Code-Review: 2如果你的项目使用其他标签如Verified、Sanity或有不同取值请相应修改LABELS字典。处理 Submit 失败重试某些 CI 系统如 Jenkins会为 Verified 标签异步投票。如果依赖链中某个 Change 缺少Verified 1Submit 会失败。可以在脚本中加入等待与重试逻辑或者提前确保 CI 已通过。权限要求执行批量 2 与 Submit 的账号必须具备相应项目的标签权限与 Submit 权限通常是 Maintainer 或更高的权限。大批量操作时降低通知频率在 Review 请求中将notify字段设为NONE、OWNER或OWNER_REVIEWERS可避免对团队产生邮件轰炸视团队规范调整。使用 SSH 时的密码/TOTP若 SSH 需要在命令行输入密码会阻碍自动化。请确保使用密钥认证或在脚本中使用sshpass不推荐。因此更推荐使用 REST API。7. 总结通过 Gerrit 的Related Changes REST API我们可以非常容易地获取一个 Change 的完整依赖关系链。结合Review 与 Submit 端点或 SSH 命令按照从祖先到后代的顺序自动化执行打分与合入就能将原本重复、易错的手工操作压缩为一条命令。本文提供的 Python 脚本已经过实际生产环境的变体验证读者可根据自己的项目标签和权限进行适配快速落地“依赖链一键合入”的自动化能力。希望这篇总结能帮你从重复劳动中解放出来把精力留给更有价值的代码评审本身。

最新新闻

日新闻

周新闻

月新闻