Gerrit命令行与REST API自动化代码评审实战指南
做代码评审最烦什么界面上一排 change 翻来翻去先给张三发评审邀请再等李四打完2最后还得手动点一下 Submit。一个人同时盯好几个 change 的时候光是在 Gerrit Web UI 上点点点就能耗掉半小时。Gerrit 本身是个好用的代码审查平台但在批量操作、脚本集成和自动化流程面前图形界面天生吃亏。这篇博文专门聊怎么用命令行把“添加 Reviewer、打 Review 分数、Submit 合入”这一整套动作跑起来。核心工具就是 Gerrit 自带的 SSH 命令和 REST API不需要装任何第三方插件装好 Gerrit 就可以直接用。适合开发者、测试、DevOps还有需要把评审流程接进 CI/CD 流水线的同学。1. 准备工作先把 SS H通道打通再谈效率1.1 生成并配置SSH密钥Gerrit 支持原生 SSH 服务默认监听29418端口这个端口通常和常见的22不一样第一次连接很容易踩坑。要让命令行工具能识别你的身份第一步是把你的 SSH 公钥加到 Gerrit 账号里。先在本地生成一对密钥这里建议用 ed25519 算法兼容性和安全性都够ssh-keygen -t ed25519 -C 你的邮箱或备注生成完成后公钥默认在~/.ssh/id_ed25519.pub把里面的内容复制出来。然后登录 Gerrit进入个人设置Settings - SSH Keys粘贴保存。这一步做完先不要急着敲完整命令用一条最简单的命令验证一下链路是否通ssh -p 29418 your_gerrit_usernamegerrit.example.com如果你的用户名和公钥都配置正确SSH 会直接登录到 Gerrit 服务端并显示类似Welcome to Gerrit Code Review的欢迎信息。看到这个说明 SSH 通道已经通了。有个容易忽略的细节这里登录的用户名不是服务器系统账号而是你在 Gerrit 里注册的用户名。头几次用很容易下意识填系统账号结果一直提示认证失败反复折腾半天才发现是用户名的问题。如果确认用户名没问题还是连不上优先检查防火墙是否放行了29418/TCP。1.2 Gerrit SSH命令的通用格式SSH 通道通了之后所有 Gerrit 管理指令都遵循同一个壳子ssh -p 29418 your_gerrit_usernamegerrit.example.com gerrit 子命令 参数也就是说你在本地机器通过 SSH 执行远程的gerrit命令。后面接的是什么子命令Gerrit 就会执行对应的操作。常用的子命令包括gerrit set-reviewers添加或移除 Reviewergerrit review对某个 revision 打分数、写评论、甚至直接提交gerrit query按条件查询 change 的状态和信息gerrit set-project、gerrit create-project等项目管理和权限管理命令如果对参数不熟随时可以加--help查看版本对应的帮助信息ssh -p 29418 your_gerrit_usernamegerrit.example.com gerrit review --help不同版本的 Gerrit 在小参数上会有细微差别官方文档反而不如命令自带帮助来得直接。建议实际操作之前先瞄一眼帮助确认参数名没记错。除了 SSHGerrit 的另一个可选方案是 REST API适合在脚本和 CI 工具里调用。使用 REST API 需要在Settings - HTTP Password里生成一个密码这个密码配合用户名走 HTTP Basic Auth。两者各有用处SSH 适合人肉敲命令时用敲起来顺手REST API 适合程序调用输出是 JSON方便解析。后面的部分我会把两种方式都覆盖到。2. 添加Reviewerset-reviewers指令详解2.1 基础用法与change定位方式给一个 change 添加 Reviewer最常用的 SSH 指令是gerrit set-reviewers。举个例子ssh -p 29418 your_gerrit_usernamegerrit.example.com \ gerrit set-reviewers I1234567890abcdef --add aliceexample.com指令本身不复杂难点在于怎么准确告诉 Gerrit 你要操作的是哪个 change。这里提供三种定位方式按推荐程度排Change-Id形如I1234567890abcdef的 40 位字符串。这是 Gerrit 跨分支识别 change 的唯一标识推荐优先使用。Change-Id 在 Gerrit UI 的 change 详情页顶部能看到不依赖分支和项目上下文。数字 change 号比如12345。这个数字虽然好记但在不同项目里是递增分配的如果同时管着多个仓库只给数字容易混淆。项目~分支~Change-Id组合例如myproject~main~I1234567890abcdef。当 Change-Id 在多个分支或同项目多个 change 里存在歧义时这个写法最精准。shell 命令里建议给~打引号或用转义否则某些环境会把它解释成特殊符号。我自己的习惯是手敲的时候用数字 change 号脚本和 CI 里一律拼接成项目~分支~Change-Id的完整三元组避免任何歧义。2.2 批量增删reviewerset-reviewers支持在一条命令里同时增删多个评审人。--add和--remove可以混着用比如ssh -p 29418 your_gerrit_usernamegerrit.example.com \ gerrit set-reviewers myproject~main~I1234567890abcdef \ --add aliceexample.com \ --add bobexample.com \ --remove carolexample.com这条命令会一次性添加 Alice、Bob同时移除 Carol。需要注意--add指定的用户必须是 Gerrit 里已经存在的用户。如果对方从来没登录过 Gerrit系统会直接报错提示fatal: user xxx not found。想让同事被添加成功得先让他们至少登录一次 Gerrit 完成账号激活。另一个常被忽视的点是权限。给 change 添加评审人需要项目上授予Add Reviewer权限。大多数团队给注册用户默认开了这个权限但如果你发现命令执行后提示fatal: not permitted不要怀疑命令写错了先去看项目Access配置里有没有授予相应权限。尤其是从零搭建 Gerrit 的团队默认权限模板可能没完全配好这里卡住非常正常。2.3 用REST API添加Reviewer脚本场景我更推荐 REST API因为返回结构化 JSON方便在 CI 日志里排查。添加评审人的接口是POST /changes/{change-id}/reviewerscurl -u your_gerrit_username:your_http_password \ -X POST \ -H Content-Type: application/json \ --data {reviewer: aliceexample.com, state: REVIEWER} \ https://gerrit.example.com/a/changes/myproject~main~I1234567890abcdef/reviewers这里有三个细节必须注意第一Gerrit 的认证请求 URL 要在/changes前加/a/前缀也就是/a/changes/...。不加会收到401 Unauthorized或者被当作匿名请求处理。第二state字段可以设成REVIEWER也可以设成CC。CC表示把用户加入抄送列表对方能看到 change 但不会出现在 Reviewer 名单里。如果只是想让某个人“知道有这个变更”用 CC 比 Reviewer 更合适。第三Gerrit REST API 的响应为了防 XSS会在最前面加一段)]}前缀。用 curl 在终端里看问题不大但如果用jq解析 JSON要先去掉第一行。常见做法是curl -s -u user:password ... | sed 1d | jq .3. 执行Review打分、评论与机器人评审3.1 先搞懂Gerrit的评分机制Gerrit 里最核心的两种标签是Code-Review和Verified。Code-Review 表示代码审查意见取值从-2到2共五档Verified 表示验证结果一般只有-1、0、1三档。具体含义可以这样理解Code-Review 1表示“我看了没问题”2表示“我同意合入责任我扛”-1是“我不太满意但不想一票否决”-2是“我强烈反对这个改动不能合”。Verified 则是 CI 或者测试人员的管辖范围1代表编译、单测、集成测试全部通过-1代表有任何一个环节挂了。按默认配置一个 change 要能被 submit至少需要一个Code-Review 2和至少一个Verified 1。很多团队还加了自定义标签比如QA-Verified、CI-Verified原理完全一样。搞清楚这套评分语义你就明白了为什么 Gerrit 的审阅不是“点一下按钮”那么简单——它把每个环节的“同意权”和“验证权”拆开了。如果你是在写自动化脚本记得新 patchset 推送后旧 label 通常会被重置或重新验证。也就是说CI 必须对最新的 revision 重新打Verified 1而不是只关心最初那版。这点在搭建流水线时非常容易漏漏掉的结果就是 change 永远无法 submit。3.2 用gerrit review指令打分gerrit review是给某个具体 revision也就是某个 patchset 对应的 commit打分的指令。基本用法ssh -p 29418 your_gerrit_usernamegerrit.example.com \ gerrit review \ --project myproject \ --branch main \ --verified 1 \ --code-review 2 \ --message CI与人工评审均通过可以合入 \ 6a5f0d3e8b7c1a2b3c4d5e6f7a8b9c0d1e2f3a4b最后一个参数是完整的 commit SHA-1也就是你要评审的那个 patchset。实际开发中可以从本地仓库用git rev-parse HEAD拿到最新提交的 SHA也可以从 Gerrit UI 的 patchset 列表里复制。--message是留评论多条评论可以重复传--message或者用多行字符串。如果评论内容包含特殊字符比如引号、$、反斜杠建议用双引号把整个 message 包起来。内容特别长的时候可以先写到文件里再读进来MESSAGE$(cat review_comment.txt) ssh -p 29418 userhost gerrit review --project myproject --branch main \ --message $MESSAGE revision-sha这个技巧在自动化场景里很实用因为 AI 生成的评审意见通常很长而且充满换行和特殊符号直接往命令行里拼字符串很容易出错。gerrit review还可以追加--submit参数在打分的同时直接提交省一条命令。不过我不建议把打分和提交混在一条命令里写因为 submit 能否成功取决于 label 是否满足如果分数还没生效命令会先报错排查时反而不如分开执行来得直观。3.3 通过REST API完成ReviewREST API 的 review 接口是POST /changes/{change-id}/revisions/{revision-id}/review。revision-id在自动化流程里可以固定写成current表示请求自动解析到最新 patchset不用每次拼 SHAcurl -u your_gerrit_username:your_http_password \ -X POST \ -H Content-Type: application/json \ --data { labels: { Code-Review: 2, Verified: 1 }, message: CI编译通过审查意见合理同意合入 } \ https://gerrit.example.com/a/changes/myproject~main~I1234567890abcdef/revisions/current/review如果请求成功Gerrit 通常会返回202 Accepted。这里不要惊讶它不是200 OK代表 Gerrit 接收了请求并异步处理。只要 HTTP 状态码是 2xx就说明请求已经被接受。REST API 的优势在于响应体是 JSON可以很轻松地在脚本里判断是否成功。比如用curl -s -w %{http_code}拿到状态码结合sed 1d清理响应头一套组合拳下来CI 日志能做得相当友好。4. Submit合入代码的最后一公里4.1 命令行提交change的两种方式当 change 上的所有 label 都满足条件后就到了最后一步Submit。用 SSH 可以这样提交ssh -p 29418 your_gerrit_usernamegerrit.example.com \ gerrit review revision-sha --submit这条命令会把该 change 提交合入前提是权限足够、label 满足、且没有 merge conflict。用 REST API 同样可以完成curl -u your_gerrit_username:your_http_password \ -X POST \ -H Content-Type: application/json \ --data {wait_for_merge: true} \ https://gerrit.example.com/a/changes/myproject~main~I1234567890abcdef/submitwait_for_merge建议设成true这样接口会等待合并真正落库后才返回CI 下一阶段拿到证据才安心。如果设成false请求只是触发了 submit 动作后续的状态需要额外轮询才能确定。4.2 submit的权限与策略细节Submit 操作需要Submit权限。在项目配置里这个权限经常只开放给集成负责人或代码维护者普通开发者不一定有。所以如果你的账号被提示没权限别硬刚找维护者在 Project Access 里把Submit授予给对应用户组就行。还要注意 Gerrit 的提交策略常见的有Fast Forward Only只允许快进式合并不允许产生 merge commit历史保持线性。Merge If Necessary必要时自动生成 merge commit允许分支合入。Merge Always总是生成 merge commit。Cherry Pick将当前 change 以 cherry-pick 方式提到目标分支同时保留 Change-Id。提交策略通常在项目配置里设置不影响命令行本身但它会影响合入后的分支形态。比如团队想严格保持线性历史就选Fast Forward Only否则遇到并行开发时一个简单的 submit 可能因为非快进而被拒。搞清楚策略再看 submit 失败原因很多疑惑就解开了。4.3 submit被拒的排查思路submit 被拒是家常便饭集中注意这四类原因。第一类是 label 不满足。最常见是还差一个Code-Review 2或者 CI 的Verified 1还没打上。查看方式很简单通过 Gerrit UI 打开 change 详情或者用 REST APIcurl -u user:password \ https://gerrit.example.com/a/changes/myproject~main~I1234567890abcdef/detail看返回 JSON 里的labels字段能直观看到每个 label 的当前状态以及还差什么条件。第二类是 change 不是 open 状态。如果 change 已经被 abandoned 或 mergedsubmit 会直接失败。这类 change 需要先restore恢复再用gerrit review --restore命令可以解决。第三类是 merge conflict。如果目标分支在你的 patchset 之后又有了新提交或者与你改的文件产生冲突submit 会被拦截。需要先git rebase到最新目标分支重新推一个 patchset。第四类是权限不足。检查账号是否有Submit权限以及项目是不是被refs/heads/*之类的规则限制了合入权限。5. 自动化场景把整套流程写进脚本5.1 一个可直接复制的完整示例命令行最大的价值在于可以编排成脚本。下面是一个比较完整的 Bash 示例输入项目名、Change-Id、revision SHA 和评审人列表脚本自动完成“加评审人、打分评论、提交合入”三步操作#!/bin/bash set -euo pipefail GERRIT_SSH_PORT29418 GERRIT_HOSTgerrit.example.com GERRIT_USERci-bot BRANCH${BRANCH:-main} PROJECT${1:?请输入project名} CHANGE_ID${2:?请输入Change-Id} REVISION_SHA${3:?请输入revision SHA} REVIEWERS${4:-aliceexample.com,bobexample.com} IFS, read -ra REVIEWER_LIST $REVIEWERS for reviewer in ${REVIEWER_LIST[]}; do echo 添加评审人: $reviewer ssh -p $GERRIT_SSH_PORT $GERRIT_USER$GERRIT_HOST \ gerrit set-reviewers $PROJECT~$BRANCH~$CHANGE_ID --add $reviewer done MESSAGECI 编译通过测试用例全部通过评审意见已复核。 echo 打 Review 分数 ssh -p $GERRIT_SSH_PORT $GERRIT_USER$GERRIT_HOST \ gerrit review --project $PROJECT --branch $BRANCH \ --verified 1 --code-review 2 --message $MESSAGE \ $REVISION_SHA echo 提交合入 ssh -p $GERRIT_SSH_PORT $GERRIT_USER$GERRIT_HOST \ gerrit review $REVISION_SHA --submit echo 已完成: $CHANGE_ID脚本的关键在于set -euo pipefail任何一步失败都会立即退出避免 CI 出现“假装成功”的情况。尤其多人协作时reviewer 临时写错、revision SHA 过期、label 未满足——任何一步失败都应该暴露出来而不是静默通过。5.2 参数化、错误处理与AI评论接入上面的脚本能用但真实场景往往更复杂。比如 reviewer 列表由外部参数传入项目分支不固定甚至要对多个 change 批量执行。这时候建议把 SSH 命令封装成函数再包一层 for 循环gerrit_add_reviewers() { local project$1 change_id$2 shift 2 for reviewer in $; do ssh -p $GERRIT_SSH_PORT $GERRIT_USER$GERRIT_HOST \ gerrit set-reviewers $project~$BRANCH~$change_id --add $reviewer done } gerrit_review_and_submit() { local project$1 branch$2 change_id$3 sha$4 ssh -p $GERRIT_SSH_PORT $GERRIT_USER$GERRIT_HOST \ gerrit review --project $project --branch $branch \ --verified 1 --code-review 2 --message $MESSAGE $sha ssh -p $GERRIT_SSH_PORT $GERRIT_USER$GERRIT_HOST \ gerrit review $sha --submit }把命令封装成函数的好处是CI 脚本里出现的是语义明确的调用而不是一长串难维护的 SSH 拼接。团队成员接手也容易。最近很多人问我 AI 生成代码怎么接入 Gerrit 评审流程。我的做法很简单让 LLM 对 diff 输出结构化审查意见然后把意见写入临时文件再用--message $(cat ai_review.txt)的方式挂到 change 上。这样 AI 的意见就能完整呈现在评审记录里人工 Reviewer 无需重复发现低级问题只用聚焦 AI 判断不准的领域。本质上就是把 AI 当成一个自动评审器打Code-Review 1而不是2最终合入权仍然保留给人。6. 实战问题排查与经验笔记6.1 高频报错速查表把命令行操作 Gerrit 高频报错整理成了一份速查表都是我实际踩过或者帮同事排查过的报错信息可能原因处理方式fatal: user xxx not found指定用户不存在或未激活账号让对方至少登录一次 Gerrit 并设置用户名fatal: not permitted账号缺少 Add Reviewer 或 Code-Review 权限检查 Project Access 并对应授权label Verified is not set没打 Verified 分就尝试 submit先执行--verified 1或 REST 设置 labelchange is not openchange 已被 merged 或 abandoned确认当前 change 状态必要时 restore409 Conflictlabel 不满足、merge conflict、提交策略拒绝查看labels和 merge 状态按原因处理401 UnauthorizedREST 请求没带认证或漏掉/a/前缀加上/a/并确认 HTTP Password 正确Connection refused端口错误或防火墙拦截确认29418端口开放且 SSH 端口监听正常这张表前三种情况占了日常问题的八成。发现问题先看状态码和报错文本再对照表查基本都能定位。6.2 这几个坑我替你踩过了先说 message 里的引号问题。Gerrit 的--message参数对引号非常敏感。我吃过一次大亏CI 脚本里双引号包裹 message 内部变量又出现双引号结果整个命令被 shell 拆得四分五裂。后来强制规范是message 一律先写入临时文件再用$(cat file)读取杜绝一切内联引号问题。其次是 revision SHA 容易过期。Gerrit 的gerrit review针对的是某个具体 revision不是 change。如果你的 CI 拿的是旧 patchset 的 SHA打分和评论会落到旧版本上新 patchset 的 label 还是空的提交自然失败。规范做法是每次取最新 patchset 的 SHAREST 场景就直接用revisions/current代替手工维护 SHA。还有一点想特别提醒不要拿 root 或者共享账号去连 Gerrit SSH。Gerrit 会把操作记录归属到登录用户名如果所有人用一个 CI 账号出了问题你根本不知道是谁评的、谁批的。规范做法是每个服务建专用技术账号比如ci-bot、deploy-bot并按最小权限原则只授予必要标签。最后一个容易被忽略的是 REST API 响应里的)]}前缀。很多人在脚本里用jq解析 Gerrit 返回结果莫名其妙报 JSON parse error其实就是忘了先用sed 1d把首行剥离。这个问题很小但几乎每个刚开始用 Gerrit REST API 的人都会遇到一次记牢能省不少排查时间。我个人在实际操作中的体会是把 Gerrit 命令行用顺之后再也不想回到纯 Web 界面去手动操作重复性流程了。尤其是接 CI 和写自动化评审SSH 指令和 REST API 就像给 Gerrit 开了个后门把原本需要人为反复确认的步骤一次性跑完。如果你的团队每天有大量 change 在流转建议从中挑一个高频动作开始比如只做自动添加 Reviewer 或自动 Verified跑顺了再逐步扩展到 review 和 submit。一次只推进一个小环节自动化流程才不会一上来就复杂到没人维护。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →