Git 是一套內容定址檔案系統。什麼意思呢?
就是Git的核心部分是一個簡單的鍵值資料庫(
key-value data store
)。你可以向該資料庫插入任意型別的內容,並會返回一個鍵值,通過該鍵值可以在任何時候再取出該內容。
(一)Git物件的存放目錄
Git中物件都儲存在本地版本庫的.git/objects
目錄(即:物件資料庫)中。
首先初使化一個趕緊的Git倉庫:
# 建立一個本地的git倉庫git_learning
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository
$ mkdir git_learning
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository
$ cd git_learning/
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning
$ git init
Initialized empty Git repository in J:/git-repository/git_learning/.git/
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ ll -a
total 8
drwxr-xr-x 1 L 197121 0 4月 10 20:24 ./
drwxr-xr-x 1 L 197121 0 4月 10 20:23 ../
drwxr-xr-x 1 L 197121 0 4月 10 20:24 .git/
確認 objects
目錄是預設初始狀態:
# 檢視.git/objects/目錄
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ ll -a .git/objects/
total 4
drwxr-xr-x 1 L 197121 0 4月 10 20:24 ./
drwxr-xr-x 1 L 197121 0 4月 10 20:24 ../
drwxr-xr-x 1 L 197121 0 4月 10 20:24 info/
drwxr-xr-x 1 L 197121 0 4月 10 20:24 pack/
# 檢視info目錄和pack目錄中的內容
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ ll -a .git/objects/info/
total 0
drwxr-xr-x 1 L 197121 0 4月 10 20:24 ./
drwxr-xr-x 1 L 197121 0 4月 10 20:24 ../
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ ll -a .git/objects/pack/
total 0
drwxr-xr-x 1 L 197121 0 4月 10 20:24 ./
drwxr-xr-x 1 L 197121 0 4月 10 20:24 ../
從上可以看到Git初始化一個本地版本庫的時候,就已經初始化了objects
目錄,並在其中建立了pack
和info
子目錄,但是沒有其他常規檔案,pack
和info
子目錄中也沒有檔案。我們只關注objects
目錄下除了info
和pack
目錄之外的變化。
(二)Git中物件型別
Git中物件型別有四種:blob(塊)
物件,tree(目錄樹)
物件,commit(提交)
物件和tag(標籤)
物件,這四種原子物件構成了Git高層資料結構的基礎。
(三)blob物件
1、blob物件說明
(1)blob物件定義
blob
物件有叫資料物件。
blob
物件是用來儲存文字內容的。即把一個文字檔案的內容,作為一個blob
物件儲存在Git系統中。
翻譯:
- Git中
blob
物件就是對應檔案系統中的檔案,確切的說是檔案的內容,包含
鍵:一個hash值和校驗值的組合,
值:檔案的內容。 - 比較特殊的是:
blob
物件只存內容,不存檔名,檔名在tree
物件中儲存。
blob
物件儲存方式如下圖:
(2)blob物件說明
通過底層命令 git hash-object
來演示,該命令可將任意資料儲存於 .git/objects
目錄(即 物件資料庫)中,並返回指向該資料物件的唯一的鍵。
1)建立一個新的資料物件,並將它手動存入你的新Git資料庫中:
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ echo 'git object test content' | git hash-object -w --stdin
cb2eb834126f53952590c448f14fece6cbb1bff3
說明:這是在Git中以最簡單的形式儲存資料到Git版本庫中,git hash-object
命令會接受你傳給它的東西,而它只會返回,可以儲存在Git倉庫中資料物件的唯一的鍵。
命令含義如下:
git hash-object
:Git底層命令,可以根據傳入的文字內容返回表示這些內容的鍵值。git object test content
:為文字檔案中的內容。-w
選項:表示hash-object
命令將資料物件儲存到Git資料庫中;若不指定此選項,則該命令僅返回對應的鍵(也就是那串Hash值)--stdin
選項:表示該命令從標準輸入(比如鍵盤)讀取內容,若不指定此選項,則須在命令尾部給出待儲存檔案的路徑。例如:git hash-object -w 檔案路徑
。
此命令輸出一個長度為40個字元的校驗和,這是一個SHA-1
雜湊值,如上cb2eb834126f53952590c448f14fece6cbb1bff3
。該值是檔案原內容加上特定頭部資訊拼接起來,做雜湊計算得到的數值。(只要文字內容相同,計算出的結果都是一樣的)
2)在計算機中檢視本地版本庫.git/objects
目錄中的變化。
可以看到.git/objects
目錄中多了一個cb
資料夾,如下:
進入cb
資料夾,可以看到有一個檔案,如下:
好,我們就看到這裡就行,這樣看比較複雜,還是回到Git Bash中檢視。
3)看本地版本庫.git/objects
目錄中的變化。
# 只看檔案可以加 -type f選項引數
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ find .git/objects
.git/objects
.git/objects/cb
.git/objects/cb/2eb834126f53952590c448f14fece6cbb1bff3
.git/objects/info
.git/objects/pack
我們可以看到cb
目錄和cb
目錄中的2eb834126f53952590c448f14fece6cbb1bff3
檔案。
這就是Git中一個blob
物件的儲存。
(3)blob物件儲存的方式
Git物件的定址使用40位的16進位制數表示,也就是SHA-1
雜湊碼,例如:cb2eb834126f53952590c448f14fece6cbb1bff3
。
為了管理方便,在檔案系統中前兩位作為.git/objects/
子目錄的名字,後38為作為檔名字。
如下:
# 檢視objects目錄中的檔案,一個檔案對應一個Git物件(資料內容)
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ find .git/objects -type f
.git/objects/cb/2eb834126f53952590c448f14fece6cbb1bff3
# 輸出資料物件的hash鍵,不儲存該資料物件。
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ echo 'git object test content' | git hash-object --stdin
cb2eb834126f53952590c448f14fece6cbb1bff3
我們可以看到兩個Hash串是一樣的。
提示:你可能感覺用40位作為Git物件的定址ID,可能會存在不同的內容但是雜湊碼相同的情況,你的感覺是正確的,但是這種情況出現的概率肯定可以忽略不計了。
(4)檢視blob物件內容
我們先用cat
命令直接讀取上面檔案,看看是什麼情況,如下圖:
可以看到顯示的內容是一片亂碼。
我們需要根據Hash鍵讀取資料,使用命令git cat-file -p 鍵
。
-p
選項可指示該命令自動判斷內容的型別,併為我們使用友好的格式顯示內容。
如下:
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ git cat-file -p cb2eb834126f53952590c448f14fece6cbb1bff3
git object test content
提示:
用
cat
命令直接讀取Git物件檔案,為什麼是亂碼資訊?檔案內容是先通過
zlib
壓縮,然後將zlib
壓縮後的內容寫入磁碟檔案(SHA-1
前兩個字元作為子目錄名稱,後 38 個字元作為子目錄檔案的名稱)
(5)檢視Git物件的型別
通過git cat-file -t 鍵
命令,可以檢視.git/objects
目錄中Git物件的型別
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ git cat-file -t cb2eb834126f53952590c448f14fece6cbb1bff3
blob
這裡也說明,我們之前儲存的Git物件是一個blob
物件。
(6)Git管理檔案
至此,你已經掌握瞭如何向 Git 中存入內容,以及如何將它們取出。
我們同樣可以將這些操作應用於檔案中的內容。 例如,可以對一個檔案進行簡單的版本控制。
1)首先,建立一個檔案。
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ echo "hello-git.txt v1" > hello-git.txt
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ ll
total 1
-rw-r--r-- 1 L 197121 17 4月 10 23:17 hello-git.txt
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ cat hello-git.txt
hello-git.txt v1
此時檔案還有被Git管理。
2)將hello-git.txt
檔案存入Git資料庫。
# 加入版本庫,生成blob物件
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ git hash-object -w ./hello-git.txt
a620c95d3001e1f64cecfc6715f9750cc7bbbf98
3)檢視Git資料庫內容。
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ find .git/objects -type f
.git/objects/a6/20c95d3001e1f64cecfc6715f9750cc7bbbf98
.git/objects/cb/2eb834126f53952590c448f14fece6cbb1bff3
可以看到有多了一個a6
子目錄,就說明有新增了一個物件。
4)檢視a6
物件的內容。
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ git cat-file -p a620c95d3001e1f64cecfc6715f9750cc7bbbf98
hello-git.txt v1
5)檢視a6
物件的型別。
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ git cat-file -t a620c95d3001e1f64cecfc6715f9750cc7bbbf98
blob
可以看到,不管是你儲存一個檔案還是儲存控制檯內容到Git中,最終儲存到Git資料庫中的都是一個blob
型別的Git物件。(即:blob
物件是儲存資料內容的)
(7)Git管理修改過的檔案
1)我們繼續向hello-git.txt
檔案中新增內容。
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ echo "hello-git.txt v2" >> hello-git.txt
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ cat hello-git.txt
hello-git.txt v1
hello-git.txt v2
2)檢視Git資料庫中的物件。
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ find .git/objects -type f
.git/objects/a6/20c95d3001e1f64cecfc6715f9750cc7bbbf98
.git/objects/cb/2eb834126f53952590c448f14fece6cbb1bff3
可以看到還是之間的兩個物件cb
和a6
,說明我們修改過的檔案不會自動的儲存到Git資料庫中。
我們還需要手動的把修改後的hello-git.txt
檔案,儲存到Git資料庫中。
3)把修改後的hello-git.txt
檔案新增到Git資料庫中。
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ git hash-object -w ./hello-git.txt
7c320a2d671f2ff177063f98343a0123432521dd
4)再次檢視Git資料庫中的物件。
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ find .git/objects -type f
.git/objects/7c/320a2d671f2ff177063f98343a0123432521dd
.git/objects/a6/20c95d3001e1f64cecfc6715f9750cc7bbbf98
.git/objects/cb/2eb834126f53952590c448f14fece6cbb1bff3
我們可以看到Git資料庫中多了一個7c
物件。
5)檢視7c
物件儲存的內容。
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ git cat-file -p 7c320a2d671f2ff177063f98343a0123432521dd
hello-git.txt v1
hello-git.txt v2
如上所示,我們可以看到7c
物件儲存了v1
和v2
的內容,v1
內容即在a6
物件中,也在7c
物件中。所以對於Git來說,儲存的不是檔案內容的增量。
2、blob物件總結
- Git的核心部分是一個簡單的鍵值資料庫(
key-value data store
),鍵就是文字內容的Hash
,值就是文字內容。 blob
物件都儲存在.git/objects
目錄中,子目錄+目錄中的檔名,就是40位Hash
值,也就是物件的鍵值。- 通過這個鍵就能找到對應的內容。
- 每個文字內容儲存到Git資料庫的時候,內容都會進行
zlib
壓縮再儲存。 blob
物件儲存的是檔案的內容,相同的內容不產生新的blob
物件。blob
物件並沒有儲存檔名。
提示:Git物件的
hash
鍵,我們擷取前幾位就行,練習時物件不那麼對,就不用全部都寫,能夠表示唯一物件就行。
3、問題
我們對檔案做一次修改,儲存到Git資料庫中,都會在Git資料庫中建立一個新的blob
物件。而在實際的工作中,我們需要做很多的改動,才提交一個版本,我們是否可以用一個blob
物件代表整個專案的一次快照。
不能,只能代表一次儲存時,一個檔案中的內容,與之前資料內容相同時不新增Git物件,資料內容不同時再次新增blob
物件。即:只要有新的內容被Git納入管理,必定有一個blob
物件與之對應。
那麼還有如下問題:
- 記住檔案的每一個版本所對應的
SHA-1
值並不現實。 - 在
blob
物件中,檔名並沒有被儲存,僅儲存了檔案的內容。
所以,沒有檔名就沒有辦法通過檔名來讀取資料,只能用40位Hash
值讀取,非常的不現實。
解決方案:樹物件。
提示:以上的操作都是在工作區和本地版本庫之間進行操,不涉及暫存區,因為我們直接儲存到了本地版本庫中。
4、本文用到的命令總結
Git底層命令:
git hash-object -w 檔案路徑
:把工作區的一個檔案提交到本地版本庫中。find .git/objects -type f
:檢視Git資料庫中的物件。(Linux命令)git cat-file -p 鍵
:檢視該Git物件的內容。git cat-file -t 鍵
:檢視該Git物件的型別。
參考: