在 Day 3 的文章中,我們提到 git 有 blob、tree 跟 commit 三種物件,但事實上還有第四種:tag(標籤)。
標籤可以用 git tag
指令做出來,但不是每次做出標籤時,都會形成物件,本文的實驗將帶大家觀察何時會有物件形成、何時不會。
Git 標籤本身其實也是 Day 9 提到的一種「參考(refs)」,因此產生標籤也會影響 .git/refs/tags
目錄。
首先我們依照 Day 14 的前置準備流程,先建立兩個 commit,經 git log 後如下:
圖示如下:
Git 的標籤有兩種,分別為:
要加上兩種標籤的指令十分簡單,分別為:
git tag <tag name> <commit>
,如果是要把標籤加在當下所在分支的最新 commit,就不用特別打 <commit>
。-a
表示要加上註解,因此為 git tag <tag name> -m "tag message" <commit>
,一樣若是要加在當下所在分支的最新 commit,最後面的 <commit>
可以忽略。現在我們要在 a49055d...
這個 commit 加上輕量級標籤,只要輸入以下指令:
git tag v1.0 a49055d
這時候下 git tag
就可發現目前多了 v1.0
這個標籤:
現在在 .git/
發生了哪些變化呢?
.git/
├── hooks/
├── info/
├── objects/ # 無變化
├── refs/
│ ├── heads/
│ └── tags/
│ └── v1.0 # 多了v1.0
│
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── index
└── packed-refs # 多了這個檔案
objects/
無變化
建立的是輕量級標籤,因此只會產生參考,不會產生新的 tag 物件。
refs/tags/
多了一個名為 v1.0
的檔案
建立了一個名為 v1.0
的標籤,而這標籤是一個指向 a49055d...
這個 commit 的參考,因此 refs/tags/
中多一個檔案。
packed-refs
檔案
Git 中可能有很多參考,而像 tag 這種參考通常不太會改動,如果「一個標籤一檔案」、全部都塞到 refs/tags/
將浪費儲存空間、且效率較低,因此 git 有時會把這些參考都放到「同一個檔案」中,有助於提升效能。Git 如果要找一條參考,沒在 refs/tags/
找到的話就會在這個 packed-refs
檔案中找。
目前內文為 # pack-refs with: peeled fully-peeled sorted
這條註解,表示所有的參考都被「剝皮」(也就是所有 tag 物件都已經追溯到其最後指的 commit),而且已經排序完成,若要用二元搜尋找目標將更為容易。
有關 git 的打包機制,我們將在 Day 27 的文章深入說明。
相對於輕量級標籤,有註解的標籤需要多一個 -a
,我們把它放在 ebb02d4...
上:
git tag v0.0 -m "Original version" ebb02d4
再下 git tag
就可發現新增的 v0.0
標籤也出現了:
那此時的 .git/
資料夾結構長怎樣呢?
├── hooks/
├── info/
│
├── objects/
│ ├── ...
│ └── eb/
│ ├── 26c6f827cfff7ee6d4dd3503ce6511721912f8 # 新增的tag物件
│ └── b02d4722d536a59a7c8e111342586a614d569e # 原有Initial commit物件
│
├── refs/
│ ├── heads/
│ └── tags/
│ ├── v0.0 # 再多v0.0
│ └── v1.0
│
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── index
└── packed-refs # 內容不變
objects/
多了一個物件
有註解的標籤會形成 tag 物件,因此多了一個雜湊碼為 eb26c6f...
的物件,若用 git cat-file
指令檢視,可得其物件資訊,跟 commit 很像,其中 type commit
表示這個標籤指著一個 commit 物件。
refs/tags/
多了一個名為 v0.0
的檔案
在原有的 v1.0
之外,我們還建立了一個名為 v0.0
的標籤。
綜合兩個標籤來看,可得目前結構下圖:
我們可以用 git tag
指令新增標籤,如果新增的是「輕量級標籤」,只會單純形成一個參考;如果新增的是「有註解的標籤」,則還會做出一個完整的 tag 物件。