Git算不算程式設計師的必備技能?

Git的背後有著一個非常精彩的成功故事。2005年4月,Linus Torvalds因不滿當時任何一個可用的開源版本控制系統,就親自著手實現了Git

時至今日,如果我們在Google中搜索“git version control”這幾個關鍵詞,都會看到數以百萬計的返回結果。Git已經儼然成為了新型開源項目的一個標準。許多大型的開源項目都已經或正在計劃遷移到Git上來。Git算不算程序員的必備技能?答案是肯定的。

為什麼要用Git?

站在巨人的肩膀上,我們要特別感謝Linus Torvalds、Junio C. Hamano以及Git項目的眾多提交者,是他們給開發者社區帶來了這個奇妙的工具。

  • Git允許我們利用分支來開展工作:在一個由多個開發者並行協作的項目中,開發者各自會有很多不同的開發路線。Git的優勢在於,它提供了一整套針對開發鏈的重新整合工具,以便我們對其進行合併、變基和撿取等操作。
  • 工作流上的靈活性:Git非常靈活。不但單一開發者可以用它,敏捷團隊也可以找到使用它工作的合適方法,甚至一個由眾多開發者在不同的工作地點參與的大型國際項目也可以用它開發出一個很好的工作流。
  • 適合奉獻合作:大多數開源項目所依靠的都是開發者的無私奉獻。因此,讓這種無私奉獻的方式儘可能地簡單化是一件非常重要的事。而這在一個集中式的版本控制系統中通常是很難做到的,因為我們不可能讓所有人都有權限去寫版本庫。但如果我們使用Git,那麼每個人都先可以克隆一個獨立的工作版本庫,然後再對其進行後續的改動。
  • 高性能:Git在處理擁有許多文件且歷史悠久的項目時速度也依然是非常快的。例如,使用Git將Linux內核源碼的當前版本切換到6年前的舊版本時,在一臺MacBook Air上所需的時間不到1分鐘。考慮這兩個版本之間有著超過200000次的提交和40000個更改文件,這已經足以讓人印象深刻了。
  • 強大的抗故障和抗攻擊能力:由於項目歷史被分散存儲在多個分佈式版本庫中,因此數據嚴重流失的可能性不大。再加上版本庫中有著巧妙簡單的數據結構,這確保了其中的數據即使在遙遠的未來也仍然會被正確地解釋。而且,它還使用了統一的加密校驗,這使得攻擊者難以對版本庫進行篡改。
  • 離線開發與多點開發:分佈式的體系結構可以使得離線開發或者邊旅行邊開發的方式變得非常容易。而且該結構在多點開發模式下,我們既不需要設置中央服務器,也不需要固定的網絡連接。
  • 強大的開源社區:除官方提供的詳細文檔外,你還可以在該社區找到無數相關的手冊、論壇、維基網站等,另外還有各種工具生態系統、託管平臺、出版物、服務以及針對各個開發環境的插件,整個社區都正在茁壯成長。
  • 可擴展性:Git為用戶提供了許多實用命令,其中包括了能使我們更便於直接訪問其遠程版本庫的命令。這可以讓Git變得非常靈活,這種靈活性將允許其各種獨立應用提供比默認的Git版本更為強大的功能。

為什麼要用工作流

Git非常靈活。可為多種不同的角色所用,從偶爾需要版本化少量shell腳本的單一系統管理員,到Linux內核項目中的上百個開發人員,一切皆有可能。

當然,這種靈活性不是沒有代價的。在開始用Git來開展工作之前,你還必須要做一組決定。例如以下幾種。

Git中固然已經是分佈式版本庫。但你是真的打算只在本地工作,還是更願意建立一箇中央版本庫?

Git支持push和pull兩種數據傳輸類型,但我們需要同時使用它們嗎?如果讓你選,你會選哪一個?為什麼不是另一個?

分支與合併是Git中兩個強大的功能。但是,我們應該開多少個分支呢?是根據每個軟件功能來開?還是針對每個發行版來開?還是隻該有一個分支?

為了便於入門,下面我們來總結一下工作流及其作用

  • 工作流指的是相關項目的日常操作規程。
  • 工作流會給出具體的步驟。
  • 工作流會顯示必要的命令和選項。
  • 工作流非常適用於密切的團隊合作,而目前的這些現代軟件項目通常就出自這樣的合作。

一些工作流可能並不是目標問題唯一正確的解決方案,但它們是一個很好的起點,我們可以從中為自己的項目開發出高效的工作流。

