2016-03-12 31 views
2

我是新的Git並試圖知道如何git merge命令工作。所以我嘗試了一個簡單的git項目,例如:Git合併影響工作目錄和臨時區域

我有兩個分支:masterb。你可以看到git的日誌都在這裏(1,2,4是在分支機構創建示例文件):

* cf3456b (HEAD, b) add and modify 4 in b 
* 68b9086 edit 1 in branch b 
| * 81e6490 (master) remove 1 from branch b1 
| * e0a6844 modify 2 in branch b1 
| * 06bad1d add2 in branch b1 
|/ 
* c667d3b add 1 in branch master 

所以
master只有一個文件:2
b只有兩個文件:14

現在,當我嘗試這樣做:

git merge master 

我看到這個我ssage:

CONFLICT(修改/刪除):1在master中刪除並在HEAD中修改。 版本HEAD of 1留在樹中。自動合併失敗;修復衝突 然後提交結果。

這是奇怪的,我之前想:

  1. mergecheckout嘗試複製的內容從最新提交merge in分支到最新提交merge into分支,但相反的checkoutmerge嘗試不要更改merge into中的任何文件。最後,如果merge inmerge into分支具有不同狀態的相同文件,則會發生衝突。
  2. 我知道git只保存snapshot的文件而不是differences。那麼git如何知道最新的分支提交的變化? (在消息你可以看到1 deleted in master and modified in HEAD哪些差異)

現在,如果我的想法是真實的,應當對文件1沒有衝突。所以我的想法是不正確的,但merge真的工作?

+0

可能重複的(雖然他們不詳談了):http://stackoverflow.com/q/14961255/ 1256452 – torek

回答

4

重點#2,你是正確的,git保存快照。但是,考慮到任何兩個快照ab,我們可以通過比較ba來獲得變更集(差異) - 並且git根據需要執行此操作。

至於合併本身,首先,我們要注意的是混帳保持一個提交圖形(又稱「DAG」或「提交DAG」,因爲圖是d irected 一個循環G raph或DAG)。這就是你的輸出顯示的內容,儘管在這種情況下我們有這樣一種簡單的圖形形式,它只是一個樹(直到合併提交)。

對於任何樹,並有許多的DAG,樹給出了兩個節點,我們可以找到一個獨特的大號把所欠的Ç ommon 一個 ncestor節點,或LCA。這裏的兩個節點是兩個分支的提示 - 提交cf3456b(您當前分支的提示)和81e6490(提示master) - 並且此特定情況下的LCA是第一個提交c667d3b。在這樣的簡單情況下,LCA很容易在視覺上發現:您只需查看提交圖來查找兩個分支加入的位置(從此處提交回到根目錄的所有提交都在兩個分支上)。

該LCA節點是合併基地。 Git首先找到當前提交和您提供的參數的合併基礎。 (對於一個「章魚合併」,你可以直接把git合併到當前分支的多個提交中,這個工作稍微牽扯一點,但我們可以在這裏忽略它們。)

接下來,考慮到現有的合併基礎和兩個不同的提示提交,git必須計算兩個變更集:一個從合併基礎到當前提交,另一個從合併基礎到參數提交。請注意,git會在整個過程中先做一次,之後它可以繼續進行合併操作。

現在,對於每個有變化的文件,git都必須組合這些變化。對於最簡單的修改,該方法很簡單:如果在一個分支中修改了文件1,請按原樣進行修改。如果它在兩個分支中都被修改,請嘗試合併修改,只需兩個分支進行相同更改即可獲得一個副本。當然,如果兩個分支對單個文件的同一區域進行不同的更改,則會發生衝突。

對於文件創建或刪除的情況,或者有重命名時,事情會變得有點棘手。如果一個文件在一個分支中被刪除而另一個文件未被觸發,git可以通過刪除文件來解決這個問題(如果文件已經在HEAD中被刪除,刪除它(如果它在另一個提交中被刪除則刪除)。如果文件在一個分支中被重命名並在另一個分支中被修改,那麼git也可以結合這些更改(做或保留重命名,同時還導入或保留其他更改)。然而,對於其他任何事情,git只是聲明一個衝突,拋出它的隱喻手,並讓你解決衝突。

在這種情況下,文件1確實在您當前的分支中進行了修改,並且確實在master中被刪除。 Git不確定是否刪除文件(按照merge-base的指示對master diff),或者保留更改(按照merge-base指示爲HEAD差異),所以它會給你帶來文件和衝突。

如果你在兩個分支中創建了一個新文件5,git會再次給你一個衝突(除非可能,如果文件的新內容在兩個分支中都是相同的 - 我沒有測試過這種情況)。


隨着複雜的DAG可以有多個最近公共祖先候選。對於git的「遞歸」合併,git通過合併每個LCA候選人來形成一個新的「虛擬基礎」來處理這個問題。這個虛擬基礎成爲兩個提交進行比較的合併基礎。

git checkout -b temp candidate_1 
git merge candidate_2 
git commit -a -m "" 

任何合併這種「內合併」被忽略期間出現的衝突::如果只有兩個LCA候選,虛擬合併基部由做的大致相當於得到的衝突合併基礎使用具有其衝突已到位。這可能會造成一些時髦的衝突,特別是當「外部」合併的同一區域發生衝突時:請參閱this SO question

如果有兩個以上的LCA候選,merge-recursive取前兩個併合並它們,然後合併第三個,然後迭代合併第四個,依此類推。它可以做到這一點,即使它可以合併成對數字,然後合併合併對以再次將其切成兩半,依此類推。也就是說,給定N個LCA候選者,可能會執行ceil(log2(N))合併,而不是N-1合併,但git不會很少超過2。

+0

我有一個關於'git merge'的問題,似乎打破了你的預測。請參閱:http://stackoverflow.com/questions/35969245/why-merge-does-not-cause-conflict – hasanghaforian

0

衝突消息:

衝突(刪除/修改):1在主刪除,並且在HEAD

意味着1是在主分支你合併刪除修改,但在HEAD中修改(在你現在實際上的分支中)。

所以,你必須決定是否

remove file using "git rm 1" 

accept version from HEAD with "git add 1"