古詩詞大全網 - 藝術簽名 - git合並如何確定沖突?

git合並如何確定沖突?

在解決git合並的沖突時,有時候會忍不住抱怨git太不明智。顯然,我只是在代碼中插入了幾行。沒想到合並失敗了,只好壹個個手動確認。真的不知道如何判斷git的合並沖突。

解決了壹次涉及幾十個文件的合並沖突後(花了我整整壹夜,整整壹上午!),我終於下定決心要去看看git。

合並代碼中沖突判斷的具體實現。俗話說,有委屈就有債。至少下次遇到同樣的問題,妳就知道自己栽在誰身上了。於是有了這樣壹篇文章,說說吧。

git合並中的沖突判定機制。

遞歸三向合並和祖先

Git源代碼

首先使用merge作為關鍵字搜索,查看涉及的相關代碼。

找了壹段時間,找到git merge,對比了要合並的文件的函數入口:ll_merge。還有壹個文檔,也指出ll_merge是合並實現的入口。

從函數簽名可以看出,mmfile_t應該代表要合並的文件。有趣的是,這裏要合並的不是兩個文檔,而是三個。

int ll _ merge(mm buffer _ t * result _ buf,

const char *path,

mmfile_t *ancestor,const char *ancestor_label

mmfile_t *ours,const char *our_label,

mmfile _ t * theirs,const char *their_label,

構造結構ll_merge_options *opts)

看過git幫助合並的讀者應該知道,我們的代表當前分支,他們的代表要合並的分支。可以看出,這個功能是合並壹個文件在不同分支中的版本。那麽祖先位於哪個分支呢?反過來讀調用者的代碼,可以看到大致流程是這樣的。git merge會找到三個commit,然後對每個要合並的文件調用ll_merge,生成最終的合並結果。根據註釋,ancestor是最後兩個commit(我們的和他們的)的共同祖先。此外,上述文檔還顯示,合並git時使用了遞歸三路合並。

關於遞歸三路合並,維基百科有相關介紹#Recursive_three-

way_merge).在合並時,我們、他們和祖先的文件被比較以獲得我們和祖先的文件。

Diff,以及他們的和祖先的diff,可以找出兩個不同的分支做了什麽改變。畢竟,git需要在以後確定沖突的內容,如果不是。

原始版本的信息,簡單地比較兩個文件,是不可能的。

由於我的目標是探索git確定沖突的機制,所以我沒有在git中尋找ancestor的實現。但是,您可以在圖形界面中用肉眼看到祖先提交。(比如gitlab的網絡接口,回到兩個分支的commit行,直到岔路口)。

需要註意的壹點是提交不會改變它的祖先。所謂的恢復只是向當前提交添加壹個新的。

取消

Commit並沒有改變“岔路口”的位置。不要想當然地認為revert之後,ancestor會成為最後壹個提交的祖先。特別

當還原合並提交時,總是很容易忘記這個事實。如果您恢復合並。

Commit,再次合並時,git引用的祖先不會是合並前的祖先,而是revert後的祖先。

祖先.於是他掉進了坑裏。建議所有讀者看壹下git官方關於還原合並提交的潛在後果的聲明:/git/git/blob/master/documentation/how to/Revert-a-fault-merge . txt。

結論是,如果合並提交引入的錯誤很容易修復,請不要輕易恢復合並提交。

分析xdiff

從ll_merge往下追,可以看到後面有個旁路:ll_binary_merge。這個函數處理bin文件的合並。它的實現簡單粗暴。如果不指定合並策略(theris或ours),可以直接報告無法合並二進制文件錯誤。似乎在git看來,二進制文件沒有diff的值。

主路徑從ll_xdl_merge到xdl_merge,進入壹個名為xdiff的庫。終於找到了git合並的具體實現。

平心而論,xdiff的代碼風格很糟糕,不僅註釋太少,而且結構成員變量的命名也像i1和i2,讓我暈頭轉向,心煩意亂。

吐槽結束,先說xdl_merge的流程。Xdl_merge做了以下四件事:

雙向diff(ours和ancestor,theirs和ancestor)由xdl_do_diff完成,修改記錄生成並存儲在xdfenev _ t中。

Xdl_change_compact壓縮相鄰的修改記錄,然後用xdl_build_script建立xdchange_t鏈表記錄雙方的修改。Xdchange_t主要包括修改的起始行號和修改範圍。

這時候有三種情況,其中兩種是只有壹方修改(只有我們的或者他們的是鏈表)直接退出。最後壹種是雙方都修改了,需要合並修改的記錄。

記錄。因為修改的記錄是按行號順序排列的,所以兩個鏈表是直接合並的。如果修訂記錄中沒有重疊,它們將按順序標記為我們的修訂/其他修訂。如果有重疊,就表示發送了。

在那之後有壹個沖突,我們將檢查兩個鏈表來再次合並。對於那些標記為沖突的部分,我們會比較它們是否相等。如果是這樣,我們將把它們標記為雙方修改過的。

合並結果由xdl_fill_merge_buffer輸出。如果有沖突,調用fill_conflict_hunk輸出沖突。如果沒有沖突(標記為我方修改/他人修改/雙方修改),則合並祖先的原始內容和修改記錄,根據標記的類型和輸出取修改的內容。

輸出沖突情況的代碼位於fill_conflict_hunk中。它的實現非常簡單。畢竟這個時候我們已經有了雙方修改的內容,現在只需要同時輸出沖突的內容供用戶選擇。這就是花了壹夜壹上午才解決掉的沖突源頭,而兇手就是妳,哼。

輸出格式恐怕大家都不陌生。這個函數將打印幾個

& lt& lt& lt& lt& lt& lt& lt頭

=======

2

& gt& gt& gt& gt& gt& gt& gtbranch1

摘要

git merge的沖突判定機制是這樣的:首先尋找兩個commit的共同祖先,分別比較我們的和他們的下同壹個文件的差異,然後合並兩組差異。如果雙方同時修改壹個地方,修改的內容不同,則判定為合並沖突,雙方修改的內容依次輸出。