想象一下有例如以下情形:**庫中存在兩個分支,而且每乙個分支都進行了改動。最後你想要將當中的乙個分支合併到其它的分支中。個人部落格**
那麼要問合併的處理過程是怎麼樣的呢?git
是對每乙個分支,根據分支的歷史資料依照序列化操作,還是它僅僅是合併每乙個分支裡檔案的最後版本號?這是乙個問題,我想對git
的merge
操作有必要進行分析一下。
回顧一下。我們知道git
的版本號庫內部結構是以有向無環圖(directed acyclic graph
)組織起來的:每一次commit
都會生成乙個版本號樹的快照(snapshot
),而且該快照儲存了乙個指向其父節點(該分支的近期上一次的提交快照)的引用(通常當前提交僅僅有乙個父節點,可是初試提交快照沒有父節點,而一次合併(merge
)操作有2個或多個父節點)。
就像這樣。每次提交都遞迴的建立某些節點集指向父節點的引用。
有時候,當我們考慮提交的父節點提交樹和當前節提交節點樹做差異比較時(diff
),將一次提交想象成一次修補補丁(patch
)是有助於我們理解git
的工作機理。依照這樣的方式,我們能夠這樣覺得,提交樹就是整合應用了全部父節點的補丁修補。一顆在兩個分支上做merge操作的樹,因此就能夠覺得是兩個分支應用了其各自全部的父節點修補補丁程式,然後做一次聯合操作union。
可是那不是git merge
的真正執行方式。原因是。首先。假設以那樣工作的話。執行會很的慢!,而且在執行過程中它要再一次又一次處理全部的之前合併時造成的衝突。如此,git merge
真正是怎樣操作的呢?
我喜歡用數學的思維方式思考:給定兩個提交 a和
b,合併提交(commit
)操作 a∨
b 就能夠描寫敘述為: [a∨
b]=[
a]+[
b]−[
c]這裡的 c是
a 和
b的合併共同擁有項(近期提交樹祖先共同含有的部分)。我們必需要「減去」
c。由於假設不這種話,我們就會有兩個a∧
b。這個操作x+
y−z 被叫做三向合併。
你能夠覺得執行路徑為將x−
z 應用到
x 上,或者將 x−
z應用到
y 上。
其實diff
和patch
操作並沒有字面上依照上面的的操作行事,相反而是使用了:最長公共子串行演算法來實現。x−
w和序列
x,序列w的差異就是我們知道的在求最長公共子串行時的賦值(中間可能要去除到兩個序列的公共部分)。為了構造三向合併x+
y−w,我們對x和
w在求公共子串行的時候進行賦值。對y和
w在求公共子串行的時候賦值。然後輸出每乙個要麼:
同一時候我們要刪除那些序列,要麼:
舉個栗子,下面是x,
y,z 執行merge操作後的結果:
x:w
:y:↦
merged
:
milk milk milk milk
juice juice
flour flour flour flour
sausage sausagegit
eggs eggs eggs eggs
butter butter
在x,y與
w的行序可能只說明了一種在三向合併的輸出行上的一種偏序關係。假設是這種話,因為相同的塊w,在
x,y 之間以不同的方式被編輯-因此我們說那就是乙個合併衝突。將會輸出該資訊,讓使用者手動解決。
當git
向你顯示合併衝突的時候,預設情況下,你將會看到x和的衝突塊:
我建議開啟開關:
~/.
gitconfig
通過設定merge.conflictstyle
為diff3
,則
git config
--global
merge
.conflictstyle diff3
i had two eggs
andthree sausages
forbreakfast
.
(注意,這個操作會對稱性的(關於w和結果進行交換,因此你真正須要的是檢視w)這裡有另外兩種其它的案例須要考慮,可能行為:
某些三向合併演算法常常將這樣的行標記為衝突行。
然而git
,將會優雅的輸出或者直接刪除該行,依次,假定該行沒有改變。
這樣的效果叫做意外清理合併。偶爾某些情形在實際應用中非常實用,尤其是使用者把版本號搞砸了,各自合併同乙個補丁的兩個不同的版本號。
可是我覺得掩蓋這樣的錯誤不是一種好的行事方式,我希望這樣的行為能夠並關閉。盡量避免由於他所能帶來的這樣的長處而使用它吧。
假設你細緻。非常有觀察力,你可能已經發現我在上述說明中存在的乙個漏洞了:因為commit
提交 a和
b可能各自又包括commit
,他們近期的共同祖先可能不是唯一的!
一般。他們最有可能的情形是,近期的共同祖先是 c1,
c2,c
3,c4
,⋯ck
−1,c
k ,在這樣的情況下,git merge
操作將會遞迴的執行:它首先構造合併 c=c
1∨c2
∨c3⋯
ck−1
∨ck 。並以此作為三向合併[a]
+[b]
−[c]
的基礎(base
)。
這就是為什麼git
的預設合併策略並稱為遞迴的。 假定兩個分支例如以下圖所看到的。a,
b,c,
d,e是master
分支的歷史快照(snapshot
);a,
b,x,
y,z是feature
分子的歷史快照。
git merge feature
首先查詢「master
」(當前分支)和「feature
」的共同祖先。它或多或少的等價於下面命令:
git merge
-base
master feature
在我們的舉的樣例裡,他們的共同祖先是b。
假設在c,d
,e和x
,y,z
提交中沒有衝突,git
將會建立一次「merge commit
」merge commit
會有兩到多個父親。 新的圖將會是以下這個樣子。git commit
提交都會生成一棵樹,一到多個「父親節點」。作者的名字,email
,日期和提交者的姓名。email
,日期。
merge
提交和普通的提交的唯一差別就是祖先的數量。
在第二幅圖中,merge commit
提交被以
m標註出來了。 假設提交存在衝突,使用者就會被要求解決衝突,並手動建立合併提交,在衝突解決後
git commit
-a
將會建立合併提交。這條命令沒什麼特殊的語法。git
已經知道了使用者已經在進行合併了(已經在嘗試合併)。 git中merge和rebase的區別
最開始實習的時候是使用svn,之後正式工作就一直在使用git,這樣算起來,使用git也有兩年的時間了。以前帶我的同事,讓我在拉 的時候要我使用git pull rebase,一直很納悶為什麼要那樣做,後來遇到拉 的時候有許多衝突要解決,然後去查詢資料,才了解到其中的一些事情。今天分享一下,順便自己也...
Git分支merge和rebase的區別
git merge是用來合併兩個分支的。git merge b 將b分支合併到當前分支 同樣 git rebase b,也是把 b分支合併到當前分支 原理 如下 假設你現在基於遠端分支 origin 建立乙個叫 mywork 的分支。git checkout b mywork origin 假設遠端...
Git分支merge和rebase的區別
git merge是用來合併兩個分支的。git merge b 將b分支合併到當前分支 同樣 git rebase b,也是把 b分支合併到當前分支 原理 如下 假設你現在基於遠端分支 origin 建立乙個叫 mywork 的分支。git checkout b mywork origin 假設遠端...