[英]Why does this GIT merge not result in conflicts?
我們今天在工作中發現了 GIT 的一個嚴重問題,我想知道這是一個錯誤還是設計使然,以及如何解決這個問題。
考慮以下事件序列:
結果:即使文件已在兩個分支中進行編輯,也不會報告合並沖突,更糟糕的是,即使是最近的提交,步驟 3.2 中的還原也沒有保留。
這是一個巨大的問題,從以下最近的示例中可以看出:我的一位同事對不同的分支進行了類似的更改,注意到這些更改的一部分是惡意的,因此在其中一個分支上手動還原了其中的一部分. 合並分支后,他驚訝地發現他的恢復沒有通過合並。
我上傳了一個最小的例子到谷歌驅動器來演示這個問題。 您可以將 Master 合並到 test1 中,反之亦然,以自己查看。
https://drive.google.com/drive/folders/19a-QPwOQKsn9PywUPd2DRnvUOml03nZ-?usp=sharing
如果有任何問題,我將 TortoiseGIT 2.12.0.0 與 Git for Windows 2.32.0.2 一起使用。
你得到那個結果,因為那是正確的結果。
好吧,讓我們修改該語句:這是 Git merge 規則的正確結果。 (據我所知,根據大多數其他合並程序的規則,這也是正確的,但有些算法至少會將此標記為注意。Git 不使用這樣的算法。)
如果 Git 的合並結果不是你想要的結果,你有補救措施:見下文。
當 Git 進行合並時,Git 會注意三個快照:
一個快照是當前快照,即,如果您運行git rev-parse HEAD
您將獲得其哈希 ID 的提交。 如果HEAD
附加到分支名稱(通常是這樣),那就是給定分支的提示提交。
一個快照是您在命令行中命名的快照: git merge foo
查找foo
以獲取提交哈希 ID。
第三個,在許多方面也是最重要的,快照是合並基礎。 (Git 將這個編號為“#1”, HEAD
/ --ours
為 #2,另一個 / --theirs
為 #3,在內部,即使我們必須先定位其他兩個輸入才能定位此合並基礎輸入.) 合並庫的提交哈希 ID 通過提交圖定位。 在您的情況下,它是您正在調用 commit X 的提交之前的提交。
讓我們像這樣繪制這些提交,將更新的提交放在右側,單個大寫字母代表每個實際提交哈希 ID:
X <-- master
/
...--G--H <-- here's where both branches start diverging
\
X'-X" <-- test1
在這里, commit X
包含您在master
所做的更改; 提交X'
具有相同的更改,而X"
撤消這些更改,以便X"
中的快照與H
中的快照完全匹配。
Git 的合並算法包括執行以下操作,假設您在master
(因此提交X
是當前/ HEAD
提交)並且正在合並test1
(提交X"
):
將提交H
提取為“階段 1”。
將提交X
提取為“階段 2”。
將提交X"
提取為“階段 3”。(請注意,我們剛剛確定X"
的內容與H
的內容匹配。)
對於索引/階段中的每個文件,都存在於所有三個階段插槽中:
對於不存在於所有三個插槽中的文件(例如,可能發生了重命名或復制的位置),事情變得更加復雜。 這些可能會導致高級別的樹沖突,這些沖突被視為沖突,但不會顯示為沖突的工作樹副本。 但是這種情況在這里並不適用,所以我們可以忽略它。
git merge
已經完成,但現在有清理步驟:
正確合並的文件被放到槽 0(如果/根據需要寫出到工作樹)。
有合並沖突的文件留在所有三個插槽中; 工作樹在合並時得到 Git 的最大努力,包括沖突標記。
在發生沖突的情況下,合並現在在中間停止; 用戶必須完成它。 每個文件的工作樹和索引副本都存在於此處供用戶使用。
否則,除非被告知停止而不提交,否則git merge
自行完成合並,通常是通過創建一個新的合並提交。
如果此合並結果不是您想要的,您的補救措施包括但不限於以下內容:
更改輸入(例如,通過向一個或兩個分支添加更多提交)。
使用git merge -n
以便git merge
在提交合並結果之前停止。 使用索引和工作樹文件——現在都暫存提交,索引槽 #0 中每個文件只有一個版本——來產生你想要的結果。 然后,提交結果。 請注意,這稱為邪惡合並。 它沒有什么問題,但是如果你讓 Git 重復合並——例如,使用花哨的新git rebase --rebase-merges
代碼git rebase --rebase-merges
不會知道讓新合並成為一個邪惡的合並,所以明智的做法是用提交消息或其他東西清楚地標記這一點。
進行合並,讓它成為“錯誤的”,提交它(並且可能標記它,特別是對於稍后的skip-during-bisect),然后添加一個提交來修復問題。
在您描述的情況下:這是git merge
操作的預期結果——torek 的回答詳細說明了原因。
在 git 方面:
您提到的工作流程的一個問題是git cherry-pick
沒有注冊原始提交的鏈接。 您必須知道,不知何故,管理cherry-pick
有點像管理代碼中的“復制/粘貼”:如果在一側修復了錯誤,則應手動將其移植到另一側。
順便說一下, git rebase
也是如此:如果您應用了一個 rebase 操作,該操作以某種方式保留了原始分支,並且還創建了一個帶有一些復制提交的新分支,那么這也是復制/粘貼提交。
一個更通用的原則是:
“ git merge
沒有沖突的情況下成功”並不意味着“生成的代碼沒有錯誤”。
您應該始終驗證生成的代碼是否符合您的期望:
對於您的發布分支尤其如此; 有時,例如您描述的那個(開發人員合並到一個分支),這可能更多是關於您的團隊意識到潛在的陷阱——並檢查差異。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.