拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Gerrit代码评审实战:从push到Submit的完整操作指南

在日常开发里我见过太多团队把 Git 用得风生水起一上 Gerrit 就卡壳。明明代码 push 上去了同事却说“没看到你的提交”工单状态永远停在 Active甚至有人把“Review 2”和“Verified 1”混为一谈评审通过了却怎么都没法合入。这些问题的根源多半不是 Git 不会用而是没搞懂 Gerrit 和 Git 是两套逻辑。这篇文章就是一份可以直接拿去用的 Gerrit 操作手册。我会从 Gerrit 的核心概念讲起带你完整走一遍“提交代码 → 评审 → 验证 → 合入”的全流程重点回答很多新手都会问的一个问题为什么评审已经给了 Review 2却依然卡在 Verified 上没办法 Submit文章里所有步骤都是我实际验证过的命令、界面路径、权限配置都按最常见的 Gerrit 版本写你照着操作就能跑通。这份手册适合刚接触 Gerrit 的开发者也适合准备给团队搭建或优化代码评审流程的负责人。如果你已经用了一段时间但总踩坑直接翻到后面的“常见问题与排查实录”大概率能找到你想要的答案。1. 先从整体上认识 Gerrit1.1 Gerrit 到底是个什么东西Gerrit 本质上是一个基于 Git 的代码评审工具但它和普通的 Git 托管平台有很大不同。Git 是分布式的开发者 clone、commit、push、pull一切都围绕本地仓库和远端仓库展开。Gerrit 在 Git 远端仓库前面加了一个“关卡”每一次 push 到远端时并不是直接更新分支而是先变成一条待评审的“Change”只有通过评审和验证代码才会被真正合入目标分支。这个“关卡”设计听起来简单但它改变了整个团队的协作方式。你不再能随心所欲地把代码推到主分支上所有改动都必须经过多人的审查。对于中大型团队、开源项目、以及对代码质量要求比较高的企业项目来说这套机制能有效防止低质量代码、错误提交甚至误操作进入主干。我在实际工作中见过不少团队是从 GitLab 或 GitHub 切到 Gerrit 的初期会觉得怎么这么麻烦push 都推不上去。但用顺了以后会发现Gerrit 的评审粒度更细能精确到每一行代码的讨论而且有完备的权限控制、标签机制和可追溯的审批记录这是普通 Git 平台很难替代的。1.2 为什么团队会选 Gerrit 而不是直接用 Git先说直白的一点Gerrit 不是用来替代 Git 的它是坐在 Git 上面的一个评审网关。团队继续用 Git 做版本控制只是 push 的地址和方式发生了变化。Git 自带的 pull request、merge request 本质上也是评审流程但 Gerrit 有一个关键差异它把“提交”和“合入”彻底分开了。在 GitHub 上你可以先推到自己的 Fork再发 Pull Request分支上随便改、随便推。在 Gerrit 里所有待评审代码以 Change 的形式独立存在每次你 push 同一个 Change 的新版本就会生成一个新的 Patch Set评审人看的是新旧版本之间的 diff而不是整条分支的混乱历史。这对代码评审的质量提升非常明显。评审人不再是“看个大概”而是能精确看到你这次修正到底改了什么是不是只解决了上次提出的问题有没有引入新问题。再加上 Gerrit 内置的 Verified、Review 等标签系统可以把“代码审查是否通过”和“自动化验证是否通过”两条线分开管理形成强制入场门禁。这也是很多对代码质量要求极高的项目比如 AOSP、OpenStack、Tizen都长期使用 Gerrit 的原因。1.3 一个 Patch 从提交到合入的一生我习惯把这个流程比喻成“代码坐飞机”你的提交是旅客Gerrit 是安检口Review 2 是人工安检通过Verified 1 是仪器扫描通过最后 Submit 才是登机放行。整个生命周期是这样的开发者基于某个分支新建本地提交然后执行git push origin HEAD:refs/for/masterGerrit 会把它变成一个 Change。Change 初始状态是 Active需要至少一个或多个评审人给出 Review 2同时还要有 CI 系统或人工给出 Verified 1。当这些标签都满足项目的合入门槛后Change 右上角的 Submit 按钮才会变成可点击状态。点击 Submit 后Gerrit 会把当前 Patch Set 的代码合入目标分支Change 状态变成 Merged。如果评审过程中被打了 -1 或 -2Change 会保持 Active开发者需要修改代码并重新推送新 Patch Set然后整个评审流程再来一轮。值得强调的是这一轮不是推倒重来评审人是在上次讨论的基础上继续看新的 diff这比反复折腾分支合并要清爽得多。2. 上手前必须搞清楚的核心概念2.1 Change 与 Patch SetChange 是 Gerrit 里最核心的单位可以理解为一个“评审任务单”。它由三部分组成目标分支、提交消息、以及一串连续更新的 Patch Set。每个 Change 最初基于你第一次 push 的提交生成Change-ID 是它的唯一标识通常类似I0bfe...这样以 I 开头的哈希。Gerrit 要求提交消息里必须包含 Change-Id这是通过 commit-msg hook 自动生成的。只要 Change-Id 不变后续无论你怎么修改代码、提交多少次Gerrit 都会把这些提交归属到同一个 Change 下而不是新建评审任务。Patch Set 就是同一个 Change 的不同版本。第一个版本叫 Patch Set 1你修改代码后再次 push生成 Patch Set 2以此类推。评审人收到的通知和看到的 diff都是基于最新 Patch Set 和上一个 Patch Set 之间的差异。这个设计非常实用评审人不需要重新看完整文件只需看增量变化效率会高很多。2.2 Review / Verified / Submit 三套评分体系Gerrit 的合入门禁通常由多个标签共同控制最常见的是 Review 和 Verified 两个。Review 衡量“人工代码审查是否通过”Verified 衡量“自动化或人工验证是否通过”Submit 则是合入动作本身。Review 标签的取值范围一般是从 -2 到 2。2 表示“我同意合入”这是有合入权限的评审人给出的肯定结论1 表示“我认可这个改动但不想独自拍板”-1 表示“有问题需要修改但我不是强烈反对”-2 表示“有严重问题坚决不能合入”。在很多项目里一条 Change 必须至少有一个 2 并且没有 -2才算通过人工评审。Verified 标签通常是 -1 到 1 的区间。CI 系统跑完编译、单元测试、静态检查后通过就打 1失败就打 -1。如果项目还没有接 CI也可以由具备权限的人手动打这个标签。这里要特别注意Review 2 和 Verified 1 是两条独立的线背后代表的是“人同意”和“机器验证通过”两者缺一不可。Submit 是真正把 Patch Set 合入分支的动作。只有所有阻塞性标签都满足要求Submit 按钮才会亮起。Gerrit 里还有 Submit Type 的概念比如 Fast Forward Only、Merge If Necessary、Rebase Always策略不同合入的实际效果也不同。这个细节放在后面章节单独说。2.3 refs/for/ 与 refs/heads/ 的区别这是 Gerrit 新手最容易踩的坑同一条分支push 的写法不一样结果完全不一样。常规 Git 推法是git push origin HEAD:master这会把本地提交直接推到远端 master在 Gerrit 项目里通常只有管理员或拥有直接推送权限的人才允许这么做普通开发者执行这条命令往往会被拒绝。Gerrit 上提交评审的标准写法是git push origin HEAD:refs/for/master表示“我不直接更新 master而是为 master 提交一个待评审的 Change”。除此之外还有refs/for/refs/heads/master的写法效果和refs/for/master一样。如果你要推送到分支的名字里带斜杠比如feature/user-login就要写成git push origin HEAD:refs/for/feature/user-login。理解了 refs/for 的精髓你就明白了为什么 Gerrit 的流程天生适合严格评审因为普通开发者根本碰不到目标分支本身。3. 环境准备与连接配置3.1 SSH 密钥配置Gerrit 支持 HTTP(S) 和 SSH 两种方式但我在实际使用中强烈建议 SSH原因是SSH 不需要每次 push 都输用户名密码配合 rsync 和 hook 也更稳定而且 Gerrit 的很多高级用法都依赖 SSH。首先要生成密钥Linux 和 macOS 已经自带 OpenSSHWindows 建议用 Git Bash 或者 WSL。命令很简单ssh-keygen -t ed25519 -C your_emailexample.com一路回车会在~/.ssh/下生成id_ed25519和id_ed25519.pub。然后把公钥内容复制下来登录 Gerrit 页面点击右上角头像进入 Settings找到 SSH Keys把公钥粘贴进去保存。添加完成后在本地终端测试ssh -p 29418 yourusernamegerrit.example.comGerrit 默认 SSH 端口是 29418不要写成常见的 22 或 2222。如果看到欢迎信息和“Interactive shell is not supported”之类的提示说明认证已经成功。这是 Gerrit 特有的测试方式很多第一次配置的人就会卡在这一步但端口写对了基本就能通。3.2 下载并安装 commit-msg Hookcommit-msg hook 是 Gerrit 自动添加 Change-Id 的关键工具不装它你 push 到 refs/for/ 会被直接拒掉。错误信息通常很明确missing Change-Id in message footer。在 Gerrit 项目首页一般会给出标准的 hook 安装命令gitdir$(git rev-parse --git-dir) scp -p -P 29418 yourusernamegerrit.example.com:hooks/commit-msg $gitdir/hooks/如果没有 scp 权限也可以直接从 Gerrit 页面下载commit-msg文件放到.git/hooks/目录记得加上可执行权限chmod x .git/hooks/commit-msg要注意的是commit-msg hook 只对新建的 commit 生效。如果你已经写好 commit 才安装 hook需要重新执行git commit --amend让它把 Change-Id 补进去。这也是一个特别常见的问题很多人装完 hook 后发现 push 仍然报 missing Change-Id就是因为提交在 hook 生效之前就已经生成了。3.3 配置方便的 Git 别名Gerrit 的 push 写法比较长每次手敲很容易出错。我会在自己的 Git 配置里加两个别名减少重复劳动git config --global alias.gerrit push origin HEAD:refs/for/master git config --global alias.gerritw push origin HEAD:refs/for/workspace如果项目分支不固定直接写一个 shell 函数更灵活gp() { git push origin HEAD:refs/for/$1; }这样执行gp feature/user-login就能推送到对应分支比反复复制长 refs 路径要舒服得多。还可以配置推送时带上%w参数比如refs/for/master%w1这是设置 WIP 状态可以用在你还不想被评审人正式审查的时候。关于%后面的 magic 参数后面“进阶技巧”那一节再展开。4. 提交代码的完整操作流程4.1 新建分支并提交在开始新功能或修复 Bug 时我建议总是在一个干净的基线分支上创建自己的特性分支。假设当前项目在 mastergit checkout master git pull git checkout -b feature/user-login在分支上进行修改后按正常流程提交git add . git commit -m feat(user-login): add login page and token validation如果 commit-msg hook 正常生效git log -1会在提交信息尾部看到 Change-Id。没有 Change-Id 的话先返回 3.2 节那一步处理。4.2 推送到 Gerrit 审核提交完成后执行git push origin HEAD:refs/for/feature/user-login如果一切正常终端会输出一个新的回显地址类似remote: http://gerrit.example.com/c/project-name//12345在浏览器打开这个地址就能看到你的 Change 详情页。这里要留意你的本地分支和远端分支名称最好保持一致但真正决定 Change 归属的并不是本地分支名而是 push 时 refs/for/ 后面写的那段内容。如果你写错分支名推送到完全不同的目标分支Gerrit 会生成一条全新的 Change。4.3 修改后如何更新 Patch Set评审人提出修改意见以后你不需要新建分支也不需要重新发起评审只需要基于同一个 Change 的最近版本继续修改然后提交、推送。因为 Change-Id 没变Gerrit 会自动把它归入原 Change并生成新的 Patch Set。推荐的做法是继续用--amend修改同一个提交保持提交信息里的 Change-Id 不变git add . git commit --amend --no-edit git push origin HEAD:refs/for/feature/user-login也有人习惯在做大幅调整时新建一个 commit 再 push这时 Change-Id 会保留在原有 commit 的 footer 里吗答案是如果 commit 消息里保留了 Change-IdGerrit 仍然会识别为同一个 Change。不过我实测下来--amend是最不容易出错的路径尤其是当项目配置了多种 Submit Type 时用 amend 保持单提交记录会更利于后续打补丁和合并。我特别提醒一点永远不要手动去改 Change-Id。有些开发者在解决冲突时会把提交历史打散然后复制粘贴 Change-Id一旦抄错字符或者多个提交用了同一个 Change-IdGerrit 会把它们合并成一个 Change场面非常混乱。Change-Id 老老实实交给 hook 处理就好。5. Review 流程与权限管理5.1 评审人怎么看 Change 和评论作为评审人打开 Change 详情页后要关注的几块区域分别是顶部状态栏、侧边的标签面板、以及中间的代码 diff 区。顶部状态栏会明确显示当前 Change 的编号、目标分支、最新 Patch Set 版本和状态。标签面板则是联合登录不可少的信息Review、Verified 分别处于什么分数合入门禁是否满足Submit 按钮是否可用。代码 diff 区默认显示新旧 Patch Set 的差异你也可以切换成 Base也就是和真正合入前的父提交做比较。在 diff 界面里鼠标悬停在任意一行代码上右侧会出现一个“评论”图标。点击就能写行内评论这对精确指出问题非常有用。写完评论后必须点击顶部的“Reply”按钮写下总体评论并保存否则其他评审人很可能看不到你的留言。很多新人在代码行里留言后不点 Reply 就下线结果作者完全没收到通知白白浪费时间。5.2 如何给 Change 打 Review 2打 Review 分数的地方在 Change 详情页的右侧。通常界面里会显示Review -2 -1 0 1 2这样一个分数条选择 2 后下方的“Reply”按钮就会亮起来。我建议在打 2 之前至少在 diff 页面确认三个问题改动是否满足需求、代码风格是否与项目规范一致、是否存在明显的逻辑缺陷。确认无误后再选择 2。如果你希望对某些小问题提出建议但又不阻塞合入可以选 1 并补充评论。如果发现有严重问题直接 -2 并说明理由。这里要注意-2 是阻塞性的有些团队的评审规范要求 -2 必须经过团队讨论后才能解除所以下手要慎重。Review 2的含义是“我人工审查了这一版同意合入”。这个分数是针对当前 Patch Set 的。如果你打了 2 之后作者又上传了新的 Patch Set这个 2 就会自动失效系统会要求重新评审。这不是 bug这是 Gerrit 为了防止旧审查结论被“夹带私货”的提交蒙混过关而刻意设计的安全机制。5.3 为什么 Review 2 了还不能 Submit这是我在各种技术群里看到最多的问题之一“我的 Change 已经 Review 2 了为什么 Submit 按钮还是灰的” 答案十有八九是Verified 标签没满足。Gerrit 默认可以把多个标签做成合入门槛。常见的配置是标签最小分值最大分值阻塞Verified11是Review22是也就是说只有 Review 同时达到 2、Verified 达到 1Change 才能 Submit。Review 2 只解决了一半问题剩下的一半要看 Verified。如果你打开标签面板看到Verified旁边是空白或者 0那就说明验证流程还没通过。怎么补上 Verified 1分两种情况如果项目接了 CIJenkins、GitLab CI、或者 Gerrit 自带的 CI Bot那么会自动触发验证流程CI 跑完通过后会自动投 Verified 1刷新页面就能看到。如果项目没有接 CI或者 CI 没有自动触发就需要有人手动投 Verified 1。手动投这个分数需要权限普通开发者通常没有“Label Verified”权限一般由项目管理员、验证负责人或集成运维人员来操作。“Review 2 后如何 Verified 1”这个操作的本质就是让具备验证权限的人或系统对该 Patch Set 投出 Verified 1。如果你的权限足够在 Change 详情页右侧标签面板里找到Verified分数条选择1然后点击 Reply 提交即可。如果你没有这个权限页面上根本不会显示可选的 Qualified 分数条这时候要找项目管理员或者让 CI 重新跑一次验证任务。5.4 Verified 1 的自动化与手动触发绝大多数成熟项目都会把 Verified 交给 CI 自动处理。这样做的原因是验证不应该依赖于某个人的自觉而是应该由机器跑固定步骤编译、单元测试、静态检查、甚至部署到测试环境通过就 1失败就 -1。如果你的 Gerrit 已经配置好了 Jenkins 插件每次新的 Patch Set 上传Jenkins 会收到事件自动触发对应 Job。Job 跑完插件会把结果回写到 Gerrit 标签。开发者只需要等待几分钟并刷新页面就能看到 Verified 状态的变化。如果你想手动触发一次 CI常见做法是在 Change 详情页提交一个空评论或者在评论里包含特定关键字触发 Jenkins 的Gerrit Trigger。有的团队还配置了recheck命令评论recheck就会重新触发验证。具体命令随项目配置不同而不同最稳妥的办法是看项目 README 或问一下 CI 负责人。如果你确实要手动补 Verified 1 来做演示或绕过 CI记得这是有权限管理的不要随意放宽权限否则会破坏门禁机制的意义。6. 评审过程中的常见问题与排查实录6.1 提交后找不到自己的 Change第一种常见情况是 push 成功了但浏览器打开 Gerrit 首页看不到自己的 Change。这通常是因为你的 Change 被过滤掉了。Gerrit 默认的“我的待办”视图只显示与当前登录用户相关的 Change或者状态为 Active 且与你相关。你可以在搜索框里直接输入owner:self来查找自己提交的 Change或者输入change:12345用 Change 编号直达。另一种情况是 push 时 URL 写错或者本地仓库的 remote 没指向 Gerrit 而是指向了旧的 Git 地址。排查方法很简单git remote -v如果 origin 指向不对用git remote set-url origin ssh://yourusernamegerrit.example.com:29418/project-name修正。还有一种可能性是你推送到了错误的 refs比如把refs/for/master写成了refs/heads/master如果没有权限会被拒如果某项目配置了允许直推代码就直接进分支了自然也不会出现在评审列表里。这种情况要马上找管理员确认必要时回滚。6.2 Merge Conflict 怎么处理当你的 Change 基于的代码已经过时目标分支上其他人合入了冲突代码Gerrit 会在 Change 详情页提示 Merge Conflict。此时即使你已经拿到 Review 2 和 Verified 1Submit 按钮也点不了。最标准的解决姿势是把目标分支的最新代码合并到你的本地分支然后重新推送git checkout feature/user-login git fetch origin master git merge origin/master # 在这里手动解决冲突 git add . git commit --amend --no-edit git push origin HEAD:refs/for/feature/user-login如果你是老手可以用git rebase origin/master让提交历史更线性但 rebase 会重写你的提交如果 Change 已经有很多评论我还是推荐 merge 方式保留原始提交哈希和 Change-Id减少评审上下文丢失的风险。解决冲突后Review 标签通常会重新变为未通过状态需要评审人再确认一次。这是 Gerrit 的自我保护防止冲突解决过程中引入新问题。别嫌麻烦这正是它对质量负责的表现。6.3 代码被驳回后如何重新提交评审人给了 -2 或者大量修改意见后Change 不会关闭只是无法合入。你要做的是根据评论逐条修改然后重新推送新的 Patch Set。操作流程和 4.3 节完全一样修改代码、git add、git commit --amend --no-edit、再 push 到同一个 refs/for 目标。推送成功后在 Change 详情页的 History 区域可以看到新的 Patch Set 记录。为了让评审人尽快重新关注我建议在 Reply 区写清楚每一处修改对应的是哪条评审意见比如已按 zhang 的意见调整了登录接口的校验逻辑。 新增了 Token 过期时间的单元测试。 其余评论中的代码风格问题已处理。这种清晰的回应方式能让评审过程更高效也能减少来回沟通的时间损耗。很多人忽略这一点只投一个空版本评审人还得自己重新翻 diff体验很糟糕。6.4 常见问题速查表症状常见原因解决方式push 被拒提示 missing Change-Id未安装 commit-msg hook安装 hook 后用git commit --amend补 Change-Idpush 被拒提示 not permitted没有直接推送分支的权限使用refs/for/分支名提交评审Change 无法 SubmitSubmit 按钮灰色Verified 或 Review 标签未满足检查标签条件补齐 Verified 1 或 Review 2同一个功能出现了多个 ChangeChange-Id 不一致或被修改保持 Change-Id 不变用 amend 更新提交本地代码与 Gerrit 上的最新 Patch Set 不同步本地还是旧版本git fetch origin refs/changes/xx/yy/zz获取指定 Patch Set合并时冲突目标分支有新提交合并或 rebase 最新代码后重新推送评审人看不到我的评论行内评论没点 Reply 提交在 diff 写评论后必须点击 Reply 保存Reviewed 状态是 -1 或 -2评审人要求修改按评论修改并生成新 Patch Set7. 进阶技巧与我的实操心得7.1 善用 push 命令里的 Magic 参数Gerrit 在 refs/for/ 后面支持很多魔法参数用%分隔非常实用。常用参数包括%wip把 Change 标记为 Work In Progress不通知评审人适合还没准备好的草稿。写法是git push origin HEAD:refs/for/master%wip。在网页端也可以手动把 Change 设为 WIP但从 push 层面直接设更省事。%ready与%wip相反把 WIP 状态的 Change 置为 Ready。%private只对特定用户可见适合敏感或实验性改动。%remove-private取消私有状态。%topicxxx给 Change 设置 Topic多个 Change 可以归为一个逻辑主题方便一起搜索、一起合并。%mreviewer1,reviewer2在 push 时直接添加评审人。我测试过多个版本这个参数对权限配置比较敏感如果提示未支持就直接用网页端的 Add Reviewer。比如我想把当前分支推送到 master 评审同时标记 WIP 并加上一个 Topicgit push origin HEAD:refs/for/master%wip,topicuser-login-cleanup这样一次 push 就完成了多条元数据设置。这个技巧在需要频繁创建多个草稿 Change 的场景下特别省时间。7.2 用 Operator 和 Keyword 快速检索Change 列表翻页找太慢了我平时更多用搜索框。Gerrit 的搜索语法叫做 Query 命令支持很多操作符。我在工作里最常用的几个owner:self我提交的 Change。reviewer:self需要我评审的 Change。is:open所有打开的 Change配合project:xxx过滤到具体项目。age:2h两小时内活跃的 Change。label:Verified1已经通过验证的 Change。label:Code-Review-1评审被打了 -1 的 Change。has:unresolved-comment有未解决评论的 Change。message:recheck评论里包含 recheck 的 Change。组合使用威力很大例如我每天早上的固定检索词是is:open reviewer:self -label:Code-Review2这一条能列出所有需要我评审但还没给 2 的 Change配合 Inbox 设置基本不会被漏掉。7.3 我最想提醒新手的三点经验第一不要在本地大量堆积未推送的 commit。Gerrit 的评审模式是“小步快走”一个 Change 只做好一件事。我见过有人把 10 个 commit 攒在一起推上去不仅评审难度大冲突概率也高出了问题还不好定位。宁可多创建几个 Change 分开提。第二警惕“Review 2 后又被重置”的情况。任何一次新 Patch Set 上传都会让之前打过的标签失效。所以不要在评审人已经通过后为了“顺便改一行”就再 push。如果确实要改改完一定要在 Reply 里明确说明并且主动请求评审人重新 2。第三把 Verified 交给 CI不要把验证责任压在个人身上。很多团队初期设备齐全手动打 Verified 1 也很顺利。但一旦代码量上来人工验证的遗漏和主观性会成为隐藏风险。就算暂时没有能力接入完整 CI也至少给 Verified 标签做个脚本触发比如本地跑完测试后手动打 1形成固定流程而不是看心情做事。就我个人经验而言Gerrit 这套流程前期“阻碍感”很强但它是把所有不确定性尽量前置到合入之前。如果你现在正被评审流程搞得很烦躁不妨换个角度这东西逼着你把每一行代码都交代清楚对个人代码习惯和团队工程质量都是长期正收益。最后再分享一个小技巧把 Change 搜索页的默认条件保存成“我的常用”比如owner:self -is:wip is:open每天早上扫一眼就知道自己还有几个 Change 待处理。配合邮件通知基本不可能漏掉评审请求。等你真正跑通了从 push 到 Submit 的全流程再回头看就会发现Gerrit 没有那么难它就是一套需要耐心遵守规则的协作协议而规则本身就是质量的基石。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门