前面說到,pull
的內(nèi)部操作其實(shí)是把遠(yuǎn)程倉庫取到本地后(使用的是 fetch
)涮较,再用一次 merge
來把遠(yuǎn)端倉庫的新 commits
合并到本地迁筛。這一節(jié)就說一下商佑,merge
到底是什么豪嚎。
含義和用法
merge
的意思是「合并」搔驼,它做的事也是合并:指定一個(gè) commit
,把它合并到當(dāng)前的 commit
來侈询。具體來講舌涨,merge
做的事是:
從目標(biāo) commit
和當(dāng)前 commit
(即 HEAD
所指向的 commit
)分叉的位置起,把目標(biāo) commit
的路徑上的所有 commit
的內(nèi)容一并應(yīng)用到當(dāng)前 commit
妄荔,然后自動(dòng)生成一個(gè)新的 commit
泼菌。
例如下面這個(gè)圖中:
HEAD
指向了 master
,所以如果這時(shí)執(zhí)行:
git merge branch1
Git 會(huì)把 5
和 6
這兩個(gè) commit
的內(nèi)容一并應(yīng)用到 4
上啦租,然后生成一個(gè)新的提交,并跳轉(zhuǎn)到提交信息填寫的界面:
merge
操作會(huì)幫你自動(dòng)地填寫簡要的提交信息荒揣。在提交信息修改完成后(或者你打算不修改默認(rèn)的提交信息)篷角,就可以退出這個(gè)界面,然后這次 merge
就算完成了系任。
適用場景
merge
有什么用恳蹲?最常用的場景有兩處:
-
合并分支
當(dāng)一個(gè)
branch
的開發(fā)已經(jīng)完成,需要把內(nèi)容合并回去時(shí)俩滥,用merge
來進(jìn)行合并嘉蕾。那
branch
又應(yīng)該怎么用呢?下節(jié)就說霜旧。
-
pull
的內(nèi)部操作之前說過错忱,
pull
的實(shí)際操作其實(shí)是把遠(yuǎn)端倉庫的內(nèi)容用fetch
取下來之后,用merge
來合并挂据。
特殊情況 1:沖突
merge
在做合并的時(shí)候以清,是有一定的自動(dòng)合并能力的:如果一個(gè)分支改了 A 文件,另一個(gè)分支改了 B 文件崎逃,那么合并后就是既改 A 也改 B掷倔,這個(gè)動(dòng)作會(huì)自動(dòng)完成;如果兩個(gè)分支都改了同一個(gè)文件个绍,但一個(gè)改的是第 1 行勒葱,另一個(gè)改的是第 2 行,那么合并后就是第 1 行和第 2 行都改巴柿,也是自動(dòng)完成凛虽。
但,如果兩個(gè)分支修改了同一部分內(nèi)容篮洁,merge
的自動(dòng)算法就搞不定了涩维。這種情況 Git 稱之為:沖突(Conflict)。
直白點(diǎn)說就是,你的兩個(gè)分支改了相同的內(nèi)容瓦阐,Git 不知道應(yīng)該以哪個(gè)為準(zhǔn)蜗侈。如果在 merge
的時(shí)候發(fā)生了這種情況,Git 就會(huì)把問題交給你來決定睡蟋。具體地踏幻,它會(huì)告訴你 merge
失敗,以及失敗的原因:
git merge feature1
提示信息說戳杀,在 shopping list.txt
中出現(xiàn)了 "merge conflict"该面,自動(dòng)合并失敗,要求 "fix conflicts and then commit the result"(把沖突解決掉后提交)信卡。那么你現(xiàn)在需要做兩件事:
- 解決掉沖突
- 手動(dòng)
commit
一下
1. 解決沖突
解決掉沖突的方式有多個(gè)隔缀,我現(xiàn)在說最直接的一個(gè)。你現(xiàn)在再打開 shopping list.txt
看一下傍菇,會(huì)發(fā)現(xiàn)它的內(nèi)容變了:
可以看到猾瘸,Git 雖然沒有幫你完成自動(dòng) merge
,但它對(duì)文件還是做了一些工作:它把兩個(gè)分支沖突的內(nèi)容放在了一起丢习,并用符號(hào)標(biāo)記出了它們的邊界以及它們的出處牵触。上面圖中表示,HEAD
中的內(nèi)容是 移動(dòng)硬盤(已買)
咐低,而 feature1
中的內(nèi)容則是 移動(dòng)硬盤(不買了)
揽思。這兩個(gè)改動(dòng) Git 不知道應(yīng)該怎樣合并,于是把它們放在一起见擦,由你來決定钉汗。假設(shè)你決定保留 HEAD
的修改,那么只要?jiǎng)h除掉 feature1
的修改锡宋,再把 Git 添加的那三行 <<<
===
>>>
輔助文字也刪掉儡湾,保存文件退出,所謂的「解決掉沖突」就完成了执俩。
你也可以選擇使用更方便的 merge
工具來解決沖突徐钠,這個(gè)你可以自己搜索一下。
2. 手動(dòng)提交
解決完沖突以后役首,就可以進(jìn)行第二步—— commit
了尝丐。
git add shopping\ list.txt # 嗯是的,這里 commit 前也需要先 add 一下
git commit
可以看到衡奥,被沖突中斷的 merge
爹袁,在手動(dòng) commit
的時(shí)候依然會(huì)自動(dòng)填寫提交信息。這是因?yàn)樵诎l(fā)生沖突后矮固,Git 倉庫處于一個(gè)「merge 沖突待解決」的中間狀態(tài)失息,在這種狀態(tài)下 commit
譬淳,Git 就會(huì)自動(dòng)地幫你添加「這是一個(gè) merge commit」的提交信息。
放棄解決沖突盹兢,取消 merge邻梆?
同理,由于現(xiàn)在 Git 倉庫處于沖突待解決的中間狀態(tài)绎秒,所以如果你最終決定放棄這次 merge
浦妄,也需要執(zhí)行一次 merge --abort
來手動(dòng)取消它:
git merge --abort
輸入這行代碼,你的 Git 倉庫就會(huì)回到 merge
前的狀態(tài)见芹。
特殊情況 2:HEAD 領(lǐng)先于目標(biāo) commit
如果 merge
時(shí)的目標(biāo) commit
和 HEAD
處的 commit
并不存在分叉剂娄,而是 HEAD
領(lǐng)先于目標(biāo) commit
:
那么 merge
就沒必要再創(chuàng)建一個(gè)新的 commit
來進(jìn)行合并操作,因?yàn)椴]有什么需要合并的玄呛。在這種情況下阅懦, Git 什么也不會(huì)做,merge
是一個(gè)空操作把鉴。
特殊情況 3:HEAD 落后于 目標(biāo) commit——fast-forward
而另一種情況:如果 HEAD
和目標(biāo) commit
依然是不存在分叉故黑,但 HEAD
不是領(lǐng)先于目標(biāo) commit
,而是落后于目標(biāo) commit
:
那么 Git 會(huì)直接把 HEAD
(以及它所指向的 branch
庭砍,如果有的話)移動(dòng)到目標(biāo) commit
:
git merge feature1
這種操作有一個(gè)專有稱謂,叫做 "fast-forward"(快速前移)混埠。
一般情況下怠缸,創(chuàng)建新的 branch
都是會(huì)和原 branch
(例如上圖中的 master
)并行開發(fā)的,不然沒必要開 branch
钳宪,直接在原 branch
上開發(fā)就好揭北。但事實(shí)上,上圖中的情形其實(shí)很常見吏颖,因?yàn)檫@其實(shí)是 pull
操作的一種經(jīng)典情形:本地的 master
沒有新提交搔体,而遠(yuǎn)端倉庫中有同事提交了新內(nèi)容到 master
:
那么這時(shí)如果在本地執(zhí)行一次 pull
操作,就會(huì)由于 HEAD
落后于目標(biāo) commit
(也就是遠(yuǎn)端的 master
)而造成 "fast-forward":
git pull
簡單解釋一下上圖中的 origin/master
和 origin/HEAD
是什么鬼:它們是對(duì)遠(yuǎn)端倉庫的 master
和 HEAD
的本地鏡像半醉,在 git pull
的「兩步走」中的第一步——git fetch
下載遠(yuǎn)端倉庫內(nèi)容時(shí)疚俱,這兩個(gè)鏡像引用得到了更新,也就是上面這個(gè)動(dòng)圖中的第一步:origin/master
和 origin/HEAD
移動(dòng)到了最新的 commit
缩多。
為什么前面的圖里面從來都沒有這兩個(gè)「鏡像引用」呆奕?因?yàn)槲覜]有畫呀!其實(shí)它們是一直存在的衬吆。
而 git pull
的第二步操作 merge
的目標(biāo) commit
梁钾,是遠(yuǎn)端倉庫的 HEAD
,也就是 origin/HEAD
逊抡,所以 git pull
的第二步的完整內(nèi)容是:
git merge origin/HEAD
因此 HEAD
就會(huì)帶著 master
一起姆泻,也指向圖中綠色的最新 commit
了。
小結(jié)
本節(jié)對(duì) merge
進(jìn)行了介紹,內(nèi)容大概有這么幾點(diǎn):
-
merge
的含義:從兩個(gè)commit
「分叉」的位置起拇勃,把目標(biāo)commit
的內(nèi)容應(yīng)用到當(dāng)前commit
(HEAD
所指向的commit
)四苇,并生成一個(gè)新的commit
; -
merge
的適用場景:- 單獨(dú)開發(fā)的
branch
用完了以后潜秋,合并回原先的branch
蛔琅; -
git pull
的內(nèi)部自動(dòng)操作。
- 單獨(dú)開發(fā)的
-
merge
的三種特殊情況:- 沖突
- 原因:當(dāng)前分支和目標(biāo)分支修改了同一部分內(nèi)容峻呛,Git 無法確定應(yīng)該怎樣合并罗售;
- 應(yīng)對(duì)方法:解決沖突后手動(dòng)
commit
。
-
HEAD
領(lǐng)先于目標(biāo)commit
:Git 什么也不做钩述,空操作寨躁; -
HEAD
落后于目標(biāo)commit
:fast-forward。
- 沖突