當前位置:
首頁 > 知識 > 深入理解Git的實現原理

深入理解Git的實現原理

0、導讀

本文適合對git有過接觸,但知其然不知其所以然的小夥伴,也適合想要學習git的初學者,通過這篇文章,能讓大家對git有豁然開朗的感覺。在寫作過程中,我力求通俗易懂,深入淺出,不堆砌概念。你能夠從本文中了解以下知識:

  • Git是什麼
  • Git能夠解決哪些問題
  • Git的實現原理

請注意,本文的闡述邏輯是:Git是什麼——>Git要解決的根本問題是什麼——>git是如何解決這些問題的。

1、Git是什麼?

Git是一種分散式版本控制系統。

有人要問了,什麼是「版本控制」?Git又為什麼被冠以「分散式」的名頭呢?這兩個問題我們一一解答。

版本控制這個說法多少有一點抽象。事實上,版本控制這件事兒我們一直在做,只是平時不這麼稱呼。舉一個栗子,boss讓你寫一個策劃案,你先完成了一稿,之後又有了一些新的想法,但是並不確定新的想法是否能得到boss的認可,於是你保存了一個初稿,之後在初稿的基礎上另存了一個文件,做了部分修改完成了一個修改稿。OK,這時你的策劃案就有了兩個版本——初稿和修改稿。如果boss對修改稿不滿意,你可以很輕易的把初稿拿出來交差。

在這個簡單的過程中,你已經執行了一個簡單的版本控制操作——把文檔保存為初稿和修改稿的過程就是版本控制。

學術點說,版本控制就是對文件變更過程的管理。說白了,版本控制就是要把一個文件或一些文件的各個版本按一定的方式管理起來,目的是需要用到某個版本的時候可以隨時拿出來。

另一個個問題,為什麼說Git是「分散式」版本控制系統呢?

這裡的「分散式」是相對於「集中式」來說的。把數據集中保存在伺服器節點,所有的客戶節點都從服務節點獲取數據的版本控制系統叫做集中式版本控制系統,比如svn就是典型的集中式版本控制系統。

與之相對,Git的數據不止保存在伺服器上,同時也完整的保存在本地計算機上,所以我們稱Git為分散式版本控制系統。

Git的這種特性帶來許多便利,比如你可以在完全離線的情況下使用Git,隨時隨地提交項目更新,而且你不必為單點故障過分擔心,即使伺服器宕機或數據損毀,也可以用任何一個節點上的數據恢復項目,因為每一個開發節點都保存著完整的項目文件鏡像。

2、Git能夠解決哪些問題?

就像上文舉的例子一樣,在未接觸版本控制系統之前,大多人會通過保存項目或文件的備份來達到版本控制的目的。通常你的文件或文件夾名會設置成「XXX-v1.0」、「XXX-v2.0」等。

這是一種簡單的辦法,但過於簡單。這種方式無法詳細記錄版本附加信息,難以應付複雜項目或長期更新的項目,缺乏版本控制約定,對協作開發無能為力。如果你不慎使用了這種方式,那麼稍稍過一段時間你就會發現連自己都不知道每個版本間的區別,版本控制形同虛設。

Git能夠為我們解決版本控制方面的大多數問題,利用Git

  • 我們可以為每一次變更提交版本更新並且備註更新的內容;
  • 我們可以在項目的各個歷史版本之間自如切換;
  • 我們可以一目了然的比較出兩個版本之間的差異;
  • 我們可以從當前的修改中撤銷一些操作;
  • 我們可以自如的創建分支、合併分支;
  • 我們可以和多人協作開發;
  • 我們可以採取自由多樣的開發模式。

諸如此類,數不勝數。然而實現這些功能的基礎是對文件變更過程的存儲。如果我們能抓住這個根本,提綱挈領的學習git,會事半功倍。

隨著對Git更深入的學習,你會發現它會變得越來越簡單,越來越純粹。道家有萬法歸宗的說法,用在這裡再合適不過。因為Git之所以有如此多炫酷的功能,根源只有一個:它很好的解決了文件變更過程存儲這一個問題。

所以,如果問「Git能夠解決哪些問題?」我們可以簡單的回答:Git解決了版本控制方面的很多問題,但最核心的是它很好的解決了版本狀態存儲(即文件變更過程存儲)的問題。

3、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資料庫中數據

首先,我們用git init新建一個空的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倉庫的結構是什麼樣的。執行

$ cd GitTest

$ ls

$ find .git

.git

.git/HEAD

.git/config

.git/objects

