本章節是Git的核心知識點,主要是介紹Git底層原理與在使用Git過程中的幾個重要區域,弄懂Git的整個使用流程,以及數據的存儲過程。
工作區(Working Directory):
工作區就是我們平時編寫文本文件的地方
暫存區(Stage/Index):
暫存區是我們提交文本文件到本地倉庫的來源地,只有把工作區的文件添加至暫存區,才可以被提交至本地倉庫。
本地倉庫(Repository):
本地倉庫是保存每次文件更新的記錄,包括提交人,提交時間,提交的內容等詳細信息,方便追溯歷史版本。
遠程倉庫(Remote Repository):
遠程倉庫算是本地倉庫的壹個副本,主要是方便合作夥伴之間的倉庫文件同步。
因此它的使用流程可以簡單的概括為:
1、在本地搭建壹個目錄,用來創建git倉庫
$ git init gitDirectory
2、在倉庫目錄下創建文本文件(工作區)
$ cd gitDirectory
$ echo "first txt" > first.txt
3、把工作區的first.txt文件添加至git暫存區
$ git add first.txt
4、將暫存區中的文件first.txt提交至本地倉庫
$ git commit -m "first commit"
5、將文件保存至本地倉庫就已經可以記錄我們每次提交的歷史信息了,但是為了方便其他夥伴壹起協作,還需要搭建壹個遠程服務。(本次以GitHub為例)
在GitHub創建壹個和本地壹樣名稱的倉庫,創建成功後會生成壹個倉庫地址:
/mr-kings/gitDirectory.git
6、將本地倉庫和遠程倉庫關聯起來
$ git remote add origin /mr-kings/gitDirectory.git
7、第壹次將本地倉庫提交至遠程倉庫
$ git push -u origin master
第壹次需要添加 -u 參數,即把本地的master分支和遠程倉庫的master分支對應上
8、此時本地倉庫和遠程倉庫就已經實現了同步,其他協作夥伴只需到遠程倉庫把倉庫克隆到自己的電腦即可進行協作編輯
$ git clone /mr-kings/gitDirectory.git
9、克隆下來以後會在本地生成本地倉庫以及工作區,後續的操作和2步驟及以後步驟壹致
需要註意的是:遠程倉庫有兩種連接方式mit
description(文件):創建倉庫的描述文件
HEAD(文件):指示當前被檢出(所在)的分支,如當前在test分支,文件內容則為ref: refs/heads/test。
hooks/(文件夾):包含客戶端或服務端的鉤子腳本(hook scripts),如pre-commit,post-receive等
info/ (文件夾):用以存儲壹些有關git倉庫的信息,如exclude
objects/ (文件夾):用以存儲git倉庫中的所有數據內容
refs/(文件夾):包含 heads 文件夾,remote文件夾。heads 記錄本地相關的各 git分支操作記錄,remote 記錄遠程倉庫相關的各git分支 操作記錄
當第壹次提交的時候還會生成以下文件及文件夾:
index (文件) -- (在git add file的時候生成):是當前版本的文件索引,包含生成當前樹(唯壹確定的)對象的所虛信息,可用於快速比對工作樹和其他提交樹對象的差異(各commit和HEAD之間的diff),可用於存儲單文件的多個版本以有效的解決合並沖突。可使用git ls-files 查看index文件內容
COMMIT_EDITMSG(文件) -- (在git commit -m "first commit"的時候生成):最近壹次的 commit edit message
logs/ (文件夾) -- (在git commit -m "first commit"的時候生成):放置git倉庫操作記錄的文件夾,包含HEAD文件 和 refs文件夾
以上簡單介紹了.git目錄下的文件及文件夾,重點則是objects文件夾:
經過第壹次提交後objects文件夾下多出了3個文件夾:44/、d0/、f6/。通過提交的日誌我們發現,commit後會生成壹個40位的16進制字符串(前兩位作為文件夾名稱,後38位為內容哈希值)
官方描述:這是壹個 SHA-1 哈希值——壹個將待存儲的數據外加壹個頭部信息(header)壹起做 SHA-1 校驗運算而得的校驗和(前2位作為文件夾名稱 -- 後面38位作為內容的哈希值)
通過cat-file -t sha-1 命令查看當前哈希值所屬的類型:
由圖可知它是壹個commit對象
再通過命令cat-file -p sha-1查看該commit對象包含有哪些信息
由圖知壹個commit對象包含了tree對象,本地倉庫信息,提交人信息及提交時的備註信息。
再通過上述命令查看tree對象又包含又哪些信息:
由圖知tree對象又包含了blob對象,在多目錄的情況下tree對象還會包含其他tree對象。
由此得出結論:剛才提交的時候生成的那個3個文件夾分別對應的是3個對象即:44/文件夾對應的是commit對象,f6/文件夾對應的是tree對象,d0/文件夾對應的是blob對象。層級關系是:commit對象對應壹個tree對象,tree對象可以包含壹個或多個其他tree對象和blob對象。
下面就簡單介紹git中的幾個對象:
blob:
blob對象只跟文本文件的內容有關,和文本文件的名稱及目錄無關,只要是相同的文本文件,會指向同壹個blob。
tree:
tree對象記錄文本文件內容和名稱、目錄等信息,每次提交都會生成壹個頂層tree對象,它可以指向其引用的tree或blob。
commit:
commit對象記錄本次提交的所有信息,包括提交人、提交時間,本次提交包含的tree及blob。
tag:
標簽引用,它指向某壹個commit。
用下面的圖可以把今天的內容概括起來:上半部分描述了git的操作流程圖,下半部分描述git底層數據存儲結構圖
Git流程及底層結構圖
下面就對圖的下半部分做個詳細說明:
1、在與.git同級目錄下新建文件夾directory,再在directory目錄下新建壹個文本文件first.txt,裏面的內容為1。當執行$ git add first.txt 的時候就會在.git/objects/生成壹個blob對象的文件夾(前兩位為文件夾名稱 -- 後38位為文本內容的哈希值)對應上圖的第壹個d00491 -- blob對象。
2、當執行$ git commit -m "first commit" 的時候就會在.git/objects/下新生成了2個tree對象和壹個commit對象的文件夾(前兩位為文件夾名稱 -- 後38位為文本內容的哈希值)對應上圖的f6589b -- tree對象,4a2e3e -- tree對象,6b18a7 -- commit對象。之所以生成兩個tree對象是因為directory目錄為壹個tree對象還有與commit對象壹壹對應的頂層tree對象。這個時候HEAD遊標指向的是當前master分支的first commit。並且在這次提交的時候打個v1.0版本的標簽。
3、在與.git同級目錄下新建壹個新的文本文件second.txt,內容為2。當執行$ git add second.txt 的時候就會在.git/objects/生成壹個新的blob對象的文件夾(前兩位為文件夾名稱 -- 後38位為文本內容的哈希值)對應上圖的0cfbf0 -- blob對象。
4、當執行$ git commit -m "second commit" 的時候就會在.git/objects/下新生成了1個tree對象和壹個commit對象的文件夾(前兩位為文件夾名稱 -- 後38位為文本內容的哈希值)對應上圖的35e40c -- tree對象,d6dca9 -- commit對象。只生成壹個與commit對象壹壹對應的頂層tree對象。由於本次提交directory目錄下的first.txt內容沒有變化,所以上圖的35e40c -- tree對象還會指向f6589b -- tree對象。這個時候HEAD遊標指向的是當前master分支的second commit(HEAD索引向前移動),second commit 會指向上壹次的提交即 parent指向first commit。
後續的操作以此類推,但需要註意的點是:
1、blob對象只對文件的內容有關,和文件名稱無關,如果不同的文件名稱,內容相同只會有壹個blob對象,生成的新tree對象會指向該blob對象。例如上圖的third.txt和four.txt裏面的內容都為3。所以不會生成新的blob對象,新的tree對象只會指向同壹個blob。
2、如果每次提交的時候包含的某些文件並沒有改動(更新),那麽就會直接指向它原來的索引,不會重新生成。例如上圖的directory/first.txt,second.txt
3、每次commit對象都會和頂層的tree對象壹壹對應。