天天看點

Linux核心社群遷移到github?

本文主要根據  http://blog.ffwll.ch/2017/08/github-why-cant-host-the-kernel.html

 做了摘要和改編而來。原作者Daniel Vetter是來自 Intel OTC 核心開發。

Daniel 說,這篇文章的寫作動機來自兩個事情:

其一是在 maintainerati 會議 (一個開源軟體維護者的業内聚會) 上的讨論。 Daniel 和幾個開源軟體維護者讨論了如何擴充真正的大規模開源項目,以及 Github 如何強制項目隻能以幾種特定的方式來擴充。而 Linux 核心開發恰恰使用了不同的模型,這些正是在 Github 上搞開源的維護者不懂的,是以 Daniel 在讨論中解釋 Linux 如何運作,以及 Linux 模型為何不同;

其二是在 Daniel 的另一篇部落格 Maintainers Don’t Scale 的評論區裡,支援最高的評論就是 “……為啥這些恐龍們不去使用現代的開發工具?“。(其實這個問題曾經在筆者心中也盤旋了很久….) 一些核心社群頂級的維護者,捍衛者傳統的郵件清單及布丁的送出方式,抵制使用 Github pull request 的方式。難道真的就是這一小撮掌權者的問題?Daniel 認為不是的。根本原因是,Github 的模式根本支撐不來 Linux 核心這種有着巨大體量的貢獻者的開發模式。是以,哪怕是把核心社群的幾個子系統移到 Github 上開發都是不可能的。Daniel 說,這不僅和托管 git 的資料有關,而且還和 how pull request, issue 和 fork 這幾個 Github 的功能的工作方式有關。(寫到這裡,我有點兒懷疑這種結論,Github 那麼多大型項目,托管整個社群也就罷了,說連個核心子系統都搞不定,就有點兒糊弄人了吧?)

Github 擴充的方式

Git 牛的地方就是讓每個人都可以很友善地 fork 開源項目,在其上建立分支。最後一但有了不錯的改進,又可以基于上遊主倉庫建立一個 pull request,并且得到代碼的 review,測試,然後被合并。Github 也很牛,因為它把這些 Git 複雜的方式都用 UI 簡單化了,易學易用,讓新手很容易去為開源項目做貢獻。

最終,一個獲得巨大成功的開源項目,沒辦法在一個代碼倉庫裡,能夠利用 tagging, labelling, sorting, bot-herding 和 automating 等手段管理好所有 pull request 和 issue,于是就必須通過把單一代碼庫拆分成多個可管理的部份組成。更重要的是,項目的各部分因為規模和成熟度的不同,需要不同的規則和流程:非常新的實驗性質的庫,與主線代碼相比,有不同的穩定性和 CI (持續內建) 條件。并且,或許項目裡還有一大堆過時的,不會被支援的代碼,但是目前還不能删除:這需要把非常大的項目拆分成多個子項目,每個子項目都有自己風格的流程,合并條件,獨立的倉庫,獨立的 pull request 和 issue 的跟蹤。一般來說,直到管理的開銷成為痛點,龐大的重組成為必要時,項目管理可能需要幾十甚至幾百全職的貢獻者。

幾乎所有的 Github 托管項目,解決這個問題時,都是靠把單一的源碼樹,按照功能區分,拆分成許多不同的項目。通常拆分的結果就是,一些核心代碼,加上一堆插件,庫,和擴充的代碼。所有這些通過某種插件或者安裝包管理器再建構到一起,在一些情況下,可能就是直接從其它的 Github 倉庫裡直接拉取代碼建構。

因為幾乎所有的大項目都是這麼搞的,在這裡,Daniel 不再贅述它的好處,而是主要把這種做法的缺點指出來了,這裡簡單摘要如下:

  • 社群被不必要地割裂了。大多數貢獻者隻有他們直接貢獻代碼的倉庫,忽略掉了項目的其它部份。這個對他們有好處,但是也會導緻在不同的子項目裡并行的,重複的工作被及時發現,并且共享成果。
  • 項目重構和代碼共享存在導緻低效的障礙:首先你需要為新代碼變更,釋出核心項目的新版本,然後過一遍所有的其它子項目的代碼,然後更新它們,然後再去删除核心項目裡的舊的被共享的代碼。
  • 理論上,支援多個子項目版本組合難以為繼。必須通過內建測試來保證。
  • 對同屬一個大項目的多個子項目重組很痛苦,因為這需要重組 Git 倉庫和決定如何拆分。而在一個單一的代碼倉庫,重組僅僅是把維護者資訊檔案重新更新一下。

(看到這裡,筆者認為,如果一個内部強耦合的代碼庫,非要因為規模大拆分,是存在這些問題…… 可是确實,項目如果足夠大,其實這種耦合關系就可以通過一些流程來規避了。例如,Linux 發行版,不就可以看作是按照上述方式拆分的多個倉庫的集合麼?當然,Linux 發行版本來就是很多獨立開源項目組成的。但是筆者之前工作在 Solaris OS 上,其實就是把弱耦合的項目拆分成多個庫了。為了規避接口耦合的問題,才在不同的倉庫之間設計了接口穩定性上的約定,如公開接口,私有接口。對公開接口,要支援多版本,EOL 也會存在相應的規則。)

為什麼要有 Pull Request?

Daniel 說,Linux 核心是他知道的幾個沒有像之前所說的那樣拆分的項目之一。Linux 核心是一個巨型項目,沒有子項目規劃的話,也沒辦法運轉。是以,看一下為什麼 Git 需要 Pull Request 也是必要的:在 Github 上,Pull Request 是貢獻者的代碼得到合并的真正方式。但是,核心改動則是通過送出 patch 到郵件清單,甚至在 Git 被核心項目使用之後。

但在 Git 非常早期版本,Pull Request 就被支援了。最初的 Pull Request 的使用者,是核心維護者們,Git 早先就是為了解決 Linus Torvalds 的維護核心的問題的。毫無疑問,Pull Request 非常有必要,而且有用,但是,它不是為了處理獨立貢獻者的代碼改動的:直到今天,Pull Request 被用來轉發整個子系統的代碼變化,或者同步代碼的重構,或者橫跨多個子項目之間,同步交割代碼的改動。舉個例子,在 4.12 網絡子系統維護者 Dave S. Miller 的 Pull Request,被 Linus 送出: 這個送出裡包含了兩千多的,來自 600 個獨立貢獻者的代碼送出,并且還有一堆代碼合并和來自下一級維護者的 pull request。但是,這裡面所有的更新檔,都是從郵件清單裡挑選出來,由維護者送出,而不是由原作者送出。Linux 核心流程的古怪之處就是,原作者不送出代碼到代碼庫。這也是為啥 Git 獨立地跟蹤記錄送出者和作者。

Github 的創新和改進就包括了,到處使用 Pull Request,Pull Request 下放給了獨立貢獻者。但是,這并不是 Pull Request 最早被創造出來的目的。

Linux 核心擴充的方式

初看 Linux 就是一個單體的倉庫,所有東西都被塞進 Linus 的主倉庫。但是實際上相差甚遠:

  • 用 Linus Torvalds 主倉庫運作 Linux 的人很少。如果他們運作 upstream 核心,通常都是用穩定核心分支。但更多的是用 Linux 發行版,這些發行版通常會有額外的更新檔和 backport,并且甚至不是 kernel.org 托管的,而通常會是完全不同的組織。或者,他們用硬體制造商那裡拿到的核心,與主倉庫相比,這裡常常有非常多的改動。
  • 除了 Linus 自己,沒有人直接在 Linus 的倉庫裡做開發。每個子系統,甚至是大的驅動,有它們自己的 Git 代碼庫,及自己的郵件清單去跟蹤更新檔送出和問題讨論,這些子系統都是互相獨立的。
  • 跨子系統的工作,是在 linux-next 的內建代碼樹裡做的,它通常包含了上百個 Git 分支,這些分支來自不同的 Git 代碼庫。
  • 所有的這些都通過 MAINTAINERS 的檔案和 get_maintainers.pl 的腳本來管理,它可以告訴任意給定的代碼片段相關的一切,誰是維護者,誰是代碼 review 者,哪裡是 Git 倉庫,使用哪個郵件清單,哪裡去報告 bug。這個工具不僅僅是根據檔案的位置,也通過捕捉代碼的一些特征來确定跨子系統的改動,例如,裝置樹的處理,kobject 的層級結構,都會被合适的專家處理。

Daniel 認為,這種方式有有如下好處 (自然是和前面 Github 上的子項目拆分相比的),摘要如下:

  • 重新組織子項目的拆分超級得容易,隻需要更新 MAINTAINERS 檔案,随後建立新的 Git 倉庫就可以了。
  • 跨子系統的 Pull Request 讨論和問題的讨論,非常非常容易在子項目之間重新分派,隻需在郵件回複裡增加要 Cc: 的子系統的郵件清單。類似地,做跨子系統的工作,也會非常容易的協同,因為同一個 Pull Request 可以被送出到多個子項目,并且隻有一個全局的讨論。
  • 跨子系統的工作,不需要多項目之間任何釋出的協同。直接在自己的倉庫裡修改全部代碼即可。
  • 它也不妨礙你建立自己的實驗性改動,這是多倉庫重要好處之一。直接在自己的 fork 裡增加代碼就好,沒有人強迫你把代碼改回去,或者把你的代碼放到單一的倉庫,因為沒有中心倉庫。

(說了這麼多,Github 唯一的問題就是不支援跨倉庫的工作流呗….)

Linux 的模式: monotree with multiple repositories

有人可能會質疑,Linux 的模式看起來像是一個,本文開頭說過的,有擴充問題的單體代碼庫。接下來,Daniel 在文章中,花了很多篇幅解釋了,Linux 其實是 單體代碼樹,多個代碼倉庫 (monotree with multiple repositories) 的模式。

Daniel 還以核心社群的維護者們之間的 Pull Request 工作流為例,說明 Linux 這種模式在 Github 裡為啥不行,摘要如下:

簡單的場景就是在核心維護者的層級結構中擴散代碼改動,直到改動最終落地到了可以最終做軟體釋出的代碼樹裡。對 Github 來說,這個很容易,直接用 Pull Request UI 就可以做到。

更有意思的是,跨越多個子系統的改動,因為 Pull Request 的工作流會成為一個無環圖及其變體組成的 Mesh 網格。第一步是讓代碼改動都被所有子系統的維護者 Review 和測試。在 Github 工作流裡,這個将是一個 Pull Request 同時送出到多個代碼庫,隻要有一個單一的讨論線索,并在所有的倉庫裡共享。在核心社群裡,這個是靠更新檔同時送出給一大堆郵件清單和維護者來實作的。

核心這種 Review 方式,通常還不是代碼最終合并的方式。而是在所有其它子系統維護者同意的情況下,多個子系統其中的一個被确定為接受 Pull Request 的方式。通常,被選的子系統是受到改動影響最多的子系統,但是有時候,也可以是因為某個子系統正在做的工作與這個 Pull Request 有沖突。有時候,還可以是一個全新的代碼倉庫和其維護者被确立。這通常發生在改動的功能跨越了整個代碼樹,不是很容易地在一個地方,一個目錄下的被一些檔案囊括的情形。最新的例子就是 DMA mapping tree (DMA 映射項目的代碼樹),該項目試圖将各種驅動,平台維護者,還有處理器架構支援的改動都在一個項目裡完成。

但是,有時候,多個子系統的代碼改動存在沖突的情況,導緻它們都需要不小的努力去解決合并沖突。在這種情況下,這種跨子系統的更新檔,通常不能直接被接受 (相當于一個 Github 上的 rebase 的 Pull Request),而是 Pull Request 經過修改,使這些更新檔對所有子系統都通用,然後再合并到所有子系統。為了避免無關的改動影響到某個子系統,共同的基線是非常重要的。由于這種 Pull Request 是為了一個特定的 Topic 而存在的,這些代碼分支也通常被叫做 Topic 分支。

Daniel 還舉了微軟公司的 OS 項目的例子,說它是一個單體代碼樹。并且根據他和微軟的人交流,這個代碼樹大到需要微軟寫一個 GVFS 的虛拟檔案系統來支援更高效的開發。(筆者認為,這個例子略有些不恰當,微軟的 OS 不光有核心,還有使用者态的許多其它代碼,而這裡的核心代碼樹就是核心代碼,從更全局和更平等的 Linux 發行版的角度看,Linux 發行版其實是多個代碼樹了…….. 恰恰是 Daniel 在前面抨擊 Github 上用多代碼樹劃分子項目的方法……)

親愛的 Github,輪到你了…

很不幸的是,Github 不能支援前面讨論的跨子系統的工作流,至少在原生的 UI 是不能的。當然,這些工作可以用原始的 git 指令完成,但是不得不回到通過郵件清單發 patch 的方式,Pull Request 也得回到郵件,然後手工合并。在 Daniel 看來,這是唯一的原因,為什麼 kernel 社群不能搬到 github 上。也還有一些小問題 (真的是小問題麼?),一些頂級的維護者對 Github 極度地反對,但這個不是一個技術問題。并且,不隻是 Linux 核心,它是所有巨型的 Github 項目要擴充所面對的一般問題,因為 Github 沒有給他們一個擴充到多個倉庫,但是保持一個單體代碼樹的選擇。

簡而言之,Daniel 提出了一個他認為簡單的新特性需求給 Github:

請支援 Pull Request 和 issue 跟蹤管理跨多個不同的,基于單體代碼樹的代碼倉庫。

非常簡單的想法,但是有巨大的影響。

Daniel 不但給出了核心想法,還給出了一些建議的細節,摘要如下:

倉庫群組織

首先,需要支援同一個組織可以擁有同一個代碼庫的多個派生倉庫。以 git.kernel.org 為例,上面大多數代碼庫不是個人的。

在一個代碼庫裡使用多個分支不能替代這個需求,因為之是以要拆分代碼庫,主要就是想讓 Pull Request 和 issue 彼此隔離。

還有就是,需要可以基于既成事實(曆史)去建立代碼庫派生關系。對新項目來說,這個不是問題。但以 Linux 搬遷為例,這是個問題:一次要把 Linux 所有子系統都搬過來,并且,Github 上已經存在大量的 Linux 代碼庫,彼此間沒有正确的 Github 派生關系。

Pull Request

Pull Request 需要能同時送出到多個代碼庫,但是還可以保一個讨論線索。一次送出給所有代碼庫以外,還要能夠重新指派 Pull Request 到某個代碼庫的不同分支。

并且,Pull Request 的狀态需要因每個代碼庫而不同。一個代碼庫的維護者或許關閉了這個 Pull Request 而不去合并,因為維護者們一緻同意其中一個子系統拉取這個 Pull Request,而那個維護者将會合并和關閉 Pull Request。另一個代碼樹或許要按照無效的請求去關閉這個 Pull Request,因為這個 Pull Request 不适合這個舊版本的代碼庫,或者某個特定廠商的派生代碼庫。甚至更有意思的是,一個 Pull Request 或許會被合并很多次,在每個子系統的代碼庫裡,且使用不同的合并送出 (Commit ID)。

Issues

和 Pull Request 類似,問題跟蹤也需要按照多個代碼庫隔離,且需要有移動的能力。一個例子就是,一個 bug 可能先報告給了一個發行版的核心代碼倉庫,然後經過 bug 分析,這個驅動的 bug 也在最新的開發分支存在,是以這個 issue 不但和這個代碼庫有關,還需加上主線 upstream 分支,或許更多的代碼倉庫。

Issue 狀态應該在不同代碼庫獨立,因為一次在一個倉庫的 push,不會了解在所有的倉庫可用。甚至,backport 到老核心和發行版,可能需要更多的工作,而一些代碼庫可以決定不值得修這個 bug,以 WONTFIX 關閉這個 bug,但是同時在其它代碼庫裡它辨別為成功解決。(筆者之前的公司,這些功能都有啊。的确這是一個商業軟體的缺陷跟蹤系統必須有的功能)

總結: 單體代碼樹,不是單體代碼庫 (Monotree, not Monorepo)

Linux 核心沒打算搬到 Github 上去。但是在 Github 上支援搬遷“單體代碼樹,不是單體代碼庫”這種類型的項目到 Github 上,将會是對所有存量的巨型項目非常大的好處。

Tradeoff 無處不在

Daniel 的文章到此為止,接下來是筆者談一談自己的看法,一家之言,僅供參考。

首先,因為 Github 的設計問題,把緊耦合的代碼庫強行拆分成多個子項目确實是一個大問題。但是,對原本就是松耦合的代碼之間,拆分的好處也是明顯的。大家可以按照各自的方向,節奏,去開發和維護。廣義的看,Glibc 和 Linux 核心的關系就是這樣,之是以運轉良好,穩定的系統調用接口是關鍵。是以,代碼庫的劃分邊界應該以是否存在穩定的協定和接口為前提。

其次, Github 作為世界上最大的同性社交平台, 解決的是個體開源程式愛好者的需求問題。是以,在管理模型上,Github 不支援“單體代碼樹,不是單體代碼庫” 這種大型開源項目需要的模式也不奇怪。作為産品的定位來說,或許目前 Github 的設計,正是它成功的一個原因。正是它才讓 Git 從少數 Linux 核心開發者手裡迅速傳播到整個程式員群體。

最後,以 KISS (keep it simple stupid) 的原則看,對主流使用者足夠傻瓜化,是 Github 的最大優勢。但随着 Github 的影響越來越大,越來越多的企業群組織開始在其上托管項目,或許 Daniel 的建議值得認真考慮?至少,企業使用者才有可能付出真金白銀呢。

看來,不單在技術上我們面臨各種 tradeoff,在産品的發展方向上,也是一樣的。誰知道這篇文章所诟病的 Github 的缺點,不是當初 Github 産品經理的深度思考後的決定呢?而 Daniel 的建議,說不定又是新形勢下滿足大企業需求的一個需要呢?

—— 完 ——

加入龍蜥社群

加入微信群:添加社群助理-龍蜥社群小龍(微信:openanolis_assis),備注【龍蜥】拉你入群;加入釘釘群:掃描下方釘釘群二維碼。歡迎開發者/使用者加入龍蜥社群(OpenAnolis)交流,共同推進龍蜥社群的發展,一起打造一個活躍的、健康的開源作業系統生态!

Linux核心社群遷移到github?
Linux核心社群遷移到github?

龍蜥社群釘釘交流群                            龍蜥社群-小龍

關于龍蜥社群

龍蜥社群(OpenAnolis)是由企事業機關、高等院校、科研機關、非營利性組織、個人等按照自願、平等、開源、協作的基礎上組成的非盈利性開源社群。龍蜥社群成立于2020年9月,旨在建構一個開源、中立、開放的Linux上遊發行版社群及創新平台。

短期目标是開發龍蜥作業系統(Anolis OS)作為CentOS替代版,重新建構一個相容國際Linux主流廠商發行版。中長期目标是探索打造一個面向未來的作業系統,建立統一的開源作業系統生态,孵化創新開源項目,繁榮開源生态。

龍蜥OS 8.4已釋出,支援x86_64和ARM64架構,完善适配Intel、飛騰、海光、兆芯、鲲鵬晶片。

歡迎下載下傳:

https://openanolis.cn/download

加入我們,一起打造面向未來的開源作業系統!

https://openanolis.cn