簡介
谷歌的 Android 開源項目在 Git 的使用上有兩個重要的創新,一個是為多版本庫協同而引入的 repo,另外一個重要的創新就是 Gerrit —— 代碼審核服務器。Gerrit 為 Git 引入的代碼審核是強制性的,就是說除非特別的授權設置,向 Git 版本庫的推送(Push)必須要經過 Gerrit 服務器,修訂必須經過代碼審核的一套工作流之后,才可能經批准並納入正式代碼庫中。gerrit目前也被用於Openstack中,用於審核Openstack項目下的各子項目。
Gerrit安裝與配置
安裝Gerrit需要裝有最低1.6版本的JDK:
apt-getinstalldefault-jre
安裝git: apt-get install git
wgethttp://gerrit.googlecode.com/files/gerrit-full-2.5.1.war
mysqlCREATEUSER'gerrit2'@'localhost'IDENTIFIEDBY'secret';CREATEDATABASEreviewdb;ALTERDATABASEreviewdbcharset=latin1;GRANTALLONreviewdb.TO'gerrit2'@'localhost';FLUSHPRIVILEGES;
addusergerrit2sugerrit
- 執行交互式配置
java-jargerrit-full-2.5.1.warinit-d~
接下來進行交互式的配置后,就完成了全部的設置。
- 啟動gerrit
sugerrit2&&cd~&&./bin/gerrit.shstart
Gerrit工作原理和流程
首先貢獻者的代碼通過 git 命令(或git review封裝)推送到 Gerrit 管理下的 Git 版本庫,推送的提交轉化為一個一個的代碼審核任務,審核任務可以通過 refs/changes/<change-id>下的引用訪問到。代碼審核者可以通過 Web 界面查看審核任務、代碼變更,通過 Web 界面做出通過代碼審核或者打回等決定。測試者也可以通過 refs/changes/<change-id>引用獲取(fetch)修訂對其進行測試,如果測試通過就可以將該評審任務設置為校驗通過(verified)。最后經過了審核和校驗的修訂可以通過 Gerrit 界面中提交動作合並到版本庫對應的分支中。更詳細的流程描述見下圖所示:
Gerrit的實現原理
SSH 協議的 Git 服務器
Gerrit 本身基於 SSH 協議實現了一套 Git 服務器,這樣就可以對 Git 數據推送進行更為精確的控制,為強制審核的實現建立了基礎。 Gerrit 提供的 Git 服務的端口並非標准的 22 端口,缺省是 29418 端口。可以訪問 Gerrit 的 Web 界面得到這個端口。對 Android 項目的代碼審核服務器,訪問 https://review.source.android.com/ssh_info 就可以查看到 Git 服務的服務器域名和開放的端口。下面我們用 curl 命令查看網頁的輸出。
root@lx1:~# curl -L -k http://gerrit.newptone.com/ssh_info
gerrit.newptone.com 29418
特殊引用 refs/for/<branch-name> 和 refs/changes/nn/<task-id>/m
Gerrit 的 Git 服務器,禁止用戶向 refs/heads 命名空間下的引用執行推送(除非特別的授權),即不允許用戶直接向分支進行提交。為了開發者能夠向 Git 服務器提交修訂,Gerrit 的 Git 服務器只允許用戶向特殊的引用refs/for/<branch-name> 下執行推送,其中 <branch-name> 即為開發者的工作分支。向refs/for/<branch-name> 命名空間下推送並不會在其中創建引用,而是為新的提交分配一個 ID,稱為 task-id ,並為該 task-id 的訪問建立如下格式的引用 refs/changes/nn/<task-id>/m ,其中:
- task-id 為 Gerrit 為評審任務順序分配的全局唯一的號碼。
- nn 為 task-id 的后兩位數,位數不足用零補齊。即 nn 為 task-id 除以 100 的余數。
- m 為修訂號,該 task-id 的首次提交修訂號為 1,如果該修訂被打回,重新提交修訂號會自增。
Git 庫的鈎子腳本 hooks/commit-msg
為了保證已經提交審核的修訂通過審核入庫后,被別的分支 cherry-pick 后再推送至服務器時不會產生新的重復的評審任務,Gerrit 設計了一套方法,即要求每個提交包含唯一的 Change-Id,這個 Change-Id 因為出現在日志中,當執行 cherry-pick 時也會保持,Gerrit 一旦發現新的提交包含了已經處理過的 Change-Id ,就不再為該修訂創建新的評審任務和 task-id,而直接將提交入庫。為了實現 Git 提交中包含唯一的 Change-Id,Gerrit 提供了一個鈎子腳本,放在開發者本地 Git 庫中(hooks/commit-msg)。這個鈎子腳本在用戶提交時自動在提交說明中創建以 "Change-Id: " 及包含 git hash-object 命令產生的哈希值的唯一標識。當 Gerrit 獲取到用戶向 refs/for/<branch-name> 推送的提交中包含 "Change-Id: I..." 的變更 ID,如果該 Change-Id 之前沒有見過,會創建一個新的評審任務並分配新的 task-id,並在 Gerrit 的數據庫中保存 Change-Id 和 Task-Id 的關聯。如果當用戶的提交因為某種原因被要求打回重做,開發者修改之后重新推送到 Gerrit 時就要注意在提交說明中使用相同的 “Change-Id” (使用 --amend 提交即可保持提交說明),以免創建新的評審任務,還要在推送時將當前分支推送到refs/changes/nn/task-id/m 中。其中 nn 和 task-id 和之前提交的評審任務的修訂相同,m 則要人工選擇一個新的修訂號。以上說起來很復雜,但是在實際操作中只要使用 repo 這一工具,就相對容易多了。 其余一切交給Web Gerrit 另外一個重要的組件就是 Web 服務器,通過 Web 服務器實現對整個評審工作流的控制。
下面是一個review.openstack.org上的一個提交修改https://review.openstack.org/#/c/22035/:
- URL 中顯示的評審任務編號為 22035。
- 該評審任務的 Change-Id 以字母 I 開頭,包含了一個唯一的 40 位 SHA1 哈希。
- 整個評審任務有三個人以及自動測試工具Jenkins參與,一個人進行了檢查(verify),兩個人進行了代碼審核,並有一人最終批准了該提交(approve),Jenkins進行了verify。
- 該評審任務的狀態為打開狀態:“open”。
- 該評審任務總共包含一個補丁集: Patch set 1。


