资讯详情

Gerrit代码评审系统实战指南:从搭建到最佳实践

📅 2026/10/19 4:35:09 | 华诺云谱 👁 阅读
Gerrit代码评审系统实战指南:从搭建到最佳实践
写这篇 Gerrit 使用简介之前我先说句真心话如果你所在的团队还在用微信群收代码、口头 review、或者靠 GitHub 的 Pull Request 勉强撑着那么 Gerrit 可能是你下一个值得花时间研究的工具。Gerrit 最早出自 Android 开源项目的代码审查流程本质是一套基于 Git 的代码评审系统核心玩法是所有提交不直接进分支而是先推到临时引用区经过人工审查和自动验证后才能合入。很多人第一次用 Gerrit 会觉得“反人类”因为 push 的语法、Change-Id、Patch Set 这些概念和 GitHub 那套完全不同。但一旦团队习惯了这种“评审驱动”的节奏代码质量和管理清晰度会明显上一个台阶。这篇内容不打算写成官方文档的翻译而是把我实际用过的搭建、配置、提交流程、权限设置、问题排查以及那些文档里不会写的坑按顺序整理出来。适合刚接触 Gerrit 的开发、测试、运维也适合正在评估自建代码审查平台的团队参考。1. 内容整体设计与思路拆解Gerrit 看上去是一个 Git 仓库管理工具很多人第一反应是“这和 GitHub 有什么不一样” 但真正理解 Gerrit 后你会发现它把 Git 和代码审查强行绑定在一起甚至可以说仓库管理只是它的副产品审查流程才是核心。1.1 核心需求把“审查”变成必经环节传统 Git 工作流里开发者 push 到远程分支后其他人能不能看到、要不要看、什么时候看靠的是自觉和协作约定。GitHub 的 PR 虽然把审查变成了一个显式流程但 PR 本身仍然是一个可选的合并路径而且很多团队对自己仓库的合并权限管得比较松。Gerrit 的设计理念更硬核在它的默认配置下任何提交都无法直接写入受保护的分支必须先生成一个评审 Change拿到足够的分数并执行 Submit 操作后提交才会真正落到目标分支上。这带来的好处不只是“有人看过了”而是整个项目的提交历史变成了经过过滤的、可追溯的序列。每一条合入记录都有自己的 Change ID、评审人、得分、验证结果出了问题可以精确回看是哪个环节放行的。1.2 方案选型为什么用 Gerrit 而不是本地 Git 或 GitHub如果在内部想搭一套自己的代码审查平台可选项其实不少。GitLab 有 Merge RequestGitea 有 Pull RequestGitHub 也可以私有化部署。那为什么还要费劲上 Gerrit我个人的结论是如果你对代码审查的严格程度要求非常高或者团队规模较大、分支同时提交非常频繁那么 Gerrit 的优势能吃得很透。它把每个提交都孤立成一个可独立审查和验证的单元多个开发者同时在同一个分支上工作不会互相阻塞每一次 push 新版本都形成一个补丁集审查者可以清楚看到新旧版本的差异而不是整个变更重新看一遍。这种设计在大型项目里非常省时间。Gerrit 另一个常被人忽略的地方是它与 CI 的耦合方式。它提供了 Verified 标签专门留给自动验证工具使用。Jenkins 等工具可以在后台轮询或通过事件触发构建然后把结果写回到对应的 Change 上。很多团队用 Gerrit 之后连“要不要等 CI 通过再合并”这类容易扯皮的问题都直接靠标签规则落地了少了很多口水战。1.3 整体架构前端、后端与存储Gerrit 本质上是一个 Java Web 应用用 Jetty 或者内嵌的 HTTP 服务来跑。代码存储在文件系统的 Git 仓库目录中元数据用户、群组、权限、Change 记录一部分存在仓库的refs/meta/config分支里一部分存在底层数据库里默认 H2 或 MariaDB/PostgreSQL。它支持 SSH 和 HTTP/HTTPS 两种 Git 访问方式也可以配反向代理对外提供服务。这套架构的好处是部署相对简单一台中小型的服务器就可以支撑一个几十人的团队坏处是高可用和集群方案不如现代托管平台那么自动化。如果团队超过百人或者对可靠性要求比较苛刻你可能需要把仓库文件放共享存储并在前面挂负载均衡。2. 核心细节解析与实操要点Gerrit 的安装配置本身并不难难的是理解它的对象模型。很多新手卡住不是因为命令敲错而是因为脑子里的模型还停留在 GitHub 的 PR 上。所以这一节先讲透核心概念再给实际初始化和配置命令。2.1 核心对象Change 和 Patch Set当开发者执行git push origin HEAD:refs/for/master时Gerrit 会收到一个提交但不会把它直接放到refs/heads/master上而是把提交存到一个内部空间里并生成一个整数编号的 Change。打个比方Change 相当于一次“评审会话”而每次你修改后又重新 push 的版本叫 Patch Set相当于这次会话的第 1 版、第 2 版。这里有三个细节容易搞混第一同一个 Change 之所以能识别出是“同一件事”靠的是提交信息里的Change-Id行。如果你改动提交信息时把这个行弄丢了Gerrit 就会认为你提交的是一个全新的 Change那你就得从头再走一遍评审流程前面攒下的评论和分数全部作废。第二每个 Patch Set 代表一次完整的提交快照审查者可以在 Gerrit Web 界面里对比任意两个 Patch Set 之间的差异。老的 Patch Set 不会消失方便回溯。第三Change 的状态包括 New、Merged、Abandoned 等。如果中途你重新拉新分支推了一版原 Change 可以选择 Abandon 掉避免历史里堆一堆死编号。2.2 refs/for 与 refs/heads 的区别refs/for/master是 Gerrit 工作流里最重要的魔法引用。它表示“我不是要把提交推送到 master 这个分支而是要提交给 master 分支的评审队列”。Gerrit 收到这个 ref 后会先校验提交信息然后走权限校验最后生成或更新一个 Change。对应地refs/heads/*是真实分支的命名空间。只有拥有 Submit 权限的人才会通过 Gerrit 界面把 Change 合入refs/heads/master。平时开发者是不应该直接 pushrefs/heads/master的除非权限里允许直接推送。实际使用中我见过有人图省事直接 push 到 refs/heads 绕过审查结果全局的 Push 权限又没锁死。这种属于权限配置事故后面我会在权限部分专门讲怎么看规则。2.3 认证方式选择与初始配置Gerrit 的认证方式在$GERRIT_SITE/etc/gerrit.config里配置。常见的有 OpenID、LDAP、OAuth 和 HTTP。对内部团队来说LDAP 和 HTTP Header 认证最常用。如果只是本地试验可以直接用默认的 OpenID 模式并在etc/secure.config里临时打开auth.gitBasicAuth方便用账号密码做 Git 操作。初始化一个 Gerrit 站点的命令通常长这样java -jar gerrit.war init --batch -d review_site然后把 Git 仓库目录、监听端口、数据库连接都按实际环境调整好。这里我提醒一句init过程中它会问很多问题包括是否使用 H2 默认库、是否启用 SSH 端口、是否要配置 SMTP。不要全按默认一路回车SMTP 对 Gerrit 很重要因为有些功能比如注册、密码找回、评审邮件通知需要邮件服务。如果一开始没配后面可以改etc/gerrit.config再重启但不如一次性配清楚。初始化完成后默认是没有任何项目和用户的。此时你可以用管理员权限创建一个名为All-Projects的父项目它会作为所有其他项目的权限继承根节点。实际上init会自动建出All-Projects和All-Users两个项目但业务项目的创建还是需要手动做。3. 实操过程与核心环节实现进入真正的实操环节。从安装完成到项目正式跑起来我按“管理员视角”和“开发者视角”两条线来讲。管理员负责建项目、配权限、接 CI开发者负责日常推代码、更新补丁、处理冲突。3.1 创建项目与初始权限配置在 Gerrit Web 界面里进入 Browse - Projects - Create New Project。项目名建议用组名/模块名的格式例如backend/user-service这样权限继承和分组管理都清晰。创建项目时必须注意选择继承项目时如果没有特别需求继承All-Projects即可。如果你的项目需要把稳定分支保护起来可以在创建时就选Create initial empty commit。这个空提交的存在很有用可以避免某些 Git 工具处理无 HEAD 仓库时出问题。项目创建后如果开发者以 SSH 方式访问形式是这样的git clone ssh://用户名服务器:端口/项目名比如我本地常用的是git clone ssh://admin192.168.1.100:29418/backend/user-service注意这里端口不是常用的 22而是 Gerrit 专属 SSH 端口默认 29418。如果你用 HTTP则直接是http://服务器:8080/项目名。权限配置的核心入口在项目设置页的 Access 选项卡里。Gerrit 的权限规则是基于 ref 名字来限定的。比如我们要让backend/user-service项目下的所有普通开发者都能推送评审但不能直接合入可以这样建在refs/heads/*上给Registered Users添加Push权限值为refs/for/*。给Project Owners添加Code-Review 2和Submit权限。Verified标签权限默认只给 CI 机器人。实际配置界面里选权限名、组名、引用名确实有点像发牌照。你可以把规则理解成白名单所有操作默认都是拒绝的加上一条规则才放通一类操作。这里有一个容易被坑的地方Gerrit 的权限是从父项目继承的。如果你发现某个人明明没在子项目里配置权限却可以执行某项操作大概率是All-Projects里放宽了。反过来如果你想给某个项目单独收紧权限也要小心继承来的规则可能反而更宽。3.2 开发者提交代码从 clone 到 push review一个标准的 Gerrit 提交流程可以拆成下面几步clone 仓库。安装 commit-msg 钩子这个钩子会自动为提交信息加 Change-Id。新建特性分支并在分支上提交。push 到refs/for/目标分支。在 Gerrit 网页上查看 Change。根据评审意见修改后 amend 提交再次 push。评审通过后合入。第一步和第二部通常可以合并。克隆后执行一次gitdir$(git rev-parse --git-dir) curl -o ${gitdir}/hooks/commit-msg http://服务器:8080/tools/hooks/commit-msg chmod x ${gitdir}/hooks/commit-msg跳过钩子会导致提交信息里没有Change-IdGerrit 会直接拒绝这次推送。错误信息是! [remote rejected] HEAD - refs/for/master (missing Change-Id in commit message)这是最常见的第一个坑我建议第一次用 Gerrit 的团队每个人都认一眼这个报错因为后面还会遇到很多类似“格式合法但语义不满足”的拒绝。接下来创建特性分支并提交git checkout -b feature/payment-timeout git add . git commit -m fix(payment): adjust timeout logic这里的习惯是只要本地提交一次就够了不要养成频繁git commit --amend之外再叠一堆小提交的毛病因为 Gerrit 的 Change 是围绕一个提交设计的。你如果连续做多个本地提交再推Gerrit 会分别生成多个 Change反而不利于一次评审一个完整功能。推送命令git push origin HEAD:refs/for/master如果你想让评审人去推而不是用自身的信息也可以在 push 时带一个 topic 组git push origin HEAD:refs/for/master%topicpayment-timeout这个%后面还可以跟很多参数比如wip表示工作进展notifyNONE表示不发邮件通知。但一般情况下只推荐用 topic其他参数新手期少用。如果你本地有多个提交要合到同一个 Change 里通常需要先git rebase -i把它们揉成一个再 amend。Gerrit 界面里也有一个 “Squash” 功能但不同版本的实现程度不一样我习惯在本地处理干净再推上来。评审意见回来后修改代码后重新推送时不要新增一个fix review提交而是用git add . git commit --amend git push origin HEAD:refs/for/master这样推送后原 Change 会新增一个 Patch Set并且 Web 界面上的评论历史还在。千万千万不要在新提交基础上直接再 push那样 Gerrit 会为一个小意见开出一个新 Change评审人会抓狂。3.3 解决合并冲突的正确姿势多人在同一个分支上工作时冲突几乎避不开。Gerrit 检测到 Change 与目标分支的最新提交冲突时Change 页面上会提示“Cannot Merge”。这时候有两条路第一条是点界面上的 Rebase 按钮让 Gerrit 自动尝试 rebase 到目标分支。如果 Gerrit 自己能解决它会生成一个新的 Patch Set审查流程继续。第二条是手动在本地处理git fetch origin git rebase origin/master # 手动解决冲突 git add . git commit --amend --no-edit git push origin HEAD:refs/for/master这里必须注意如果当前 Change 里有多个 Patch Set你手动 rebase 后的提交仍然是同一个Change-Id所以 Gerrit 会把新版本追加为最新的 Patch Set不会变成一个新的 Change。另一种常见的处理方式是去 Gerrit 网页上复制git fetch命令。比如 Change 编号是 12345Patch Set 是 4那么可以执行git fetch origin refs/changes/45/12345/4 git cherry-pick FETCH_HEAD这种 fetch 出来的引用放在refs/changes命名空间里专门用来手工拉取一个具体的补丁集。实际用中它还有一个好处当你手里没有本地中间版本时可以直接把网上的某个 Patch Set 拉到本地来做修改。3.4 评审与合入审查者的操作管理员把权限配好之后开发者的提交会出现在项目页面的 “Open Changes” 列表里。审查者点进 Change 后可以按行评论、回复评论、给分数。分数标签常见的是Code-Review人工审查。范围从 -2 到 22 表示明确认可。Verified由 CI 或测试人员给范围 -1 到 11 表示已验证通过。要合入一个 Change通常需要得到 2 和 1然后有 Submit 权限的人点击 Submit。Submit 不是开一个计时器等通知而是真正的 merge 操作。Gerrit 会根据项目的合并策略把提交合到目标分支。这里我见过很多团队把 Submit 权限只给少数维护者代码质量确实控制得住但流程也可能变得很慢。建议至少给每个子团队两名负责人 Submit 权限避免因为一个人出差导致整个项目合并停摆。再提一个细节如果 Change 被 Submit 后发现有问题Gerrit 不会回滚。你需要直接通过一个新的 Change 来修复。所以团队里最好约定那些“先合一个紧急修复”的请求至少要让一个人快速 review 而不是直接绕过流程。4. 常见问题与排查技巧实录这一部分是我最想写的因为 Gerrit 的报错信息虽然还算清楚但它们背后的原因往往不是表面那么简单。整理了一个速查表再挑几个高频问题展开讲。现象常见原因处理建议push 被拒提示 missing Change-Id没装 commit-msg 钩子安装钩子后 amend 提交信息push 被拒提示 no new changes提交内容和上次完全一样amend 后重新修改或换新分支push 被拒提示 branch not found目标分支不存在确认项目分支名权限里放开该 refchange 一直处于 merge conflict与目标分支最新提交冲突界面 Rebase 按钮或本地 rebase 后重推找不到某个 Change 的编号用的老 Patch Set 编号看错了到网页的 Changes 列表里按摘要搜索某个开发者能直接推 refs/heads权限里给了全量 Push给普通用户只开放 refs/for/*通过 HTTP 推送提示无法认证没启用 gitBasicAuth在 secure.config 配置或改用 SSH4.1 为什么明明改了代码push 还是被拒很多人经常遇到一种情况我git commit --amend改完了重新 push结果系统还是报 “no new changes”。原因通常是你的 amend 实际上什么都没改或者改的内容和上一个 Patch Set 完全一模一样。Gerrit 认为既然代码一模一样那就不算新补丁自然拒绝。解决办法很朴素改完代码后先本地检查一下git diff和提交内容确认确有差异再推。这不是什么高深的坑但团队里至少一半人踩过。还有另一种情况你改了代码但提交信息没更新而 Gerrit 的提交内容只认提交哈希如果哈希没变它就觉得你没有提交新版本。注意--amend之后哈希一定变化但如果你是用git commit --fixup之类的操作没揉干净也可能出现提交树上多了东西实际上 Push 的还是旧状态。4.2 权限配置后没有生效Gerrit 权限的继承和优先级规则比较绕。默认情况下子项目的精确 ref 规则会覆盖父项目的同名规则但你如果一不小心把All-Projects里的某个组的权限加得太宽子项目再怎么收紧也可能被父项目放开。有一次我给某个项目设了普通用户只能通过refs/for/*推送结果测试时发现开发者还是直接 push 到了refs/heads/master。查了一圈发现All-Projects里有一条refs/heads/*的 Push 权限给了Registered Users而且优先级是BLOCK还是ALLOW我又没注意。其实父子项目的规则不是简单的“谁更具体听谁的”还要看规则类型。排查时打开项目的 Access 页面有个 “Effective Access” 预览功能能看到某个用户/组在该项目下的最终权限集合。先把这条链路看明白再动手改规则能省很多时间。4.3 用 SSH 操作时频繁要求输密码Gerrit 最常见的 SSH 连接方式是密钥认证但很多第一次用的人只把公钥粘到了用户设置页却没有检查本机私钥是否加载到 ssh-agent导致每次执行git push都要输一遍密码。尤其是像 Windows 的多用户环境密钥权限和 agent 服务问题都会造成认证失败。建议这样处理ssh-keygen -t ed25519 -C 你的邮箱 -f ~/.ssh/id_ed25519 cat ~/.ssh/id_ed25519.pub把公钥内容复制到 Gerrit 的 SSH Keys 列表里然后本地测试ssh -p 29418 用户名服务器如果连接成功回显有欢迎信息那就说明 key 没问题。如果出现Permission denied (publickey)先去查 agent 是否加载了私钥ssh-add -l另外需要注意 Gerrit SSH 的端口不是默认 22你需要在 clone 地址里显式写端口或者在~/.ssh/config里给这个 Host 配置端口否则会一直超时。4.4 大量历史提交需要补充 Change-Id团队迁移到 Gerrit 时经常会碰到一批存量分支它们本地的提交信息里都没有 Change-Id。如果你直接把整批分支推到refs/for上就会整批被拒。Gerrit 提供两种办法。一种是每个提交手动 amend 一行Change-Id几十个的话很痛苦。另一种是用消息重写的方式git filter-branch --msg-filter cat; echo; echo Change-Id: Ixxxx -- --all不过更推荐的是用 Gerrit 自带的gerrit-cherry-pick脚本或者把旧分支先推成一个临时分支再逐条 cherry-pick。反正不管哪种方式重点都是让每个提交都带一个稳定的 Change-Id。实际操作里我建议在仓库初始化阶段就直接给所有开发者统一推 commit-msg 钩子的脚本否则每次新人进来都要手工装一遍漏装一次就是一条报错。4.5 与 Jenkins 集成时 Verified 标签不刷新很多团队把 Gerrit 和 Jenkins 接起来做自动构建。Jenkins 通过事件触发构建后会把结果写回 Change但如果 Jenkins 的账户在 Gerrit 里分配的权限不对或者 Job 配置的 ref 监听有问题那么 Change 页面上的 Verified 标签一直是空的。排查思路分三步第一确认 Jenkins 对应的 Gerrit 账户有Verified标签的1权限。第二确认 Jenkins 的 webhook 或事件监听配置正确。Gerrit 插件常用的是com.googlesource.gerrit.plugins.events或者直接通过 REST API 查询不同版本方案不同。第三如果发现 Jenkins 显示构建成功但 Gerrit 的 Verified 一直是 0很可能是 Jenkins 写回时用的 Change 编号不对。最稳定的做法是把 Change 号、Patch Set 号都从 Gerrit 的 Trigger 触发参数里读不要自己拼。5. 深度使用建议与体验总结写完上面的流程我觉得有必要再聊聊这个工具的适用边界和长期使用体会。在团队规模小、节奏特别快、且大家都能自觉遵守规范的时候Gerrit 确实显得“重”因为它强制每个 commit 都走评审。但在稍微正式一点的项目里哪怕只有两三个协作方严格评审带来的收益都会迅速超过流程摩擦成本。尤其是当你要为外部贡献者开放提交通道时Gerrit 可以做到别人只能推送refs/for/*连分支名都不能乱建这种可控性对项目维护者来说是很踏实的。我个人的实操心得还有几条第一不要把 Gerrit 的 Change 当 GitHub PR 来用。Gerrit 的粒度是“一个提交”不是“一个分支”。你准备把它推成一次评审时提交信息里就承载着完整改动说明所以写清楚 commit message比在网页上补充长篇描述更重要。第二善用 Dashboard。Gerrit 的 Web 界面里为每个用户提供了动态 Dashboard可以看到别人指派给你的待办评审、你自己参与的 Open Changes 等。我平时上班打开第一件事就是刷一遍我的 Dashboard比自己记住哪些 Change 待处理要高效得多。第三分支和权限规则尽量保持简单。曾经有一段时间我为了精细管控给每个模块单独建一个权限规则结果后来连管理员看着都头大。Gerrit 的权限系统灵活度很高但很多场景下一个“主干全保护 功能分支随意 refs/for 推流”的粗粒度配置就足够用了。最后再分享一个小技巧如果要批量拉取并检查多个 Change可以用git review这个第三方命令行工具。它是对 Gerrit 工作流的封装通过简单的git review -d 12345就能把远程某个 Change 拉下来并切换到对应提交省去手拼 refs/changes 路径的麻烦。当然前提是你已经理解了底层机制否则遇到报错还是得绕回原生 Git 命令来排查。Gerrit 的学习曲线确实比 GitHub 陡但这个陡峭的过程恰恰会逼着团队把代码审查本身变成一项正式活动。只要团队愿意投入几天时间去适应这种“先评审后合入”的节奏你会发现它带来的收益远超付出的成本。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