Git 的故事:這一次沒這麼好玩

目錄

Linus Torvalds 曾寫了一本提到,當初創造 Linux 只是因為好玩,卻意外掀起一場革命。Git 是 Linus 的第二代表作,同樣也是意外的革命,是現在軟體工程師的標配,但至少對 Linus 本人來說,它的起源可就沒這麼好玩了。

Linus 擴展不了

1998 年是 Linux 風光的一年,許多大公司,如昇陽、IBM 和甲骨文,都紛紛投入 Linux 的業務。那年春天,Linus 的二女兒出生,他們一家從芬蘭搬到美國加州也差不多一年,生活步入正軌。雖然 Linux 還尚未給 Linus 帶來什麼收益,但 Linus 也可算是事業家庭兩得意。

反觀 Linux Kernel 的開發者社群,隨著愈來愈多人加入開發,既有的合作方式開始力不從心。Linus 開始顯得沒辦法跟上開發者們修改程式碼的速度,逐漸成為瓶頸。

1998 年 9 月 28 日,Linus 和往常一樣,讀著 Linux Kernel 郵件列表上的信。

請不要浪費時間送修補了,這些在 vger 上早就修好了。

Linus 看到這句不太高興。一直以來,Linux 程式碼的修改重度仰賴 Linus 本人,Linus 本人就是版本控管。如果你要修改程式碼,寄封信到郵件列表上,Linus 看到了並認可,就會將你的修補送進他自己的版本,然後不時在 FTP 上釋出新版本。Linus 喜歡這樣的合作方式,因為他可以掌控一切變更,大家也信任 Linus,覺得 Linux 本來就該由 Linus 掌控。

但自從 David Miller,一位 Linux Kernel 的資深開發者,架設了一個名為 vger 的 CVS 伺服器,有些人就以為可以繞過 Linus 本人,將變更送到 vger 就沒事了。這不是 Linus 第一次遇到同樣的問題,他在郵件列表上不悅的回應

「在 vger 上就不要浪費時間」這個說法完全是愚蠢的,因為 vger 上很多東西可能永遠都不會進 2.2。

於是幾個開發者與 Linus 展開一場激烈的筆戰,他們開始陳情,說 Linus 回信太慢,甚至有時候要寄三次才會得到這個「仁慈獨裁者」的親回。

「這些傢伙也不照照鏡子,」Linus 心想:「我一天要看多少信,如果連寄三次信都嫌麻煩,那這個修補我寧可不要。」 Linus 在討論串留下這個訊息隨即怒離:

這次的討論只是讓我煩燥,增加我的壓力。走開吧,不要再 CC 我了,我要度假去了,我不要再聽到任何有關它的事。總之,滾出我的信箱就對了。

Linus 情緒性的發言反而讓一些人開始提出正面的幫助。

開源運動代表人物之一 Eric S. Raymond,著名文章《大教堂和市集》就是他寫的。看到 Linus 的怒離文,他冷靜地呼籲

各位,這些是職業倦怠的早期徵兆。Linus 一直以來的毅心確實令人敬佩,但他也是有極限的。我們所有人(是的,也包括你 Linus)要一起想辦法幫這個重要人物減少壓力,而不是增加壓力。

另一位伸出援手的是 Larry McVoy,在一篇標題為「成長痛的解決方案」的郵件,他這麼寫:

問題癥結在於 Linus 擴展不了(“Linus doesn’t scale”)。我們不能期待 Linus 一個人能跟上 Kernel 變化的速度,我們也不想看到 Linus 失去對 Kernel 的掌控,他已一再證明自己非常適任此職。

(解決方案基本上是)將工作分擔給 Linus 身邊的幾個人,要做到分擔,我們要導入新工具。

(這個新工具是)一個分散式版本控管系統⋯⋯

Larry 當時正好在開發一套新的版控軟體,叫做 BitKeeper。

BitKeeper 的起源

在 1990 年代早期,昇陽導入了一套名為 Network Software Environment (NSE) 的內部工具來管理程式碼,但 NSE 太慢了,使用體驗極差,有些工程師甚至為此憤而離職。

Larry McVoy 不僅是個資深的作業系統開發者,過去也做過許多效能相關的工作,所以昇陽的主管就找上了 Larry 來調教一下 NSE 的效能。

Larry 一看 NSE 的程式碼嚇了一跳,「這東西很多地方當初設計時都沒有考量到效能。」他還發現 NSE 的底層其實是 SCCS,SCCS 是 1970 年代的版本控管軟體,出現比 CVS 和 Subversion 還早。與其修改體質不良 NSE,Larry 選了另一條路:他用 Perl 寫了 NSElite,在 SCCS 之上實作了 resync/resolve 指令,基本上類似現今 Git 的 clone/pull/push 指令。

NSElite 比 NSE 快多了,所以昇陽的工程師一個一個背棄 NSE,改用 NSElite。昇陽某 VP 見狀,覺得有商機,就組了一個八人團隊,想要用 C++ 重寫 Larry 寫的 Perl 腳本,將其產品化為 TeamWare

TeamWare 大概是最早的分散式版控系統,後來昇陽 Solaris 作業系統的開發全是藉助於它。工程師們用過 TeamWare 都表示回不去了:不同於 CVS 和 Subversion,TeamWare 讓你將整個專案複製到你自己的機器,你可以盡情地在本地端提交變更,等到準備好再將你自己本地的版本合併回遠端版本。

