← 上一章:【冷知識】為什麼大家都說在 Git 開分支「很便宜」? 下一章:【狀況題】我可以從過去的某個 Commit 再長一個新的分支出來嗎? →


【冷知識】Git 怎麼知道現在是在哪一個分支?

當執行 git branch 指令的時候,可以看到目前所有的分支列表:

$ git branch
cat
dog
* master

看得出來目前共有 3 個分支,前面那個 * 號表示目前是處於 master 這個分支。那 Git 是在哪裡記錄這個資訊呢?

在 .git 目錄裡藏了很多有趣的東西,我們會在「【超冷知識】在 .git 目錄裡有什麼東西?」章節有更詳細的介紹。其中在 .git 目錄裡有一個名字叫做 HEAD 的檔案:

git folder

看看裡面是什麼東西:

$ cat .git/HEAD
ref: refs/heads/master

還記得 refs/heads/master 這個檔案的內容嗎?如果忘了可參閱「【冷知識】為什麼大家都說在 Git 開分支「很便宜」?」的說明。

其實 HEAD 的內容就這麼簡單,它就是記錄著目前指向的分支。我們來試著切換分支看看:

$ git checkout cat
Switched to branch 'cat'

然後再看一次 HEAD 檔案的內容:

$ cat .git/HEAD
ref: refs/heads/cat

在切換分支的時候,HEAD 的內容也會跟著變化。

【冷知識】HEAD 也有縮寫喔

從 Git 1.8.5 之後的版本,在使用 Git 的時候,可以用 @ 來代替 HEAD,例如原本的指令是這樣:

$ git reset HEAD^

可以用 @ 來替代:

$ git reset @^

但這樣有沒有比原來使用 HEAD 來得清楚,就見人見智了。

ORIG_HEAD 是什麼東西?

在 .git 目錄裡除了剛剛說的 HEAD 檔案之外,還有另一個叫做 ORIG_HEAD 的檔案,當你在做一些比較「危險」的操作(例如像 mergerebasereset 之類的),Git 就會把 HEAD 的狀態存放在這裡,讓你隨時可以跳回危險動作之前的狀態。

雖然 git reflog 指令也可以查到相關資訊,但 Reflog 的資料比較雜一點,這個 ORIG_HEAD 會更方便的讓你找到最近一次危險動作之前的 SHA-1 值。舉例來說:

$ git log --oneline
b174a5a (HEAD -> cat) add cat 2
c68537b add cat 1
e12d8ef (master) add database.yml in config folder
85e7e30 add hello
657fce7 add container
abb4f43 update index page
cef6e40 create index page
cc797cd init commit

然後我故意使用 hard 模式執行 Reset 指令,倒退一步:

$ git reset HEAD^ --hard
HEAD is now at c68537b add cat 1

看一下 ORIG_HEAD 的內容:

$ cat .git/ORIG_HEAD
b174a5a95a75c963617409d2fdb514c210d86ba6

它記錄的這個內容,正是進行 reset 指令前的 SHA-1 值 b174a5a,所以如果想要取消剛剛這次 reset

$ git reset ORIG_HEAD --hard
HEAD is now at b174a5a add cat 2

直接使用 reset 指令到 ORIG_HEAD 就能退回 reset 之前的狀態了。再來試一下合併,首先,我先切換到 master 分支:

$ git checkout master
Switched to branch 'master'

接著準備合併 cat 分支:

$ git merge cat
Updating e12d8ef..b174a5a
Fast-forward
 cat1.html | 0
 cat2.html | 0
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 cat1.html
 create mode 100644 cat2.html

沒有意外的使用快轉模式(Fast Forward)進行合併。這時候的狀態應該像這樣:

git folder

看一下現在的 ORIG_HEAD 的內容:

$ cat .git/ORIG_HEAD
e12d8ef0e8b9deae8bf115c5ce51dbc2e09c8904

看得出來它指向哪裡嗎?它正指向著合併前的那個 Commit,所以如果想取消這次的合併:

$ git reset ORIG_HEAD --hard
HEAD is now at e12d8ef add database.yml in config folder

剛剛那次的合併就取消了。這個技巧用在取消 Rebase 合併的時候相當方便,詳情請見「另一種合併方式(使用 rebase)」章節說明。


← 上一章:【冷知識】為什麼大家都說在 Git 開分支「很便宜」? 下一章:【狀況題】我可以從過去的某個 Commit 再長一個新的分支出來嗎? →

Comments