大神說:“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”,語言無力時,直接上代碼吧。這就是我們今天要讨論的話題——代碼評審中的代碼協同。
一 基于郵件清單的代碼評審
這是一種和代碼倉庫松耦合的代碼評審模式,100%的代碼都要經由一位或多位“仁慈的獨裁者”(benevolent dictator)代碼評審後才能合并入代碼倉庫。這種開發模式還需要開發者掌握一些指令行操作技巧以便完成代碼在倉庫和郵件清單之間的轉換。采用這個模式的項目不多,不過 Linux、Git 開源社群就是按照這種模式運作的。
1 代碼和郵件的互相轉換
代碼轉換為電子郵件,要使用 git format-patch 指令。例如下面的指令将指定範圍的代碼送出(例如在 origin/master 之後的新送出)轉換為電子郵件:
git format-patch origin/master..HEAD
生成的更新檔檔案的格式如下所示:
From: Author Name <author@email>
Subject: [PATCH] first line of commit message
more commit message...
---
diff --git ...
<content of patch>
- 郵件頭中的 Subject: 字段是郵件标題,使用 [PATCH] 作為标題字首,以送出說明的第一行作為标題内容。
- 更多的送出說明作為郵件内容,和郵件頭之間用一個空行分隔開。
- 用分隔符 --- 作為送出說明的結束。
- 在分隔符 --- 和 diff --git 開始的更新檔内容之間的文字被忽略。通常此處内容是送出的變更統計,開發者也可以在此處寫入不宜列入送出說明中的附加說明。
git format-patch 指令有很多參數,要結合不同場景使用,例如:
- 一個特性由多個送出構成,分散在多個送出中的送出說明難以描述整個特性,可以使用 --cover-letter 參數,生成一封編号為 0000 的郵件,作為後續送出的摘要說明,便于評審者了解代碼。
- 一個特性通常會多次疊代,就需要為每次疊代設定不同的版本。這就要用到 -v {num} 參數指定更新檔的版本。版本将展現在郵件标題中,例如第二版本的更新檔,郵件标題将使用 [PATCH v2] 作為字首。
- 回複特定郵件,以便形成可追蹤的郵件線索,使用參數 --in-reply-to="{Message-ID}",為電子郵件生成相關的 In-Reply-To: 和 References: 頭資訊。
- 預設送出本身的作者、送出說明的簽名區(trailer)提及的貢獻者會自動添加為郵件的收件人。要添加更多參與者,可以使用 --to={email}、--cc={email} 參數。
将電子郵件轉換為代碼,則使用指令 git am [options...] mail... 。該指令會将郵件正确轉換為 Git 倉庫中的送出。
使用 git send-email 指令,将包含代碼送出的郵件發送到郵件清單。
2 評審中的代碼片段轉換為送出
代碼評審以郵件回複的方式完成。注意郵件回複都要求用純文字格式,否則會被郵件伺服器退信。
代碼評審中發現小的文字錯誤,例如将 warning 寫成了 waring,評審者可能做出如下簡潔的回複:
s/waring/warning/
這種約定俗成的格式大概是源于 sed 指令實作文本替換的文法。
評審者有時候會在回複中貼上大段的代碼更新檔,為了使代碼更新檔和郵件上下文做出區分,會使用特殊的剪刀分隔符将郵件中的評論和代碼更新檔分隔開。
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)在郵件中給出的一個示範,看到其中的剪刀分隔符了麼?剪刀分隔符由多個減号(穿孔的分割線)和一個剪刀符号組成至少8個字元的分隔符。可選的分隔符有:-- 8< -- 、-- >8 -- 、-- %< -- 或 --- >% --- 等。
使用 git am --scissors 指令,能夠識别郵件中的剪刀分隔符,将郵件中的代碼轉換為送出。
3 為送出貢獻者署名
Git的送出元資訊中隻包含兩個署名資訊,一個是送出的原始作者(Author),一個是将送出合入倉庫或者對送出做了修補的送出者(Committer),而在送出評審過程中有過貢獻的人往往不隻兩人,如何緻敬貢獻者呢?Git 社群的實踐是在送出尾部(trailer)添加貢獻者簽名。貢獻者簽名由一個被動語态的關鍵字和貢獻者ID組成,例如:
- Signed-off-by: User <Email> :通常由代碼的貢獻者(Author)和代碼合入時的送出者(Committer)提供的簽名。可由指令 git commit -s 、 git am -s 等指令自動添加。
- Reported-by: User <Email> :問題的報告者。
- Helped-by: User <Email>:對送出有過幫助的人。
- Reviewed-by: User <Email> :評審者。
可以通過 Git 項目倉庫的送出曆史,看到更多的簽名示例。
4 使用 GitHub PR 實作代碼到郵件的轉換
一個名為 GitGitGadget 工具借助 GitHub 強大的擴充能力,通過向 gitgitgadget/git 倉庫發送 pull request,實作送出到郵件的轉換,并發送到 Git 項目的郵件清單中。使用 GitGitGadget 參與 Git 社群代碼貢獻詳見。
二 GitHub
中的協同
GitHub 使用 pull request 進行代碼評審,評論中的代碼塊兒也可以轉換為送出。
1 代碼評論中嵌入代碼塊
下圖中,點選評論工具欄第一個按鈕,可以在評論中嵌入代碼塊:
2 評論中代碼塊轉換為送出
對 pull request 的源倉庫具有寫權限的使用者,可以将評審中的代碼庫轉換為送出,如下圖所示:
于是代碼評審中會增加一個新的修正送出。
GitHub 的這個功能對于代碼評審中發現的一些小問題,還是非常友善的。但是大的修改就無能為力了。
3 線下評審
對于功能複雜的 pull request,線上上浏覽代碼不友善,也不能線上調試代碼,這時線下擷取并浏覽代碼,就非常有必要了。
GitHub 的代碼倉庫中為每一個代碼評審設定了特殊的關聯引用:
- refs/pull/{ID}/head :關聯 pull request 的源送出。
- refs/pull/{ID}/merge :對于沒有沖突的 pull request,這個引用指向一個成功的合并送出。
代碼評審者使用如下指令可以擷取 pull request (例如編号為 123 的 PR)指向的送出:
git fetch origin refs/pull/123/head
git switch -d FETCH_HEAD
評審者可以線下調試 pull request 指向的代碼,但是對代碼做出的本地修改,沒有辦法直接更新到線上的代碼評審中。
阿裡巴巴的雲效Codeup,支援線下到線上的代碼協同。
三
雲效Codeup代碼評審中的協同
無論是 GitHub 還是 Gitlab,開發者建立代碼評審首先需要将代碼推送到線上獨立的分支中(無論是線上上的派生倉庫,還是目标倉庫),然後再通過網頁選擇來源倉庫、分支及目标倉庫、分支,建立代碼評審。
GitHub 和 Gitlab 這種代碼評審方式,或者要引入備援的派生倉庫,或者需要為開發者賦予在倉庫中的寫入權限,并容易引發雜亂的分支管理。
1 适合主幹開發的無分支建立代碼評審
雲效 Codeup 可以通過 git push 指令在用戶端直接建立代碼評審,無需建立派生倉庫或者在倉庫中建立特性分支。例如在用戶端執行如下指令:
git push origin HEAD:refs/for/master/topic1
該指令會在服務端建立新的代碼評審,或者如果已經存在相同使用者、相同指令建立的代碼評審則會更新評審中的送出。
建議安裝我們開源的 git-repo 工具,則可以用更簡單的指令行,實作從用戶端建立/更新代碼評審。
git pr
2 線下評審,線上協同
和 GitHub 類似,雲效 Codeup 建立的代碼評審都有一個特殊引用相關聯,格式為:refs/merge-requests/{ID}/head。
代碼評審者可以使用 git fetch 指令擷取特定的代碼評審(以編号123為例)指向的代碼,進行線下代碼評審。
git fetch origin refs/merge-requests/123/head
git switch -d FETCH_HEAD
如果安裝了 git-repo,可以使用下面更為簡潔的指令:
git download 123
代碼評審者除了可以在本地倉庫中浏覽、調試代碼,還可以更新代碼、建立送出,然後将本地新增送出更新到線上的代碼評審中。指令示例如下:
git pr -c 123
在雲效 Codeup,開發者和評審者可以基于代碼評審進行更為流暢的代碼協同。
3 Git proc-receive 挂鈎
上述“線下評審、線上協同”功能的核心是 Git 的 proc-receive 挂鈎和 report-status-v2 新能力。這一功能由阿裡巴巴貢獻給 Git 社群,并在 Git 2.29.0 釋出。
雲效 Codeup彙集了阿裡巴巴最新的代碼托管、代碼協同技術,希望能夠造福更多中國和世界的開發者。