這八人團隊的成員本來是寫 C 語言的,當時 C++ 是新的火熱語言,他們邊學 C++ 邊開發 TeamWare。在 TeamWare 還未完成前,Larry 曾嘗試繼續開發 NSElite,但這無疑是給 TeamWare 團隊難堪:一個人用 Perl 竟然比八個人用 C++ 還來得快,VP 見狀便告訴 Larry:「這件事已經上報給 Scooter(即 Scott McNealy,昇陽的執行長),如果你再發佈一次,你就被解雇了。」

為此,1991 年,Larry 停止 NSElite 的開發,但建造一個分散式版控軟體的念頭始終在他心中揮之不去。他本來以為會有其他商用軟體會跟進 TeamWare 的腳步,但並沒有。1997 年 Larry 開始著手開發一款名為 BitKeeper 的分散式版控軟體。然而,直到 1998 年 9 月,當他看到郵件列表上 Linus 處於崩潰邊緣,這才真正激勵他開始認真看待 BitKeeper 這個專案。

Linux Kernel 採用 BitKeeper

1998 年秋天某日,Larry 邀請了 Linus Torvalds、David Miller、Richard Henderson 來家中,吃完晚餐後,他們席地而坐,開始協商對策要如何減少 Linus 的工作量。他們在地板畫了三、四個小時的圖,這些圖大致上就是 TeamWare 在昇陽內部行之有年的運作方式,Larry 對此十分熟悉。

在這個框架中,開發者們可利用 BitKeeper 各自開發,不互相干擾,而且在 Linus 這邊做最後整合時,能夠不失去修改的歷史記錄,讓 Linus 能看出一個變更的來龍去脈,審查程式碼時更容易。

「好吧,如果你做出來,而且跑起來跟你說的一樣,我就會用它。」Linus 說道。
「沒問題,這我以前做過,大概需要六個月。」Larry 回答。

Larry 馬上就意識到他低估了問題的複雜度,所以他成立了一間名為 BitMover 公司,找了幾個熟悉版控系統的好手,一起打造 BitKeeper。19 個月後,2000 年 5 月,BitKeeper 公開釋出第一版,此時 BitMover 是一個七人團隊。

第一版的 BitKeeper 有一個命令列工具 bk,也有一些圖形介面的工具。其中 bk clone/pull/push 命令作用有猶如 git clone/pull/push

當時昇陽的 TeamWare 已是有口皆碑,而 BitKeeper 更是 TeamWare 的強化版。例如,TeamWare 只允許在 NFS 檔案系統間傳遞資料,而 BitKeeper 可以走 HTTP 來傳輸檔案,實現了真正的分散。因此不久後,BitKeeper 就為 BitMover 帶來充沛的現金流,到了 2002 年,BitMover 團隊成長到了 25 人,完全自給自足,沒靠外部資金。

Larry McVoy, Linux Expo, 1999
Larry McVoy, Linux Expo, 1999

2002 年 1 月,Linus 工作量太大的問題又再度浮現,開發者送出的修補不是等很久才遲遲回應,不然就是被忽略。有人寫了一篇「小小的提議」嘗試改善這個問題。討論串中,有人隨口提到「bitkeeper 真是個好工具」,喚醒了三年前,Larry 家中的那頓晚餐,在 Linus 腦中種下的那顆種子。Linus 問道:「有多少人在用 bitkeeper 做 kernel 開發?」

果不其然,當時已經有些 Kernel 開發者早就在使用 BitKeeper。Linux PowerPC (PPC) 團隊早在 1999 年 12 月就開始試用 BitKeeper,BitMover 為了幫助他們,架設了 bkbits.net 伺服器。

過沒幾天,2002 年 2 月 5 日,郵件列表上就看到 Linus 開始在測試 BitKeeper。此後,Linux Kernel 主要開發者們也跟進開始採用 BitKeeper。你不一定要用 BitKeeper 才能參與開發,但如果你是 BitKeeper 的使用者,流程大概是:

# 下載倉儲(repository)
bk clone bk://linux.bkbits.net/linux-2.5 linux-2.5
bk clone linux-2.5 alpha-2.5

# 從另一個地方下載變更
cd alpha-2.5
bk pull bk://gkernel.bkbits.net/alpha-2.5

# 編輯檔案並將變更上傳回遠端
bk vi fs/inode.c 
bk push bk://gkernel@bkbits.net/alpha-2.5 

若要將變更送給 Linus,你就再寄封信到郵件列表上,內容大致是:

Here is an update for something something...

Please pull from: bk://gkernel.bkbits.net/alpha-2.5

example/file1.c | 6 ++++++
example/file2.c | 4 ----
2 files changed, 6 insertions(+), 4 deletions(-)
-----

沒有免費的 BitKeeper 了

Larry McVoy 讓 Linux Kernel 開發者免費使用 BitKeeper,但免費是有代價的。例如,他們的免費用戶的使用執照裡規定:

  • 你不可以關掉 Open Logging──此機制會將使用紀錄傳送至 BitMover 伺服器。
  • 如果你從事的是版控軟體的業務,你不可以免費使用 BitKeeper。
  • 如果你要讓 BitKeeper 和其他同類軟體一起運行,你必須徵求 BitMover 同意。

Linux 是開源軟體的始祖,社群裡有很多自由軟體的擁護者,對這些條款不是嗤之以鼻,就是敬而遠之。可是對 Linus 和主要的 Kernel 開發者來說,重點是 BitKeeper 減輕了他們的工作量,況且當時沒有更好的替代工具,他們也就接受 BitKeeper 的使用條款,以換取它的便利。

以 Linus 來說,他對專用軟體(proprietary software)一直持開放態度,他當初會選用自由軟體 GPL 作為 Linux Kernel 的授權協議,純粹是因為他不樂見商業市場「玷污」了 Linux,他的方法就是讓 Linux 保持開放,而GPL 剛好在那邊符合他的需求,他就拿來用了。但他從不覺得所有軟體都該走自由軟體的路線,他覺得作者有權以任何他想要的方式散佈軟體,怎麼使用軟體不應該是社會運動。

自由軟體的擁護者可不這麼想,比較極端的甚至會認為專用軟體都是邪惡的。這群駭客寧可擁有修改軟體的自由,也不要 BitKeeper 的「便利」。

Larry 備受來自社群的壓力,為解決此問題,BitKeeper 團隊在 2003 年架設了一個 BitKeeper 至 CVS 的鏡像,讓那些不想裝 BitKeeper 的人,也可以透過 CVS 來取得程式碼的修改歷史。但從 CVS 取得的歷史資料和 BitKeeper 上的並不完整,人們還是不滿意:「為什麼我們的資料要被鎖在 BitKeeper 的專用格式裡,而且還明文禁止我們用其他程式去讀取我們自己的資料?」

有鑑於此,Samba 和 rsync 的作者,澳洲程式設計師 Andrew Tridgell(簡稱 Tridge),在 2005 年 2 月開始著手寫一個免費的 BitKeeper 客戶端程式,要解決自由軟體用戶面臨的問題。

Tridge 做了以下嘗試。

「這裡有一個 BitKeeper 位址,bk://thunk.org:5000,用 telnet 連上去試試看。」

$ telnet thunk.org 5000
Trying 69.25.196.29...
Connected to thunk.org.
Escape character is '^]'.

「連上去了,何不輸入 help 命令?」

help
? - print this help
abort - abort resolve
check - check repository
clone - clone the current repository
help - print this help
httpget - http get command
[...]

「BitKeeper 伺服器居然這麼好心,列出所有指令。」
「所以 clone 應該就是下載倉儲用的指令吧?」

他接著輸入 clone,發現輸出僅是一連串 SCCS 格式的檔案。至此,「反向工程」的工作已大功告成,剩下的只是把程式寫出來。

不知 Linus 是從何得知 Tridge 在幹的事,或許是 Tridge 自己私下告訴他的。總之,Linus 得知後就將這件事告訴他的好朋友 Larry。Larry 聽到後無法接受,因為一個免費的第三方客戶端會毀了 BitKeeper 的商業模式,於是 Larry 連手 Linus 和時任 OSDL(即現在的 Linux 基金會)執行長的 Stuart Cohen,要求 Tridge 停手。

Stuart Cohen 選擇冷處理,覺得這不關 OSDL 的事。但 Linus 非常不想失去 BitKeeper,所以很努力地居中調解,試著找到出雙方都能接受的折衷方案。Tridge 堅定地認為他沒錯,他甚至認為有一個第三方客戶端對 BitKeeper 和 Kernel 開發者們是雙贏 。他在 2005 年 4 月在 Freshmeat(後來併到 SourceForge)發佈 SourcePuller,裡面的 README 文件寫道:

有一個開源的客戶端本應是一大進步,BitMover 可以在商業環境繼續成長,自由軟體社群也能使用並從中受益。

BitMover 有權為 BitKeeper 制定使用規範。當然,BitMover 這樣描繪我,我很失望。但請理解他們承受了很大的壓力,而在壓力大的情況下,人們會說錯話。

Larry 不認同這會是雙贏,支持 Kernel 的開發是要成本的,沒收錢的就算了,還可能會違害到既有的商業模式,所以為了維護 BitMover 的生計,他選擇撤下 BitKeeper 的免費使用執照。

Linus 交涉了幾個禮拜,也不想再當和事佬了。這下沒有免費的 BitKeeper 可以用,Linus 怒了,他公開在論譠上責備 Tridge,說他「破壞別人的創新」、「惡搞別人」。當然,Linus 大可付錢,但他沒辦法要求其他 Kernel 開發者也付錢使用 BitKeeper,所以他必須另闢途徑。貼文中他寫道:

現在,我得收拾善後。因為最好的版控工具不能用了,我會自己寫一個給 Kernel 用。但沒關係──我自己的問題,我自己解決,謝謝你了(Tridge)。

2005 年 4 月 6 日,Linus 在郵件列表上宣佈 Linux Kernel 與 BitKeeper 要分手的消息。他首先感謝 Larry 和他團隊這三年來的幫助。接著他說他會離線一個禮拜,找出一個替代方案。最後他提到:

用不著跟我說 subversion 了,如果你真的想給建議,去看看 monotone,它應該是最可行的替代方案。

Monotone

Monotone 的原作者是 Graydon Hoare。2001 年,住在加拿大的 Graydon 和他澳洲朋友為了讓跨時區合作更容易一些,他們打造了一個類似現在的持續整合(CI)系統(當時 CI 還不是顯學),確保程式碼無時無刻都通過測試。

2002 年,Graydon 開始對版本控管結合 CI 產生興趣,當時他找到只有 Aegis 有這樣的概念。同時,Graydon 又看到朋友使用 BitKeeper,他想到 Aegis 結合分散式版控應該是不錯的機會,Monotone 就此應運而生。

值得一提,Graydon 後來加入 Mozilla,並創造了 Rust 程式語言。

好巧不巧,Linus 挑了一個壞的時機點來把玩 Monotone。原本 Monotone 0.7 性能還算快,但自 0.14 開始,Monotone 的開發者開始加入許多驗證機制。就在 Linus 下載 Monotone 前,原作者 Graydon 發佈 0.17 後就跑出度假了,裡頭加了一堆嚴謹的檢查程式碼,以確保資料寫入資料庫前是正確的,但這些程式碼尚未優化,反而拖慢了性能。在 0.17 的發佈紀錄提到

pull 命令可能會非常慢,而且佔用很多 CPU

有人測試用 Monotone 下載它自己,竟然花了兩小時,其中 71 分鐘是 CPU 時間。「沒有腿的樹懶可能都比它快。」那位人士如此形容

Linus 向 Monotone 的開發者回報性能問題。2005 年 4 月 10 日,Monotone 0.18 釋出,許多操作都至少快了一倍。雖然 Linus 也名列在 0.18 的貢獻名單中,但根據 Monotone 開發者之一 Nathaniel Smith 的說法

Linus 其實沒有對 Monotone 貢獻過任何程式碼,或者據我所知,他也沒有對 git 以外的任何版控軟體貢獻過程式碼。除了「這太慢了!」之外,他也沒有提出什麼實質建議 ;-)。他之所以名列為貢獻者,是因為在與他的討論中,我找到了一個測試案例,追蹤到一個重大性能瓶頸。我曾猶豫是否應該把他的名字列上去,因為這可能會讓人產生奇怪的想法,但我想,如果是其他人我也會這麼做,所以⋯⋯(聳肩)。

與此同時,受到 Monotone 的設計啟發,Linus 也從零開始,開始著手寫了一些 C 程式碼。

Git v0.01 初探

2005 年 4 月 7 日,Linus 上傳了一個名叫 Git 的東西,他在郵件列表寫道

這邊有一個小小的挑戰要給瘋狂的駭客們,如果你有想玩玩看很亂(但非常快)的東西,看一下 kernel.org:/pub/linux/kernel/people/torvalds/

第一個將 sparse-git 的變更紀錄樹寄給我的,將會得到一個金色星星和公開表揚,我在裡頭放了很多線索。

這是 Linus 第一次在公開場合提到 Git。

該網址有以下檔案和目錄:

git.git/                  09-Apr-2005 16:09    -
sparse.git/               07-Apr-2005 20:07    -
git-0.01.tar.bz2          07-Apr-2005 14:25   39K
git-0.01.tar.bz2.sign     07-Apr-2005 14:25  248
git-0.01.tar.gz           07-Apr-2005 14:25   40K
...
sparse-git.tar.bz2        08-Apr-2005 17:26   15M

git-0.01.tar.bz2 裡的 C 程式碼加起來大約 1000 行:

---------------------------------------------------------------------
File                             blank        comment           code
---------------------------------------------------------------------
./read-cache.c                      31             14            219
./update-cache.c                    32             23            198
./commit-tree.c                     23             26            128
./show-diff.c                        8              5             73
./cache.h                           17             23             53
./write-tree.c                      11              7             53
./read-tree.c                        4              5             39
./init-db.c                          4             14             38
./Makefile                          14              0             26
./cat-file.c                         2              5             21
---------------------------------------------------------------------
SUM:                               146            122            848
---------------------------------------------------------------------

有別於現在的 Git 有一個單一執行檔 git,Linus 最早上傳的 Git ,編譯後會產生七個獨立執行檔:

  • init-db
  • update-cache
  • show-diff
  • write-tree
  • read-tree
  • commit-tree
  • cat-file

init-db 做的事情很單純,它會在當前目錄下建立一個名為 .dircache/objects 的目錄,然後在 .dircache/objects 裡,再建立 256 個以十六進位數字編號的子目錄,依序為 00, 01, 02, …, 0f, 10, …, ff

.dircache/objects 目錄代表著一個物件資料庫,物件的種類有:

  • 二進位資料(blob)──即檔案內容。
  • 樹(tree)──即目錄,本質上是一些檔案(二進位資料)和目錄(樹)的名稱。
  • 變更集(changeset)──由兩棵樹的名稱定義而成,意義上代表的是 A 樹變更成為了 B 樹。「變更集」是 Git 早期的用詞,之後成為「提交」(commit)。

這裡物件的名稱不是檔名或目錄名,而是物件內容壓縮後的 SHA-1 雜湊。這個設計是 Linus 從 Monotone 學來的,差別在於 Monotone 底層使用 SQLite 儲存 SHA-1 物件名稱和內容,而 Linus 選擇直接使用系統呼叫和檔案系統。

SHA-1 幾乎具有唯一性,因此我們可以假設 Git 資料庫中不會有兩個不同名、但相同內容的物件。假如有個物件的名稱是 ba93e701c0fe6dcd181377068f6b3923babdc150,Git 就會將它儲存在 .dircache/objects/ba/ 目錄下,一個名為 93e701c0fe6dcd181377068f6b3923babdc150 的檔案。

這七個執行檔就是以這個「內容可定址」(content-addressable)檔案系統為中心的七種操作。例如:

  • write-tree 建立一個樹物件,即將某個時間點樹的樣子寫到資料庫。
  • commit-tree 建立一個變更集,即在資料庫中連結兩棵樹,類似現今的 git commit 命令。
  • update-cache 加一個檔案到 .dircache/index 索引中,類似現今 git add 將檔案加到預存區(staging area)。

Linus 當時看到 Monotone 是如何利用 SHA-1 為物件命名,就立刻愛上這個點子,原因無他,就是「簡單」二字。「簡單」也是 Linus 欣賞 Unix 的原因。在《只是為了好玩》一書,他這麼形容 Unix:

Unix 讓我(和大多數人)著迷的地方,就是它簡單的設計。在 Unix 中,幾乎所有事都可以用六個基本操作(稱為「系統呼叫」)來完成。

它給你幾種不同形狀的積木,足以用來建造所有東西,乾淨的設計就該是這個樣子。

Git 也是如此,它的資料模型比 CVS、Subversion、BitKeeper 都要簡單。它儲存的東西基本上就是改變前和改變後的樹,如此而已。它沒有在管哪個檔案、在哪一行做了什麼改變,它也不需要,因為改變前後的樹就埋有這些資訊。

Linus 用兩天寫出來的 Git 原型,功能簡單,沒有多餘的驗證,沒有關聯式資料庫,只有 C 程式碼、SHA-1 雜湊、系統呼叫,完全針對 Linus 自己的需求訂製。而 Monotone 專案當時邁入第三年,功能完善,還要應付各種場合需求。再加上 Monotone 原作者 Graydon 加了一堆未優化的程式碼後跑去度假,家裡沒大人。因此在速度上,Monotone 當時自然是比不過 Git 。

Linus 當初上傳的檔案 sparse-git.tar.bz2,應該是史上第一個 Git 倉儲。Sparse 是 Linus 於 2003 年寫的一個 C 語言的靜態分析器。如果你還對 Linus 出的挑戰題有興趣,sparse-git.tar.bz2 解壓縮後稍微修改就可以用現今的 git log 命令去讀取變更紀錄:

# 假設你在 sparse-git 目錄下
mv .dircache .git
mkdir .git/refs
git log

Git 早期貢獻者

初版 Git 引來熱烈討論,過沒幾天,Linus 就開設了 Git 專用的郵件列表,這讓 Linux Kernel 郵件列表能夠稍微回歸正題。頭一個月,Git 郵件列表就出現了約 2600 則訊息,而 Linux Kernel,史上合作人數最多的軟體專案,同期每月有 7000-9000 則訊息。

對在版本控管領域耕耘已久的專家們來說,Git 只是「另一個專案」,因為 Linus 最初上傳的 Git 只有一些低階操作,連 clone 和 merge 的命令都沒有,離真正可用的版控軟體還差得遠。而 Linus 在說 Git 多快多棒時,無意間貶低了其他版控軟體,這讓以 BitTorrent 聞名的 Bram Cohen 很不滿意,槓上 Linus

Bram 當時正在推廣他自己的同類產品 Codeville。Codeville 在當時已經是個成熟的版控軟體,媲美 Monotone,並且內建十分聰明的合併演算法。看到 Linus 如何與 Git 的早期追隨者討論合併演算法,Bram 覺得 Linus 根本就是門外漢在重造輪子,稱 Git 只是個「週末小專案」。

Bram 說的有道理,但這個小專案不是一般人的小專案,它是 Linus Torvalds,Linux Kernel 創始人的小專案。身為開源軟體界的民間英雄,Linus 的一舉一動都是萬眾矚目,年輕開發者景仰他,視他為榜樣。因此在 Linus 上傳 Git 後,隨即吸引到一群新血參與討論和開發。

其中一個早期貢獻者是來自捷克的 Petr Baudis。Linus 宣布 Git 的消息當天,Petr 就下載了程式碼,為之著迷,並開始貢獻。首先,鑑於早期的 Git 很難用,Petr 在 Git 的基礎上開發了 git-pasky(pasky 是 Petr 的別名),這個專案後來成為 Cogito。如果 Git 的基礎是水管工程(plumbing),Cogito 就是陶瓷(porcelain)馬桶和洗手台,它將 Git 包裝得更容易使用。

在軟體開發用語中,將低階基礎建設比喻成水管工程的用法已不可考,但「陶瓷」一詞用來指高階的包裝工作最早起源自 Git 郵件列表。至今,Git 內部仍使用 plumbing 和 porcelain 二詞來稱呼低階和高階命令。

此外,Petr 還架設了 Git 第一個專案首頁 git.or.cz,以及源碼託管服務 repo.or.cz。這兩個網址一直算是 Git 的「官方」網站──直到 GitHub 取代了它們。

Petr 是從外部貢獻,在 Git 基礎之上向上堆疊,向外架設服務。另一個早期貢獻者,濱野純(Junio Hamano)則是從內部切入,直接貢獻 Git 本身。後來他是更從 Linus 手上接下 Git 維護人的工作,直到今天。

接班人

濱野純(Junio Hamano)是一個來自日本的軟體工程師。大概是 1995 年,畢業後工作不到一年,他就被他任職的公司 Twin Sun 派到洛杉磯,從此在美國定居。在那裡,他遇到了當時也在同公司工作的 Paul Eggert。

Paul Eggert 維護過許多自由或開源軟體專案,包括 RCS(一個更早的版控軟體)和 Tar。目前,他是 UCLA 大學的教授,也是時區資訊資料庫的維護人。

濱野純受到 Paul 的影響,對開源軟體的世界產生興趣。儘管他並非 Kernel 開發者,他仍訂閱了 Linux Kernel 等開源專案的郵件列表,純粹因為好玩

2005 年 4 月,濱野純在郵件列表上看到 Linux Kernel 與 BitKeeper 要分手的消息。濱野純一直想要在開源軟體界大展身手,而這個叫做 Git 的東西便是個好機會──全新專案,沒有歷史包袱,好上手。他下載了壓縮檔,花了約兩個小時,一口氣讀完 Git 初代程式碼,發現寫得很漂亮,感到由衷佩服。

初版 Git 出現後,Linus 馬上又加入了提交(commit)和檢視差異(diff)的命令,但還沒有合併(merge)的功能。

Linus 之前沒有寫過版控軟體,但他用過三年的 BitKeeper,在那之前,他更有十年「版控人體」的經驗,他知道自己想要的合併演算法應該長什麼樣。但合併的邏輯較為複雜,Linus 覺得可能使用腳本語言會比較合適,他寫道

我一直在避免做 merge-tree,因為我希望有其他人來做我所描述的。我不擅長寫腳本語言,這東西用 C 語言來寫會很冗長,所以沒意義。

過了一星期,誰也沒上鉤。濱野純剛好正值兩個專案的空窗期,有多餘時間,於是他用 Perl 把 Linus 想要的東西寫出來,發佈到郵件列表上。

我現在有一個 Perl 腳本利用了 rev-tree、cat-file、⋯⋯還有 merge(從 RCS 來的),又快又髒。

濱野純大概從他的導師 Paul 那邊有學到一些 RCS 的知識,對版控軟體略懂。在郵件中,他還詳細寫了大約 30 個測試用例,涵蓋各種分支情況。

當時已是深夜一點,而小孩早上七點起床,所以 Linus 平常十點就睡了。但看到濱野純的 Perl 腳本,Linus 龍心大悅,忍不住回覆

這就是我想要的,「又快又髒」才會讓事情有進展。

他熱切地繼續與濱野純討論。

和 git-pasky 合併 II」這個討論串原本是 Petr 問 Linus 要不要合併他的版本,後來離題變成討論合併演算法,其間 Linus 也向眾人解譯為什麼 Git 底層不需要處理檔案改名。

從 4 月 14 日半夜的往後 48 小時,濱野純與 Linus 在該討論串上通了十幾封信。濱野純耐心的修改程式碼,要做到 Linus 心目中的合併。字裡行間不難發現濱野純是 Linus 的忠實粉絲。例如,濱野純會引用 Linus 四年前說過的金句「我永遠是對」,也會適度地拍 Linus 的馬屁。

在 4 月 16 日半夜,Linus 突然靈機一動,說他想到一個「狡滑的計劃」。

該死,我的狡滑計劃真是好東西。

或者也許因為它狡猾了,連我自己都感到困惑。但看起來它確實有效,而且幾乎能瞬間完成合併。

Linus 巧妙的重用既有的索引,在其上引入了「階段」的概念,大幅簡化合併演算法的實作。

看到 Linus 的解法,濱野純讚嘆

我很喜歡這個解法,它非常簡單、乾淨、靈活,而且優雅。這是那種我會心甘情願地說「天啊!為什麼我之前沒想到!!!」的東西。

這意味著濱野純之前寫的 Perl 程式碼將淪為白工,但新的解法實在太漂亮,濱野純心服口服。

合併演算法只是個開端,之後濱野純陸續向 Linus 貢獻更多修補,逐漸贏得 Linus 的信任。

Linus 曾說過他不會長期維護 Git,等到時機成熟,他會將 Git 交給別人,然後回去做他的 Linux Kernel 本業。濱野純是最明顯的人選,Linus 欣賞他寫程式的「好品味」。於是,三個多月後,7 月 26 日,Linus 宣佈將 Git 維護人的工作交棒給濱野純。

濱野純也貼了一篇公告

正如有些人在 Linus 宣布之前似乎已經注意到的那樣,kernel.org 上的官方 GIT 倉儲現在由我擁有。如同 Linus 在他的訊息中所說,這並不意味著他要離開我們,所以請不要驚慌。

我也要感謝 Twin Sun(我的雇主)和 NEC,他們承諾將支持我以兼職方式開發 GIT。我預計每週會有 8 到 12 小時的工作時間,晚間和週末仍然是我的自由時間。我暫定的計劃是將星期三和星期六作為主要的 GIT 工作日。

之前,每當想到一個點子,我就把修補丟郵件列表上,看哪個能被接受,完全依賴上游某個有好品味的人來篩選掉不好的。雖然這樣和 Linus 一起工作很有趣,但遺憾的是,我浪費了他很多時間。

從今開始,身為「主倉儲的牧羊人」,我會放慢速度,變得更加謹慎。至少目前,你會看到我的修補會像其他人一樣,在進入主倉儲之前,先在郵件列表上發佈。

後來,在濱野純的帶領下,Git 1.0.0 正式在 12 月 21 日釋出。19 年後的今天(本文發佈於 2024 年 7 月),濱野純任職於谷歌,至今他仍是 Git 的維護人。

本文提及 Linus 的次數比濱野純多,不過 Git 之所以是現在的樣子,最大功臣還是默默耕耘多年的濱野純和其他開發者。「1% 的天分加上 99% 的努力」或許是老梗,但在 Git 與 Linux Kernel 等成功專案裡都是事實。

GitHub 與 Ruby 人

雖然 Git 在早期引來不少關注,但仍是小眾。2006 年 1 月,X Window System 團隊放棄 CVS,改用 Git,濱野純得知後還感到很驚喜,他沒料到像 X Window System 這像的大專案會願意大費周章轉換版控系統。

自 BitKeeper 之後,分散式版管系統如雨後春筍般冒出,除了 Monotone 之外,還有 Mercurial、Darcs、Bazaar、Arch、Fossil 等。聲勢最不容小覻的是 Mercurial,作者 Olivia Mackall 晚了 Git 幾天發佈它,但功能已比當時的 Git 完整,而且更為使用者友善,之後更有 Google Code 和 BitBucket 的背書。總之,版控市場猶如戰國時代,每家都佔有一席之地。

真正把 Git 推上巔峰、變成主流的是 GitHub。或者根據 Linus 所述,是 Ruby 人,一群奇怪的人,讓 Git 一夕爆紅。

2007 年 2 月,Git 1.5 釋出,Git 總算變得好用一點。當時在舊金山的 Ruby 聚會口耳相傳著 Git 這個「新」東西。而 GitHub 的共同創辦人 Tom Preston-Werner,最早是從他的同事 Dave Fayram 聽到 Git。Tom 認為 Dave 是在 Ruby 社群傳播 Git 的「零號病人」。

儘管在 Ruby 社群中 Git 廣受好評,但當時唯一一家 Git 託管服務是 Petr Baudis 的 repo.or.cz,它功能陽春。例如,你的程式碼在上面只能公開,沒有私人倉儲的選項。Tom 覺得其中大有商機。

2007 年,社群媒體當道,Facebook、YouTube、Twitter 都先紅了一波。Tom 萌生了一個名叫 GitHub 的點子:一個給程式設計師用的社群媒體,一個讓程式設計師分享 Git 倉儲,交流意見的集散地(hub)。

2007 年 10 月某天,Tom 在一間舊金山的運動酒吧遇到 Chris Wanstrath。他們之前在 Ruby 聚會上認識,但不算熟識。Tom 主動向 Chris 打了招呼,他們聊了起來,Tom 談起 GitHub 這個點子。Chris 聽到後覺得有趣,就同意加入了。

當時 Tom 和 Chris 都還有正職工作,所以他們每晚和星期六用來開發 GitHub。Tom 設計介面,並使用一個名為 Grit 的 Ruby 套件來操作 Git 倉儲;Chris 則使用 Ruby on Rails 開發網站。

三個月後,他們開始發送邀請給朋友們試用 GitHub。2008 年 2 月,第三個共同創辦人 PJ Hyett 加入。4 月 10 日,GitHub 正式開站,它的副標題是 Social Code Hosting。

GitHub,2008 年 8 月
GitHub,2008 年 8 月

Ruby 的殺手級應用 Rails,在 GitHub 開站前夕就從 Subversion 移到了 GitHub,這無疑是幫 Git 在 Ruby 社群注入更大量的強心針。因為當時在寫 Ruby 的人,八成都是在開發 Rails 應用。看到自己賴以為生的框架也用 GitHub ,更多 Ruby 人也欣然跟隨。

Git 與 GitHub 有衝突的合併

Scott Chacon 不是一個尋常的 Git + Ruby 人,除了寫 Ruby 程式外,他還是一個出色的講師/寫手/傳道者。他會錄製影片、寫文件,教大家怎麼使用 Git。不只如此,他對 Git 內部運作也有深入研究,曾經寫過《Git Internals》這本電子書。

三年來,Git 的「官方」首頁一直是 Petr Baudis 在 2005 年架設的 git.or.cz,而 Scott 想設計一個更為新手友善的 Git 首頁。2008 年 7 月,他將 git.or.cz 上的內容整理過後,開設了一個新的首頁 git-scm.com,並在 Git 郵件列表上詢問 Git 核心開發成員(尤其是 Petr)的意見。

Git 在 Ruby 社群裡雖然已流行了一陣子,但 Git 郵件列表上卻鮮少出現 Ruby 人的蹤跡。Git 核心開發成員,大部分是資深的 C 程式設計師,出沒在郵件列表;而 Ruby 人,大部分是年輕一代的網頁開發者,出沒在實體聚會、網頁式的論譠及 GitHub,搞不好一輩子都沒用過郵件列表。兩個族群互不來往,而 Scott 在 Git 郵件列表上那篇關於 git-scm.com 的訊息,是兩個族群早期的少數幾次接觸之一。

另一個讓 Git 核心成員傷腦筋的地方是 Tom 未經討論,擅自用 Erlang 客製 Git Daemon,以滿足 GitHub 單方面的需求。這是因為第一,Tom 對 C 語言不熟;第二,在郵件列表張貼訊息是一件嚇人的事,列表上都是比你聰明的人,如果你寫的東西沒好好斷行,就會看起來像個白痴。這過程實在太緩慢了,Tom 才會自己來。

git-scm.com 上有一個「託管由 GitHub 贊助」的標語,於是就有人質疑 Scott 背後的動機不單純,也有人藉此表達 GitHub 用 Git 賺錢,Git 核心開發者卻分不到一杯羹的不滿。但整體來說,正面回應居多,最終 git-scm.com 成為 Git 的官方首頁,git.or.cz 功成身退。

Tom 是在某次 Ruby 聚會認識 Scott 的,當時 Tom 心想:「這傢伙未來不是強大的盟友,就是危險的對手。」2008 年 10 月,Scott 加入 GitHub,繼續他的 Git 傳道之路。他寫更多文件,提供顧問服務,到其他公司教大家使用 Git。他還寫了一本書《Pro Git》,為 Git 官方推薦書。GitHub 的大外宣策略驟效了,它成功將 Git 發揚光大到 Ruby 以外的社群,GitHub 自己就是最大受惠者。

2008 年 10 月,谷歌贊助了第一屆 GitTogether 會議,Git 和 GitHub 兩組人馬 20 餘人齊聚在谷歌總部山景城。他們放下之前的成見,因為他們很清楚,合作才會讓彼此變得更強大。

GitTogether 2008
GitTogether 2008

後記

無法與 Git 和 GitHub 競爭,BitKeeper 後來被迫退出市場,它的團隊在 2016 年將其程式碼開源。這個祖父級的版控軟體,曾經是 Git、Mercurial、Monotone 等軟體的靈感來源,如今成了歷史文物,供人觀賞與研究。被問到有何感想,Larry McVoy 這麼回應

事後諸葛最簡單了。BitKeeper 的生意經營得很好,我們運營了 18 年,賺的錢足夠讓我和我的商業夥伴退休,不過還不夠讓每個人都可以選擇退休。

我們曾經有一個類似 github 的服務,現在看來我們應該投入大量資金到那個服務,然後開源 BitKeeper。

我只能說,當你已經有一個金雞母,要割捨它是很困難的。

早知道、早該、早會,我最大的遺憾不是錢,而是 Git 是個糟糕的版本控管系統。它讓我抓狂的是,它的模型只是個壓縮檔伺服器。甚至 Linus 也曾向我承認這個設計很爛。它滿足了 Linus 的需求,但他的需求不代表這是這個世界所需要的。

現在,Larry 享受他的退休生活,他平時喜歡和他的孩子們出海釣魚。

Stack Overflow 在 2022 的調查中,Git 市佔率高達 94%,以至於隔年 Stack Overflow 乾脆放棄問大家用什麼版控系統。

歷史上從來沒有一個版控軟體能如此稱霸市場,下一個能取代 Git 的會是什麼?不少人說可能會與 AI 有關,但沒人說得準,不過可以確信的是,過程中一定會有一連串的偶發事件和一群傑出的駭客。

參考資料

除了文章中的連結以外,其他參考資料表列如下:

  1. Just for Fun: The Story of an Accidental Revolutionary” by Linus Torvalds and David Diamond, 2002
  2. Show HN: BitKeeper – Enterprise-ready version control, now open-source” on Hacker News
  3. Larry McVoy Interview with KernelTrap
  4. Larry McVoy Interview with LinuxWorld
  5. Linuxexpo 1999: Day 2: BitKeeper
  6. Hierarchy, Laboratory and Collective: Unveiling Linux as Innovation, Machination and Constitution” by Tony Cornford, Maha Shaikh, and Claudio Ciborra (2010)
  7. How Tridge reverse engineered BitKeeper” on LWN.net
  8. Tridgell drops Bitkeeper bombshell” on The Register
  9. No More Free BitKeeper” on KernelTrap
  10. The kernel and BitKeeper part ways” on LWN.net
  11. not rocket science (the story of montone and bors)”, Graydon Hoare on LiveJournal
  12. A Git Origin Story” by Zack Brown
  13. 10 Years of Git: An Interview with Git Creator Linus Torvalds” from the Linux Foundation
  14. Petr Baudis on LinkedIn
  15. Junio Hamano on LinkedIn
  16. Version Control Shenanigans”, Bram Cohen on LiveJournal
  17. Gitメンテナ 濱野 純”, 技術評論社
  18. How I Turned Down $300,000 from Microsoft to go Full-Time on GitHub” by Tom Preston-Werner
  19. Replacing Git with Git featuring Scott Chacon, the Changelog podcast
  20. GitTogether Group Photo, Junio Hamano on LiveJournal
  21. Larry McVoy’s resume

討論