代碼評審中的代碼協(xié)同
大神說:“Show me the code”,于是就有了代碼評審。
“Talk is cheap. Show me the code.”
——Linus Torvalds, founder of Linux and Git.
代碼評審中同樣存在著“Talk is cheap. Show me the code”,語言無力時(shí),直接上代碼吧。這就是我們今天要討論的話題——代碼評審中的代碼協(xié)同。
一 基于郵件列表的代碼評審
這是一種和代碼倉庫松耦合的代碼評審模式,100%的代碼都要經(jīng)由一位或多位“仁慈的獨(dú)裁者”(benevolent dictator)代碼評審后才能合并入代碼倉庫。這種開發(fā)模式還需要開發(fā)者掌握一些命令行操作技巧以便完成代碼在倉庫和郵件列表之間的轉(zhuǎn)換。采用這個(gè)模式的項(xiàng)目不多,不過 Linux、Git 開源社區(qū)就是按照這種模式運(yùn)作的。
1 代碼和郵件的相互轉(zhuǎn)換
代碼轉(zhuǎn)換為電子郵件,要使用 git format-patch 命令。例如下面的命令將指定范圍的代碼提交(例如在 origin/master 之后的新提交)轉(zhuǎn)換為電子郵件:
- git format-patch origin/master..HEAD
 
生成的補(bǔ)丁文件的格式如下所示:
- From: Author Name <author@email>
 - Subject: [PATCH] first line of commit message
 - more commit message...
 - ---
 - diff --git ...
 - <content of patch>
 
- 郵件頭中的 Subject: 字段是郵件標(biāo)題,使用 [PATCH] 作為標(biāo)題前綴,以提交說明的第一行作為標(biāo)題內(nèi)容。
 - 更多的提交說明作為郵件內(nèi)容,和郵件頭之間用一個(gè)空行分隔開。
 - 用分隔符 --- 作為提交說明的結(jié)束。
 - 在分隔符 --- 和 diff --git 開始的補(bǔ)丁內(nèi)容之間的文字被忽略。通常此處內(nèi)容是提交的變更統(tǒng)計(jì),開發(fā)者也可以在此處寫入不宜列入提交說明中的附加說明。
 
git format-patch 命令有很多參數(shù),要結(jié)合不同場景使用,例如:
- 一個(gè)特性由多個(gè)提交構(gòu)成,分散在多個(gè)提交中的提交說明難以描述整個(gè)特性,可以使用 --cover-letter 參數(shù),生成一封編號為 0000 的郵件,作為后續(xù)提交的摘要說明,便于評審者理解代碼。
 - 一個(gè)特性通常會多次迭代,就需要為每次迭代設(shè)置不同的版本。這就要用到 -v {num} 參數(shù)指定補(bǔ)丁的版本。版本將體現(xiàn)在郵件標(biāo)題中,例如第二版本的補(bǔ)丁,郵件標(biāo)題將使用 [PATCH v2] 作為前綴。
 - 回復(fù)特定郵件,以便形成可追蹤的郵件線索,使用參數(shù) --in-reply-to="{Message-ID}",為電子郵件生成相關(guān)的 In-Reply-To: 和 References: 頭信息。
 - 默認(rèn)提交本身的作者、提交說明的簽名區(qū)(trailer)提及的貢獻(xiàn)者會自動(dòng)添加為郵件的收件人。要添加更多參與者,可以使用 --to={email}、--cc={email} 參數(shù)。
 
將電子郵件轉(zhuǎn)換為代碼,則使用命令 git am [options...] mail... 。該命令會將郵件正確轉(zhuǎn)換為 Git 倉庫中的提交。
使用 git send-email 命令,將包含代碼提交的郵件發(fā)送到郵件列表。
2 評審中的代碼片段轉(zhuǎn)換為提交
代碼評審以郵件回復(fù)的方式完成。注意郵件回復(fù)都要求用純文本格式,否則會被郵件服務(wù)器退信。
代碼評審中發(fā)現(xiàn)小的文字錯(cuò)誤,例如將 warning 寫成了 waring,評審者可能做出如下簡潔的回復(fù):
- s/waring/warning/
 
這種約定俗成的格式大概是源于 sed 命令實(shí)現(xiàn)文本替換的語法。
評審者有時(shí)候會在回復(fù)中貼上大段的代碼補(bǔ)丁,為了使代碼補(bǔ)丁和郵件上下文做出區(qū)分,會使用特殊的剪刀分隔符將郵件中的評論和代碼補(bǔ)丁分隔開。
- Subject: Re: whatever thread you're in
 - Somebody else said:
 - > blah blah blah
 - I disagree. You should do it like this instead:
 - -- >8 --
 - first line of commit message
 - more commit message
 - ---
 - diff --git ...
 
上面是 Peff(Jeff King)在郵件中給出的一個(gè)示范,看到其中的剪刀分隔符了么?剪刀分隔符由多個(gè)減號(穿孔的分割線)和一個(gè)剪刀符號組成至少8個(gè)字符的分隔符??蛇x的分隔符有:-- 8< -- 、-- >8 -- 、-- %< -- 或 --- >% --- 等。
使用 git am --scissors 命令,能夠識別郵件中的剪刀分隔符,將郵件中的代碼轉(zhuǎn)換為提交。
3 為提交貢獻(xiàn)者署名
Git的提交元信息中只包含兩個(gè)署名信息,一個(gè)是提交的原始作者(Author),一個(gè)是將提交合入倉庫或者對提交做了修補(bǔ)的提交者(Committer),而在提交評審過程中有過貢獻(xiàn)的人往往不只兩人,如何致敬貢獻(xiàn)者呢?Git 社區(qū)的實(shí)踐是在提交尾部(trailer)添加貢獻(xiàn)者簽名。貢獻(xiàn)者簽名由一個(gè)被動(dòng)語態(tài)的關(guān)鍵字和貢獻(xiàn)者ID組成,例如:
- Signed-off-by: User 
:通常由代碼的貢獻(xiàn)者(Author)和代碼合入時(shí)的提交者(Committer)提供的簽名??捎擅? git commit -s 、 git am -s 等命令自動(dòng)添加。  - Reported-by: User 
:問題的報(bào)告者。  - Helped-by: User 
:對提交有過幫助的人。  - Reviewed-by: User 
:評審者。  
可以通過 Git 項(xiàng)目倉庫的提交歷史,看到更多的簽名示例。
4 使用 GitHub PR 實(shí)現(xiàn)代碼到郵件的轉(zhuǎn)換
一個(gè)名為 GitGitGadget 工具借助 GitHub 強(qiáng)大的擴(kuò)展能力,通過向 gitgitgadget/git 倉庫發(fā)送 pull request,實(shí)現(xiàn)提交到郵件的轉(zhuǎn)換,并發(fā)送到 Git 項(xiàng)目的郵件列表中。使用 GitGitGadget 參與 Git 社區(qū)代碼貢獻(xiàn)詳見。
二 GitHub 代碼評審中的協(xié)同
GitHub 使用 pull request 進(jìn)行代碼評審,評論中的代碼塊兒也可以轉(zhuǎn)換為提交。
1 代碼評論中嵌入代碼塊
下圖中,點(diǎn)擊評論工具欄第一個(gè)按鈕,可以在評論中嵌入代碼塊:
2 評論中代碼塊轉(zhuǎn)換為提交
對 pull request 的源倉庫具有寫權(quán)限的用戶,可以將評審中的代碼庫轉(zhuǎn)換為提交,如下圖所示:
于是代碼評審中會增加一個(gè)新的修正提交。
GitHub 的這個(gè)功能對于代碼評審中發(fā)現(xiàn)的一些小問題,還是非常方便的。但是大的修改就無能為力了。
3 線下評審
對于功能復(fù)雜的 pull request,在線上瀏覽代碼不方便,也不能線上調(diào)試代碼,這時(shí)線下獲取并瀏覽代碼,就非常有必要了。
GitHub 的代碼倉庫中為每一個(gè)代碼評審設(shè)置了特殊的關(guān)聯(lián)引用:
- refs/pull/{ID}/head :關(guān)聯(lián) pull request 的源提交。
 - refs/pull/{ID}/merge :對于沒有沖突的 pull request,這個(gè)引用指向一個(gè)成功的合并提交。
 
