我們可以借助類似于 git log 1a410e 這樣的命令來瀏覽完整的提交歷史,但為了能遍歷那段歷史從而找到所有相關(guān)對象,你仍須記住 1a410e 是最后一個提交。 我們需要一個文件來保存 SHA-1 值,并給文件起一個簡單的名字,然后用這個名字指針來替代原始的 SHA-1 值。
在 Git 里,這樣的文件被稱為“引用(references,或縮寫為 refs)”;你可以在 .git/refs 目錄下找到這類含有 SHA-1 值的文件。 在目前的項目中,這個目錄沒有包含任何文件,但它包含了一個簡單的目錄結(jié)構(gòu):
$ find .git/refs
.git/refs
.git/refs/heads
.git/refs/tags
$ find .git/refs -type f
若要創(chuàng)建一個新引用來幫助記憶最新提交所在的位置,從技術(shù)上講我們只需簡單地做如下操作:
$ echo "1a410efbd13591db07496601ebc7a059dd55cfe9" > .git/refs/heads/master
現(xiàn)在,你就可以在 Git 命令中使用這個剛創(chuàng)建的新引用來代替 SHA-1 值了:
$ git log --pretty=oneline master
1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
我們不提倡直接編輯引用文件。 如果想更新某個引用,Git 提供了一個更加安全的命令 update-ref 來完成此事:
$ git update-ref refs/heads/master 1a410efbd13591db07496601ebc7a059dd55cfe9
這基本就是 Git 分支的本質(zhì):一個指向某一系列提交之首的指針或引用。 若想在第二個提交上創(chuàng)建一個分支,可以這么做:
$ git update-ref refs/heads/test cac0ca
這個分支將只包含從第二個提交開始往前追溯的記錄:
$ git log --pretty=oneline test
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
至此,我們的 Git 數(shù)據(jù)庫從概念上看起來像這樣:
http://wiki.jikexueyuan.com/project/pro-git-two/images/185.png" alt="" />
Figure 10-4. 包含分支引用的 Git 目錄對象。 當(dāng)運行類似于 git branch (branchname) 這樣的命令時,Git 實際上會運行 update-ref 命令,取得當(dāng)前所在分支最新提交對應(yīng)的 SHA-1 值,并將其加入你想要創(chuàng)建的任何新引用中。
現(xiàn)在的問題是,當(dāng)你執(zhí)行 git branch (branchname) 時,Git 如何知道最新提交的 SHA-1 值呢? 答案是 HEAD 文件。
HEAD 文件是一個符號引用(symbolic reference),指向目前所在的分支。 所謂符號引用,意味著它并不像普通引用那樣包含一個 SHA-1 值——它是一個指向其他引用的指針。 如果查看 HEAD 文件的內(nèi)容,一般而言我們看到的類似這樣:
$ cat .git/HEAD
ref: refs/heads/master
如果執(zhí)行 git checkout test,Git 會像這樣更新 HEAD 文件:
$ cat .git/HEAD
ref: refs/heads/test
當(dāng)我們執(zhí)行 git commit 時,該命令會創(chuàng)建一個提交對象,并用 HEAD 文件中那個引用所指向的 SHA-1 值設(shè)置其父提交字段。
你也可以手動編輯該文件,然而同樣存在一個更安全的命令來完成此事:symbolic-ref。 可以借助此命令來查看 HEAD 引用對應(yīng)的值:
$ git symbolic-ref HEAD
refs/heads/master
同樣可以設(shè)置 HEAD 引用的值:
$ git symbolic-ref HEAD refs/heads/test
$ cat .git/HEAD
ref: refs/heads/test
不能把符號引用設(shè)置為一個不符合引用格式的值:
$ git symbolic-ref HEAD test
fatal: Refusing to point HEAD outside of refs/
前文我們剛討論過 Git 的三種主要對象類型,事實上還有第四種。 標(biāo)簽對象(tag object)非常類似于一個提交對象——它包含一個標(biāo)簽創(chuàng)建者信息、一個日期、一段注釋信息,以及一個指針。 主要的區(qū)別在于,標(biāo)簽對象通常指向一個提交對象,而不是一個樹對象。 它像是一個永不移動的分支引用——永遠(yuǎn)指向同一個提交對象,只不過給這個提交對象加上一個更友好的名字罷了。
正如 Git 基礎(chǔ) 中所討論的那樣,存在兩種類型的標(biāo)簽:附注標(biāo)簽和輕量標(biāo)簽。 可以像這樣創(chuàng)建一個輕量標(biāo)簽:
$ git update-ref refs/tags/v1.0
cac0cab538b970a37ea1e769cbbde608743bc96d
這就是輕量標(biāo)簽的全部內(nèi)容——一個固定的引用。 然而,一個附注標(biāo)簽則更復(fù)雜一些。 若要創(chuàng)建一個附注標(biāo)簽,Git 會創(chuàng)建一個標(biāo)簽對象,并記錄一個引用來指向該標(biāo)簽對象,而不是直接指向提交對象。 可以通過創(chuàng)建一個附注標(biāo)簽來驗證這個過程(-a 選項指定了要創(chuàng)建的是一個附注標(biāo)簽):
$ git tag -a v1.1 1a410efbd13591db07496601ebc7a059dd55cfe9 -m 'test tag'
下面是上述過程所建標(biāo)簽對象的 SHA-1 值:
$ cat .git/refs/tags/v1.1
9585191f37f7b0fb9444f35a9bf50de191beadc2
現(xiàn)在對該 SHA-1 值運行 cat-file 命令:
$ git cat-file -p 9585191f37f7b0fb9444f35a9bf50de191beadc2
object 1a410efbd13591db07496601ebc7a059dd55cfe9
type commit
tag v1.1
tagger Scott Chacon <schacon@gmail.com> Sat May 23 16:48:58 2009 -0700
test tag
我們注意到,object 條目指向我們打了標(biāo)簽的那個提交對象的 SHA-1 值。 另外要注意的是,標(biāo)簽對象并非必須指向某個提交對象;你可以對任意類型的 Git 對象打標(biāo)簽。 例如,在 Git 源碼中,項目維護者將他們的 GPG 公鑰添加為一個數(shù)據(jù)對象,然后對這個對象打了一個標(biāo)簽。 可以克隆一個 Git 版本庫,然后通過執(zhí)行下面的命令來在這個版本庫中查看上述公鑰:
$ git cat-file blob junio-gpg-pub
Linux 內(nèi)核版本庫同樣有一個不指向提交對象的標(biāo)簽對象——首個被創(chuàng)建的標(biāo)簽對象所指向的是最初被引入版本庫的那份內(nèi)核源碼所對應(yīng)的樹對象。
我們將看到的第三種引用類型是遠(yuǎn)程引用(remote reference)。 如果你添加了一個遠(yuǎn)程版本庫并對其執(zhí)行過推送操作,Git 會記錄下最近一次推送操作時每一個分支所對應(yīng)的值,并保存在 refs/remotes 目錄下。 例如,你可以添加一個叫做 origin 的遠(yuǎn)程版本庫,然后把 master 分支推送上去:
$ git remote add origin git@github.com:schacon/simplegit-progit.git
$ git push origin master
Counting objects: 11, done.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (7/7), 716 bytes, done.
Total 7 (delta 2), reused 4 (delta 1)
To git@github.com:schacon/simplegit-progit.git
a11bef0..ca82a6d master -> master
此時,如果查看 refs/remotes/origin/master 文件,可以發(fā)現(xiàn) origin 遠(yuǎn)程版本庫的 master 分支所對應(yīng)的 SHA-1 值,就是最近一次與服務(wù)器通信時本地 master 分支所對應(yīng)的 SHA-1 值:
$ cat .git/refs/remotes/origin/master
ca82a6dff817ec66f44342007202690a93763949
遠(yuǎn)程引用和分支(位于 refs/heads 目錄下的引用)之間最主要的區(qū)別在于,遠(yuǎn)程引用是只讀的。 雖然可以 git checkout 到某個遠(yuǎn)程引用,但是 Git 并不會將 HEAD 引用指向該遠(yuǎn)程引用。因此,你永遠(yuǎn)不能通過 commit 命令來更新遠(yuǎn)程引用。 Git 將這些遠(yuǎn)程引用作為記錄遠(yuǎn)程服務(wù)器上各分支最后已知位置狀態(tài)的書簽來管理。