什么是分支

在玩劇情類游戲時,不同的選擇會觸發不同的劇情路線,每條劇情路線都會獨立發展,最終走向不同的結局。
Git中所謂的“分支(branch)”就如同游戲中的劇情路線,用戶可以為項目建立不同的分支,使得項目能在不同的分支里獨立進行,並且互不干預。
當用戶初始化一個倉庫時,Git會自動為其建立一條主分支,默認稱其為master。若用戶沒有創建其他分支,那么項目發展的各個版本就默認存儲在這條 master
分支上。
分支指令介紹
分支的新建和切換
假設你正在項目上工作,並且在 master
分支上已經有了如下的提交。

對應的圖即為如下:

master
指針指向該分支上最新的提交; HEAD
指針指向的是當前所在分支。
此時你發現了項目中有一個代號為#11的bug需要你解決。你決定專門新建一個分支,在那條分支上把該問題解決。於是你敲入了新建分支的指令。

上面兩行命令可以僅用一條命令替代:$ git branch -b bug#11
。
當我們在創建新的分支時,實際上是創建一個新的指針。它會指向 master
此時指向的提交。當我們切換到分支 bug#11
時,HEAD
指針就會指向 bug#11
,如下圖所示。

此外,我們可以用如下命令查看了目前倉庫中存在的分支。

可以看到目前共存在2個分支,其中帶*號的是目前所在分支:master
。
分支的作用
於是你開始在 bug#11
這條分支上工作,並作出了如下的提交(commit)。

於是 bug#11
指針往前移動了一步,而 master
指針還是留在原地。

這時,當你用 $ git checkout master
命令切換回 master
分支時,你發現剛才在 bug#11
分支上新建的 log.txt
文件不見了,當前的工作目錄和你開始在 bug#11
分支工作前一模一樣。
這就是分支的好處,它將工作切分開來,讓工作多線獨立發展。
合並分支
我們將介紹在合並分支時會遇到的3種情況。
Fast-Forward
此時,你已經把 #bug11
修復完畢,你決定把這項修復合並到主線 master
中。於是,你使用了 $ git merge
命令來實現這個目的。

在合並的時候,你注意到了“快進(Fast-forward)”這個詞。當你試圖合並兩個分支時, 如果順着一個分支走下去能夠到達另一個分支,那么 Git 在合並兩者的時候, 只會簡單的將指針向前推進,因為這種情況下的合並操作沒有需要解決的分歧——這就叫做 “快進(fast-forward)”,如圖所示。

此時合並完成,所以你也不再需要 bug#11
分支了。你可以使用帶 -d
選項的 $ git branch
命令來刪除分支:

'recursive' strategy
此時,你又發現了項目中有一個代號為 bug#31
的漏洞需要修復,於是你先用 $ git branch bug#31
命令新建了一個分支。
現在的你還在 master
這條分支上工作:你將 Scheduling.txt
的文件內容修改為 Project Scheduling - 85% Done.
, 然后commit這次的修改。
此時,對應的圖為如下:

現在的你感到心滿意足,決心切換到 bug#31
分支上開始修復 bug#31
。於是你先用 $ git checkout bug#31
命令切換到了該分支。
接着你辛辛苦苦了一整天,總算把 bug#31
修復完了。於是你修改了 log.txt
文件里的內容,加上了一行Fixed bug#31.
,並commit這次的修改,此時的圖對應如下:

現在,你決定把對 bug#31
的修復應用到 master
分支中來,於是你切換回 master
分支,然后再次調用了 $ git merge
命令:

你發現這次提示的和之前在合並 bug#11
分支時提示的不太一樣。因為此時,你想合並的 bug#31
指向的最新提交 C4
並不是 master
指向的最新提交 C3
的直接后繼(換句話說,C3
不能順着一條路走到 C4
)。所以Git沒法再那么通過簡單的移動指針來完成合並了。
Git會找到 C3
和 C4
的共同祖先 C2
,然后做一個三方合並。
那么什么是三方合並呢?
例子1:A是B和C的共同祖先。B相對A的改動是刪掉了3,C相對A的改動是增加了1。那么B和C合並的結果就是123。

例子2:A是B和C的共同祖先。B相對A的改動是刪掉了1,C相對A的改動是增加了3。那么B和C合並的結果就是23。

所以簡單來說,三方合並就是把B方和C方相對於共同祖先A方的改動分別應用到合並結果上。
剛才,C3
相對於 C2
的改動是修改了 Scheduling.txt
文件的內容,而 C4
相對於 C2
的改動是修改了 log.txt
文件里的內容,將這兩項改動都應用到合並結果中,就得到了一個既修改了 Scheduling.txt
又修改了 log.txt
的 C5
。

遇到沖突時的合並
有時候合並操作不會如此順利。 如果你在兩個不同的分支中,對同一個文件進行了不同的修改,Git 就沒法干凈的合並它們。
例如:你在 bug#31
分支中在 README.md
文件中添加了一行 I like Git.
;在 master
分支中對 README.md
文件添加了一行 Git is good.
,然后再執行 $ git merge
命令:

在兩個分支中,你都對 README.md
文件進行了修改,因此就會產生合並沖突。
打開 README.md
文件會看到如下內容,其中 <<<<<<< HEAD
到 =======
之間的內容是 HEAD
指向分支里的內容,即 master
;=======
到 >>>>>>> bug#31
之間的是 bug#31
分支里的內容。

你手動修改了這個文件,修改后的內容如下。

在Git輕松入門1:本地倉庫篇,我們提到過,文件修改后,需要執行 $ git add
和 $ git commit
兩個步驟。所以現在,你同樣需要執行這兩個步驟,而當你敲完命令,你會發現提示合並成功。

總結
總結一下今天出現過的幾個Git命令:
# 列出該倉庫存在的分支,當前分支的前面會帶有*號
$ git branch
# 創建分支
$ git branch [branch-name]
# 切換分支
$ git checkout [branch-name]
# 創建分支並立刻切換到該分支下
$ git checkout -b [branch-name]
# 刪除分支
$ git branch -d [branch-name]
# 合並分支
$ git merge [branch-name]
分支的作用在於將工作切分開來,使得工作可以多線獨立發展。以上是本講關於分支命令的概括。此外,本講還介紹了Git采取的3種合並策略:Fast-forward(直接移動指針),recursive strategy(采取三方合並)以及merge conflict(手動修改存在沖突的文件)。
參考
- http://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging
- https://www.zhihu.com/question/30200228
- https://www.liaoxuefeng.com/wiki/896043488029600/900003767775424
- https://www.runoob.com/git/git-branch.html
有問題歡迎大家在評論區留言,轉載請注明出處。