本章節(jié)是Git的核心知識(shí)點(diǎn),主要是介紹Git底層原理與在使用Git過(guò)程中的幾個(gè)重要區(qū)域,弄懂Git的整個(gè)使用流程,以及數(shù)據(jù)的存儲(chǔ)過(guò)程。
Git相關(guān)區(qū)域介紹
工作區(qū)(Working Directory):
工作區(qū)就是我們平時(shí)編寫文本文件的地方
暫存區(qū)(Stage/Index):
暫存區(qū)是我們提交文本文件到本地倉(cāng)庫(kù)的來(lái)源地,只有把工作區(qū)的文件添加至?xí)捍鎱^(qū),才可以被提交至本地倉(cāng)庫(kù)。
本地倉(cāng)庫(kù)(Repository):
本地倉(cāng)庫(kù)是保存每次文件更新的記錄,包括提交人,提交時(shí)間,提交的內(nèi)容等詳細(xì)信息,方便追溯歷史版本。
遠(yuǎn)程倉(cāng)庫(kù)(Remote Repository):
遠(yuǎn)程倉(cāng)庫(kù)算是本地倉(cāng)庫(kù)的一個(gè)副本,主要是方便合作伙伴之間的倉(cāng)庫(kù)文件同步。
因此它的使用流程可以簡(jiǎn)單的概括為:
1、在本地搭建一個(gè)目錄,用來(lái)創(chuàng)建git倉(cāng)庫(kù)
$ git init gitDirectory
2、在倉(cāng)庫(kù)目錄下創(chuàng)建文本文件(工作區(qū))
$ cd gitDirectory
$ echo "first txt" > first.txt
3、把工作區(qū)的first.txt文件添加至git暫存區(qū)
$ git add first.txt
4、將暫存區(qū)中的文件first.txt提交至本地倉(cāng)庫(kù)
$ git commit -m "first commit"
5、將文件保存至本地倉(cāng)庫(kù)就已經(jīng)可以記錄我們每次提交的歷史信息了,但是為了方便其他伙伴一起協(xié)作,還需要搭建一個(gè)遠(yuǎn)程服務(wù)。(本次以GitHub為例)
在GitHub創(chuàng)建一個(gè)和本地一樣名稱的倉(cāng)庫(kù),創(chuàng)建成功后會(huì)生成一個(gè)倉(cāng)庫(kù)地址:
https://github.com/mr-kings/gitDirectory.git
6、將本地倉(cāng)庫(kù)和遠(yuǎn)程倉(cāng)庫(kù)關(guān)聯(lián)起來(lái)
$ git remote add origin https://github.com/mr-kings/gitDirectory.git
7、第一次將本地倉(cāng)庫(kù)提交至遠(yuǎn)程倉(cāng)庫(kù)
$ git push -u origin master
第一次需要添加 -u 參數(shù),即把本地的master分支和遠(yuǎn)程倉(cāng)庫(kù)的master分支對(duì)應(yīng)上
8、此時(shí)本地倉(cāng)庫(kù)和遠(yuǎn)程倉(cāng)庫(kù)就已經(jīng)實(shí)現(xiàn)了同步,其他協(xié)作伙伴只需到遠(yuǎn)程倉(cāng)庫(kù)把倉(cāng)庫(kù)克隆到自己的電腦即可進(jìn)行協(xié)作編輯
$ git clone https://github.com/mr-kings/gitDirectory.git
9、克隆下來(lái)以后會(huì)在本地生成本地倉(cāng)庫(kù)以及工作區(qū),后續(xù)的操作和2步驟及以后步驟一致
需要注意的是:遠(yuǎn)程倉(cāng)庫(kù)有兩種連接方式https/ssh,上面的例子使用的https,其實(shí)ssh方式會(huì)比https快的多,它還可以通過(guò)添加密鑰的方式省去每次提交時(shí)都要輸入用戶名和密碼的問題,這里不做詳細(xì)介紹。https也是可以通過(guò)配置省去每次推送都需要輸入用戶名和密碼的問題。
Git底層原理
Git安裝成功后,在本地新建一個(gè)Git倉(cāng)庫(kù),$ git init Gitstudy會(huì)生成一個(gè).git文件夾,如果你創(chuàng)建的時(shí)候沒有發(fā)現(xiàn).git目錄那應(yīng)該是你的電腦默認(rèn)隱藏了.git文件夾,有兩種方式可以查看它:
第一種方式:
命令行工具,在當(dāng)前目錄下,在命令行里輸入 $ ll -a 即可查看
第二種方式:
在當(dāng)前目錄下,點(diǎn)擊查看菜單,然后勾選上隱藏的項(xiàng)目即可
.git目錄就是暫存區(qū)和本地倉(cāng)庫(kù)的位置,所以它的核心就在這里,下面看看它有哪些內(nèi)容:
由上圖可知,初始化的時(shí)候.git目錄下有以下文件及文件夾:
config(文件):存放當(dāng)前倉(cāng)庫(kù)的一些配置信息,比如記住用戶名和密碼,別名等
下面是它的常用選項(xiàng):
[core] ignorecase 是否忽略文件大小寫
[remote "origin"] url 配置遠(yuǎn)程倉(cāng)庫(kù)地址
[remote "origin"] fetch 遠(yuǎn)程分支映射關(guān)系
[user] name 用戶名
[user] email 郵箱
[alias] 命令別名配置 : cmt = commit
description(文件):創(chuàng)建倉(cāng)庫(kù)的描述文件
HEAD(文件):指示當(dāng)前被檢出(所在)的分支,如當(dāng)前在test分支,文件內(nèi)容則為ref: refs/heads/test。
hooks/(文件夾):包含客戶端或服務(wù)端的鉤子腳本(hook scripts),如pre-commit,post-receive等
info/ (文件夾):用以存儲(chǔ)一些有關(guān)git倉(cāng)庫(kù)的信息,如exclude
objects/ (文件夾):用以存儲(chǔ)git倉(cāng)庫(kù)中的所有數(shù)據(jù)內(nèi)容
refs/(文件夾):包含 heads 文件夾,remote文件夾。heads 記錄本地相關(guān)的各 git分支操作記錄,remote 記錄遠(yuǎn)程倉(cāng)庫(kù)相關(guān)的各git分支 操作記錄
當(dāng)?shù)谝淮翁峤坏臅r(shí)候還會(huì)生成以下文件及文件夾:
index (文件) -- (在git add file的時(shí)候生成):是當(dāng)前版本的文件索引,包含生成當(dāng)前樹(唯一確定的)對(duì)象的所虛信息,可用于快速比對(duì)工作樹和其他提交樹對(duì)象的差異(各commit和HEAD之間的diff),可用于存儲(chǔ)單文件的多個(gè)版本以有效的解決合并沖突??墒褂胓it ls-files 查看index文件內(nèi)容
COMMIT_EDITMSG(文件) -- (在git commit -m "first commit"的時(shí)候生成):最近一次的 commit edit message
logs/ (文件夾) -- (在git commit -m "first commit"的時(shí)候生成):放置git倉(cāng)庫(kù)操作記錄的文件夾,包含HEAD文件 和 refs文件夾
以上簡(jiǎn)單介紹了.git目錄下的文件及文件夾,重點(diǎn)則是objects文件夾:
經(jīng)過(guò)第一次提交后objects文件夾下多出了3個(gè)文件夾:44/、d0/、f6/。通過(guò)提交的日志我們發(fā)現(xiàn),commit后會(huì)生成一個(gè)40位的16進(jìn)制字符串(前兩位作為文件夾名稱,后38位為內(nèi)容哈希值)
官方描述:這是一個(gè) SHA-1 哈希值——一個(gè)將待存儲(chǔ)的數(shù)據(jù)外加一個(gè)頭部信息(header)一起做 SHA-1 校驗(yàn)運(yùn)算而得的校驗(yàn)和(前2位作為文件夾名稱 -- 后面38位作為內(nèi)容的哈希值)
通過(guò)cat-file -t sha-1 命令查看當(dāng)前哈希值所屬的類型:
由圖可知它是一個(gè)commit對(duì)象
再通過(guò)命令cat-file -p sha-1查看該commit對(duì)象包含有哪些信息
由圖知一個(gè)commit對(duì)象包含了tree對(duì)象,本地倉(cāng)庫(kù)信息,提交人信息及提交時(shí)的備注信息。
再通過(guò)上述命令查看tree對(duì)象又包含又哪些信息:
由圖知tree對(duì)象又包含了blob對(duì)象,在多目錄的情況下tree對(duì)象還會(huì)包含其他tree對(duì)象。
由此得出結(jié)論:剛才提交的時(shí)候生成的那個(gè)3個(gè)文件夾分別對(duì)應(yīng)的是3個(gè)對(duì)象即:44/文件夾對(duì)應(yīng)的是commit對(duì)象,f6/文件夾對(duì)應(yīng)的是tree對(duì)象,d0/文件夾對(duì)應(yīng)的是blob對(duì)象。層級(jí)關(guān)系是:commit對(duì)象對(duì)應(yīng)一個(gè)tree對(duì)象,tree對(duì)象可以包含一個(gè)或多個(gè)其他tree對(duì)象和blob對(duì)象。
下面就簡(jiǎn)單介紹git中的幾個(gè)對(duì)象:
blob:
blob對(duì)象只跟文本文件的內(nèi)容有關(guān),和文本文件的名稱及目錄無(wú)關(guān),只要是相同的文本文件,會(huì)指向同一個(gè)blob。
tree:
tree對(duì)象記錄文本文件內(nèi)容和名稱、目錄等信息,每次提交都會(huì)生成一個(gè)頂層tree對(duì)象,它可以指向其引用的tree或blob。
commit:
commit對(duì)象記錄本次提交的所有信息,包括提交人、提交時(shí)間,本次提交包含的tree及blob。
tag:
標(biāo)簽引用,它指向某一個(gè)commit。
用下面的圖可以把今天的內(nèi)容概括起來(lái):上半部分描述了git的操作流程圖,下半部分描述git底層數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)圖
Git流程及底層結(jié)構(gòu)圖
下面就對(duì)圖的下半部分做個(gè)詳細(xì)說(shuō)明:
1、在與.git同級(jí)目錄下新建文件夾directory,再在directory目錄下新建一個(gè)文本文件first.txt,里面的內(nèi)容為1。當(dāng)執(zhí)行$ git add first.txt 的時(shí)候就會(huì)在.git/objects/生成一個(gè)blob對(duì)象的文件夾(前兩位為文件夾名稱 -- 后38位為文本內(nèi)容的哈希值)對(duì)應(yīng)上圖的第一個(gè)d00491 -- blob對(duì)象。
2、當(dāng)執(zhí)行$ git commit -m "first commit" 的時(shí)候就會(huì)在.git/objects/下新生成了2個(gè)tree對(duì)象和一個(gè)commit對(duì)象的文件夾(前兩位為文件夾名稱 -- 后38位為文本內(nèi)容的哈希值)對(duì)應(yīng)上圖的f6589b -- tree對(duì)象,4a2e3e -- tree對(duì)象,6b18a7 -- commit對(duì)象。之所以生成兩個(gè)tree對(duì)象是因?yàn)閐irectory目錄為一個(gè)tree對(duì)象還有與commit對(duì)象一一對(duì)應(yīng)的頂層tree對(duì)象。這個(gè)時(shí)候HEAD游標(biāo)指向的是當(dāng)前master分支的first commit。并且在這次提交的時(shí)候打個(gè)v1.0版本的標(biāo)簽。
3、在與.git同級(jí)目錄下新建一個(gè)新的文本文件second.txt,內(nèi)容為2。當(dāng)執(zhí)行$ git add second.txt 的時(shí)候就會(huì)在.git/objects/生成一個(gè)新的blob對(duì)象的文件夾(前兩位為文件夾名稱 -- 后38位為文本內(nèi)容的哈希值)對(duì)應(yīng)上圖的0cfbf0 -- blob對(duì)象。
4、當(dāng)執(zhí)行$ git commit -m "second commit" 的時(shí)候就會(huì)在.git/objects/下新生成了1個(gè)tree對(duì)象和一個(gè)commit對(duì)象的文件夾(前兩位為文件夾名稱 -- 后38位為文本內(nèi)容的哈希值)對(duì)應(yīng)上圖的35e40c -- tree對(duì)象,d6dca9 -- commit對(duì)象。只生成一個(gè)與commit對(duì)象一一對(duì)應(yīng)的頂層tree對(duì)象。由于本次提交directory目錄下的first.txt內(nèi)容沒有變化,所以上圖的35e40c -- tree對(duì)象還會(huì)指向f6589b -- tree對(duì)象。這個(gè)時(shí)候HEAD游標(biāo)指向的是當(dāng)前master分支的second commit(HEAD索引向前移動(dòng)),second commit 會(huì)指向上一次的提交即 parent指向first commit。
后續(xù)的操作以此類推,但需要注意的點(diǎn)是:
1、blob對(duì)象只對(duì)文件的內(nèi)容有關(guān),和文件名稱無(wú)關(guān),如果不同的文件名稱,內(nèi)容相同只會(huì)有一個(gè)blob對(duì)象,生成的新tree對(duì)象會(huì)指向該blob對(duì)象。例如上圖的third.txt和four.txt里面的內(nèi)容都為3。所以不會(huì)生成新的blob對(duì)象,新的tree對(duì)象只會(huì)指向同一個(gè)blob。
2、如果每次提交的時(shí)候包含的某些文件并沒有改動(dòng)(更新),那么就會(huì)直接指向它原來(lái)的索引,不會(huì)重新生成。例如上圖的directory/first.txt,second.txt
3、每次commit對(duì)象都會(huì)和頂層的tree對(duì)象一一對(duì)應(yīng)。