-
A1. 附錄 A: Git 在其他環境
- A1.1 圖形介面
- A1.2 Visual Studio 中的 Git
- A1.3 Visual Studio Code 中的 Git
- A1.4 IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine 中的 Git
- A1.5 Sublime Text 中的 Git
- A1.6 Bash 中的 Git
- A1.7 Zsh 中的 Git
- A1.8 PowerShell 中的 Git
- A1.9 小結
-
A2. 附錄 B: 在應用程式中嵌入 Git
-
A3. 附錄 C: Git 命令
10.3 Git 內部原理 - Git 引用
Git 引用
如果你對檢視從某個提交(比如 1a410e
)可達的倉庫歷史感興趣,你可以執行類似 git log 1a410e
的命令來顯示該歷史,但是你仍然需要記住 1a410e
是你想用作歷史起點提交。如果能有一個檔案,你可以用一個簡單的名稱儲存這個 SHA-1 值,然後使用這個簡單的名稱而不是原始的 SHA-1 值,那會更容易。
在 Git 中,這些簡單的名稱被稱為“引用(references)”或“refs”;你可以在 .git/refs
目錄中找到包含這些 SHA-1 值的檔案。在當前專案中,這個目錄不包含檔案,但它確實包含一個簡單的結構
$ find .git/refs
.git/refs
.git/refs/heads
.git/refs/tags
$ find .git/refs -type f
要建立一個新的引用來幫助你記住最新的提交在哪裡,你可以在技術上做一些像這樣簡單的事情
$ echo 1a410efbd13591db07496601ebc7a059dd55cfe9 > .git/refs/heads/master
現在,你可以在 Git 命令中使用剛剛建立的 head
引用,而不是 SHA-1 值
$ git log --pretty=oneline master
1a410efbd13591db07496601ebc7a059dd55cfe9 Third commit
cac0cab538b970a37ea1e769cbbde608743bc96d Second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d First commit
不鼓勵你直接編輯引用檔案;相反,Git 提供了更安全的命令 git update-ref
來更新引用
$ git update-ref refs/heads/master 1a410efbd13591db07496601ebc7a059dd55cfe9
這基本上就是 Git 中分支的含義:一個指向工作線頭部的簡單指標或引用。要在第二個提交處建立一個分支,你可以這樣做
$ git update-ref refs/heads/test cac0ca
你的分支將只包含從該提交往下的工作
$ git log --pretty=oneline test
cac0cab538b970a37ea1e769cbbde608743bc96d Second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d First commit
現在,你的 Git 資料庫在概念上看起來是這樣的

當你執行諸如 git branch <branch>
的命令時,Git 基本上是執行 update-ref
命令,將你所在分支的最新提交的 SHA-1 值新增到你想要建立的任何新引用中。
HEAD
現在的問題是,當你執行 git branch <branch>
時,Git 如何知道最新提交的 SHA-1 值?答案是 HEAD 檔案。
通常 HEAD 檔案是你當前所在分支的符號引用。透過符號引用,我們指的是與普通引用不同,它包含一個指向另一個引用的指標。
然而,在某些罕見情況下,HEAD 檔案可能包含 Git 物件的 SHA-1 值。這發生在你檢出標籤、提交或遠端分支時,這會將你的倉庫置於 “分離頭指標(detached HEAD)” 狀態。
如果你檢視該檔案,通常會看到類似這樣的內容
$ cat .git/HEAD
ref: refs/heads/master
如果你執行 git checkout test
,Git 會更新檔案,使其看起來像這樣
$ cat .git/HEAD
ref: refs/heads/test
當你執行 git commit
時,它會建立提交物件,並將該提交物件的父級指定為 HEAD 中引用指向的任何 SHA-1 值。
你也可以手動編輯這個檔案,但同樣存在一個更安全的命令:git symbolic-ref
。你可以透過此命令讀取 HEAD 的值
$ git symbolic-ref HEAD
refs/heads/master
你也可以使用相同的命令設定 HEAD 的值
$ git symbolic-ref HEAD refs/heads/test
$ cat .git/HEAD
ref: refs/heads/test
你不能設定 refs 樣式之外的符號引用
$ git symbolic-ref HEAD test
fatal: Refusing to point HEAD outside of refs/
標籤
我們剛剛討論了 Git 的三種主要物件型別(blob、tree 和 commit),但還有第四種。tag 物件非常像一個提交物件——它包含一個標籤建立者、日期、訊息和一個指標。主要區別在於標籤物件通常指向一個提交而不是一個樹。它類似於分支引用,但它從不移動——它總是指向相同的提交,但給它一個更友好的名稱。
正如 Git 基礎 中所討論的,標籤有兩種型別:附註標籤(annotated tag)和輕量標籤(lightweight tag)。你可以透過執行類似這樣的命令來建立輕量標籤
$ git update-ref refs/tags/v1.0 cac0cab538b970a37ea1e769cbbde608743bc96d
輕量標籤就是這樣——一個永不移動的引用。然而,附註標籤更為複雜。如果你建立一個附註標籤,Git 會建立一個標籤物件,然後寫入一個引用來指向它,而不是直接指向提交。你可以透過建立一個附註標籤(使用 -a
選項)來看到這一點
$ git tag -a v1.1 1a410efbd13591db07496601ebc7a059dd55cfe9 -m 'Test tag'
這是它建立的物件 SHA-1 值
$ cat .git/refs/tags/v1.1
9585191f37f7b0fb9444f35a9bf50de191beadc2
現在,對那個 SHA-1 值執行 git cat-file -p
$ 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
請注意,物件條目指向你打標籤的提交 SHA-1 值。還要注意,它不需要指向提交;你可以標記任何 Git 物件。例如,在 Git 原始碼中,維護者已將其 GPG 公鑰新增為 blob 物件,然後對其進行了標記。你可以在 Git 倉庫的克隆中執行此命令來檢視公鑰
$ git cat-file blob junio-gpg-pub
Linux 核心倉庫也有一個非提交指向的標籤物件——建立的第一個標籤指向原始碼匯入的初始樹。
遠端倉庫
你將看到的第三種引用是遠端引用。如果你添加了一個遠端倉庫並推送到它,Git 會在 refs/remotes
目錄中儲存你上次推送到該遠端倉庫的每個分支的值。例如,你可以新增一個名為 origin
的遠端倉庫並將其 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
檔案,檢視你上次與伺服器通訊時 origin
遠端倉庫上的 master
分支是什麼樣子
$ cat .git/refs/remotes/origin/master
ca82a6dff817ec66f44342007202690a93763949
遠端引用與分支(refs/heads
引用)的主要區別在於它們被認為是隻讀的。你可以 git checkout
到一個遠端引用,但 Git 不會符號引用 HEAD 到它,因此你永遠不會用 commit
命令更新它。Git 將它們作為對那些分支在伺服器上最新已知狀態的書籤進行管理。