我們之所以會重點介紹商業項目中敏捷開發團隊的工作,是因為我們相信目前許多專業開發者(包括作者)都處於這樣的工作環境中。當然,這裡並不包括那些具有特殊要求的大型項目,因為這些項目通常有著很誇張的工作流,而且我們相信這些也不是大多數開發者會感興趣的項目。另外,這裡也不包括那些開源項目的開發,雖然這些項目也可以用Git規劃出一個很有意思的工作流。

分佈式版本控制,有什麼過人之處

在具體探討分佈式版本控制的概念之前,讓我們先來快速回顧一下傳統的集中式版本控制架構。

圖1.1中所顯示的就是一個集中式版本控制系統(例如CVS或Subversion)的典型佈局。每個開發者都在他或她自己的計算機上有一個包含所有項目文件的工作目錄(即工作區)。當該開發者在本地做了修改之後,他或她就會定期將修改提交給某臺中央服務器。然後,開發者在執行更新操作的同時也會從該服務器上撿取出其他開發者所做的修改。這臺中央服務器上存儲著這些文件(即版本庫)的當前版本和歷史版本。因此,這些被並行開發的分支,以及各種被命名(標記)的版本都將會被集中管理。

Git算不算程序員的必備技能?

​圖1.1 集中式版本控制

而在分佈式版本控制系統(見圖1.2)中,開發者環境與服務器環境之間是沒有分隔的。每一個開發者都同時擁有一個用於當前文件操作的工作區與一個用於存儲該項目所有版本、分支以及標籤的本地版本庫(我們稱其為一份克隆)。每個開發者的修改都會被載入成一次次的新版本提交(commit), 首先提交到其本地版本庫中。然後,其他開發者就會立即看到新的版本。通過推送(push)和拉回(pull)命令,我們可以將這些修改從一個版本庫傳送到另一個版本庫中。這樣一來,從技術上來看,這裡所有的版本庫在分佈式架構上的地位是同等的。因此從理論上來講,我們不再需要藉助服務器,就可以將某一臺開發工作機上所做的所有修改直接傳送給另一開發工作機。當然在具體實踐中,Git中的服務器版本庫也扮演了重要的角色,例如以下這些特型版本庫。

Git算不算程序員的必備技能?

​​圖1.2 分佈式版本控制

  • 項目版本庫(blessed repository):該版本庫主要用於存儲由“官方”創建併發行的版本。
  • 共享版本庫(shared repository):該版本庫主要用於開發團隊內人員之間的文件交換。在小型項目中,項目版本庫本身就可以勝任這一角色了。但在多點開發的條件下,我們可能就會需要幾個這樣的專用版本庫。
  • 工作流版本庫(workflow repository):工作流版本庫通常只用於填充那些代表工作流中某種特定進展狀態的修改,例如審核通過後的狀態等。
  • 派生版本庫(fork repository):該版本庫主要用於從開發主線分離出某部分內容(例如,分離出那些開發耗時較長,不適合在一個普通發佈週期中完成的內容),或者隔離出可能永遠不會被包含在主線中的、用於實驗的那部分開發進展。

下面,我們再來看看分佈式系統相對於集中式的優點有哪些。

  • 高性能:幾乎所有的操作都無需進行網絡訪問,均可直接在本地執行。
  • 高效的工作方式:開發者可通過多個本地分支在不同任務之間進行快速切換。
  • 離線功能:開發者可以在沒有服務器連接的情況下執行提交、創建分支、版本標籤等操作。之後再將其上傳服務器。
  • 靈活的開發進程:我們可以在團隊和公司中為其他部門建立專用的版本庫,例如為方便與測試人員交流而建的版本庫。這樣相關修改就很容易發佈,因為只是特定版本庫上的一次推送。
  • 備份作用:由於每個開發者都持有一份擁有完整歷史版本的版本庫副本,所以因服務器故障而導致數據丟失的可能性是微乎其微的。
  • 可維護性:
    對於那些難以對付的重構工作,我們可以在將成功傳送給其原始版本庫之前,先在該版本庫的副本上嘗試一下。

版本庫,分佈式的基礎所在

其實,版本庫本質上就是一個高效的數據存儲結構而已,由以下部分組成。

文件(即blob):這裡既包含了文本也包含了二進制數據,這些數據將不以文件名的形式被保存。

目錄(即Tree):目錄中保存的是與文件名相關聯的內容,其中也會包含其他目錄。

版本(即commit):每一個版本所定義的都是相應目錄的某個可恢復的狀態。每當我們創建一個新的版本時,其作者、時間、註釋以及其之前的版本都將會被保存下來。

