← 上一章:對分支的誤解 下一章:【狀況題】為什麼我的分支都沒有「小耳朵」? →


合併分支

在前面章節的例子中,我從 master 分支開了一個 cat 分支,並且做了兩次 Commit,現在看起來的樣子大概像這樣:

branch

任務執行的差不多了,就要準備合併回來了。如果我想要 master 分支來合併 cat 分支的話,我會先切回 master 分支:

$ git checkout master
Switched to branch 'master'

接下來,要合併分支是使用 git merge 指令:

$ git merge cat
Updating 35c42e..f17acb
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

看一下檔案列表:

 $ ls -al
total 16
drwxr-xr-x   9 eddie  wheel   306 Aug 18 05:14 .
drwxrwxrwt  84 root   wheel  2856 Aug 18 04:29 ..
drwxr-xr-x  16 eddie  wheel   544 Aug 18 05:15 .git
-rw-r--r--   1 eddie  wheel     0 Aug 18 05:14 cat1.html
-rw-r--r--   1 eddie  wheel     0 Aug 18 05:14 cat2.html
drwxr-xr-x   3 eddie  wheel   102 Aug 17 15:06 config
-rw-r--r--   1 eddie  wheel     0 Aug 18 03:27 hello.html
-rw-r--r--   1 eddie  wheel   161 Aug 18 04:24 index.html
-rw-r--r--   1 eddie  wheel    11 Aug 17 14:56 welcome.html

cat 分支新增的 cat1.htmlcat2.html,因為 master 現在已經合併 cat 分支,所以現在在 master 分支也有一份了。

回到 SourceTree 看一下目前的狀況:

從左邊的「BRANCHES」選單看得出來現在正處於 master 分支,同時從右方的 Commit 紀錄也看得出來 cat 分支現在領先 master 分支 2 個 Commit:

branch

如果要合併 cat 分支,在分支上按滑鼠右鍵,選擇「Merge cat into master」:

branch

它會跳出一個對話框,點擊 OK 按鈕便可完成合併。這時候再看一下右邊的 Commit 紀錄:

branch

本來落後 2 個 Commit 的 master 分支,在進行合併之後,進度也已經跟上 cat 分支,跟它在同一個 Commit 上。

至於已經合併的分支要不要留下來?請見「【常見問題】合併過的分支要留著嗎?」章節說明。

A 合併 B,跟 B 合併 A 有什麼不同?

這個問題在我一開始學 Git 的時候也曾經困擾過我好一陣子,到底誰合併誰有那麼重要嗎?這就要看你著眼的重點是什麼了。如果以最終結果來看是一樣的,但過程可能會有些差別。我先各別從 master 分支做出了 catdog 這兩個分支,並且現在正在 cat 分支:

branch

catdog 這兩個分支都是來自 master 分支,可以想像成是「cat 分支跟 dog 分支啊,你們身上都流著我 master 的血…」的意思,所以如果是 master 不管是要合併 cat 或是 dog 分支,Git 會直接使用快轉模式(Fast Forward)進行合併,說得白一點就是 master 直接「收割」 catdog 的成果了。

但如果是 catdog 這兩個分支要互相合併就不一樣了,雖然它們有同樣的來源,但各自長大之後要合併就不會這麼順利了(想想看要你把你的家產跟你哥哥或姐姐的家產合併在一起…)。在這個情況下,Git 會產生一個額外的 Commit 來處理這件事。一般的 Commit 只會指向某一個 Commit,但這個 Commit 會指向二個 Commit,明確的標記是來自哪兩個分支,親兄弟也是要明算帳啊!

來看看會怎麼演變。假設我想用 cat 分支來合併 dog 分支:

$ git merge dog
Merge made by the 'recursive' strategy.
 dog1.html | 0
 dog2.html | 0
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 dog1.html
 create mode 100644 dog2.html

執行這個指令的時候會跳出一個 Vim 編輯器視窗,如果忘記 Vim 怎麼操作,請再回顧一下「超精簡 Vim 操作介紹」章節。為了要進行這次的合併,Git 做出了這個額外的 Commit 物件,這個 Commit 會分別指向 catdog 這兩個分支,HEAD 隨著 cat 分支往前,而 dog 分支停留在原地:

branch

如果用 SourceTree 來看會像這樣:

branch

如果改由 dog 分支來合併 cat 分支:

$ git merge cat
Merge made by the 'recursive' strategy.
 cat1.html | 0
 cat2.html | 0
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 cat1.html
 create mode 100644 cat2.html

流程上跟剛才幾乎是一樣的。這時候的狀態會變成這樣:

branch

如果用 SourceTree 來看會像這樣:

branch

哪裡不一樣?

你有看出哪裡不一樣嗎?其實就以結果來看,不管是誰合併誰,這兩個分支的檔案最後都拿到了。你可能看 SourceTree 的畫面,會認為誰合併誰會有誰在前面、誰在後面的差別,但事實上並不是這樣,那只是因為軟體沒辦法畫出來「平行」的效果而已。

事實上不管誰合併誰,這兩個分支上的 Commit 都是對等的。硬是要說哪裡不一樣,就是 cat 分支合併 dog 分支的時候,cat 分支會往前移動,反之亦然。不過前面曾經提到分支就像貼紙一樣,隨時要刪掉或改名都不會影響現在已經存在的 Commit。

有啦,真的不一樣的還有一點,就是這個為了合併而產生的這個額外的 Commit 物件,裡面會記錄兩個老爸是誰,誰合併誰就會有「誰放前面」的差別,不過這可能就有點太過細節了。

這是 cat 分支合併 dog 分支,所以 cat 分支 b174a5a95a 放前面:

branch

這是 dog 分支合併 cat 分支,所以 dog 分支 053fb212bb 放前面:

branch

這很重要嗎?國外曾經有一個 Ruby 跟 Python 一起合辦的研討會名字叫做 RuPy (後來已改名成 PolyConf),那為什麼 Ruby 要放前面?Python 的人可能會想為什麼不叫 PyRu?這大概就跟 A 合併 B 跟 A 合併 B 的差別差不多吧。


← 上一章:對分支的誤解 下一章:【狀況題】為什麼我的分支都沒有「小耳朵」? →

Comments