終於可以進入 Git 的重頭戲,分支篇!
分支是 Git 極度重要的功能之一,因為有了分支的各種運作,Git 的世界才會變得如此精彩。 同時也會讓我的文章變得很難解釋
在 Git 中要操作分支是一件非常簡單的事情,無論是建立分支、切換分支、分支更名稱,基本上只要簡單一個指令,或是點幾下 GUI,就能完成。
不過在實際學習操作之前,想先用一篇文章的內容,幫大家先建構好看待 Git 分支應該有的觀念,避免在直接學習操作的過程,不知不覺對分支建立錯誤的印象。
接著就來聊聊「為什麼要使用分支」,以及「分支到底是什麼」吧!
有句話是這樣說的:
不知道不會怎樣,但知道了會很不一樣。
分支這東西,在 Git 的世界大概就是這樣的存在。
有人比喻分支是建立 平行宇宙,也有人比喻分支是鳴人的 影分身之術 (註1)。
我自己是比較喜歡 影分身之術 的比喻!! 沒人在意
無論是哪種比喻都是在說明一件事情:建立了新的分支之後,在這條分支所做的版控行為 不會 對原本分支的內容造成任何影響!
這樣的特性就很適合拿來進行 實驗性 的操作,無論是開發新功能,或是一些可能會讓專案壞掉的動作,透過分支另闢一條路,就是一個最簡單的預防行為。
在 第一個 Commit 該放什麼內容 文章中曾經提過,有了分支之後,我們同時可以做到「讓成品存放在獨立一條時間線」以及「保存開發過程各種關鍵時間點」兩個需求。除此之外,建立分支進行開發,也能避免多人共用同一條分支造成的各種衝突與混亂。
也因為這樣,熟悉 Git 的開發人員,通常專案起始的第一個動作,就是先建立一個「開發用的分支」才會開始開發。這個行為並不限定於「團隊」,即便專案只有一個人,也很推薦這麼做!
註1. 影分身之術的比喻,出自 《為你自己學 Git》 - 高見龍(龍哥)
相信大多數人應該都會把 「Git 的分支」 看待成「樹枝的分支」或是「鐵軌的分支」。
畢竟就算還不知道怎麼用分支,也大概看過人家用 Git 會出現的線圖,那分岔的樣子跟樹枝大概有 87% 像:
不管讀者之前對於分支到底有什麼印象,現在開始來說明我們應該怎麼看待分支,以及使用上要小心的地方。
跟 HEAD
一樣,分支也是指標。
除了「指標」之外,我們也可以把分支想成貼在 commit 上的「便籤」,如下圖的 master
分支跟 Emilia
分支:
這裡說的「便籤」,就是用來在筆記本做記號的那個「便籤」:
所謂建立一個分支的動作,就像是撕一張便籤起來,把名稱寫上去之後,貼到 commit 上做記號。
這同時也帶到一個觀念:建立分支的舉動,不是將資料「複製」一份出來,而是在同一份資料上做不同的記號。
既然分支只是「便籤」,就代表我們是可以把分支「撕」起來貼到其他 commit,這裡示範一下把 Emilia
分支撕起來,貼到 master
分支所在的 commit :
操作觀念之後會再說明,這裡只是讓大家「感受」分支就是個「便籤」。
開始使用分支後,Git 線圖中將會出現好幾條線,而每條線的末端應該都會貼著分支的便籤,像這樣:
此時可能會有新手開始疑惑:
我們在講某個分支時,會是在說 Git 線圖上的那整條「分支線」嗎?
這個問題在絕大多數的情況下,答案都是「否定」的。
在建立了「分支是便籤」的觀念後,我們可以把 發佈新版本 (git commit
) 解釋成:不斷的把「分支便籤」貼到新的 commit 的行為。
雖然就是這個舉動讓 Git 線圖上的「分支線」不斷的增長,不過如果真的在講「操作分支」,基本上會是對 「分支便籤」 進行 新增/改名/刪除 的行為 ,或者是要操作「分支所在的 commit」,絕不會是指操作「分支線」本身。
事實上在 Git 中,沒有指令會用來操作「分支線」,即便有指令執行完之後,會讓「分支線」發生巨大的變化,那也是因為我們操作了「分支便籤」或是操作了「commit」而導致的「結果」。
所以在 Git 的世界中聽到「分支」,請把關注焦點放在「分支便籤」,或是分支所在的 「commit」,不要認為是在講整條線。
初次接觸分支時,我們可能都會在不知不覺中產生的「錯誤觀念」,認為預設的 master
分支是 主要的 分支,而自己建立的分支都是 次要 的分支,而且「主要分支」的內容很重要,「次要分支」的內容刪掉也沒差。
實際上對 Git 來說,分支叫什麼名稱,他根本不在意。
所有的「意義」是我們這些使用者「定義」出來的
如同我們在筆記本使用「便籤」時:
哪天心血來潮,也可以來個風水輪流轉:
分支名稱就是這樣的道理,雖然那顆「分支樹」(Git 線圖) 確實是因為我們建立了不同的分支,才能讓他分岔茁壯,不過在成長過程中,分支究竟是標記什麼名稱,Git 根本不在乎。
Git 在我們版控的的時候唯一做的事情,就只是把 git commit
的內容,記錄在「當下」(HEAD) 的分支上而已。
重要的是 Git 線圖在不同路線中記錄的「內容」,而不是分支被定義的名稱。
因為分支除了像是「便籤」之外,他其實還有另一個特性,很像「圖釘」。
你可以想像 Git 線圖中的每一條分支,都是一條「線」,這條線上打了很多結,這些結就是一個又一個的 commit。
而 Git 線圖的背景把他想成「軟木板」,為了讓「分支線」有辦法固定在「軟木板」上,我們必須用「圖釘」釘在「分支線」的「頂端」。
那誰可以扮演圖釘的腳色?
有兩個東西,一個叫做 HEAD
,另一個就是「分支」本身了。
如果專案只有一條分支的話(例如預設的 master
分支),「HEAD
」 跟「分支」會同時「指著」最上端的 commit ,就好像這個節點上,同時釘著 HEAD
圖釘,還有 分支圖釘 一樣。
但如果專案同時存在多個分支,HEAD 可能會在不同的分支換來換去,像這樣:
注意到上面的範例中有一個動作,我刻意把 HEAD 切到 沒有分支 的 commit,但四條線不會因此垂下來(或是不見),就是因為「分支便籤」沒被拔掉,線依然能被釘在那裡。
再一個例子,下面這個線圖就好像一條線上,同時釘著 master
圖釘,跟 Emilia
圖釘,於此同時,HEAD
圖釘也在 master
的位置(注意 master
分支前有個圈圈):
如果把 Emilia
圖釘拔掉,那麼就會有三個 commit 點點固定不住而掉下來:
前面已經提到,版控就是持續把分支便籤持續貼到最新的 commit 的行為。
再加上現在的這個觀念,分支便籤同時也扮演著把分支線圖固定住的腳色。
為了在使用多個分支的時候,分支線都有辦法固定在「軟木版」上,版控的操作一定要在有分支的 commit 上進行。
銜接上面所說的觀念,既然版控行為一定要在分支上,代表 HEAD 必須指著某個分支才是正常的狀態。
如果 HEAD 沒有指向任何分支,而是指著 commit 時,我們會叫他 「分離的 HEAD」(detached HEAD)。
通常只會在特殊操作,或是某些指令必要過程,才會出現 detached HEAD 的現象。
詳細的操作行為,之後都會詳細說明,包含「該怎麼讓 HEAD 指向 commit」、「特殊操作」是什麼,以及什麼指令會有 detached HEAD 的現象,之後都會有文章說明。
如果你不知道什麼叫 HEAD 指著 commit ,可以參考一下 聊聊 commitID 與 HEAD 這篇文章
這觀念也是銜接「觀念四」帶到的「分支像圖釘」的概念。
如果一條分支線,只有最尾端 釘著 分支,直接把這個分支「拔掉」(或你把他當成便籤而說「撕掉」),整條線會因為固定不住,而從 Git 線圖中消失!
因為分支的存在,會讓 Git 認為這條線圖是「有意義」的。(無論名稱是什麼都一樣,Git 並不在乎分支名稱)。
所以把分支拔掉的行為,等同於告訴 Git :「這條線我不要了」,Git 只好把他給移除囉....
實際執行一次給大家看:
紫色那條 Subaru
分支,由於尚未合併到 dev
,如果直接刪掉分支便籤,就如同拿掉圖釘一般,線會直接消失給你看:
建立一個分支進行開發,通常是為了把開發的過程跟原本所在的路線進行切割
在開發完成之後,原本的分支會去把開發過程的內容給合併回來,這是 Git 版控很常見的行為。
當分支 被合併 之後,便籤位置並不會有任何更動,只是線圖會繼續往前走到別的分支便籤上。
以這個圖來說明,Rem
、Emilia
、Ram
三個分支 已經 被合併到 dev
分支,所以分支的線最後都是連到 dev
上的,這個狀況也代表這三個分支功成身退了,此時即便把分支便籤給「拔掉」,也不會影響 Git 線圖紀錄的內容。
繼續拿圖釘的比喻來說明,這個狀況就像所有的線都被 dev
圖釘給釘著,即使把下面的圖釘拿起來,線也不會掉到地上。
口說無憑,直接拔給你看:
我們以分支的本質是「便籤」,是「圖釘」的觀念,打開【分支篇】的序章。
這篇文章除了說明分支的使用觀念之外,也算是間接的預告接下來的內容會遇到的事情。
主要是希望大家在學習操作分支之前就先有個底,知道什麼樣的行為是可行的,又什麼樣的行為會直接影響資料。
下一篇文章,預計開始說明:如何使用指令與 GUI 來操作分支。