Git (13)——Git 分支——分支的新建與合併

@[TOC]

讓我們來看一個簡單的分支新建與分支合併的例子,實際工作中你可能會用到類似的工作流。 你將經歷如下步驟:

1。 開發某個網站。

2。 為實現某個新的使用者需求,建立一個分支。

3。 在這個分支上開展工作。

正在此時,你突然接到一個電話說有個很嚴重的問題需要緊急修補。 你將按照如下方式來處理:

1。 切換到你的線上分支(

production branch

)。

2。 為這個緊急任務新建一個分支,並在其中修復它。

3。 在測試透過之後,切換回線上分支,然後合併這個修補分支,最後將改動推送到線上分支。

4。 切換回你最初工作的分支上,繼續工作。

0、準備工作

新建

Repository

Git (13)——Git 分支——分支的新建與合併

clone

到本地:

Git (13)——Git 分支——分支的新建與合併

進入倉庫目錄,檢視提交記錄:

Git (13)——Git 分支——分支的新建與合併

新建測試檔案,

add

到暫存區:

Git (13)——Git 分支——分支的新建與合併

第二次提交(新建倉庫時自動提交第一次),檢視提交記錄:

Git (13)——Git 分支——分支的新建與合併

修改測試檔案,第三次提交:

Git (13)——Git 分支——分支的新建與合併

1、新建分支

此時在

main

分支上已經有了一些提交。

一個簡單提交歷史:

Git (13)——Git 分支——分支的新建與合併

現在要解決公司使用的問題追蹤系統中的

#53

問題。 想要新建一個分支並同時切換到那個分支上,你可以執行一個帶有

-b

引數的

git checkout

命令:

$ git checkout -b iss53Switched to a new branch “iss53”

它是下面兩條命令的簡寫:

$ git branch iss53$ git checkout iss53

Git (13)——Git 分支——分支的新建與合併

建立一個新分支指標:

Git (13)——Git 分支——分支的新建與合併

這時繼續在

#53

問題上工作,並且做了一些提交。 在此過程中,

iss53

分支在不斷的向前推進,因為你已經檢出到該分支 (也就是說,你的

HEAD

指標指向了

iss53

分支)

新建

index。html

,編輯並新增到暫存區,

issue53

分支第一次提交:

Git (13)——Git 分支——分支的新建與合併

iss53

分支隨著工作的進展向前推進:

Git (13)——Git 分支——分支的新建與合併

現在你接到那個電話,有個緊急問題等待你來解決。 有了

Git

的幫助,你不必把這個緊急問題和

iss53

的修改混在一起, 你也不需要花大力氣來還原關於

53#

問題的修改,然後再新增關於這個緊急問題的修改,最後將這個修改提交到線上分支。 你所要做的僅僅是切換回

main

分支。

但是,在你這麼做之前,要留意你的工作目錄和暫存區裡那些還沒有被提交的修改, 它可能會和你即將檢出的分支產生衝突從而阻止

Git

切換到該分支。 最好的方法是,在你切換分支之前,保持好一個乾淨的狀態。 有一些方法可以繞過這個問題(即,暫存(

stashing

) 和 修補提交(

commit amending

))。 現在,我們假設你已經把你的修改全部提交了,這時你可以切換回

main

分支了:

$ git checkout mainSwitched to branch ‘main’

Git (13)——Git 分支——分支的新建與合併

這個時候,你的工作目錄和你在開始 #53 問題之前一模一樣,現在你可以專心修復緊急問題了。 請牢記:當你切換分支的時候,Git 會重置你的工作目錄,使其看起來像回到了你在那個分支上最後一次提交的樣子。 Git 會自動新增、刪除、修改檔案以確保此時你的工作目錄和這個分支最後一次提交時的樣子一模一樣。

接下來,你要修復這個緊急問題。 我們來建立一個

hotfix

分支,在該分支上工作直到問題解決:

Git (13)——Git 分支——分支的新建與合併

Git (13)——Git 分支——分支的新建與合併

基於 main 分支的緊急問題分支

hotfix branch

Git (13)——Git 分支——分支的新建與合併

你可以執行你的測試,確保你的修改是正確的,然後將 hotfix 分支合併回你的 main 分支來部署到線上。 你可以使用

git merge

命令來達到上述目的:

Git (13)——Git 分支——分支的新建與合併

在合併的時候,你應該注意到了“快進(

fast-forward

)”這個詞。 由於你想要合併的分支 hotfix 所指向的提交 C5 是你所在的提交 C3 的直接後繼, 因此 Git 會直接將指標向前移動。換句話說,當你試圖合併兩個分支時, 如果順著一個分支走下去能夠到達另一個分支,那麼 Git 在合併兩者的時候, 只會簡單的將指標向前推進(指標右移),因為這種情況下的合併操作沒有需要解決的分歧——這就叫做 “快進(

fast-forward

)”。

Git (13)——Git 分支——分支的新建與合併

現在,最新的修改已經在

main

分支所指向的提交快照中,你可以著手釋出該修復了。

main 被快進到 hotfix:

Git (13)——Git 分支——分支的新建與合併

關於這個緊急問題的解決方案發布之後,你準備回到被打斷之前時的工作中。 然而,你應該先刪除 hotfix 分支,因為你已經不再需要它了 —— main 分支已經指向了同一個位置。 你可以使用帶

-d

選項的

git branch

命令來刪除分支:

Git (13)——Git 分支——分支的新建與合併

現在你可以切換回你正在工作的分支繼續你的工作,也就是針對

#53

問題的那個分支(

iss53

分支)。

Git (13)——Git 分支——分支的新建與合併

Git (13)——Git 分支——分支的新建與合併

繼續在 iss53 分支上的工作:

Git (13)——Git 分支——分支的新建與合併

你在 hotfix 分支上所做的工作並沒有包含到 iss53 分支中。 如果你需要拉取 hotfix 所做的修改,你可以使用

git merge main

命令將 main 分支合併入 iss53 分支,或者你也可以等到 iss53 分支完成其使命,再將其合併回 main 分支。

2、分支的合併

假設你已經修正了 #53 問題,並且打算將你的工作合併入 main 分支。 為此,你需要合併 iss53 分支到 main 分支,這和之前你合併 hotfix 分支所做的工作差不多。 你只需要檢出到你想合併入的分支,然後執行

git merge

命令:

Git (13)——Git 分支——分支的新建與合併

這和你之前合併 hotfix 分支的時候看起來有一點不一樣。 在這種情況下,你的開發歷史從一個更早的地方開始分叉開來(diverged)。 因為,main 分支所在提交併不是 iss53 分支所在提交的直接祖先,Git 不得不做一些額外的工作。 出現這種情況的時候,Git 會使用兩個分支的末端所指的快照(C5 和 C)以及這兩個分支的公共祖先(C3),做一個簡單的三方合併。

一次典型合併中所用到的三個快照:

Git (13)——Git 分支——分支的新建與合併

和之前將分支指標向前推進所不同的是,Git 將此次三方合併的結果做了一個新的快照並且自動建立一個新的提交指向它。 這個被稱作一次合併提交,它的特別之處在於他有不止一個父提交。

一個合併提交:

Git (13)——Git 分支——分支的新建與合併

既然你的修改已經合併進來了,就不再需要 iss53 分支了。 現在你可以在任務追蹤系統中關閉此項任務,並刪除這個分支。

$ git branch -d iss53

3、遇到衝突時的分支合併

有時候合併操作不會如此順利。 如果你在兩個不同的分支中,對同一個檔案的同一個部分進行了不同的修改,Git 就沒法乾淨的合併它們。 如果你對 #53 問題的修改和有關 hotfix 分支的修改都涉及到同一個檔案的同一處,在合併它們的時候就會產生合併衝突:

Git (13)——Git 分支——分支的新建與合併

Git (13)——Git 分支——分支的新建與合併

此時 Git 做了合併,但是沒有自動地建立一個新的合併提交。 Git 會暫停下來,等待你去解決合併產生的衝突。 你可以在合併衝突後的任意時刻使用 git status 命令來檢視那些因包含合併衝突而處於未合併(unmerged)狀態的檔案:

Git (13)——Git 分支——分支的新建與合併

任何因包含合併衝突而有待解決的檔案,都會以未合併狀態標識出來。 Git 會在有衝突的檔案中加入標準的衝突解決標記,這樣你可以開啟這些包含衝突的檔案然後手動解決衝突。 出現衝突的檔案會包含一些特殊區段,看起來像下面這個樣子:

Git (13)——Git 分支——分支的新建與合併

Git (13)——Git 分支——分支的新建與合併

這表示

HEAD

所指示的版本(也就是你的

main

分支所在的位置,因為你在執行

merge

命令的時候已經檢出到了這個分支)在這個區段的上半部分(

=======

的上半部分),而

iss53

分支所指示的版本在

=======

的下半部分。 為了解決衝突,你必須選擇使用由

=======

分割的兩部分中的一個,或者你也可以自行合併這些內容。 例如,你可以透過把這段內容換成下面的樣子來解決衝突:

Git (13)——Git 分支——分支的新建與合併

上述的衝突解決方案僅保留了其中一個分支的修改,並且

<<<<<<<

=======

, 和

>>>>>>>

這些行被完全刪除了。 在你解決了所有檔案裡的衝突之後,對每個檔案使用

git add

命令來將其標記為衝突已解決。 一旦暫存這些原本有衝突的檔案,

Git

就會將它們標記為衝突已解決。

Git (13)——Git 分支——分支的新建與合併

如果你想使用圖形化工具來解決衝突,你可以執行

git mergetool

,該命令會為你啟動一個合適的可視化合並工具,並帶領你一步一步解決這些衝突:

Git (13)——Git 分支——分支的新建與合併

Git (13)——Git 分支——分支的新建與合併

如果你想使用除預設工具(在這裡

Git

使用

opendiff

做為預設的合併工具) 外的其他合併工具,你可以在 “下列工具中(

one of the following tools

)” 這句後面看到所有支援的合併工具。 然後輸入你喜歡的工具名字就可以了。

enter

Git (13)——Git 分支——分支的新建與合併

Git (13)——Git 分支——分支的新建與合併

$ git mergetoolThis message is displayed because ‘merge。tool’ is not configured。See ‘git mergetool ——tool-help’ or ‘git help config’ for more details。‘git mergetool’ will now attempt to use one of the following tools:opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse diffmerge ecmerge p4merge araxis bc codecompare smerge emerge vimdiff nvimdiffMerging:readme。md | readme。mdNormal merge conflict for ‘readme。md | readme。md’: {local}: modified file {remote}: modified fileHit return to start merge resolution tool (vimdiff):4 files to edit

等你退出合併工具之後,

Git

會詢問剛才的合併是否成功。 如果你回答是,

Git

會暫存那些檔案以表明衝突已解決: 你可以再次執行

git status

來確認所有的合併衝突都已被解決:

Git (13)——Git 分支——分支的新建與合併

如果你對結果感到滿意,並且確定之前有衝突的的檔案都已經暫存了,這時你可以輸入

git commit

來完成合並提交。 預設情況下提交資訊看起來像下面這個樣子:

Git (13)——Git 分支——分支的新建與合併

如果你覺得上述的資訊不夠充分,不能完全體現分支合併的過程,你可以修改上述資訊, 新增一些細節給未來檢視這個合併的讀者一些幫助,告訴他們你是如何解決合併衝突的,以及理由是什麼。

Git (13)——Git 分支——分支的新建與合併

既然你的修改已經合併進來了,就不再需要

iss53

分支了。 現在你可以在任務追蹤系統中關閉此項任務,並刪除這個分支。

$ git branch -d iss53

Git (13)——Git 分支——分支的新建與合併

Git (13)——Git 分支——分支的新建與合併

Git (13)——Git 分支——分支的新建與合併