分支的新建與合并
現(xiàn)在讓我們來看一個(gè)簡單的分支與合并的例子,實(shí)際工作中大體也會(huì)用到這樣的工作流程:
- 開發(fā)某個(gè)網(wǎng)站。
- 為實(shí)現(xiàn)某個(gè)新的需求,創(chuàng)建一個(gè)分支。
- 在這個(gè)分支上開展工作。
假設(shè)此時(shí),你突然接到一個(gè)電話說有個(gè)很嚴(yán)重的問題需要緊急修補(bǔ),那么可以按照下面的方式處理:
- 返回到原先已經(jīng)發(fā)布到生產(chǎn)服務(wù)器上的分支。
- 為這次緊急修補(bǔ)建立一個(gè)新分支,并在其中修復(fù)問題。
- 通過測試后,回到生產(chǎn)服務(wù)器所在的分支,將修補(bǔ)分支合并進(jìn)來,然后再推送到生產(chǎn)服務(wù)器上。
- 切換到之前實(shí)現(xiàn)新需求的分支,繼續(xù)工作。
分支的新建與切換
首先,我們假設(shè)你正在項(xiàng)目中愉快地工作,并且已經(jīng)提交了幾次更新(見圖 3-10)。
圖 3-10. 一個(gè)簡短的提交歷史
現(xiàn)在,你決定要修補(bǔ)問題追蹤系統(tǒng)上的 #53 問題。順帶說明下,Git 并不同任何特定的問題追蹤系統(tǒng)打交道。這里為了說明要解決的問題,才把新建的分支取名為 iss53。要新建并切換到該分支,運(yùn)行git checkout
并加上-b
參數(shù):
$ git checkout -b iss53
Switched to a new branch 'iss53'
這相當(dāng)于執(zhí)行下面這兩條命令:
$ git branch iss53
$ git checkout iss53
圖 3-11 示意該命令的執(zhí)行結(jié)果。
圖 3-11. 創(chuàng)建了一個(gè)新分支的指針
接著你開始嘗試修復(fù)問題,在提交了若干次更新后,iss53
分支的指針也會(huì)隨著向前推進(jìn),因?yàn)樗褪钱?dāng)前分支(換句話說,當(dāng)前的HEAD
指針正指向iss53
,見圖 3-12):
$ vim index.html
$ git commit -a -m 'added a new footer [issue 53]'
圖 3-12. iss53 分支隨工作進(jìn)展向前推進(jìn)
現(xiàn)在你就接到了那個(gè)網(wǎng)站問題的緊急電話,需要馬上修補(bǔ)。有了 Git ,我們就不需要同時(shí)發(fā)布這個(gè)補(bǔ)丁和iss53
里作出的修改,也不需要在創(chuàng)建和發(fā)布該補(bǔ)丁到服務(wù)器之前花費(fèi)大力氣來復(fù)原這些修改。唯一需要的僅僅是切換回master
分支。
不過在此之前,留心你的暫存區(qū)或者工作目錄里,那些還沒有提交的修改,它會(huì)和你即將檢出的分支產(chǎn)生沖突從而阻止 Git 為你切換分支。切換分支的時(shí)候最好保持一個(gè)清潔的工作區(qū)域。稍后會(huì)介紹幾個(gè)繞過這種問題的辦法(分別叫做 stashing 和 commit amending)。目前已經(jīng)提交了所有的修改,所以接下來可以正常轉(zhuǎn)換到master
分支:
$ git checkout master
Switched to branch 'master'
此時(shí)工作目錄中的內(nèi)容和你在解決問題 #53 之前一模一樣,你可以集中精力進(jìn)行緊急修補(bǔ)。這一點(diǎn)值得牢記:Git 會(huì)把工作目錄的內(nèi)容恢復(fù)為檢出某分支時(shí)它所指向的那個(gè)提交對象的快照。它會(huì)自動(dòng)添加、刪除和修改文件以確保目錄的內(nèi)容和你當(dāng)時(shí)提交時(shí)完全一樣。
接下來,你得進(jìn)行緊急修補(bǔ)。我們創(chuàng)建一個(gè)緊急修補(bǔ)分支hotfix
來開展工作,直到搞定(見圖 3-13):
$ git checkout -b hotfix
Switched to a new branch 'hotfix'
$ vim index.html
$ git commit -a -m 'fixed the broken email address'
[hotfix 3a0874c] fixed the broken email address
1 files changed, 1 deletion(-)
圖 3-13. hotfix 分支是從 master 分支所在點(diǎn)分化出來的
有必要作些測試,確保修補(bǔ)是成功的,然后回到master
分支并把它合并進(jìn)來,然后發(fā)布到生產(chǎn)服務(wù)器。用git merge
命令來進(jìn)行合并:
$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
README | 1 -
1 file changed, 1 deletion(-)
請注意,合并時(shí)出現(xiàn)了“Fast forward”的提示。由于當(dāng)前master
分支所在的提交對象是要并入的hotfix
分支的直接上游,Git 只需把master
分支指針直接右移。換句話說,如果順著一個(gè)分支走下去可以到達(dá)另一個(gè)分支的話,那么 Git 在合并兩者時(shí),只會(huì)簡單地把指針右移,因?yàn)檫@種單線的歷史分支不存在任何需要解決的分歧,所以這種合并過程可以稱為快進(jìn)(Fast forward)。
現(xiàn)在最新的修改已經(jīng)在當(dāng)前master
分支所指向的提交對象中了,可以部署到生產(chǎn)服務(wù)器上去了(見圖 3-14)。
圖 3-14. 合并之后,master 分支和 hotfix 分支指向同一位置。
在那個(gè)超級重要的修補(bǔ)發(fā)布以后,你想要回到被打擾之前的工作。由于當(dāng)前hotfix
分支和master
都指向相同的提交對象,所以hotfix
已經(jīng)完成了歷史使命,可以刪掉了。使用git branch
的-d
選項(xiàng)執(zhí)行刪除操作:
$ git branch -d hotfix
Deleted branch hotfix (was 3a0874c).
現(xiàn)在回到之前未完成的 #53 問題修復(fù)分支上繼續(xù)工作(圖 3-15):
$ 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(+)
圖 3-15. iss53 分支可以不受影響繼續(xù)推進(jìn)。
值得注意的是之前hotfix
分支的修改內(nèi)容尚未包含到iss53
中來。如果需要納入此次修補(bǔ),可以用git merge master
把 master 分支合并到iss53
;或者等iss53
完成之后,再將iss53
分支中的更新并入master
。
刪除遠(yuǎn)程分支:
$ git push origin --delete BranchName>
查看遠(yuǎn)程分支:
分支的合并
在問題 #53 相關(guān)的工作完成之后,可以合并回master
分支。實(shí)際操作同前面合并hotfix
分支差不多,只需回到master
分支,運(yùn)行git merge
命令指定要合并進(jìn)來的分支:
$ git checkout master
$ git merge iss53
Auto-merging README
Merge made by the 'recursive' strategy.
README | 1 +
1 file changed, 1 insertion(+)
請注意,這次合并操作的底層實(shí)現(xiàn),并不同于之前hotfix
的并入方式。因?yàn)檫@次你的開發(fā)歷史是從更早的地方開始分叉的。由于當(dāng)前master
分支所指向的提交對象(C4)并不是iss53
分支的直接祖先,Git 不得不進(jìn)行一些額外處理。就此例而言,Git 會(huì)用兩個(gè)分支的末端(C4 和 C5)以及它們的共同祖先(C2)進(jìn)行一次簡單的三方合并計(jì)算。圖 3-16 用紅框標(biāo)出了 Git 用于合并的三個(gè)提交對象:
圖 3-16. Git 為分支合并自動(dòng)識(shí)別出最佳的同源合并點(diǎn)。
這次,Git 沒有簡單地把分支指針右移,而是對三方合并后的結(jié)果重新做一個(gè)新的快照,并自動(dòng)創(chuàng)建一個(gè)指向它的提交對象(C6)(見圖 3-17)。這個(gè)提交對象比較特殊,它有兩個(gè)祖先(C4 和 C5)。
值得一提的是 Git 可以自己裁決哪個(gè)共同祖先才是最佳合并基礎(chǔ);這和 CVS 或 Subversion(1.5 以后的版本)不同,它們需要開發(fā)者手工指定合并基礎(chǔ)。所以此特性讓 Git 的合并操作比其他系統(tǒng)都要簡單不少。
圖 3-17. Git 自動(dòng)創(chuàng)建了一個(gè)包含了合并結(jié)果的提交對象。
既然之前的工作成果已經(jīng)合并到master
了,那么iss53
也就沒用了。你可以就此刪除它,并在問題追蹤系統(tǒng)里關(guān)閉該問題。
遇到?jīng)_突時(shí)的分支合并
有時(shí)候合并操作并不會(huì)如此順利。如果在不同的分支中都修改了同一個(gè)文件的同一部分,Git 就無法干凈地把兩者合到一起(譯注:邏輯上說,這種問題只能由人來裁決。)。如果你在解決問題 #53 的過程中修改了hotfix
中修改的部分,將得到類似下面的結(jié)果:
$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
Git 作了合并,但沒有提交,它會(huì)停下來等你解決沖突。要看看哪些文件在合并時(shí)發(fā)生沖突,可以用git status
查閱:
$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add file>..." to mark resolution)
both modified: index.html
no changes added to commit (use "git add" and/or "git commit -a")
任何包含未解決沖突的文件都會(huì)以未合并(unmerged)的狀態(tài)列出。Git 會(huì)在有沖突的文件里加入標(biāo)準(zhǔn)的沖突解決標(biāo)記,可以通過它們來手工定位并解決這些沖突??梢钥吹酱宋募愃葡旅孢@樣的部分:
HEAD
div id="footer">contact : email.support@github.com/div>
=======
div id="footer">
please contact us at support@github.com
/div>
>>>>>>> iss53
可以看到=======
隔開的上半部分,是HEAD
(即master
分支,在運(yùn)行merge
命令時(shí)所切換到的分支)中的內(nèi)容,下半部分是在iss53
分支中的內(nèi)容。解決沖突的辦法無非是二者選其一或者由你親自整合到一起。比如你可以通過把這段內(nèi)容替換為下面這樣來解決:
div id="footer">
please contact us at email.support@github.com
/div>
這個(gè)解決方案各采納了兩個(gè)分支中的一部分內(nèi)容,而且我還刪除了
,=======
和>>>>>>>
這些行。在解決了所有文件里的所有沖突后,運(yùn)行git add
將把它們標(biāo)記為已解決狀態(tài)(譯注:實(shí)際上就是來一次快照保存到暫存區(qū)域。)。因?yàn)橐坏捍?,就表示沖突已經(jīng)解決。如果你想用一個(gè)有圖形界面的工具來解決這些問題,不妨運(yùn)行git mergetool
,它會(huì)調(diào)用一個(gè)可視化的合并工具并引導(dǎo)你解決所有沖突:
$ 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 為我默認(rèn)選擇了opendiff
,因?yàn)槲以?Mac 上運(yùn)行了該命令),你可以在上方"merge tool candidates"里找到可用的合并工具列表,輸入你想用的工具名。我們將在第七章討論怎樣改變環(huán)境中的默認(rèn)值。
退出合并工具以后,Git 會(huì)詢問你合并是否成功。如果回答是,它會(huì)為你把相關(guān)文件暫存起來,以表明狀態(tài)為已解決。
再運(yùn)行一次git status
來確認(rèn)所有沖突都已解決:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD file>..." to unstage)
modified: index.html
如果覺得滿意了,并且確認(rèn)所有沖突都已解決,也就是進(jìn)入了暫存區(qū),就可以用git commit
來完成這次合并提交。提交的記錄差不多是這樣:
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.
#
如果想給將來看這次合并的人一些方便,可以修改該信息,提供更多合并細(xì)節(jié)。比如你都作了哪些改動(dòng),以及這么做的原因。有時(shí)候裁決沖突的理由并不直接或明顯,有必要略加注解。
總結(jié)
到此這篇關(guān)于Git 分支的新建與合并的文章就介紹到這了,更多相關(guān)Git 分支內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
您可能感興趣的文章:- 解決idea git切換多個(gè)分支后maven不生效的問題
- git push 本地項(xiàng)目推送到遠(yuǎn)程分支的方法(git命令版)
- git 將本地文件(夾)上傳到gitee指定分支的處理方法
- 詳解git使用小結(jié)(本地分支與遠(yuǎn)程分支、git命令)
- idea手動(dòng)刷新git分支的詳細(xì)教程
- IDEA怎么切換Git分支的實(shí)現(xiàn)方法
- idea+git合并分支解決沖突及詳解步驟
- 如何使用Git實(shí)現(xiàn)切換分支開發(fā)過程解析