.git/objects/info

.git/objects/pack

.git/refs

.git/refs/heads

.git/refs/tags

.git/hooks

.git/hooks/commit-msg.sample

.git/hooks/post-update.sample

.git/hooks/update.sample

.git/hooks/pre-rebase.sample

.git/hooks/pre-applypatch.sample

.git/hooks/fsmonitor-watchman.sample

.git/hooks/applypatch-msg.sample

.git/hooks/pre-receive.sample

.git/hooks/pre-push.sample

.git/hooks/prepare-commit-msg.sample

.git/hooks/pre-commit.sample

.git/info

.git/info/exclude

.git/branches

.git/description

我們發現,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的高樓大廈。

3.3 使用Git跟蹤文件變更

我們明白,所謂跟蹤文件變更,只不過是把文件變更過程中的各個狀態完整記錄下來。

我們模擬一次文件變更的過程,看看僅僅利用git的對象資料庫能不能實現「跟蹤文件變更」的功能。

首先,我們執行

$ echo "version 1" > file.txt

$ git hash-object -w file.txt

83baae61804e65cc73a7201a7252750c76066a30

我們把文本「version 1」寫入file.txt中,並利用git hash-object -w file.txt命令將其保存入資料庫中。如果你足夠細心,會發現返回的hash鍵值和利用echo "version 1" | git hash-object -w --stdin寫入資料庫時是一致的,這很正常,因為我們寫入的內容相同。git hash-object命令在只指定-w選項的時候,會把file.txt文件內容寫入資料庫。

此時,執行

$ find .git/objects -type f

.git/objects/83/baae61804e65cc73a7201a7252750c76066a30

會發現.git/objects目錄中,依然只有一個文件。可見,git資料庫存儲文件時,只關心文件內容,與文件的名字無關。

接下來,我們修改file.txt的內容,執行

$ echo "version 2" > file.txt

$ git hash-object -w file.txt

1f7a7a472abf3dd9643fd615f6da379c4acb3e3a

$ find .git/objects -type f

.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a

.git/objects/83/baae61804e65cc73a7201a7252750c76066a30

我們發現,.git/objects下多出了一個文件,這是我們新保存進資料庫的file.txt。接下來,我們執行git cat-file搞清楚這兩條數據的內容分別是什麼。執行

$git cat-file -p 83baa

version 1

$git cat-file -p 1f7a7a

version 2

我們發現,file.txt的變更過程被完整的記錄下來了。

當前的file.txt中保存的內容是「version 2」,如果我們想把文件恢復到修改為「version 2」之前的狀態,只需執行

$ cat file.txt

version 2

$ git cat-file -p 83baa > file.txt

$ cat file.txt

version 1

file.txt的內容成功恢復到了修改前的狀態,變成了「version 1」。這其實就是版本回滾的實質。

OK,文件變更狀態跟蹤的道理就是這麼簡單。

但做到這一步還遠遠不算完美,至少有以下幾方面的問題:

  • 第一,無法記錄文件名的變化;
  • 第二,無法記錄文件夾的變化;
  • 第三,記憶每一個版本對應的hash值無聊且乏味且不可能;
  • 第四,無法得知文件的變更時序;
  • 第五,缺少對每一次版本變化的說明。

問題不少,但都是簡單的小問題,我們一一解決。

3.4 利用樹對象(tree object)解決文件名保存和文件組織問題

Git利用樹對象(tree object)解決文件名保存的問題,樹對象也能夠將多個文件組織在一起。

Git通過樹(tree)對象將數據(blob)對象組織起來,這很類似於一種文件系統——blob對象對應文件內容,tree對象對應文件的目錄和節點。一個樹(tree)對象包含一條或多條記錄,每條記錄含有一個指向blob對象或tree對象的SHA-1指針,以及相應的模式、類型、文件名。

有了樹對象,我們就可以將文件系統任何時間點的狀態保存在git資料庫中,這是不是很激動人心呢?你的一個複雜的項目可能包含成百上千個文件和文件目錄,有了樹對象,這一切都不是問題。

創建樹對象

通常,Git根據某一時刻暫存區所表示的狀態創建並記錄一個對應的樹對象,如此重複便可以依次記錄一系列的樹對象。Git的暫存區是一個文件——.git/index。下面,我們通過創建樹對象的過程來認識暫存區和樹對象。

為了創建一個樹對象,我們需要通過暫存一些文件來創建一個暫存區。為此我們引入兩個命令:

  • git update-index git底層命令,用於創建暫存區
  • git ls-files --stage git底層命令,用於查看暫存區內容
  • git write-tree git底層命令,用於將暫存區內容寫入一個樹對象

