Git 分支 - 分支的新建與合并

分支的新建與合并

讓我們來看一個(gè)簡(jiǎn)單的分支新建與分支合并的例子,實(shí)際工作中你可能會(huì)用到類似的工作流。

你將經(jīng)歷如下步驟:

開發(fā)某個(gè)網(wǎng)站。

為實(shí)現(xiàn)某個(gè)新的需求,創(chuàng)建一個(gè)分支。

在這個(gè)分支上開展工作。

正在此時(shí),你突然接到一個(gè)電話說有個(gè)很嚴(yán)重的問題需要緊急修補(bǔ)。

你將按照如下方式來處理:

切換到你的線上分支(production branch)。

為這個(gè)緊急任務(wù)新建一個(gè)分支,并在其中修復(fù)它。

在測(cè)試通過之后,切換回線上分支,然后合并這個(gè)修補(bǔ)分支,最后將改動(dòng)推送到線上分支。

切換回你最初工作的分支上,繼續(xù)工作。

新建分支

首先,我們假設(shè)你正在你的項(xiàng)目上工作,并且已經(jīng)有一些提交。

Figure 18. 一個(gè)簡(jiǎn)單提交歷史

現(xiàn)在,你已經(jīng)決定要解決你的公司使用的問題追蹤系統(tǒng)中的 #53 問題。想要新建一個(gè)分支并同時(shí)切換到那個(gè)分支上,你可以運(yùn)行一個(gè)帶有-b參數(shù)的git checkout命令:

$ git checkout -b iss53

Switched to a new branch "iss53"

它是下面兩條命令的簡(jiǎn)寫:

$ git branch iss53

$ git checkout iss53

Figure 19. 創(chuàng)建一個(gè)新分支指針

你繼續(xù)在 #53 問題上工作,并且做了一些提交。在此過程中,iss53分支在不斷的向前推進(jìn),因?yàn)槟阋呀?jīng)檢出到該分支(也就是說,你的HEAD指針指向了iss53分支)

$ vim index.html

$ git commit -a -m 'added a new footer [issue 53]'

Figure 20. iss53 分支隨著工作的進(jìn)展向前推進(jìn)

現(xiàn)在你接到那個(gè)電話,有個(gè)緊急問題等待你來解決。有了 Git 的幫助,你不必把這個(gè)緊急問題和iss53的修改混在一起,你也不需要花大力氣來還原關(guān)于 53# 問題的修改,然后再添加關(guān)于這個(gè)緊急問題的修改,最后將這個(gè)修改提交到線上分支。你所要做的僅僅是切換回master分支。

但是,在你這么做之前,要留意你的工作目錄和暫存區(qū)里那些還沒有被提交的修改,它可能會(huì)和你即將檢出的分支產(chǎn)生沖突從而阻止 Git 切換到該分支。最好的方法是,在你切換分支之前,保持好一個(gè)干凈的狀態(tài)。有一些方法可以繞過這個(gè)問題(即,保存進(jìn)度(stashing) 和 修補(bǔ)提交(commit amending)),我們會(huì)在儲(chǔ)藏與清理中看到關(guān)于這兩個(gè)命令的介紹。現(xiàn)在,我們假設(shè)你已經(jīng)把你的修改全部提交了,這時(shí)你可以切換回master分支了:

$ git checkout master

Switched to branch 'master'

這個(gè)時(shí)候,你的工作目錄和你在開始 #53 問題之前一模一樣,現(xiàn)在你可以專心修復(fù)緊急問題了。

請(qǐng)牢記:當(dāng)你切換分支的時(shí)候,Git 會(huì)重置你的工作目錄,使其看起來像回到了你在那個(gè)分支上最后一次提交的樣子。

Git 會(huì)自動(dòng)添加、刪除、修改文件以確保此時(shí)你的工作目錄和這個(gè)分支最后一次提交時(shí)的樣子一模一樣。

接下來,你要修復(fù)這個(gè)緊急問題。

讓我們建立一個(gè)針對(duì)該緊急問題的分支(hotfix branch),在該分支上工作直到問題解決:

$ git checkout -b hotfix

Switched to a new branch 'hotfix'

$ vim index.html

$ git commit -a -m 'fixed the broken email address'

[hotfix 1fb7853] fixed the broken email address

1 file changed, 2 insertions(+)

Figure 21. 基于master分支的緊急問題分支hotfix branch

你可以運(yùn)行你的測(cè)試,確保你的修改是正確的,然后將其合并回你的master分支來部署到線上。你可以使用git merge命令來達(dá)到上述目的:

$ git checkout master

$ git merge hotfix

Updating f42c576..3a0874c

Fast-forward

index.html | 2 ++

1 file changed, 2 insertions(+)

在合并的時(shí)候,你應(yīng)該注意到了"快進(jìn)(fast-forward)"這個(gè)詞。由于當(dāng)前master分支所指向的提交是你當(dāng)前提交(有關(guān) hotfix 的提交)的直接上游,所以 Git 只是簡(jiǎn)單的將指針向前移動(dòng)。換句話說,當(dāng)你試圖合并兩個(gè)分支時(shí),如果順著一個(gè)分支走下去能夠到達(dá)另一個(gè)分支,那么 Git 在合并兩者的時(shí)候,只會(huì)簡(jiǎn)單的將指針向前推進(jìn)(指針右移),因?yàn)檫@種情況下的合并操作沒有需要解決的分歧——這就叫做 “快進(jìn)(fast-forward)”。

現(xiàn)在,最新的修改已經(jīng)在master分支所指向的提交快照中,你可以著手發(fā)布該修復(fù)了。

Figure 22.master被快進(jìn)到hotfix

關(guān)于這個(gè)緊急問題的解決方案發(fā)布之后,你準(zhǔn)備回到被打斷之前時(shí)的工作中。然而,你應(yīng)該先刪除hotfix分支,因?yàn)槟阋呀?jīng)不再需要它了 ——master分支已經(jīng)指向了同一個(gè)位置。你可以使用帶-d選項(xiàng)的git branch命令來刪除分支:

$ git branch -d hotfix

Deleted branch hotfix (3a0874c).

現(xiàn)在你可以切換回你正在工作的分支繼續(xù)你的工作,也就是針對(duì) #53 問題的那個(gè)分支(iss53 分支)。

$ git checkout iss53

Switched to branch "iss53"

$ vim index.html

$ git commit -a -m 'finished the new footer [issue 53]'

[iss53 ad82d7a] finished the new footer [issue 53]

1 file changed, 1 insertion(+)

Figure 23. 繼續(xù)在iss53分支上的工作

你在hotfix分支上所做的工作并沒有包含到iss53分支中。如果你需要拉取hotfix所做的修改,你可以使用git merge master命令將master分支合并入iss53分支,或者你也可以等到iss53分支完成其使命,再將其合并回master分支。

分支的合并

假設(shè)你已經(jīng)修正了 #53 問題,并且打算將你的工作合并入master分支。為此,你需要合并iss53分支到master分支,這和之前你合并hotfix分支所做的工作差不多。你只需要檢出到你想合并入的分支,然后運(yùn)行g(shù)it merge命令:

$ git checkout master

Switched to branch 'master'

$ git merge iss53

Merge made by the 'recursive' strategy.

index.html |? ? 1 +

1 file changed, 1 insertion(+)

這和你之前合并hotfix分支的時(shí)候看起來有一點(diǎn)不一樣。在這種情況下,你的開發(fā)歷史從一個(gè)更早的地方開始分叉開來(diverged)。因?yàn)?,master分支所在提交并不是iss53分支所在提交的直接祖先,Git 不得不做一些額外的工作。出現(xiàn)這種情況的時(shí)候,Git 會(huì)使用兩個(gè)分支的末端所指的快照(C4和C5)以及這兩個(gè)分支的工作祖先(C2),做一個(gè)簡(jiǎn)單的三方合并。

Figure 24. 一次典型合并中所用到的三個(gè)快照

和之前將分支指針向前推進(jìn)所不同的是,Git 將此次三方合并的結(jié)果做了一個(gè)新的快照并且自動(dòng)創(chuàng)建一個(gè)新的提交指向它。

這個(gè)被稱作一次合并提交,它的特別之處在于他有不止一個(gè)父提交。

Figure 25. 一個(gè)合并提交

需要指出的是,Git 會(huì)自行決定選取哪一個(gè)提交作為最優(yōu)的共同祖先,并以此作為合并的基礎(chǔ);這和更加古老的 CVS 系統(tǒng)或者 Subversion (1.5 版本之前)不同,在這些古老的版本管理系統(tǒng)中,用戶需要自己選擇最佳的合并基礎(chǔ)。

Git 的這個(gè)優(yōu)勢(shì)使其在合并操作上比其他系統(tǒng)要簡(jiǎn)單很多。

既然你的修改已經(jīng)合并進(jìn)來了,你已經(jīng)不再需要iss53分支了。現(xiàn)在你可以在任務(wù)追蹤系統(tǒng)中關(guān)閉此項(xiàng)任務(wù),并刪除這個(gè)分支。

$ git branch -d iss53

遇到?jīng)_突時(shí)的分支合并

有時(shí)候合并操作不會(huì)如此順利。如果你在兩個(gè)不同的分支中,對(duì)同一個(gè)文件的同一個(gè)部分進(jìn)行了不同的修改,Git 就沒法干凈的合并它們。如果你對(duì) #53 問題的修改和有關(guān)hotfix的修改都涉及到同一個(gè)文件的同一處,在合并它們的時(shí)候就會(huì)產(chǎn)生合并沖突:

