程序員必備技能之 Git 的體系結構與歷史
十幾年前,Linux 之父 Linus Torvalds 在個人休假時,發現自己掌控下的 Linux 內核在開發過程中遇到了一些問題,於是鬱悶無比,經過多天的琢磨與實踐之後研發出一款小工具——Git,以望幫助更多的開發者有效、高速地處理從很小到非常龐大的項目版本管理。萬萬沒想到,經過多年的發展之後,彼時名不經傳的 Git 如今早已佔據行業的主導地位,而這其中到底經歷了些什麼?
作者 | Will Hay Jr.
譯者 | 梁蕊
責編 | 屠敏
出品 | CSDN(ID:CSDNNews)
截至 2018 年,全球知名的 IT 技術問答網站 Stack Overflow 調查的 74000 開發人員中,將近 90% 的人表示更喜歡使用 Git 進行版本控制。根據調查,Git 在其他所有分散式版本控制系統中佔主導地位,並且從 2017 年開始使用率幾乎增長了 20%。然而,Git 並不總是像這樣普及。讓我們一起看看它上升到大眾化的歷程。
早期歷史
Git 誕生於 Linux 內核社區對可用的 VCSs(版本控制系統)的挫敗感。Linux 內核的發展在當時是相當不尋常的:項目中有大量的貢獻者而且貢獻者的參與程度和對代碼知識庫的了解有很大的差異。由於 Linux 內核不尋常的發展狀況,開發人員很難找到適合他們需求的 VCSs(版本控制系統)。於是他們選擇了 BitKeeper 和並發修訂系統(CVS),每個系統有一組核心開發人員去負責管理內核的開發。BitKeeper 提供分散式版本控制,而 CVS 是一個客戶端-服務端版本控制系統,它可以讓開發人員「簽出」項目的副本,進行更改,然後將他們的改變「簽入」到服務端。
在 2005 年初期,BitKeeper 的版權持有人 Larry McVoy 宣布撤銷允許免費使用 BitKeeper 軟體的許可。他聲稱,正在創建與 BitKeeper 反向交互軟體的澳大利亞程序設計師 Andrew Tridgell 反向設計了 BitKeeper 的源代碼,這樣違背了它的許可。許多依賴 BitKeeper 免費軟體去開發 Linux 內核的 Linux 核心開發者現在已經無法繼續使用它了。
Linux 社區與 BitKeeper 的關係已經開始有了衝突,但是他們希望在離開 BitKeeper 之前有一個可行的選擇。Linux 內核主要的開發人員 Linus Torvalds 在看到沒有其他免費的選擇可以滿足他們的需求之後就開始開發一個新的 VCS。在發送到內核郵件列表的電子郵件中,Linus 表示了他對 BitKeeper 有多麼滿意和 BitKeeper 為 Linux 內核開發做了什麼,主要是它幫助整個團隊更細粒度的保留了一些更改和更改追蹤集的視圖。值得注意的是,雖然 BitKeeper 沒有成功,但它依然在改進內核開發方式上非常有幫助。
早期發展
為了向團隊提供 BitKeeper 的替代品,Linus 概述了一些新版本控制系統的某些設計標準。他想要保持 BitKeeper 提供給團隊的一些好處,同時進行一些改進。
他主要強調了三個主要特徵:防止內容腐敗的保障措施、高可用性和分散式開發工作流。Linus 還強調了補丁不應該超過 3 秒,引用源控制管理系統,該系統需要花費 30 秒來推送補丁並且更新相關的元數據。對於從事開發 Linux 內核的 250 名開發人員來說,這樣的一個系統,顯然不能很好的擴展。儘管 BitKeeper 對 Git 的創建有早期的影響,但 Git 比 BitKeeper 允許更多的分散式和本地工作流。項目協作者可以在存儲庫離線工作,增量提交,確定何時發布他們的工作,選擇共享哪些更改,並將他們的更改推送到不同的分支。
架構概述
一個版本控制系統通常有三個核心功能,所有的這些功能都被 Linus 內置在 Git 中。它必須能夠存儲內容、追蹤對內容所做的更改(所有的歷史紀錄,包括合併元數據)、與項目協作者選擇分發內容和提交歷史記錄。
Git 使用了有向無環圖(DAG)進行內容存儲以及提交和合併歷史記錄。DAG 是一個有著有限數量的頂點和邊(頂點之間的連接),沒有包含循環的有向圖(是非周期性的)。非循環意味著沒有辦法從A節點到B節點,並通過任意數量的邊返回到 A 節點。DAG 也必須有拓撲排序,這意味著在一個序列中,所有的頂點都有直接從最開始的節點指向最後節點的邊(如下圖中箭頭從左上角指向右下角所示)。
Git 還將這種有向無環圖結構應用在存儲內容上。Git 實質上是一個可定址的文件系統,它由構成層次結構的對象組成,這些層次結構反映了內容的文件系統樹。Git 有三種主要的原始內容,它用來表示存儲庫存儲的內容:樹、blob和提交。所有的內容實質上都作為樹或 blob 對象存儲。Blob 是存儲在存儲庫中的文件,樹對象引用了其他子樹或者 blob。你可以認為 blob 是存儲內容的文件,而樹就像是目錄。在另一方面,提交對象有三個主要的屬性,它指向了在提交時代表項目頂級快照水平的文件系統樹、它還包含對它之前提交的引用,提交作者的欄位和可選的提交信息。
所有這些對象基元都有 40 位的 SHA 哈希。兩個相同的對象將會有相同的哈希值,不同的對象將會有不同的哈希值。通過使用 SHA 哈希作為參考標識, Git 能夠有效的計算差異。為了防止數據損壞,可以重新計算一個對象的哈希值,以便輕易的識別出損壞或丟失的數據。
Git 還使用了有向無環圖來追蹤內容更改的歷史記錄。如上所述,每一個提交對象都包含它祖先的元數據,也就是說一次提交可以有任意數量的父提交。Git 使用有向無環圖的特性來存儲內容、保留歷史追蹤記錄和合併歷史記錄,這樣允許它保留完整的分支功能,因為文件的歷史記錄將其目錄結構一直鏈接到根目錄和提交對象。
分支策略
當我們將「feature7」分支合併到 master 分支時,Git 會執行「fast-forward」合併策略,向前移動主分支指針。只有將當前「feature7」分支的提交歷史記錄要合併到 master 分支的最新提交時,才會使用「fast-forward」合併。
當你所在分支的提交並不是你正在合併的分支的直接祖先時,Git 使用不同的合併策略,這意味著你的開發歷史不同。在這種情況下,Git 使用「遞歸」策略並執行三向合併。Git 創建了文件狀態的新快照和指向新快照的分支提交對象。此時這個合併提交對象有兩個父類,指向兩個分支的頭部的提交對象被合併在一起。Git 使用非線性內容存儲策略和提交歷史記錄的系統,可以將項目的兩個分支無縫的合併在一起。
分布和初始化
Git 使用分散式模型處理項目協作者之間的內容和歷史分布,用戶可以離線工作並在本地存儲庫上進行提交。每個協作者都有一個 Git 存儲庫的副本,他們可以離線工作,進行更改,提交更改,並且從遠程存儲庫中提取新的更改以保持本地副本最新。當協作者準備好共享他們的更改時,他們可以將這些更改推送到可公開訪問的存儲庫,供其他協作者訪問。一旦公共存儲庫驗證了這個提交可以應用於被推送到的分支,就會為公共存儲庫創建在本地存儲庫中創建和存儲的相同對象,並更新該存儲庫以供所有協作者訪問。
要初始化本地 Git 存儲庫,請運行 "Git init" 命令。這將在本地文件系統上創建一個新初始化的存儲庫,在當前工作目錄中創建一個 .git 目錄。.git 目錄是根 "工作目錄" 的子目錄,並作為實際的本地存儲庫,包含各種配置文件,對象資料庫,分支的引用指針以及可在項目生命周期的各個點運行的其他腳本。一旦你對文件進行了修改,就會創建另一個重要文件,Git index,位於 .git/index 下。Git index 文件是工作目錄和本地存儲庫之間的暫存區域,在要提交的一個或多個文件中暫存特定的更改。
值得注意的缺點
Git 使用工具包編寫的設計理念與 Linux 社區中使用和構建的命令行工具相同。雖然工具包設計為用戶提供了 Git 大量功能更細粒度,更低級別的訪問,但由於大量的命令可能對許多不熟悉命令行工具的人或其他 VCS 使用者不直觀,所以新用戶的學習曲線很陡峭。Git 還缺乏鏈接和構建到其他服務和應用的能力。許多在 Git 上構建或正在構建工具的應用程序開發者抱怨缺乏可連接的庫。Git 的二進位文件是不可重入的,這意味著它不能在執行過程中被中斷,之後安全地再次被調用。這會強制一些使用該二進位文件的應用程序或 Web 服務執行並調用該二進位文件後,在再次調用它之前等待其完全執行,影響應用程序的速度。有幾個項目正在努力彌補這種缺乏可連接庫的情況,其中最著名的是 libgit2,一個 Git 的跨平台可連接庫實現。
Git 的另一些問題是它無法處理大文件或者大量的文件。如果你的項目包含很多非文本文件,比如圖像,那麼經常地更新 Git 將變得非常慢,使得最大的實際存儲庫大小只有幾 GB。
最後
Git 的設計幾乎完全符合了 Linus 和 Linux 團隊所尋找的需求。它滿足了 Linus 描述的 VCS 的每個核心需求,並且在使用時儘可能優雅且簡單地做到儘可能高效。雖然 Git 存在一些小問題,但它的設計非常好,並且將在未來的許多年內繼續成為 VCS 的首選。
原文:https://medium.com/@willhayjr/the-architecture-and-history-of-git-a-distributed-version-control-system-62b17dd37742本文為 CSDN 翻譯,如需轉載,請註明來源出處。
※程序員,快收下這份比特幣「勒索病毒」應對須知!
※從 C+98到C+17,元編程是如何演進的?
TAG:CSDN |