本文適合對git有過接觸,但知其然不知其所以然的小夥伴,也適合想要學習git的初學者,通過這篇文章,能讓大家對git有豁然開朗的感覺。在寫作過程中,我力求通俗易懂,深入淺出,不堆砌概念。你能夠從本文中了解以下知識:
git是什麼
git能夠解決哪些問題
git的實現原理
git是一種分布式版本控制系統。
有人要問了,什麼是「版本控制」?git又為什麼被冠以「分布式」的名頭呢?這兩個問題我們一一解答。
版本控制這個說法多少有一點抽象。事實上,版本控制這件事兒我們一直在做,只是平時不這麼稱呼。舉乙個栗子,boss讓你寫乙個策劃案,你先完成了一稿,之後又有了一些新的想法,但是並不確定新的想法是否能得到boss的認可,於是你儲存了乙個初稿,之後在初稿的基礎上另存了乙個檔案,做了部分修改完成了乙個修改稿。ok,這時你的策劃案就有了兩個版本——初稿和修改稿。如果boss對修改稿不滿意,你可以很輕易的把初稿拿出來交差。
在這個簡單的過程中,你已經執行了乙個簡單的版本控制操作——把文件儲存為初稿和修改稿的過程就是版本控制。
學術點說,版本控制就是對檔案變更過程的管理。說白了,版本控制就是要把乙個檔案或一些檔案的各個版本按一定的方式管理起來,目的是需要用到某個版本的時候可以隨時拿出來。
另乙個個問題,為什麼說git是「分布式」版本控制系統呢?
這裡的「分布式」是相對於「集中式」來說的。把資料集中儲存在伺服器節點,所有的客戶節點都從服務節點獲取資料的版本控制系統叫做集中式版本控制系統,比如svn就是典型的集中式版本控制系統。
與之相對,git的資料不止儲存在伺服器上,同時也完整的儲存在本地計算機上,所以我們稱git為分布式版本控制系統。
git的這種特性帶來許多便利,比如你可以在完全離線的情況下使用git,隨時隨地提交專案更新,而且你不必為單點故障過分擔心,即使伺服器宕機或資料損毀,也可以用任何乙個節點上的資料恢復專案,因為每乙個開發節點都儲存著完整的專案檔案映象。
就像上文舉的例子一樣,在未接觸版本控制系統之前,大多人會通過儲存專案或檔案的備份來達到版本控制的目的。通常你的檔案或資料夾名會設定成「***-v1.0」、「***-v2.0」等。
這是一種簡單的辦法,但過於簡單。這種方式無法詳細記錄版本附加資訊,難以應付複雜專案或長期更新的專案,缺乏版本控制約定,對協作開發無能為力。如果你不慎使用了這種方式,那麼稍稍過一段時間你就會發現連自己都不知道每個版本間的區別,版本控制形同虛設。
git能夠為我們解決版本控制方面的大多數問題,利用git
我們可以為每一次變更提交版本更新並且備註更新的內容;
我們可以在專案的各個歷史版本之間自如切換;
我們可以一目了然的比較出兩個版本之間的差異;
我們可以從當前的修改中撤銷一些操作;
我們可以自如的建立分支、合併分支;
我們可以和多人協作開發;
我們可以採取自由多樣的開發模式。
諸如此類,數不勝數。然而實現這些功能的基礎是對檔案變更過程的儲存。如果我們能抓住這個根本,提綱挈領的學習git,會事半功倍。
隨著對git更深入的學習,你會發現它會變得越來越簡單,越來越純粹。道家有萬法歸宗的說法,用在這裡再合適不過。因為git之所以有如此多炫酷的功能,根源只有乙個:它很好的解決了檔案變更過程儲存這乙個問題。
所以,如果問「git能夠解決哪些問題?」我們可以簡單的回答:git解決了版本控制方面的很多問題,但最核心的是它很好的解決了版本狀態儲存(即檔案變更過程儲存)的問題。
我們說到,git很好的解決了版本狀態記錄的問題,在此基礎上實現了版本切換、差異比較、分支管理、分布式協作等等炫酷功能。那麼,這一節我們就先從最根本的講起,看看git是如何解決版本狀態記錄(即檔案變更過程記錄)問題的。
我們都有版本記錄的經驗,比如在文件撰寫的關鍵點上保留乙個備份,或在需要對檔案進行修改的時候「另存」一次。這都是很好的習慣,也是版本狀態記錄的一種常用方式。事實上,git採取了差不多的方式。
在我們向git系統提交乙個版本的時候,git會把這個版本完整儲存下來。這是不是和「另存」有異曲同工之妙呢?不同之處在於儲存方式,在git系統中一旦乙個版本被提交,那麼它就會被儲存在「git資料庫」中。
3.1 git資料庫
我們提到了「git資料庫」,這是什麼玩意兒呢?為了能夠說清楚git資料庫的概念,我們暫且引入三個git指令,通過這三個命令,我們就能一探git資料庫的究竟。
git init 用於建立乙個空的git倉庫,或重置乙個已存在的git倉庫
git hash-object git底層命令,用於向git資料庫中寫入資料
git cat-file git底層命令,用於檢視git資料庫中資料
我用的是ubuntu系統,在terminal下執行
$ git init gittest
initialized empty git repository in /home/mp/workspace/gittest/.git/
這一命令在當前目錄下生成了乙個新的資料夾-gittest,在gittest中,包含了乙個新建的空git倉庫。如果你不明白git倉庫是什麼,那麼可以簡單的理解為存放git資料的乙個空間,這這個例子中,是「/home/mp/workspace/gittest/.git」目錄。
接下來,我們看看git倉庫的結構是什麼樣的。執行
我們發現,gittest目錄下,除隱藏目錄.git之外,並沒有其他檔案或資料夾。
我們通過find .git命令檢視新生成的空git倉庫的結構,會發現其中有乙個objects資料夾,這就是git資料庫的儲存位置。
3.1 git資料庫的寫入操作
緊接著,我們利用git底層命令git hash-object向git資料庫中寫入一些內容。執行命令:
$ echo 「version 1」 | git hash-object -w --stdin
83baae61804e65cc73a7201a7252750c76066a30
$ find .git/objects/ -type f
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30
"|"表示這是一條通道命令,意思是把「|」前邊的命令的輸出作為「|」後邊命令的輸入。git hash-object -w --stdin 的意思是向git資料庫中寫入一條資料(-w),這條資料的內容從標準輸入中讀取(–stdin)。
命令執行後,會返回個長度為40位的hash值,這個hash值是將待儲存的資料外加乙個頭部資訊一起做sha-1校驗運算而得的校驗和。在git資料庫中,它有乙個名字,叫做「鍵值(key)」。相應的,git資料庫其實是乙個簡單的「鍵值對(key-value)」資料庫。事實上,你向該資料庫中插入任意型別的內容,它都會返回乙個鍵值。通過返回的鍵值可以在任意時刻再次檢索該內容。
此時,我們再次執行find .git/objects/ -type f命令檢視objects目錄,會發現目錄中多出了乙個檔案,這個檔案儲存在以新存入資料對應hash值的前2位命名的資料夾內,檔名為hash值的後38位。這就是git資料庫的儲存方式,乙個檔案對應一條內容,就是這麼簡單直接。
3.2 git資料庫的查詢操作
我們可以通過git cat-file這個git底層命令檢視資料庫中某一鍵值對應的資料。執行
$ git cat-file -t 83baa
blob
$ git cat-file -p 83baa
version 1
其中,-t選項用於檢視鍵值對應資料的型別,-p選項用於檢視鍵值對應的資料內容,83bba為資料鍵值的簡寫。
由執行結果可見,所查詢的鍵值對應的資料型別為blob,資料內容為「version 1」。blob物件我們稱之為資料物件,這是git資料庫能夠儲存的物件型別之一,後面我們還會講到另外兩種物件分別是樹(tree)物件和提交(commit)物件。
截止到這裡,你已經掌握了如何向git資料庫裡存入內容和取出內容。這很簡單但是卻意義非凡,因為對git資料庫的操作正是git系統的核心——git的版本控制功能就是基於它的物件資料庫實現的。在git資料庫裡,儲存著納入git版本管理的所有檔案的所有版本的完整映象。
git這麼簡單嗎?不用懷疑,git就是這麼簡單,我們已經準確的抓住了它的根本要義——物件資料庫。接下來我們會利用git資料庫搭建起git的高樓大廈(略)。
git 分支的本質:乙個指向某一系列提交之首的指標或引用。
git的核心是它的物件資料庫,其中儲存著git的物件,其中最重要的是blob、tree和commit物件,blob物件實現了對檔案內容的記錄,tree物件實現了對檔名、檔案目錄結構的記錄,commit物件實現了對版本提交時間、版本作者、版本序列、版本說明等附加資訊的記錄。這三類物件,完美實現了git的基礎功能:對版本狀態的記錄。
git引用是指向git物件hash鍵值的類似指標的檔案。通過git引用,我們可以更加方便的定位到某一版本的提交。git分支、tags等功能都是基於git引用實現的。
深入理解Git原理 移除檔案
如果是在檔案還未納入 git 的管理時就不想跟蹤該檔案,可以在.gitignore檔案中加入該檔案的模式。如果檔案已經納入 git 的管理,此時要從 git 中移除某個檔案,就必須要從已跟蹤檔案清單中移除 確切地說,是從暫存區域移除 然後提交。可以用git rm命令完成此項工作,並連帶從工作目錄中刪...
深入理解Git原理 祖先引用
祖先引用是一種指明乙個提交的方式。如果你在引用的尾部加上乙個 git 會將其解析為該引用的上乙個提交。假設你的提交歷史是 git log pretty format h s graph 734713b fixed refs handling,added gc auto,updated tests d...
深入理解c 多型實現原理
多型是指通過基類的指標或者引用,在執行時動態呼叫實際繫結物件函式的行為。與之相對應的編譯時繫結函式稱為靜態繫結。多型是物件導向程式設計的核心思想之一,因此我們有必要深入探索一下它的實現原理。理解了原理才能更好的使用。前置條件 現有 如下所示,非常簡單的例子。通過基類的引用呼叫recv函式來觸發多型。...