092830_Git权威指南-第5篇-第32章-Gerrit
第32章Gerrit 代码审核服务器
⾕歌 Android 开源项⽬在 Git 的使⽤上有两个重要的创新,⼀个是为多版本库协同⽽引⼊的repo,这在前⾯第25章已经详细讨论过。另外⼀个重要的创新就是 Gerrit ——代码审核服务器。Gerrit 为 Git 引⼊的代码审核是强制性的,也就是说除⾮特别的授权设置,向 Git 版本库的推送必须要经过 Gerrit 服务器,修订必须经过代码审核的⼀套⼯作流之后,才可能经批准并纳⼊正式代码库中。
⾸先贡献者的代码通过 git 命令(或 repo 封装)推送到 Gerrit 管理下的 Git 版本库,推送的提交转化为⼀个⼀个的代码审核任务,审核任务可以通过refs/changes/下的引⽤访问到。代码审核者可以通过 Web 界⾯查看审核任务、代码变更,通过 Web
界⾯做出通过代码审核或打回等决定。测试者也可以通过refs/changes/之下的引⽤获取修订然后对其进⾏测试,如果测试通过就可以将该评审任务设置为校验通过(verified)。最后经过了审核和校验的修订可以通过Gerrit 界⾯中的提交动作合并到版本库对应的分⽀中。
Android 项⽬⽹站上有⼀个代码贡献流程图1,详细地介绍了 Gerrit 代码审核服务器的⼯作流程。翻译后的⼯作流程图见图32-1。
1 www.doczj/doc/0f8cd81e580216fc700afd37.html /source/life-of-a-patch.html
图32-1:Gerrit 代码审核⼯作流
32.1Gerrit 的实现原理
Gerrit 更准确地说应该称为 Gerrit2。因为 Android 项⽬最早使⽤的评审服务器 Gerrit 不是今
天这个样⼦的。最早版本的 Gerrit 是⽤ Python 开发运⾏于 Google App Engine 上的,从 Python 之⽗ Guido van Rossum 开发的 Rietveld 分⽀⽽来。在这⾥要讨论的 Gerrit 实为 Gerrit2,是⽤Java 语⾔实现的1。
1.SSH 协议的 Git 服务器
Gerrit 本⾝基于 SSH 协议实现了⼀套 Git 服务器,这样就可以对 Git 数据推送进⾏更为精确的控制,为强制审核的实现建⽴了基础。
Gerrit 提供的 Git 服务的端⼝并⾮标准的 22 端⼝,默认是 29418 端⼝。这个端⼝是可以被发现的,当访问 Gerrit 的 Web 界⾯时可以得到这个端⼝。对 Android 项⽬的代码审核服务器,访问
www.doczj/doc/0f8cd81e580216fc700afd37.html /ssh_info就可以查看到 Git 服务的服务器域名和开放的端⼝。下⾯⽤ curl 命令查看⽹页的输出。
$ curl -L -k www.doczj/doc/0f8cd81e580216fc700afd37.html /ssh_info
www.doczj/doc/0f8cd81e580216fc700afd37.html 29418
2.特殊引⽤ refs/for 和 refs/changes
Gerrit 的 Git 服务器,禁⽌⽤户向refs/heads命名空间下的引⽤执⾏推送(除⾮特别的授权),即不允许⽤户直接向分⽀进⾏提交。为了让开发者能够向 Git 服务器提交修订,Gerrit 的Git 服务器只允许⽤户向特殊的引⽤refs/for/下执⾏推送,其中
即为开发者的⼯作分⽀。向refs/for/命名空间下推送并不会在其中创建引⽤,⽽是为新的提交分配⼀个 ID,称为 review-id ,并为该 review-id 的访问建⽴如下格式的引⽤refs/changes/nn//m,其中:
review-id 是 Gerrit 为评审任务顺序⽽分配的全局唯⼀的号码。
nn 为 review-id 的后两位数,位数不⾜⽤零补齐。即 nn 为 review-id 除以 100 的余数。
m 为修订号,该 review-id 的⾸次提交修订号为1,如果该修订被打回,重新提交修订号会⾃增。
1 www.doczj/doc/0f8cd81e580216fc700afd37.html /p/gerrit/wiki/Background
3.Git 库的钩⼦脚本 hooks/commit-msg
为了保证已经提交审核的修订通过审核⼊库后,如果被别的分⽀拣选(cherry-pick)后再推送⾄服务器时不会产⽣新的重复的评审任务,Gerrit 设计了⼀套特殊的⽅法,即要求每个提交在提交说明中包含 Change-Id 键值对作为标签,该标签在⾸次⽣成时使⽤特殊的哈希算法以保障其唯⼀性。执⾏拣选操作时,提交说明会被保持,即来⾃原始提交说明中的 Change-Id键值对也
会保持不变,这样当新提交推送到 Gerrit 服务器时,Gerrit 会发现新的提交包含了已经处理过的
Change-Id ,就不再为该修订创建新的评审任务和 review-id,⽽直接将提交⼊库。
为了使得 Git 提交中包含唯⼀的 Change-Id,Gerrit 提供了⼀个钩⼦脚本,将该脚本拷贝到开发者的本地 Git 库的钩⼦脚本⽬录中,即脚本⽂件.git/hooks/commit-msg。这个钩⼦脚本在⽤户提交时,⾃动在提交说明中创建 Change-Id 键值对。⾄于如何实现 Change-Id 值的唯⼀性,可以参考该脚本。
当 Gerrit 获取到⽤户向refs/for/推送的提交中包含“Change-Id: I...”的格式时,如果该 Change-Id 之前没有见过,会创建⼀个新的评审任务并分配新的 review-id,并在 Gerrit 的数据库中保存 Change-Id 和 review-id 的关联。
如果⽤户的提交因为某种原因被打回重做,开发者修改之后重新推送到 Gerrit 时就要注意在提交说明中使⽤相同的 Change-Id (使⽤ --amend 提交即可保持提交说明),以免创建新的评审任务。还要在推送时将当前分⽀推送到refs/changes///中,是为该评审任务的⼀个新的修订,其中和和之前提交的评审任务的修订号相同,则要⼈⼯选择⼀个新的修订号。
以上说起来很复杂,但是在实际操作中只要使⽤ repo 这⼀⼯具,就相对容易多了。
4.其余⼀切交给 Web
Gerrit 另外⼀个重要的组件就是 Web 服务器,通过 Web 服务器实现对整个评审⼯作流的控制。关于 Gerrit ⼯作流,请参见本章开头出现的 Gerrit ⼯作流程图。
想要感受⼀下 Gerrit 的魅⼒?请直接访问 Android 项⽬的 Gerrit ⽹站:
www.doczj/doc/0f8cd81e580216fc700afd37.html /,您会看到如图32-2的界⾯。
图32-2:Android 项⽬代码审核⽹站
点击菜单中的“Merged”即可显⽰已经通过评审且合并到代码库中的审核任务。图32-3中
显⽰的就是 Andorid ⼀个已经合并到代码库中的历史评审任务。
图32-3:Android 项⽬的 16993 号评审
从图32-3中可以看出:
URL 中显⽰的评审任务编号为 16993。
该评审任务的 Change-Id 以字母 I 开头,包含了⼀个唯⼀的 40 位 SHA1 哈希值。
整个评审任务有三个⼈参与,⼀个⼈进⾏了检查(verify),两个⼈进⾏了代码审核。
该评审任务的状态为已合并:“merged”。
该评审任务总共包含两个补丁集: Patch set 1 和Patch set 2。
补丁集的下载⽅法是:repo download platform/sdk 16993/2。
如果使⽤ repo 命令获取补丁集是⾮常⽅便的,因为封装后的 repo 屏蔽掉了 Gerrit 的⼀些实现细节,例
如补丁集在 Git 库中的存在位置。如前所述,补丁集实际保存在refs/changes命名空间下。使⽤git ls-remote命令,从 Gerrit 维护的代码库中可以看到补丁集对应的引⽤名称。
$ git ls-remote \
ssh://www.doczj/doc/0f8cd81e580216fc700afd37.html :29418/platform/sdk \
refs/changes/93/16993*
5fb1e79b01166f5192f11c5f509cf51f06ab023d refs/changes/93/16993/1
如何搭建git服务器
d342ef5b41f07c0202bc26e2bfff745b7c86d5a7 refs/changes/93/16993/2 接下来就来介绍⼀下 Gerrit 服务器的部署和使⽤⽅法。
32.2架设 Gerrit 的服务器
1.下载 war 包
Gerrit 是由 Java 开发的,被封装为⼀个 war 包:gerrit.war ,安装⾮常简洁。如果需要从源码编译出 war 包,可以参照相关⽂档1。不过最简单的就是从 Google Code 上直接下载编译好的war 包。
从下⾯的地址下载 Gerrit 的 war 包:
www.doczj/doc/0f8cd81e580216fc700afd37.html /p/gerrit/downloads/list。在下载页⾯会有⼀个⽂件名类似
2.数据库选择
Gerrit 需要数据库来维护账户信息、跟踪评审任务等。⽬前⽀持的数据库类型有 PostgreSQL、
1www.doczj/doc/0f8cd81e580216fc700afd37.html /svn/documentation/2.1.5/dev-readme.html
MySQL 及嵌⼊式的 H2 数据库。
选择使⽤默认的 H2 内置数据库是最简单的,因为这样⽆须任何设置。如果想使⽤更为熟悉的 PostgreSQL 或 MySQL,则需要预先建⽴数据库。
对于 PostgreSQL,在数据库中创建⼀个⽤户 gerrit,并创建⼀个数据库 reviewdb。
createuser -A -D -P -E gerrit
createdb -E UTF-8 -O gerrit reviewdb
对于 MySQL,在数据库中创建⼀个⽤户 gerrit 并为其设置⼝令(不要真如下⾯那样将⼝令设置为“secret”),并创建⼀个数据库 reviewdb。
$ mysql -u root -p
mysql> CREATE USER 'gerrit'@'localhost' IDENTIFIED BY 'secret';
mysql> CREATE DATABASE reviewdb;
mysql> ALTER DATABASE reviewdb charset=latin1;
mysql> GRANT ALL ON reviewdb.* TO 'gerrit'@'localhost';
mysql> FLUSH PRIVILEGES;
3.以⼀个专⽤⽤户帐号执⾏安装
在系统中创建⼀个专⽤的⽤户帐号如:gerrit。以该⽤户⾝份执⾏安装,将 Gerrit 的配置⽂件、内置数据库、war 包等都⾃动安装在该⽤户主⽬录下的特定⽬录中。
$ sudo adduser gerrit
$ sudo su gerrit
$ cd ~gerrit
$ java -jar gerrit.war init -d review_site
在安装过程中会提出⼀系列问题。
创建相关⽬录。
默认 Grerit 在安装⽤户主⽬录下创建 review_site ,并把相关⽂件安装在这个⽬录之下。
Git 版本库的根路径默认位于此⽬录之下的 git ⽬录中。
*** Gerrit Code Review 2.1.5.1
***
Create '/home/gerrit/review_site' [Y/n]?
*** Git Repositories
***
Location of Git repositories [git]:
选择数据库类型。
选择 H2 数据库是简单的选择,⽆须额外的配置。
*** SQL Database
***
Database server type [H2/?]:
设置 Gerrit Web 界⾯认证的类型。
默认为 openid,即使⽤任何⽀持 OpenID 的认证源(如 Google、Yahoo!)进⾏⾝份认证。此模式⽀
持⽤户⾃建帐号,⽤户通过 OpenID 认证源的认证后,Gerrit 会⾃动从认证源获取相关属性如⽤户全名和邮件地址等信息创建帐号。Android 项⽬的Gerrit 服务器即采⽤此认证模式。
如果有可⽤的 LDAP 服务器,那么 ldap 或 ldap_bind 也是⾮常好的认证⽅式,可以直接使⽤ LDAP 中的已有帐号进⾏认证,不过此认证⽅式下 Gerrit 的⾃建帐号功能是关闭的。此安装⽰例选择的就是 LDAP 认证⽅式。
HTTP 认证也是可选的认证⽅式,此认证⽅式需要配置 Apache 的反向代理,并在Apache 中配置 Web 站点的⼝令认证,通过⼝令认证后 Gerrit 在创建帐号的过程中会询问⽤户的邮件地址并发送确认邮件。
*** User Authentication
***
Authentication method [OPENID/?]: ?
Supported options are:
openid
http
http_ldap
ldap
ldap_bind
development_become_any_account
Authentication method [OPENID/?]: ldap
LDAP server [ldap://localhost]:
LDAP username :
Account BaseDN : dc=foo,dc=bar
Group BaseDN [dc=foo,dc=bar]:
发送邮件设置。
默认使⽤本机的 SMTP 发送邮件。
*** Email Delivery
***
SMTP server hostname [localhost]:
SMTP server port [(default)]:
SMTP encryption [NONE/?]:
SMTP username :
Java 相关设置。
使⽤ OpenJava 和 Sun Java 均可。Gerrit 的 war 包要复制到 review_site/bin ⽬录中。
*** Container Process
***
Run as [gerrit]:
Java runtime [/usr/lib/jvm/java-6-sun-1.6.0.21/jre]:
Copy gerrit.war to /home/gerrit/review_site/bin/gerrit.war [Y/n]?
Copying gerrit.war to /home/gerrit/review_site/bin/gerrit.war
SSH 服务相关设置。
Gerrit 的基于 SSH 协议的 Git 服务⾮常重要,默认的端⼝为 29418。换成其他端⼝也⽆妨,因为 repo 可以⾃动探测到该端⼝。
*** SSH Daemon
***
Listen on address [*]:
Listen on port [29418]:
Gerrit Code Review is not shipped with Bouncy Castle Crypto v144
If available, Gerrit can take advantage of features
in the library, but will also function without it.
Download and install it now [Y/n]?
Downloading www.doczj/doc/0f8cd81e580216fc700afd37.html /download/bcprov-jdk16-144.jar ...
OK
Checksum bcprov-jdk16-144.jar OK
Generating SSH host key ... done
HTTP 服务相关设置。
默认启⽤内置的 HTTP 服务器,端⼝为 8080,如果该端⼝被占⽤(如 Tomcat),则需要更换为其他端⼝,否则服务启动失败。如下例就换成了 8081 端⼝。
*** HTTP Daemon
***
Behind reverse proxy [y/N]? y
Proxy uses SSL () [y/N]? y
Subdirectory on proxy server [/]: /gerrit
Listen on address [*]:
Listen on port [8081]:
Canonical URL [localhost/gerrit]: