Git和GitHub上的公共存儲都是OSS開發的事實標準。微軟做了許多OSS開發,我們想讓我們的TFS和團隊服務等DevOps工具可以和這些工作流一起工作得很好。
我們希望微軟里所有團隊都使用相同的版本控制系統。標準化將使得人們在項目之間轉換,以及形成資深專業經驗的過程變得容易。因為OSS是捆綁到Git上的,而且我們也做了許多OSS開發,那Git就自然而然的成為我們的首選了。
我們希望能響應并支持社區和我們的DevOps客戶希望的方向。Git很明顯就是現代版本控制系統的領頭羊。
但如Brian Harry所說,這有一些問題:
關于選擇Git有許多爭論,最主要的一個是規模問題。沒有多少公司會有我們這么大規模的代碼庫。特別是Windows和Office(還有一些其它的),規模非常巨大。有幾千個工程師,幾百萬個文件,幾千臺構建服務器在不斷的構建它——老實說,這是令人難以置信的。說得更清楚些,當我在這篇貼子中提到Window的時候,我實際說的是一個非常模糊的概念,這包括了用于PC、手機、服務器、HoloLens、Xbox、物聯網等等方面的所有Windows系統。而Git是一個分布式的版本控制系統(distributed version control system,DVCS)。它會把整個代碼庫和所有的文件歷史都拷到你自己的機器上。要是對Windows也這么做的話會被人笑話的(事實上我們也的確被別人笑話了很多次)。TFVC和Source Depot都針對大型代碼庫和團隊做過非常專門的優化。Git在這樣的問題上卻從來沒有過先例(哪怕類似規模的也行),所以很多人都斷定這種方法肯定行不通。
Reddit用戶Ruud-v-A為這個問題提供了一些參考信息:
Linux內核的代碼庫有1.2GB了,開發了大約12年,有5700個文件。2005年第一次提交時記錄過,將整個開發歷史都導入進來一共是3.2GB。3.2GB對應5700個文件,由此推算那350萬個文件就會需要270GB。
Chromium的代碼庫(包含了Webkit從2001年開始的歷史代碼)大小時11GB,有24.6萬個文件。由此推算要20年并且有350萬個文件的話,需要196GB的空間。
將Windows代碼庫拆分成合適大小的子代碼庫這條路也行不通。假如當初一開始的時候就是這么做的,那還有可能,可是到現在代碼庫已經這么大了,而且又發展了這么久,要再想回頭把它拆分開,這事實上并不可能了。Brian繼續說:
這意味著我們必須開始著手將Git增強,讓它可以支持幾百萬個文件,幾百G的大小,可以由幾千個開發者一起使用。同時提供一點參考信息,即使是Source Depot也沒辦法支持得了整個Windows的代碼庫規模。它被拆分成了40多個代碼庫,所以我們才能將它擴展,但必須在它們之上構建一層,這樣在許多情況下,才能像使用一個代碼庫一樣使用它們。抽象當然并不完美,有時也會造成一些沖突。
為什么不干脆把歷史版本都丟掉,然后重新開始呢?SuperImaginativeName給出了一個說法:
NT內核、NT的驅動、子系統、API、硬件驅動、Win32 API等,全都被其它系統所依賴,包括客戶。你覺得為什么你可以在Windows上運行一個已經開發了30年的程序呢?沒有這些歷史版本,以內核團隊為例,他們就不會記得15年前在一款特別的CPU上必須設置一個特殊的標記位,因為它的指令集架構中有個小BUG,會導致用戶無法正常運行某個舊的應用程序。如果把這些歷史版本丟掉了,那也就意味著丟掉了非常大量的信息。你總不能期望每個開發者都能用腦子記住為什么一個特別的系統是那樣工作的。能想像某段奇怪的代碼看起來好象不怎么正確,但事實上它卻能讓你免于遭受文件損壞之苦嗎?如果只是簡單地提交一個新補丁,再注釋上“解決了一個奇怪的問題,真不明白為什么以前沒人發現它”,這么做的后果會是災難性的。再想想去查看一下代碼歷史,然后最終恍然大悟原來這么做是有道理的,這兩種行為哪個才更正確?Windows代碼的開發是非常嚴謹的,所有東西都是要經過審核的。
在經過了若干次失敗的嘗試,包括嘗試使用Git子模塊等之后,微軟開始開發Git虛擬文件系統了:
我們試過一種“虛擬化”Git的方案。通常當你用Git克隆分支時,它會把所有東西都下載下來。但如果它不下載所有東西,又會怎樣?如果我們可以把底層的存儲虛擬化,讓它只下載需要的東西,又會怎樣?這樣的話克隆一個巨大的300GB的代碼庫就會非常快了。當我執行Git命令,或者在我的文件系統里讀寫文件時,系統會無縫地從云端獲取內容(并且將它保存在本地,這樣以后就是本地訪問數據了)。這種方法的缺點在于沒有離線支持。如果你需要這一點,你就得把所有東西“touch”一遍,假裝在本地有它們,除此之外沒有任何其它缺點了——你仍然會有100%一致的Git體驗。而且對于我們的巨型代碼庫來說,這也是行得通的。
Saeed Noursalehi補充到:
有了GVFS,就意味著他們現在可以有了更好管理的Git使用體驗:每次克隆只需要花幾分鐘,而不用12多個小時;檢出代碼只需要大約30秒,而不是2-3小時;status命令也只需要4-5秒鐘,而不是10分鐘。而且我們還在不斷努力,讓它些數字變得更好(當然,代價是第一次構建會花的時間更多些,因為它要把它構建的所有文件都下載下來,但以后再構建就不會比正常構建更慢了)。
微軟對于Git的投入
要讓這種方法可以正常工作,就要改進Git訪問文件的方式。大家平時不一定會注意到,舊版的Git在訪問本地存儲的文件時,它通常會掃描比它真正需要的更多的文件。如果你去年已經注意到微軟曾向Git OSS項目提交過改進性能的代碼,這就是原因所在了。
Jeremyepling寫道:
我們微軟Git團隊為git/git和git-for-windows做了許多貢獻,來提高Git在Linux、Mac和Windows等上面的性能。在Git 2.10中,我們做了許多工作來使交互式Rebase操作更快。根據包含在Git源代碼中的一種基準測試結果看來,交互式Rebase的最終表現是在Windows速度提升了5倍,在MacOSX上提升了4倍,在Linux也還有3倍的提升。
對Git的一些改進列舉如下:
sha1:在mingw上使用openssl sha1程序 https://github.com/git-for-windows/git/pull/915preload-index:對于跳過worktree的元素不使用lstat https://github.com/git-for-windows/git/pull/955memihash perf https://github.com/git-for-windows/git/pull/964add:用預加載索引和fscache來提高性能 https://github.com/git-for-windows/git/pull/971read-cache:在后臺線程中運行verify_hdr() https://github.com/git-for-windows/git/pull/978read-cache:在檢出過程中提高add_index_entry速度 https://github.com/git-for-windows/git/pull/988string-list:在重新分配string_list時使用ALLOC_GROW宏 https://github.com/git-for-windows/git/pull/991diffcore-rename:提高register_rename_src的速度 https://github.com/git-for-windows/git/pull/996fscache:將找不到的目錄緩存加入fscache https://github.com/git-for-windows/git/pull/994Git虛擬文件系統
Git虛擬文件系統GVFS的原型是在客戶端用一個文件系統驅動,以及一個支持GVFS的Git版本實現的。這需要“Windows 10周年”版,或更新的版本。只要Git庫支持了GVFS,那平時常用的Git命令就仍可以照常使用。GVFS子系統基于文件系統工作,它會在背后從服務器下載任何你需要的文件。
因為GVFS客戶端庫是開源的,這也是大家研究我們是如何在Windows上把虛擬文件系統實現為驅動的好機會。
在服務器端,你需要一些實現GVFS協議的東西。現在,這指的是Visual Studio團隊服務,這協議是開源的,所以別的服務提供商也可以提供相同的能力。GVFS協議本身也是很簡單的,包含四個類REST的接口。