對於所有的數據,它們都會被計算成一個十六進制散列值(例如像1632acb65b01 c6b621d6e1105205773931bb1a41這樣的值)。這個散列值將會被用作相關對象的引用,以及日後恢復數據時所需的鍵值(見圖1.3)。

Git算不算程序員的必備技能?

​圖1.3 版本庫中的對象存儲

也就是說,一個提交對象的散列值實際上就是它的“版本號”,如果我們持有某一提交的散列值,就可以用它來檢查對應版本是否存在於某一版本庫中。如果存在,我們就可以將其恢復到當前工作區相應的目錄中。如果該版本不存在,我們也可以從其他版本庫中單獨導入(拉回)該提交所引用的全部對象。

接下來,我們來看看採用這種散列值和這種既定的版本庫結構究竟有哪些優勢。

  • 高性能:通過散列值來訪問數據是非常快的。
  • 冗餘度——釋放存儲空間:相同的文件內容只需存儲一次即可。
  • 分佈式版本號:由於相關散列值是根據文件,作者和日期來計算的,所以版本也可以“離線”產生,不用擔心將來會因此而發生版本衝突。
  • 版本庫間的高效同步:當我們將某一提交從一個版本庫傳遞給另一個版本庫時,只需要傳送那些目標版本庫中不存在的對象即可。而正是因為有了散列值的幫助,我們才能很快地判斷相關對象是否已經存在。
  • 數據完整性:由於散列值是根據數據的內容來計算的,所以我們可以隨時通過Git來查看某一散列值是否與相關數據匹配。以檢測該數據上可能的意外變化或惡意操作。
  • 自動重命名檢測:被重命名的文件可以被自動檢測到,因為根據該文件內容計算出的散列值並沒有發生變化。也正因為如此,Git中並沒有專用的重命名命令,只需移動命令即可。

分支的創建與合併很簡單

對於大多數版本控制系統來說,分支的創建與合併通常會因其特殊性而被認為是高級拓展操作。但由於Git最初就是為了方便那些散落在世界各地的Linux內核開發者而創建的,合併多方努力的結果一直都是其面臨的最大挑戰之一,所以Git的設計目標之一就是要讓分支的創建與合併操作變得儘可能地簡單且安全。

在下面的圖1.4中,我們向你展示了開發者是如何通過創建分支的方式來進行並行開發的。圖中的每一個點都代表了該項目的一個版本(即commit)。而由於在Git中,我們只能對整個項目進行版本化,所以每個點同時也代表了屬於同一版本的各個文件。

Git算不算程序員的必備技能?

​圖1.4 因開發者的並行開發而出現的分支創建操作

如上所示,圖中兩位開發者的起點是同一個版本。之後兩人各自做了修改,並提交了修改。這時候,對於這兩位開發者各自的版本庫來說,該項目已經有了兩個不同的版本。也就是說,他們在這裡創建了兩個分支。接下來,如果其中一個開發者想要導入另一個人的修改,他/她就可以用Git來進行版本合併。如果合併成功了,Git就會創建一個合併提交,其中會包含兩位開發者所做的修改。這時如果另一位開發者也取回了這一提交,兩位開發者的項目就又回到了同一個版本。

在上面的例子中,分支的創建是非計劃性的,其原因僅僅是兩個開發者在並行開發同一個軟件罷了。在Git中,我們當然也可以開啟有針對性的分支,即顯式地創建一個分支(見圖1.5)。顯式分支通常主要用於協調某一種功能性的並行開發。

Git算不算程序員的必備技能?

​圖1.5 針對不同任務的顯式分支

版本庫在執行拉回和推送操作時,可以具體指定其針對的是哪一些分支。當然,除了這些簡單的分支創建和合並處理外,我們也可以對分支執行以下動作。

  • 移植分支:我們可以直接將某一分支中的提交轉移到另一個版本庫中。
  • 只傳送特定修改:我們可以將某一分支中的某一次或某幾次提交直接複製到另一個分支中。這就是所謂的撿取處理。
  • 清理歷史:我們可以對分支歷史進行改造、排序和刪除。這有利於為該項目建立更好的歷史文檔。我們稱這種處理為交互式重訂(interactive rebasing)。

另外,如果你是一個繁忙的項目管理者,還在猶豫不決是否要採用Git

後面推薦幾本學習GIT的書,希望對讀者能有所幫助:

基本篇:

進階篇:

高階篇:


分享到:


相關文章: