歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Git中的merge命令實現和工作方式

Git中的merge命令實現和工作方式

日期:2017/2/28 14:28:01   编辑:Linux教程

想象一下有如下情形:代碼庫中存在兩個分支,並且每個分支都進行了修改,最後你想要將其中的一個分支合並到其他的分支中。

那麼要問合並的處理過程是怎麼樣的呢?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 和 w 中不存在的部分,或者
• 在y 中出現,但是在x 和w 中沒有出現的部分

同時我們要刪除那些序列,要麼:
• 出現在y 和 w但是在x中沒有出現,或者
• 出現在x和w中但是在y中沒有出現。

舉個栗子,以下是x,y,z 執行merge操作後的結果:
1. x: w: y: ↦ merged:
2. milk milk milk milk
3. juice juice
4. flour flour flour flour
5. sausage sausagegit
6. eggs eggs eggs eggs
7. butter butter

在x,y與w的行序可能僅僅說明了一種在三向合並的輸出行上的一種偏序關系,如果是這樣的話,由於同樣的塊w,在x,y 之間以不同的方式被編輯-因此我們說那就是一個合並沖突,將會輸出該信息,讓用戶手動解決。 當git 向你顯示合並沖突的時候,默認情況下,你將會看到x和的沖突塊:

然而,沖突塊會變得更容易解決,當你能夠看到合並基准w的時候。我建議打開開關:

~/.gitconfig

通過設置merge.conflictstyle 為diff3,則

git config --global merge.conflictstyle diff3

現在你可以看到解決方式為:
1.I had two eggs and three sausages for breakfast.

(注意,這個操作會對稱性的(關於w和結果進行交換,因此你真正需要的是查看w)這裡有另外兩種其他的案例需要考慮,可能行為:
• 出現在x和y中,但是在w中沒有出現
• 出現在w中,但是沒有在x 和y 中出現

某些三向合並算法經常將這樣的行標記為沖突行。然而Git,將會優雅的輸出或者直接刪除該行,依次,假定該行沒有改變。這種效果叫做意外清理合並。偶爾某些情形在實際應用中很有用,尤其是用戶把版本搞砸了,各自合並同一個補丁的兩個不同的版本。但是我認為掩蓋這種錯誤不是一種好的行事方式,我希望這種行為可以並關閉。盡量避免因為他所能帶來的這種優點而使用它吧。

如果你仔細,很有觀察力,你可能已經發現我在上述說明中存在的一個漏洞了:由於commit提交 A和B可能各自又包含commit,他們最近的共同祖先可能不是唯一的!一般,他們最有可能的情形是,最近的共同祖先是 C1,C2,C3,C4,⋯Ck−1,Ck ,在這種情況下,git merge 操作將會遞歸的執行:它首先構造合並 C=C1∨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 已經知道了用戶已經在進行合並了(已經在嘗試合並)。

GitHub 使用教程圖文詳解 http://www.linuxidc.com/Linux/2014-09/106230.htm

Git 標簽管理詳解 http://www.linuxidc.com/Linux/2014-09/106231.htm

Git 分支管理詳解 http://www.linuxidc.com/Linux/2014-09/106232.htm

Git 遠程倉庫詳解 http://www.linuxidc.com/Linux/2014-09/106233.htm

Git 本地倉庫(Repository)詳解 http://www.linuxidc.com/Linux/2014-09/106234.htm

Git 服務器搭建與客戶端安裝 http://www.linuxidc.com/Linux/2014-05/101830.htm

Git 概述 http://www.linuxidc.com/Linux/2014-05/101829.htm

Git權威指南 PDF高清中文版 http://www.linuxidc.com/Linux/2013-10/91053.htm

Git 的詳細介紹:請點這裡
Git 的下載地址:請點這裡

Copyright © Linux教程網 All Rights Reserved