简介从零搭建基于Git、Repo与Gerrit的代码评审服务器适合需要自建代码托管与评审环境的开发、运维人员。资源以一份docx文档呈现仅1个文件包体大小294KB便于随时查阅。文档内容覆盖Git、Gitosis、Repo、Gerrit的完整部署流程包括系统用户创建、公钥配置、gitosis-init初始化、git-daemon启动、manifest仓库与default.xml配置等关键环节并针对克隆失败、python setup.py安装失败等典型问题给出备选方案。同时梳理了服务器端与客户端的分工以及添加组成员、配置权限的具体操作。该资源已有3134人学习浏览对于想脱离第三方平台、搭建内部代码评审服务器的团队具有较高参考价值。1. 为什么要搭 gitrepogerrit 这套代码服务器而不是只装个 Git一个二十多人的 Android 研发组最怕的不是代码写不出来而是某天下午 master 分支被一次无脑 push 覆盖得无声无息。gitrepogerrit 代码服务器搭建本质不是「装一个 git 仓库」这么简单而是搭一套「多人写代码、代码先评审、仓库能批量管理」的提交流水线git 管版本repo 管几十上百个仓库的清单Gerrit 管代码评审、权限控制和合入门禁。适合做固件、BSP、系统应用的团队也适合所有被直推 master 坑过的中小开发组。这套方案不需要买商业授权一台 4 核 8G 的虚拟机就能跑起来。下面按「装起来、连得上、评得了、不翻车」的顺序把整条路走一遍。2. 装 Gerrit 前先想清楚它到底是个什么形态的服务2.1 为什么直接跑 war 包而不是从源码编译Gerrit 是一个 Java 写的应用服务器官方发布物就是一个 war 包自带 Web 界面、SSH 服务和内置数据库。常见做法是下载编译好的 war 包直接跑不要从源码编译——源码编译要装 Bazel、拉一堆依赖跟你要解决的事没关系。我一般会把 war 包放在 /opt 下站点数据单独放 /data/gerrit_site这样升级时只换 war 包、不动数据目录。选版本先看 JDKGerrit 3.x 系列跑在 JDK 11 上3.7 之后支持 JDK 17老项目如果还在 Gerrit 2.16必须配 JDK 8。别用高版本 JDK 跑老版本启动会直接报 UnsupportedClassVersionError这个错误几乎可以断定是 JDK 和 Gerrit 版本不匹配。另外服务器上还要装系统 git 和 python3repo 客户端脚本后面会用到sudo apt update sudo apt install -y openjdk-11-jdk git python3 java -version git --version这里的 openjdk-11 是 Gerrit 3.5 周边最常见的搭配。git 装系统版是给 Gerrit 的 Git 协议调用用的python3 是给开发者机器上跑 repo 做准备服务器本身不执行 repo。2.2 初始化站点交互式问答里最容易埋坑的几项初始化 Gerrit 只需要一条命令但后面的交互式问答才是真正的考场mkdir -p /data/gerrit_site cd /data/gerrit_site java -jar /opt/gerrit-3.5.1.war init -d /data/gerrit_siteinit 命令会问你一系列问题我只说三个最关键的。第一个是数据库新手直接回车用默认的 H2不要一上来就配 MySQL——Gerrit 的 ReviewDB 用 H2 足够支撑几十人的团队MySQL 是给几百人规模加归档查询用的。第二个是认证方式这个问题最悬下面单独讲。第三个是邮箱发送先填一个能用的 SMTP 服务器哪怕是公司内部邮箱也行否则后面创建 Change 时邮件通知全哑火。初始化完成后站点目录里会生成 etc/gerrit.config、git/ 和 logs/ 三个关键路径。git/ 是 Gerrit 自管仓库的存放处所有通过评审合入的提交最终落在这里logs/ 里的 error_log 是后面排障的第一入口。2.3 认证选型新手用 Become正式环境用 LDAPGerrit 的认证模式常见有三种选错会在登录页卡很久。DEVELOPMENT_BECOME_ANY_ACCOUNT是测试模式登录页出现一个「Become」按钮点一下选个名字就登录了适合第一次搭好验证流程。HTTP模式是把登录交给前面的反向代理由 nginx 或 Apache 负责弹认证框然后通过 HTTP 头把用户名传给 Gerrit这种模式配置成本最高新手不建议直接用。LDAP模式是企业里最常见的选择账号密码统一走公司 LDAPGerrit 自己不存密码。我一般这样搞第一次初始化用 Become 模式把整条流水线跑通注册好各人账号之后再把认证切到 LDAP。切换时需要手动改 /data/gerrit_site/etc/gerrit.config改完重启 Gerrit[auth] type LDAP gitBasicAuth true [ldap] server ldap://ldap.example.com accountBase ouPeople accountPattern (uid${username})这里的 gitBasicAuth 要解释一下它让 git push 走 HTTP 协议时也能用 LDAP 账号密码认证。后面配置 repo 时如果不想折腾 SSH 密钥可以用这个方式让 git 直接弹密码。但注意repo 是非交互式的不会等你输密码所以 repo 场景下还是 SSH 密钥更省事这点在第 3 章会踩到。3. 客户端接入从 git 安装到 repo 同步先过 SSH 认证这关3.1 先让 git 认识你是谁全局配置与 SSH 公钥生成服务端搭好只是第一步开发者机器上的 git 如果不认 Gerrit后面全是「git requires authentication, but repo cannot perform interactive authentication」这类幺蛾子。客户端这边先把 git 装好然后做全局配置git config --global user.name zhangsan git config --global user.email zhangsanexample.com ssh-keygen -t ed25519 -C zhangsanexample.com -f ~/.ssh/id_ed25519这里有个很多人第一次接触 Gerrit 时翻车的点习惯了自己搭 git 服务器用 ssh-copy-id 往 authorized_keys 里塞公钥但 Gerrit 不吃这一套。Gerrit 的公钥入口在网页端个人 Settings → SSH Keys 里登录后把自己的公钥粘进去。另外注意上传的是id_ed25519.pub的内容不是id_ed25519私钥一旦泄露相当于把代码仓库钥匙交给了别人。git 全局配置里的 user.name 和 user.email 不只是提交署名用Gerrit 还要拿它与 LDAP 账号和邮箱做匹配校验。团队里常见错误是有人把 user.email 配成了私人邮箱导致 Gerrit 不认这个人提交到了评审队列却找不到 reviewer。3.2 处理「ssh 认证失败」29418 端口和 ~/.ssh/config 是解药Gerrit 的 SSH 服务端口默认是 29418不是 22。很多新人 git clone 报Permission denied (publickey)时反复检查密钥就是没注意端口。我把常用连接参数固化到 ~/.ssh/config 里一劳永逸Host gerrit HostName 192.168.10.20 Port 29418 User zhangsan IdentityFile ~/.ssh/id_ed25519 IdentitiesOnly yes配置好后用ssh gerrit验证看到Welcome to Gerrit就通了。这里 User 必须是 Gerrit 里的登录用户名不是机器上的系统用户。IdentitiesOnly yes 是防止 ssh 把本机其他私钥挨个试一遍导致被服务端拒绝。git clone 时直接用别名git clone ssh://gerrit/MyProject如果你在 repo sync 时碰到git requires authentication, but repo cannot perform interactive authentication几乎可以断定 manifest 里配的 url 是 http/https 方式而 repo 又不支持弹出密码框。解法有两个一是把 manifest 里的 remote url 改成 ssh:// 形式二是保留 https但在 ~/.netrc 里写清楚机器名、用户名、密码。我推荐前者SSH 密钥免密才是 Git 服务最省心的形态这正好对应「git 免密」的诉求。3.3 repo init 与 repo syncmanifest 决定了你会拉到什么repo 是 Google 出的一个 Python 脚本它不是包管理器而是「仓库清单执行器」。它靠一个单独的 manifest 仓库来声明要拉取哪些子仓库、各自在哪个分支。搭建服务器时通常要手动建一个 manifest 仓库里面放 default.xmlrepo init -u ssh://gerrit/manifest -b master repo sync -c -j8-u指定 manifest 仓库地址-b指定 manifest 本身的分支repo sync才是真正批量克隆全部代码的动作。-c表示只同步当前分支Android 全仓源码动辄几十 GB不带 -c 会把 manifest 里声明的所有分支都拉下来磁盘瞬间爆炸。-j8是并发数8 到 16 之间要看服务器 CPU 核数和磁盘 IO机械盘开 16 并发会被 IO 拖死。repo 同步时报fatal: Not a git repository也是高频问题原因是你在 repo 工程根目录直接执行了 git 命令。repo 的根目录不是普通 git 仓库根目录下只有 .repo 这个元数据目录真正的 git 仓库分散在各个子项目里。要操作某个具体项目先 cd 进那个有 .git 的目录要批量操作全部项目用repo forall -c git status。这个细节放第 5 章再展开。4. 代码评审流refs/for、Change-Id 和权限模型4.1 为什么 git push origin master 会被拒Gerrit 和 GitLab 最大的不同是它默认不允许直接往分支推代码。svn 时代那种「提交即入库」的思维在这里要彻底扔掉。Gerrit 的逻辑是推送代码时推到一个虚拟的refs/for/引用上Gerrit 拦截这个动作把提交挂到评审页面里经过 Review 2 和 Submit 两道关卡后才真正合入目标分支。git push origin HEAD:refs/for/master这条命令的意思是「把我本地当前分支的 HEAD 提交作为一次评审提交送到 master 的评审队列」。Gerrit 收到后会在 Web 界面生成一个 Change编号类似 12345评审人可以对这个 Change 打分。如果你想更新这次评审的代码不是再 push 一次而是本地修改后git commit --amend再 push 同一个 refs/for/masterGerrit 会把新补丁挂到同一个 Change 下面评审意见和打分都保留。如果谁执行了git push origin master会被 Gerrit 默认权限拦下来提示没有权限直接更新 refs/heads/master。这不是 bug是 Gerrit 的保护机制。明白了这一点很多「Gerrit 为什么不能直接 push」的疑问就迎刃而解。4.2 装好 commit-msg 钩子否则永远缺 Change-Id刚用 Gerrit 的人经常遇到missing Change-Id in commit message的报错原因很简单Gerrit 要求每个提交消息里带一个 Change-Id用来把多次补丁串成同一个评审。这个 Change-Id 不是人写的是 git 钩子自动生成的。服务器上已经自带这个钩子客户端拉取一次scp -p -P 29418 zhangsangerrit:hooks/commit-msg .git/hooks/ chmod x .git/hooks/commit-msg这里的-P 29418是 scp 指定 SSH 端口注意 scp 用大写 Pssh 用小写 p大小写搞反会报错。执行完成后在本仓库里第一次 commit 时钩子会自动在提交消息里插入一行Change-Id: I开头、后面跟 40 位十六进制字符的标记。如果之前已经提交过、Change-Id 没生成需要git commit --amend补一次让钩子把 Change-Id 插进去然后再 push。这里顺带说下git commit --amend的正确用法amend 会改写上一次提交但 Change-Id 只要钩子在它就不会变动所以评审中途改代码用 amend 非常顺手。切记不要把 Change-Id 手动删掉删了 Gerrit 会当成一个新 Change 处理。4.3 项目级权限给不同角色开不同的门Gerrit 的权限是分层配的All-Projects 是全局基座每个具体 Project 可以继承并覆盖。默认模型下普通开发人员只有推 refs/for/ 的权限没有直推 refs/heads/ 的权限要有人负责最终合入得有 Submit 权限。一套比较常见的权限分配如下角色refs/for/refs/heads/*refs/heads/*说明普通开发Push无提交评审等别人 Review技术负责人PushPush可以直接保分支也负责 SubmitCI 机器人Push无自动验证打 Verified 1权限页面在 Project 的 Access 配置里改改完保存即生效不需要重启 Gerrit。这里容易被忽略的是 Verified 和 Code Review 是两套标签CI 打的往往是 Verified 1人打的才是 Code Review 2两个都满足才能 Submit。新手搭完发现「Review 2 了还是合不进去」多半是 Verified 没人打或者没配 CI。5. 搭建好后最容易踩的五个坑现象、原因、解法5.1 8080 端口开了但浏览器就是进不去现象服务器本机 curlhttp://localhost:8080有响应换了台电脑用 IP 访问就超时。原因Gerrit 的 Web 端口是 8080但很多人只放行了这个端口忘了 SSH 评审流还需要 29418。而且云服务器除了系统防火墙安全组也要同步放行。解决先看监听再放端口最后用 telnet 验证。ss -lntp | grep 8080 sudo firewall-cmd --permanent --add-port8080/tcp sudo firewall-cmd --permanent --add-port29418/tcp sudo firewall-cmd --reload如果用的云主机控制台安全组里也要加上这两条规则。Gerrit 是有 Web 界面的代码服务器端口放行这类基建问题看似不起眼实际是「搭好了没人连得上」的最大元凶。5.2 fatal: not a git repositoryor any of the parent directories现象在 repo 同步下来的工程根目录跑git fetch origin直接报这个错。原因repo 工程根目录不是 git 仓库根目录下只有 .repo 元数据目录真正的 .git 在 platform/xxx 这些子项目里。git 命令向上找不到 .git 目录自然会拒绝执行。解决进入具体项目目录再执行 git 命令。如果想批量看所有项目的状态用 repo 自己的命令repo forall -c git status -s这条命令的意思是遍历 manifest 里的每个项目分别执行git status -s。后面第 6 章还会用 repo forall 做仓库健康检查这是 repo 工程下最实用的维护姿势。5.3 repo「汉化」和中文乱码不是一回事现象repo sync 输出全英文团队里有新人看不懂git status 显示中文文件名变成\345\233\276\347\211\207.png这种八进制转义。原因repo 是 Google 的开源脚本没有官方中文语言包而 git 默认对非 ASCII 文件名做了转义防止终端乱码。解决中文文件名转义关掉一个配置就够git config --global core.quotepath false至于 repo 汉化我的做法是不去改 repo 源码而是给常用命令做中文 alias 包装。比如在 ~/.bashrc 里定义alias rsecho 开始同步 repo sync -c -j8团队成员执行 rs 就能看到中文提示git 命令同理。给团队做一层薄薄的封装比追着上游汉化包靠谱得多。5.4 评审邮件发不出去SMTP 参数之间的玄学现象创建 Change 成功评审页面上也有记录但相关同事收不到通知邮件。原因Gerrit 默认不启用邮件通知或者 SMTP 端口和加密方式不匹配。很多公司内网邮箱的 25 端口被禁要用 465 或 587。解决改 /data/gerrit_site/etc/gerrit.config 的 sendemail 段[sendemail] smtpServer smtp.example.com smtpServerPort 465 smtpEncryption ssl from Code Review reviewexample.com改完重启 Gerrit 再创建一次 Change 测试。如果公司邮箱用的是自签名证书还要加一行sslVerify false。测试邮件时建议直接看 logs/error_log里面有具体的 SMTP 报错原因比盲试参数快。5.5 服务器重启后 Gerrit 就没了现象机房断电或者例行重启后ps 里看不到 java 进程了。原因Gerrit init 默认只生成 gerrit.sh 启动脚本不会注册任何开机启动项。我见过不少团队把java -jar gerrit.war daemon挂在前台终端里跑SSH 一断服务就没了。解决用 systemd 托管第 6 章给完整配置。这里先说排查思路先systemctl list-units | grep gerrit看有没有 service没有就说明当初只是手工起的。搭好的服务器务必在一开始就处理掉这个问题否则等真重启一次才发现全团队都在等代码评审入口恢复。6. 让搭建结果活过试用期systemd 托管与批量健康检查6.1 用 systemd 把 Gerrit 变成常驻服务第一次搭好的 Gerrit 很容易死在「手工启动」这个习惯上。我的做法是直接交给 systemd 托管加一个重试策略进程挂了能自动拉起[Unit] DescriptionGerrit Code Review Afternetwork.target [Service] Typesimple Usergerrit Groupgerrit WorkingDirectory/data/gerrit_site ExecStart/usr/bin/java -jar /opt/gerrit-3.5.1.war daemon -d /data/gerrit_site --console-log Restarton-failure RestartSec10 EnvironmentJAVA_HOME/usr/lib/jvm/java-11-openjdk-amd64 [Install] WantedBymulti-user.target把这段写到 /etc/systemd/system/gerrit.service然后执行sudo systemctl daemon-reload sudo systemctl enable gerrit sudo systemctl start gerrit sudo systemctl status gerrit注意这里用了一个叫 gerrit 的系统用户来跑服务不是 root。需要提前创建用户并把 /data/gerrit_site 的属主改成它sudo useradd -m gerrit sudo chown -R gerrit:gerrit /data/gerrit_site这样处理后Gerrit 每次开机自启崩溃 10 秒后自动重连升级 war 包时也只要替换文件再 restart 服务即可。6.2 批量验证几十个仓库repo forall 的正确用法代码服务器跑久之后仓库对象会膨胀、孤儿对象会堆积。用 git 命令一个个进目录检查不现实repo 工程下正确的姿势是 repo forallrepo forall -c echo $REPO_PROJECT git fsck --no-dangling$REPO_PROJECT是 repo 注入的环境变量代表当前遍历的项目名。git fsck --no-dangling检查对象库完整性不报告悬空对象dangling 对象是正常现象不用吓自己。如果哪条输出里出现 corruption 字样再单独进那个目录用git count-objects -vH看仓库体积决定要不要 gc。另外还有一个安全习惯代码服务器的 Web 目录不要把裸仓库直接丢进去如果配了 nginx 反代 Gerrit记得禁止外部访问 .git 路径防止 git 目录泄露。检查一下反代配置里有没有针对/\.git的 deny 规则即可。6.3 我的习惯一周看一次日志把验证做进日常搭建工作收尾时我会建立三个固定动作每周看一次 /data/gerrit_site/logs/error_log 里的 ERROR 行每两周跑一遍上面说的 repo forall fsck每次升级 Gerrit 前先把 /data/gerrit_site 整个打包备份。这套组合下来绝大多数问题都能在爆发前被拦住。回头看整个 gitrepogerrit 代码服务器搭建真正的难点不在安装命令而在认证选型、SSH 端口、refs/for 评审流和 systemd 托管这些容易想当然的地方。我早期帮团队搭第一台 Gerrit 时就在 HTTP 认证上卡了两天后来才明白它必须配合反向代理才能用还有一次因为没注册开机启动机房一重启全组干等两小时这种经历一次就够长记性。希望这篇笔记能让你一次走通少熬几个夜。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?