iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 6
1
# Outline
一、前言
二、實驗
三、小結
A、待敘項目

# TL;DR
實際上的在 Git 線圖上看到的分支結構,只是由無數個 Commit 向前參考的一個鏈狀結構,無論我們有沒有建立 branch 都存在。而 branch 其實就只是一個姓名標籤,當它貼在某個 commit 時,我們就會稱呼從這個 commit 到最初的 commit 的這條線(鏈),叫做某某分支。

# Updated
2019-10-06: 更新文章結構

在雙十連假前,此系列文每天的發文時都會以最簡陳述為主,以求在繁忙的日常中,至少能先維持挑戰鐵人賽的進度,並且逐漸拓展思路與系列結構。預期會在國慶連假將本篇文章論述完整。

一、前言

昨天我們透過執行 git addgit 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 的線圖會真的分岔呢?就讓我們這幾天再看下去吧!

附錄 A、待敘項目

  • 可能會把 Git 分支開岔的部分也寫進這篇。
  • git tag

上一篇
Git Object
下一篇
Git Workspaces
系列文
Git 其然,Git 其所以然31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言