OK,萬事俱備,我們將file.txt的第一個版本放入暫存區,執行

$ find .git/index

find: 『.git/index』: No such file or directory

$ git update-index --add file.txt

$ find .git/index

.git/index

$ cat .git/index

DIRC[???$?;?[???$?;?A????

???a?Ne?s? rRu

vjfile.txt??3%A??,I? ?`

$ find .git/objects/ -type f

.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a

.git/objects/83/baae61804e65cc73a7201a7252750c76066a30

$ git ls-files --stage

100644 83baae61804e65cc73a7201a7252750c76066a30 0 file.txt

$ git write-tree

391a4e90ba882dbc9ea93855103f6b1fa6791cf6

$ find .git/objects/ -type f

.git/objects/39/1a4e90ba882dbc9ea93855103f6b1fa6791cf6

.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a

.git/objects/83/baae61804e65cc73a7201a7252750c76066a30

分析執行結果:

首先,我們注意.git/index文件的變化。在添加file.txt到暫存區前,index文件並不存在,這說明暫存區還沒有創建。添加file.txt到暫存區的同時,index文件被創建。

其次,我們看git資料庫的變化。我們發現在執行git update-index 之後,git資料庫並沒有改變,依然是只有兩條數據。在執行git write-tree之後,git資料庫中多出了一條新的記錄,鍵值為391a4e90ba882dbc9ea93855103f6b1fa6791cf6。

我們執行git cat-file來查看一下多出來的這條記錄是什麼內容。執行

$ git cat-file -t 391a4e

tree

$ git cat-file -p 391a4e

100644 blob 83baae61804e65cc73a7201a7252750c76066a30 file.txt

由執行結果可見,git資料庫中新增加的記錄是一個tree對象,該tree對象指向一個blob對象,hash鍵值為

83baae61804e65cc73a7201a7252750c76066a30

這一個blob對象是之前我們添加進資料庫的。

以上我們添加了一個已經存在在git資料庫中的文件到暫存區,如果我們新建一個未曾保存到git資料庫的文件存入暫存區,進而保存為tree對象,會有什麼不同嗎?我們試試看。執行

$ echo "new file" > new

$ git update-index --add new

$ find .git/objects/ -type f

.git/objects/39/1a4e90ba882dbc9ea93855103f6b1fa6791cf6 #tree對象

.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a #blob對象

.git/objects/83/baae61804e65cc73a7201a7252750c76066a30 #blob對象

.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 #新增的blob對象

$ git write-tree

228e49bb0bf19df94b49c3474f5d4ee55a371fbe #新生成的tree對象鍵值

$ find .git/objects/ -type f

.git/objects/39/1a4e90ba882dbc9ea93855103f6b1fa6791cf6

.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a

.git/objects/83/baae61804e65cc73a7201a7252750c76066a30

.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92

.git/objects/22/8e49bb0bf19df94b49c3474f5d4ee55a371fbe #新增的tree對象

$ git ls-files --stage

100644 83baae61804e65cc73a7201a7252750c76066a30 0 file.txt

100644 fa49b077972391ad58037050f2a75f74e3671e92 0 new

由執行結果我們可以看到,這一次執行update-index之後,和上次不同,git資料庫發生了變化,新增加了一條hash鍵值為「fa49b0」的數據;暫存區中也多出了文件new的信息。

這說明兩個問題:

  • 如果添加git資料庫中尚未存儲的數據到暫存區,則在執行update-index的時候,會同時把該數據保存到git資料庫。
  • 添加文件進入暫存區的操作是追加操作,

    之前已經加入暫存區的文件依然存在——很多人會有誤區,認為變更提交之後,暫存區就清空了。

此時,我們查看新添加的樹對象,執行

$ git cat-file -p 228e49

100644 blob 83baae61804e65cc73a7201a7252750c76066a30 file.txt

100644 blob fa49b077972391ad58037050f2a75f74e3671e92 new

此次write-tree寫入資料庫的是tree對象包含了兩個文件。

更進一步,我們是否能將一個子文件夾保存到樹對象呢?嘗試一下,執行

$ mkdir new_dir

$ git update-index --add new_dir

error: new_dir: is a directory - add files inside instead

fatal: Unable to process path new_dir

我們發現,無法將一個新建的空文件夾添加到暫存區。錯誤提示告訴我們,應該將文件將文件夾中的文件加入到暫存區(add files inside instead)。

OK,接下來,我們在新建的文件夾下寫入一個文件,再嘗試將這一文件加入暫存區。執行

$ echo "file in new dir" > new_dir/new

$ git update-index --add new_dir/new

$ git ls-files --stage

100644 83baae61804e65cc73a7201a7252750c76066a30 0 file.txt

100644 fa49b077972391ad58037050f2a75f74e3671e92 0 new

100644 138c554a661371c9c40ae62dfb5d51b48b9b3f6b 0 new_dir/new

$ git write-tree

06564b76e0fcf9f3600fd055265cf2d4c45847a8

$ git cat-file -p 06564b

100644 blob 83baae61804e65cc73a7201a7252750c76066a30 file.txt

100644 blob fa49b077972391ad58037050f2a75f74e3671e92 new

040000 tree 8d6bc0bdbba1d28caf4ee66a125169500080e206 new_dir

從執行結果可見,文件夾new_dir對應一個tree對象。

至此,在git資料庫中,我們可以完整的記錄文件的狀態、文件夾的狀態;並且可以把多個文件或文件夾組織在一起,記錄他們的變更過程。我們離一個完善的版本控制系統似乎已經不遠了,而這一切實現起來又是如此簡單——我們只是通過幾個命令操作git資料庫就完成了這些功能。

接下來,我們只要把資料庫中各個版本的時序關係記錄下來,再把對每一個版本更新的注釋記錄下來,不就完成了一個邏輯簡單、功能強大、操作靈活的版本控制系統嗎?

那麼,如何記錄版本的時序關係,如何記錄版本的更新注釋呢?這就要引入另一個git數據對象——提交對象(commit object)。

3.5 利用提交對象(commit object)記錄版本間的時序關係和版本注釋

commit對象能夠幫你記錄什麼時間,由什麼人,因為什麼原因提交了一個新的版本,這個新的版本的父版本又是誰。

git提供了底層命令commit-tree來創建提交對象(commit object),我們需要為這個命令指定一個被提交的樹對象的hash鍵值,以及該提交對象的父提交對象(如果是第一次提交,不需要指定父對象)。

我們嘗試將之前創建的樹對象提交為commit 對象,執行

$ git write-tree

cb0fbcc484a3376b3e70958a05be0299e57ab495

$ git commit-tree cb0fbcc -m "first commit"

7020a97c0e792f340e00e1bb8edcbafcc4dfb60f

$ git cat-file 7020a97

tree cb0fbcc484a3376b3e70958a05be0299e57ab495

author john <john@163.com> 1537961478 +0800

committer john <john@163.com> 1537961478 +0800

first commit

在git commit-tree命令中,-m選項用於指定本次提交的注釋。

我們可以很清楚的看到,一個提交對象包含著所提交版本的樹對象hash鍵值,author和commiter,以及修改和提交的時間,最後是本次提交的注釋。

其中committer和author是通過git config命令設置的。

接下來,修改某個文件,重新創建一個樹對象,並將這一樹對象提交,作為項目的第二個提交版本。執行

$ echo "new version" > file.txt

$ git update-index file.txt

$ git write-tree

848e967643b947124acacc3a2d6c5a13c549231c

$ git commit-tree 848e96 -p 7020a97 -m "second commit"

e838c8678ef789df84c2666495663060c90975d7

$ git cat-file -p e838c

tree 848e967643b947124acacc3a2d6c5a13c549231c

parent 7020a97c0e792f340e00e1bb8edcbafcc4dfb60f

author john <john@163.com> 1537962442 +0800

committer john <john@163.com> 1537962442 +0800

second commit

我們可以按照上述步驟,再提交第三個版本。

$ echo "another version" > file.txt

$ git update-index file.txt

$ git write-tree

92867fcc5e0f78c195c43d1de25aa78974fa8103

$ git commit-tree 92867 -p e838c -m "third commit"

491404fa6e6f95eb14683c3c06d10ddc5f8e883f

$ git cat-file -p 49140

tree 92867fcc5e0f78c195c43d1de25aa78974fa8103

parent e838c8678ef789df84c2666495663060c90975d7

author john <john@163.com> 1537963274 +0800

committer john <john@163.com> 1537963274 +0800

third commit

提交完三個版本,我們通過git log 查看最近一個提交對象的提交記錄

$ git log 49140

commit 491404fa6e6f95eb14683c3c06d10ddc5f8e883f

Author: john <john@163.com>

Date: Wed Sep 26 20:01:14 2018 +0800

third commit

commit e838c8678ef789df84c2666495663060c90975d7

Author: john <john@163.com>

Date: Wed Sep 26 19:47:22 2018 +0800

second commit

commit 7020a97c0e792f340e00e1bb8edcbafcc4dfb60f

Author: john <john@163.com>

Date: Wed Sep 26 19:31:18 2018 +0800

first commit

太神奇了: 就在剛才,我們圍繞git資料庫,僅憑几個底層資料庫操作便完成了一個 Git 提交歷史的創建。到此為止,我們已經完全掌握了git的內在邏輯。

接觸過git的小夥伴會發現,以上我們用到的這些指令在使用git過程中是用不到的。這是為什麼呢?因為git對以上這些指令進行了封裝,給用戶提供了更便捷的操作命令,如add,commit等。

每次我們運行 git add 和 git commit 命令時, Git 所做的實質工作是將被改寫的文件保存為數據對象,更新暫存區,記錄樹對象,最後創建一個指明了頂層樹對象和父提交的提交對象。 這三種主要的 Git 對象——數據對象、樹對象、提交對象——最初均以單獨文件的形式保存在 .git/objects 目錄下。

然而,小問題依然存在,截止目前為止,我們對版本和數據對象的操作都是基於hash鍵值的,這些毫無直觀含義的字元串讓人很頭疼,不會有人願意一直急著最新提交對應的hash鍵值的。git不會允許這樣的問題存在的,它通過引入「引用(references)」來解決這一問題。

3.6 Git的引用

Git的引用(references)保存在.git/refs目錄下。git的引用類似於一個指針,它指向的是某一個hash鍵值。

創建一個引用實在再簡單不過。我們只需把一個git對象的hash鍵值保存在以引用的名字命名的文件中即可。

執行

$ echo "491404fa6e6f95eb14683c3c06d10ddc5f8e883f" > .git/refs/heads/master

$ cat .git/refs/heads/master

491404fa6e6f95eb14683c3c06d10ddc5f8e883f

就這樣,我們便成功的建立了一個指向最新一個提交的引用,引用名為master

在此之前我們查看提交記錄需要執行 git log 491404,現在只需執行git log master。

$ git log 491404

commit 491404fa6e6f95eb14683c3c06d10ddc5f8e883f (HEAD -> master)

Author: john <john@163.com>

Date: Wed Sep 26 20:01:14 2018 +0800

third commit

commit e838c8678ef789df84c2666495663060c90975d7

Author: john <john@163.com>

Date: Wed Sep 26 19:47:22 2018 +0800

second commit

commit 7020a97c0e792f340e00e1bb8edcbafcc4dfb60f

Author: john <john@163.com>

Date: Wed Sep 26 19:31:18 2018 +0800

first commit

$ git log master

commit 491404fa6e6f95eb14683c3c06d10ddc5f8e883f (HEAD -> master)

Author: john <john@163.com>

Date: Wed Sep 26 20:01:14 2018 +0800

third commit

commit e838c8678ef789df84c2666495663060c90975d7

Author: john <john@163.com>

Date: Wed Sep 26 19:47:22 2018 +0800

second commit

commit 7020a97c0e792f340e00e1bb8edcbafcc4dfb60f

Author: john <john@163.com>

Date: Wed Sep 26 19:31:18 2018 +0800

first commit

結果完全相同。

Git並不提倡直接編輯引用文件,它提供了一個底層命令update-ref來創建或修改引用文件。

echo "491404fa6e6f95eb14683c3c06d10ddc5f8e883f" > .git/refs/heads/master 命令可以簡單的寫作:

$ git update-ref refs/heads/master 49140

這基本就是 Git 分支的本質:一個指向某一系列提交之首的指針或引用。

深入理解Git的實現原理

4. Git基本原理總結

Git的核心是它的對象資料庫,其中保存著git的對象,其中最重要的是blob、tree和commit對象,blob對象實現了對文件內容的記錄,tree對象實現了對文件名、文件目錄結構的記錄,commit對象實現了對版本提交時間、版本作者、版本序列、版本說明等附加信息的記錄。這三類對象,完美實現了git的基礎功能:對版本狀態的記錄。

Git引用是指向git對象hash鍵值的類似指針的文件。通過Git引用,我們可以更加方便的定位到某一版本的提交。Git分支、tags等功能都是基於Git引用實現的。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序員小新人學習 的精彩文章:

操作系統開發什麼是內核?
SpringMVC + security模塊 框架整合詳解

TAG:程序員小新人學習 |