開發與維運

分享實錄 | 新一代高效Git協同模型AGit-Flow詳解

00.png

【以下為分享實錄,有刪節】

Git工作流概述及AGit-Flow的優勢

目前,Git已成為源代碼管理的標準和基礎設施。“為什麼Git能這麼成功”?Git的創建者Linux在Git十週年的一次採訪中,道出了其中的奧祕:

The big thing about distributed source control is that it makes one of the main issues with SCM’s go away – the politics around “who can make changes.”

他認為Git能成功最關鍵的不是因為它更快、更安全,也不是因為Git是分佈式的,而是解決了“到底誰能夠貢獻代碼”這個問題。傳統的集中式版本控制系統只能針對核心用戶開放寫授權,長期來看這對項目做大、做強是不利的。而Git改變了傳統版本控制系統不能夠讓跟多開發者貢獻代碼這個頑疾,讓“只讀用戶”也可以通過“代碼評審”的方式參與到項目開發中。

當前業界有兩種最常用Git 工作流:GitHub和Gerrit。他們都具備倉庫的授權模型簡單,只讀用戶可參與代碼貢獻的特點。

01.jpg

如上圖所示,我對這兩種GIT工作流做了優劣勢分析:
**
代碼評審模式不同:**
GitHub 的代碼評審稱為 “pull request”,每個特性改動生成一次代碼評審。
Gerrit 的代碼評審稱為 “Change”,每個提交生都會生成一個“變更單”,這個變更單就是一次代碼評審。

工作流類型不同:
GitHub 的工作流屬於分佈式,當開發者需要參與項目的時候,雖然沒有“寫”的權限,但是可以通過“Fork”的方式創建一個個人倉庫(派生倉庫),他就可以在這個派生倉庫中去創建代碼分支,創建pull request。GitHub 底層採用的是原生的 Git(即 CGit)。

Gerrit 的工作流是集中式,所有用戶工作在統一管控的集中式倉庫中。Gerrit 要求用戶在本地克隆倉庫中安裝一個 “commit-msg” 鉤子,以便在生成的提交中插入唯一的“Change-Id”,向服務器推送要使用特殊的 git push 命令。Gerrit 採用的是 JGit(Java 的 Git 實現)。

**各自優勢:
**
GitHub簡單易用,使用標準 Git 命令即可完成代碼貢獻;對派生倉擁有完全控制力,不受上游項目影響;可以創建跨項目的開源社區,全球開發者大協同,這也是GIT可以形成全球最大的開源社區的原因之一。

Gerrit 因為採用集中式的工作流,管理員可以對項目進行嚴格管控,可以嚴格控制誰可以訪問倉庫,誰可以對我的倉庫做貢獻。Gerrit另外一個優勢是可以實現多倉庫項目管理。我們知道“Android”項目具有1000多個倉庫,就是使用Gerrit進行管理的,很難想象如何使用GitHub來管理Android的1000多個倉庫。

各自劣勢:
正如前面所說GitHub很難管理類似 Android 的多倉庫項目。另外因為GitHub 使用派生倉庫的工作模式,會產生服務端數據冗餘的問題。

Gerrit 需要集中管控,由管理員負責創建項目,而普通用戶不能創建項目,這就使得一個 Gerrit 實例通常只管理一個項目或一個組織內的項目,難以在項目之間形成代碼複用,也很難彙集跨項目的開發者組成開發者社區。

通過對GitHub和Gerrit等Git工作流的調研和學習,我們“取長補短”創建了阿里巴巴的Git工作流,即AGit-Flow。
02.jpg

在阿里巴巴,我們喜歡 pull request、CGit,喜歡在命令行直接創建代碼評審的集中式工作流,喜歡開放的開發者社區。我們不喜歡 “commit-msg” 鉤子方式關聯提交的代碼評審,我們不喜歡一個一個分散的代碼平臺。

我們還開發了配套的客戶端工具 “git-repo”,既能在單倉庫下工作,又支持類似 Android 的多倉庫項目協同。

在阿里巴巴,我們如何使用AGit-Flow

下面給大家演示一下,在阿里巴巴內部,我們是如何使用AGit-Flow工作的。
03.jpg

我們首先使用Git標準命令將倉庫克隆到本地,然後在本地倉庫內開發,創建提交。在工作區中執行 git pr 命令,推送本地提交到服務器,服務器端會自動創建一個新的代碼評審,例如:pull request #123。團隊的代碼評審者就可以打開編號“123”的代碼評審提交評審意見。開發者根據評審意見,在本地工作區繼續開發、新增或修改提交。工作區中再次執行 git pr 命令,推送本地提交到服務器。服務器發現目標分支上已經存在來自同一用戶、同一本地分支的 pull request,因此用戶此次推送沒有創建新的 pull request,而是更新已經存在的 pull request。

如果經過多次修改,代碼依然不OK。代碼評審者也可以直接發起對評審代碼的修改,幫助原開發者更新 pull request。代碼評審者可以使用 git download 123 下載編號為 123 的 pull request 到本地倉庫,代碼修改完畢後,執行 git pr --change 123 命令,將本地修改推送到服務端。服務端接收到代碼評審者的特殊 git push 命令,更新之前由開發者創建的 pull request。項目管理者通過點擊 pull request 評審界面的合併按鈕,將 pull request 合入 master 分支。master 分支被更新,同時關閉 pull request。

使用AGit-Flow工作流無需在服務器上創建新的分支,不需要給新加入的同學創建寫入權限,可以對所有開發者只分配讀取權限,然後通過創建代碼評審再合併到主幹的方式更新主幹代碼。這也是目前比較流行的主幹研發模式。

目前大家可以通過雲效代碼管理平臺(Codeup)來實現AGit-Flow工作流。

AGit-Flow實現原理

04.jpg

AGit-Flow是如何實現的呢? 首先客戶端使用特殊的 git push 命令向服務端發起代碼推送請求,觸發 AGit-Flow 工作流。為什麼說這個git push 命令特殊呢?因為它的目標分支是一個包含特殊的前綴 “refs/for/"的代碼分支,分支後面又跟了一個 “” 。這個“”用於區分本地分支名,不同開發者提交的代碼評審包含不同的“”,所以不會相互覆蓋。

我們還可以通過“-o”來傳遞不同的參數,比如可以指定由誰來對我的代碼進行評審,我的代碼評審會關聯哪個“issue”。這些操作都可以通過“git push”命令完成,後來我們發現這個git push 命令比較複雜,於是我們封裝了一個命令行工具git-repo。目前git-repo已經對外開源,大家可以免費使用。

接下來這個“Push”命令就會打入到服務端,服務器端會啟動一個進程“git-receive-pack”。(我們對服務器端的前端授權模塊做了一些修改,使其能夠識別這個特殊的git push 命令,允許只讀用戶也能“Push”)如上圖所示,“git-receive-pack”我做了星號標記,因為它是一個特殊的“git-receive-pack”。當它發現 push命令的目標是一個特殊的引用後,它不會走Git原來內部的工作流,而是走“外部鉤子”。通過“外部鉤子”完成一些好玩的操作,比如創建代碼評審。

在今年(2020年)3月份,我們已經把這個修改過的git-core貢獻給了Git社區,目前正在評審中,後續Git新版本會包含這個新特性:proc-receive。此特性已經歷經15次迭代,從最初的服務端擴展到集合了服務端擴展和客戶端協議升級的完整解決方案。我們將這個技術開源,一方面繁榮了Git 生態,讓更多人能從阿里巴巴的技術中獲益;另外一方面,阿里巴巴也得到了收益,我們的代碼貢獻得到了Git客戶端的支持, Git適配了我們新的玩法,讓包含阿里巴巴在內的Git用戶得到了更好的體驗。

AGit-Flow實現的技術細節

為了解釋AGit-Flow實現的技術細節,我們先來了解一下git push命令原有的實現方式。
![05.jpg](https://ucc.alicdn.com/pic/
developer-ecology/8552743fa0b24934bd9a93146ffedef4.jpg)

如上圖左側所示, git push命令包含兩部分信息,一個是被打成包的數據,叫“packfile”;另一個是推送的命令,叫“commands”。 “packfile”和“commands”被推送到服務端後,“packfile”會走左側的路徑,首先進入“quarantine”中,進行“隔離”。 當“commands”經過“pre-receive” 鉤子檢查,認為用戶的權限OK、提交說明OK、提交修改的文件ok,“packfile”才會從隔離區釋放出來,進入對象庫(objects)。(如果 “pre-receive” 鉤子腳本失敗,則刪除隔離區,並返回錯誤信息,終止推送命令的執行。)

接下來“commands”會傳遞給內置的 “execute_commands()” 函數,實現分支的創建、更新、刪除等操作。然後通過“repor()”函數報告給客戶端,最後執行 “post-receive”鉤子腳本,完成事件通知。

新鉤子,新生態。AGit-Flow 對 “git-receive-pack” 的源碼做了改動,新的流程如下圖所示:

06.jpg

當客戶端執行新的git push命令後,“packfile”的傳播路徑沒有改變,但是我們更改了"git-receive-pack"命令,增加了一個“過濾器”(圖中漏斗部分)。過濾器將 “commands” 分成兩組,一組是標準的Git命令(group1),一組是AGit-Flow特殊的命令(group2)。這兩組命令經過“pre-receive” 鉤子檢查後,左側普通的命令(group1)會執行Git內置的 execute_commands 函數,生成新的引用,進行分支的創建、更新等。右側這個特殊的命令會調用一個新的外部鉤子 “proc-receive” ,然後創建一個特殊的代碼評審引用,如“refs/pull/123/head”,並且可以用過特殊的Git命令將它下載到本地。

07.jpg

我們為Git貢獻的這個新特性,由三部分組成。第一個部分就是“過濾器”,通過在服務端新增新配置變量 "receive.procReceiveRefs"來實現。只要定義了這個特殊的配置變量,當客戶端使用git push命令推送的時候,Git就會根據配置變量去匹配,當匹配到相應的命令時,這個命令就會走特殊流程。這個配置變量屬於多值變量,例如阿里巴巴代碼平臺的設置是:

  • git config --add receive.procReceiveRefs refs/for
  • git config --add receive.procReceiveRefs refs/drafts
  • git config --add receive.procReceiveRefs refs/for-review

這三條配置變量對應git pr的三種推送模式,會產生標準的pull request、草稿模式的pull request,或者一個代碼評審者想推送指定的pull request。

08.jpg

07.jpg

第二部分是proc-receive 鉤子。我們這個鉤子應該說是Git中有史以來最複雜的一個鉤子,它可以和服務端(git-receive-pack)進行雙向通訊。首先服務端和鉤子會做“版本協商”,因為我們認為這個協議後續會升級,為了保證向後兼容,所以我們首先要協商一個版本。服務端告訴鉤子我是哪個版本,客戶端告訴鉤子我支持哪個版本,後面Git就可以用相應的版本協議與鉤子進行通訊。服務端會用“pkt-line”編碼命令,這個命令是三段式的,包含老的ID,新的ID和需要更新的引用名稱。服務端會把命令和參數發送給鉤子。鉤子會對這些命令和參數進行處理,這些處理由開發者以API調用的方式實現,然後將處理結果報告給鉤子。鉤子再把這個執行結果以特殊的方式報告給服務端。

08.jpg

第三部分是可定製的客戶端狀態報告。在上圖右側部分,大家可以看到有一條虛線由“proc-receive” 鉤子指向“execute-commands”函數,這個代表有些鉤子無法處理的命令會返回給“execute-commands”函數進行處理。當“execute-commands”和“execute-commands”鉤子處理完全部命令後,會統一進行“report()”(報告)。我們這個“report()”相比於Gerrit有一個優勢,能夠讓客戶端知道你沒有改成“refs/for”引用,而是改成了另外一個引用。此外,我們也考慮了向下兼容問題,讓老版本的Git,在遇到新版本的Git服務器的時候一樣可以收到報告信息。例如老版本的Git會認為你創建了“refs/for/master”;但是新版本的Git懂得擴展協議,可以識別出你創建了特殊的引用,比如知道你創建的不是“refs/for/master”而是“refs/pull/123”這樣的引用。

阿里巴巴開源的客戶端工具git-repo簡介

10.jpg

git-repo 是阿里巴巴開源的一款集中式工作流命令行工具,對原生 Git 命令做了封裝,簡化了使用 AGit-Flow 等集中式工作流時稍嫌繁瑣的 Git 命令。git-repo是使用Go語言開發,運行時除 Git 外不依賴其他軟件,所有具有拆包即用的優點。

git-repo 具有良好的兼容性,可以支持 AGit-Flow 兼容的代碼平臺以及 Gerrit。可以跨平臺,目前支持Linux、Mac、 windows系統,其中Windows是Beta版本。除了具備 Android repo 的多倉庫管理能力外,還可以對單獨的代碼倉庫進行操作。

如何下載安裝git-repo呢?git-repo目前已經開源到Git hub中,你可以訪問https://github.com/alibaba/git-repo-go/releases頁面下載合適的安裝包。然後將解壓縮後的 git-repo 文件移動到可執行目錄中(如 Linux 下的 /usr/local/bin 目錄),即完成安裝。詳細的使用方法此處不再贅述,大家可以訪問git-repo主頁瞭解。

總結:

AGit-Flow 是阿里巴巴研發的 Git 集中式協同協議,目前已經在阿里巴巴集團內部生根開花,在外部,通過雲效代碼管理平臺提供支持。

AGit-Flow的核心組件已經開源,將已經成為Git的核心組件。

為了方便開發者操作,我們開發了git-repo命令行工具。git-repo使用 Go 語言開發,具有拆包即用、跨平臺、兼容性好、可擴展等特點。git-repo已經開源道Git hub社區大家可以進入產品主頁免費下載使用。

以上內容整理自知憂的視頻分享《AGit-Flow:新一代高效Git協同模型》,歡迎大家加入雲效開發者交流群(釘釘群號:34532418)觀看視頻回放,下載演講PPT。


【關於雲效】

雲效,企業級一站式DevOps平臺,源於阿里巴巴先進的研發理念和工程實踐,致力於成為數字企業的研發效能引擎!雲效提供從“需求 ->開發->測試->發佈->運維->運營”端到端的在線協同服務和研發工具,通過人工智能、雲原生技術的應用助力開發者提升研發效能,持續交付有效價值。

【雲效官網】https://www.aliyun.com/product/yunxiao?channel=zhibo
【公測指南】https://developer.aliyun.com/article/756207
【申請公測】https://devops.aliyun.com
【學習路徑】https://help.aliyun.com/document_detail/153739.html
【開發者社區】https://developer.aliyun.com/group/yunxiao
【精彩活動】雲效公測開啟 「產品體驗官」招募
https://www.aliyun.com/activity/yunxiao/Beta2020

開發者俱樂部(組織)180.jpg

歡迎掃碼加入雲效開發者俱樂部(釘群:34532418)

Leave a Reply

Your email address will not be published. Required fields are marked *