代碼評審者使用如下命令可以獲取 pull request (例如編號為 123 的 PR)指向的提交:
- git fetch origin refs/pull/123/head
 - git switch -d FETCH_HEAD
 
評審者可以線下調(diào)試 pull request 指向的代碼,但是對代碼做出的本地修改,沒有辦法直接更新到線上的代碼評審中。
阿里巴巴的云效Codeup,支持線下到線上的代碼協(xié)同。
三 云效Codeup代碼評審中的協(xié)同
無論是 GitHub 還是 Gitlab,開發(fā)者創(chuàng)建代碼評審首先需要將代碼推送到線上獨(dú)立的分支中(無論是在線上的派生倉庫,還是目標(biāo)倉庫),然后再通過網(wǎng)頁選擇來源倉庫、分支及目標(biāo)倉庫、分支,創(chuàng)建代碼評審。
GitHub 和 Gitlab 這種代碼評審方式,或者要引入冗余的派生倉庫,或者需要為開發(fā)者賦予在倉庫中的寫入權(quán)限,并容易引發(fā)雜亂的分支管理。
1 適合主干開發(fā)的無分支創(chuàng)建代碼評審
云效 Codeup 可以通過 git push 命令在客戶端直接創(chuàng)建代碼評審,無需創(chuàng)建派生倉庫或者在倉庫中創(chuàng)建特性分支。例如在客戶端執(zhí)行如下命令:
- git push origin HEAD:refs/for/master/topic1
 
該命令會在服務(wù)端創(chuàng)建新的代碼評審,或者如果已經(jīng)存在相同用戶、相同命令創(chuàng)建的代碼評審則會更新評審中的提交。
建議安裝我們開源的 git-repo 工具,則可以用更簡單的命令行,實(shí)現(xiàn)從客戶端創(chuàng)建/更新代碼評審。
- git pr
 
2 線下評審,線上協(xié)同
和 GitHub 類似,云效 Codeup 創(chuàng)建的代碼評審都有一個(gè)特殊引用相關(guān)聯(lián),格式為:refs/merge-requests/{ID}/head。
代碼評審者可以使用 git fetch 命令獲取特定的代碼評審(以編號123為例)指向的代碼,進(jìn)行線下代碼評審。
- git fetch origin refs/merge-requests/123/head
 - git switch -d FETCH_HEAD
 
如果安裝了 git-repo,可以使用下面更為簡潔的命令:
- git download 123
 
代碼評審者除了可以在本地倉庫中瀏覽、調(diào)試代碼,還可以更新代碼、創(chuàng)建提交,然后將本地新增提交更新到線上的代碼評審中。命令示例如下:
- git pr -c 123
 
在云效 Codeup,開發(fā)者和評審者可以基于代碼評審進(jìn)行更為流暢的代碼協(xié)同。
3 Git proc-receive 掛鉤
上述“線下評審、線上協(xié)同”功能的核心是 Git 的 proc-receive 掛鉤和 report-status-v2 新能力。這一功能由阿里巴巴貢獻(xiàn)給 Git 社區(qū),并在 Git 2.29.0 發(fā)布。
云效 Codeup 匯集了阿里巴巴最新的代碼托管、代碼協(xié)同技術(shù),希望能夠造福更多中國和世界的開發(fā)者。


















 
 
 








 
 
 
 