# Outline
一、前言
二、實驗
三、小結
A、待敘項目
# TL;DR
實際上的在 Git 線圖上看到的分支結構,只是由無數個 Commit 向前參考的一個鏈狀結構,無論我們有沒有建立 branch 都存在。而 branch 其實就只是一個姓名標籤,當它貼在某個 commit 時,我們就會稱呼從這個 commit 到最初的 commit 的這條線(鏈),叫做某某分支。
# Updated
2019-10-06: 更新文章結構
在雙十連假前,此系列文每天的發文時都會以最簡陳述為主,以求在繁忙的日常中,至少能先維持挑戰鐵人賽的進度,並且逐漸拓展思路與系列結構。預期會在國慶連假將本篇文章論述完整。
昨天我們透過執行 git add
和 git commit
兩種指令,觀測 .git
這個儲存 Git 資訊的目錄會有什麼變化,除了發現 .git/objects
儲存的 Git Object 外,另外還有一個變化是當時按住不提的,就是儲存在 .gti/refs
中的 Git Reference。
為了方便回憶,我們先還原案發現場,為了專注在焦點,就只列出 .git/refs
部分的細節。
以下是還沒有 git commit
前的目錄結構:
# [Repo] f47eca7 @ dot-git
# [Repo] 5626a0e @ lab
# Location: ~/how-git-works-lab
$ tree .git/
.git/
├── HEAD
├── objects/
└── refs/
git log --pretty=format:"%h %s" --graph
fatal: your current branch 'master' does not have any commits yet
這是 git commit
後的目錄結構:
# [Repo] f47eca7 @ dot-git
# [Repo] 5626a0e @ lab
# Location: ~/how-git-works-lab
$ tree .git/
.git/
├── HEAD
├── objects
│ └── ...
└── refs
└── heads
└── master
$ git log --pretty=format:"%h %s" --graph
* b42533e Add app program
* f47eca7 add README
我們會發現 .git/refs
資料夾中,多出了 heads/master
的子目錄與檔案,透過指令顯示其內容:
# [Repo] f47eca7 @ dot-git
# [Repo] 5626a0e @ lab
# Location: ~/how-git-works-labv
$ cat .git/refs/heads/master
b42533e454e5d2692d7f0d96e2a8ed699de200e8
我們發現,這正是某個 Git Object 的 SHA 值,而透過前面 git log
的輸出,也讓我們印證,這是指向其中的 Commit Object。所以我們就會知道所謂的 master 分支,其實就是一個指向某一個 Commit 的 Refernce 罷了。
接下來我們來讓情況變得複雜一點。為了方便產生 Commit ,這邊用了 --allow-empty
參數的讓我們去產生幾個沒有任何變動的 Empty Commit。
透過下面的示例,我們可以看到 .git/refs/heads/master
是會隨著 commit 而更新的。
# [Repo] f47eca7 @ dot-git
# [Repo] 5626a0e @ lab
# Location: ~/how-git-works-lab
$ git commit --allow-empty -m "do some change A"
$ git commit --allow-empty -m "do some change B"
$ git log --pretty=format:"%h %s %d" --graph
* 451a871 do some change B (HEAD -> master)
* 8ef377c do some change A
* b42533e Add app program
* f47eca7 add README
$ cat .git/refs/heads/master
451a871aa3a41aa0019cbe48980f18580efd6c7c
接著,我們再新增一個 branch,並透過 git log
暸解現在的狀態與分支線圖:
# [Repo] f47eca7 @ dot-git
# [Repo] 5626a0e @ lab
# Location: ~/how-git-works-lab
$ git branch B1
$ git branch git branch --all
B1
* master
$ git log --pretty=format:"%h %s %d" --graph
* 451a871 do some change B (HEAD -> master, B1)
* 8ef377c do some change A
* b42533e Add app program
* f47eca7 add README
我們這時候會發現,master 和 B1 是處於同一個位置,並不如以往就 Git 分支這個名稱的誤解:「開新分支就會岔開出一個新的線」。透過前面的敘述,其實也想當然耳,所謂的 2 個分支,其實不過就是兩個 Refernce,我現在只不過新增一個 Reference,當然不會有任何岔開的事情發生。
不過這時可能也會有一個疑惑,那麼 Git 又是透過什麼知道現在是位在哪個分支呢?答案其實就在《萬丈高樓平 Git 起》中,經過一番篩選的 .git
目錄中啦!
# [Repo] f47eca7 @ dot-git
# [Repo] 5626a0e @ lab
# Location: ~/how-git-works-lab
.git/
├── HEAD
├── objects/
└── refs/
我們現在已知 objs/
是用來儲存 Git Objects,而 refs
是用來儲存 Git References,而剩下的 HEAD
正是用來記錄現在是位在哪個分支上!讓我們重新檢視之前看過的 HEAD
檔案內容:
# [Repo] f47eca7 @ dot-git
# [Repo] 5626a0e @ lab
# Location: ~/how-git-works-lab
$ cat .git/HEAD
ref: refs/heads/master
這份內容,明確的紀錄現在是的處於 refs/heads/master
這個分支上,倘若我現在切換到 B1 分支,這份檔案內容也就會隨之變動(~Reading Stiener 發動!~):
# [Repo] f47eca7 @ dot-git
# [Repo] 5626a0e @ lab
# Location: ~/how-git-works-lab
$ git switch B1
Switched to branch 'B1'
$ cat .git/HEAD
ref: refs/heads/B1
這時候再執行一次 git log
,會發現原本 HEAD -> master
的附註也產生了變動。
# [Repo] f47eca7 @ dot-git
# [Repo] 5626a0e @ lab
# Location: ~/how-git-works-lab
git log --pretty=format:"%h %s %d" --graph
* 451a871 do some change B (HEAD -> B1, master)
* 8ef377c do some change A
* b42533e Add app program
* f47eca7 add README
透過這些操作,我們大致暸解了 Git 分支的本質與運作原理:
-> 透過 HEAD
我們暸解到現在位在哪個分支
-> 透過分支我們知道現在指向那一個 Commit
-> 透過 Commit,我們可以不斷向前追溯,最後成為了一條線,也就是我們原本想像的分支。
那什麼時候 Git 的線圖會真的分岔呢?就讓我們這幾天再看下去吧!
git tag
。