$ git merge iss53

Auto-merging index.html

CONFLICT (content): Merge conflict in index.html

Automatic merge failed; fix conflicts and then commit the result.

此時(shí) Git 做了合并,但是沒有自動(dòng)地創(chuàng)建一個(gè)新的合并提交。Git 會(huì)暫停下來,等待你去解決合并產(chǎn)生的沖突。你可以在合并沖突后的任意時(shí)刻使用git status命令來查看那些因包含合并沖突而處于未合并(unmerged)狀態(tài)的文件:

$ git status

On branch master

You have unmerged paths.

(fix conflicts and run "git commit")

Unmerged paths:

(use "git add ..." to mark resolution)

both modified:? ? ? index.html

no changes added to commit (use "git add" and/or "git commit -a")

任何因包含合并沖突而有待解決的文件,都會(huì)以未合并狀態(tài)標(biāo)識(shí)出來。

Git 會(huì)在有沖突的文件中加入標(biāo)準(zhǔn)的沖突解決標(biāo)記,這樣你可以打開這些包含沖突的文件然后手動(dòng)解決沖突。

出現(xiàn)沖突的文件會(huì)包含一些特殊區(qū)段,看起來像下面這個(gè)樣子:

<<<<<<< HEAD:index.html

contact : email.support@github.com

=======

please contact us at support@github.com

>>>>>>> iss53:index.html

這表示HEAD所指示的版本(也就是你的master分支所在的位置,因?yàn)槟阍谶\(yùn)行 merge 命令的時(shí)候已經(jīng)檢出到了這個(gè)分支)在這個(gè)區(qū)段的上半部分(=======的上半部分),而iss53分支所指示的版本在=======的下半部分。為了解決沖突,你必須選擇使用由=======分割的兩部分中的一個(gè),或者你也可以自行合并這些內(nèi)容。例如,你可以通過把這段內(nèi)容換成下面的樣子來解決沖突:

please contact us at email.support@github.com

上述的沖突解決方案僅保留了其中一個(gè)分支的修改,并且<<<<<<<,=======, 和>>>>>>>這些行被完全刪除了。在你解決了所有文件里的沖突之后,對(duì)每個(gè)文件使用git add命令來將其標(biāo)記為沖突已解決。一旦暫存這些原本有沖突的文件,Git 就會(huì)將它們標(biāo)記為沖突已解決。

如果你想使用圖形化工具來解決沖突,你可以運(yùn)行g(shù)it mergetool,該命令會(huì)為你啟動(dòng)一個(gè)合適的可視化合并工具,并帶領(lǐng)你一步一步解決這些沖突:

$ git mergetool

This 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 bc3 codecompare vimdiff emerge

Merging:

index.html

Normal merge conflict for 'index.html':

{local}: modified file

{remote}: modified file

Hit return to start merge resolution tool (opendiff):

如果你想使用除默認(rèn)工具(在這里 Git 使用opendiff做為默認(rèn)的合并工具,因?yàn)樽髡咴?Mac 上運(yùn)行該程序)外的其他合并工具,你可以在 “下列工具中(one of the following tools)” 這句后面看到所有支持的合并工具。然后輸入你喜歡的工具名字就可以了。

Note

如果你需要更加高級(jí)的工具來解決復(fù)雜的合并沖突,我們會(huì)在高級(jí)合并介紹更多關(guān)于分支合并的內(nèi)容。

等你退出合并工具之后,Git 會(huì)詢問剛才的合并是否成功。如果你回答是,Git 會(huì)暫存那些文件以表明沖突已解決:你可以再次運(yùn)行g(shù)it status來確認(rèn)所有的合并沖突都已被解決:

$ git status

On branch master

All conflicts fixed but you are still merging.

(use "git commit" to conclude merge)

Changes to be committed:

modified:? index.html

如果你對(duì)結(jié)果感到滿意,并且確定之前有沖突的的文件都已經(jīng)暫存了,這時(shí)你可以輸入git commit來完成合并提交。默認(rèn)情況下提交信息看起來像下面這個(gè)樣子:

Merge branch 'iss53'

Conflicts:

index.html

#

# It looks like you may be committing a merge.

# If this is not correct, please remove the file

# .git/MERGE_HEAD

# and try again.

# Please enter the commit message for your changes. Lines starting

# with '#' will be ignored, and an empty message aborts the commit.

# On branch master

# All conflicts fixed but you are still merging.

#

# Changes to be committed:

# modified:? index.html

#

如果你覺得上述的信息不夠充分,不能完全體現(xiàn)分支合并的過程,你可以修改上述信息,添加一些細(xì)節(jié)給未來檢視這個(gè)合并的讀者一些幫助,告訴他們你是如何解決合并沖突的,以及理由是什么。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容