git已經很是普及,幾乎是程序員的標配。在熟練使用git命令的同時,你是否對底層的實現機制有所好奇?是否想更深刻的瞭解git原理?本文將從較底層的視角逐步揭開git的祕密。
文章主要參考git book 第十章,根據筆者的理解提煉,簡化沒必要要的細節,但願能幫助你們更好的理解git。linux
版本控制系統本質就是一個特殊的文件系統,可以對不一樣的文件、不一樣的版本進行存儲和恢復。git最基本的存儲是一個key-value的數據庫。git提供了一些命令來操做該數據庫,hash-object命令可以將工做區文件插入數據庫,返回一個hash,cat-file命令可以根據hash查詢內容。數據文件存儲於.git/objects目錄。
下面的例子建立一個test.txt文件並將其內容存儲到git數據庫。git
當文件內容改變時,從新存儲便可,此時會返回新的hash。只要記錄了的hash和文件名,就能進行簡單的版本管理了。下面的例子演示了一個簡單的版本恢復:首先修改test.txt,並將新值存入數據庫,而後又經過hash將工做區的文件內容回退到上一個版本。程序員
這種最基本的存儲單元git稱爲blob。數據庫
普通blob沒法支持文件名和集合的存儲,git引入tree來解決此問題。tree相似目錄的概念,包含一系列blob/tree的hash和文件名。
建立tree的方法是,先用update-index命令將內容添加到暫存區,而後經過write-tree命令生成tree。服務器
以下,先添加一項內容的暫存區。
ps:100644是linux文件類型常量。ssh
以下,再添加一項內容到暫存區(update-index命令可直接傳入文件名,此時git會作兩個步驟,讀取文件內容存至數據庫和添加至暫存區)。spa
此時暫存區有兩個項目,經過write-tree命令將暫存區內容生成tree,返回值一個hash。能夠看到tree也是以一個blob來存儲的。設計
前面已經解決了存儲的問題(包括內容和文件名),但想添加一些額外的備註信息(好比做者、時間、說明等)還不行,commit-tree可以解決此問題。
如下代碼對一個tree添加commit。commit-tree主要包含三部分:指向另外一個tree的hash、做者信息、commit message。版本控制
commit-tree還有另一個字段parent,指向前一個commit-tree的hash。如下代碼建立了一個新的commit-tree並指向前者。ip
有了commit-tree以後,不只能添加備註信息,還能造成一條commit鏈,經過這條鏈,可以更加方便地進行版本的切換和回退。
理論上commit-tree可以知足任意複雜的版本管理,只不過你須要記住一些關鍵節點的commit hash。hash對人是不友好的,git引入分支來記錄關鍵節點的commit hash。分支就是一個普通的文本文件,文件名即分支名,內容即commit hash。
如下代碼建立了一個master分支。
經過git log就能查看分支對應的commit鏈。
分支存儲於.git/refs目錄,本地分支位於heads目錄,遠程分支位於remotes目錄,.git/HEAD指向當前所在的本地分支。從這裏能夠看到git分支模型確實是很是輕量,就是一個簡單的文本文件而已。
僅僅本地進行版本管理還不夠,要和遠程倉庫通訊才能多人協做。git的遠程倉庫設計也很簡單,本地和遠程互爲拷貝。在同步信息時,只需同步.git/objects和.git/refs/remotes目錄的文件便可。數據傳輸可採用http/ssh協議,服務器交互地址配置於.git/config。
git命令很是多,概念也相對較多。但git的底層邏輯設計是很是簡單的。一個好的設計必定是簡單的,一個好的設計必定是具備美感的。