起因
我有一個開發分支antd3.x和一個主分支develop,我在合並antd3.x到develop的時候發現有些修改沒有合並進來。
查找問題
然后就去網上查,發現這篇文章《git合並丟失代碼問題分析與解決》給我了一些啟發。
其中說到git merge的原理是三方合並,簡單來說就是假設我有a和b兩個分支,我要合並b到a,這個時候git 其實還會去找到a和b的最近的父節點c,將c作為基礎的分支,然后對abc進行比較,如果有一個文件xxx.js,xxx.js的內容abc三個分支上同一行都不一樣那么就會報conflict,因為git也不知道該保留誰的代碼,就會讓你自行決斷;如果只有一個分支比如b上面的xxx.js的同一行代碼和c分支上的不一樣那么git就會自作主張的認為應該保留b上面的修改,看到這我就有了方向,我應該去找我此次合並的兩個分支的那個最近的父節點,問題應該就出在他上面。
通過執行 git merge-base antd3.x develop 找到了他倆base節點2344a88,然后查看該節點的代碼發現那些沒有合並進來的修改已經存在於這個base節點上了,怪不得進行三方合並的時候沒有合並進來,因為git發現develop分支上對比base分支沒有這些修改,於是這些修改就被刪除掉了。但是到這一步我又有疑問了,我沒有手動的刪除過develop上的這些修改,為啥這些修改會沒有了呢,於是我查看log發現我曾經通過 git revert 撤銷過一個合並,而這個合並恰巧就是那個修改的內容,到這塊問題基本就清楚了,下面我總結一下。
總結
讓我們從頭捋一下這個問題的前因后果:
- 我有三個分支develop、antd3.x和std-08,其中antd3.x是根據develop拉取的,std-08是根據antd3.x拉取的
- 此時我想要合並antd3.x到develop分支上,執行代碼 git merge --no-ff antd3.x
- 發現antd3.x上面的有些修改(x)沒有合並到develop上
- 通過 git merge-base antd3.x develop 找到了他倆base節點2344a88
- 發現base節點上面存在修改x
- 通過 git log 發現develop分支上曾經執行過 git revert
- 本來當時是想把std-08合並到antd3.x上結果合並到了develop上,也就是此時修改x合並到了develop分支上
- 然后執行了 git revert 撤銷了此次合並,並正確合並std-08到antd3.x上,這就導致了antd3.x和develop的共同父節點變成了std-08的最后一次提交2344a88
- 所以后續再想合並antd3.x到develop上時,進行三方合並,發現base節點上的修改在develop上面被刪除了,所以合並的結果就是刪除這些修改,但是實際上這些修改我們是想保留的
解決方案
解決方案有兩個:
1.在develop上執行 git reset commitId 到合並std-08之前的那次提交然后 git push -f origin ,這樣develop的commit歷史就不包含那次合並了
2.在develop上執行 git revert HEAD~ 把之前revert的再revert掉,這個我沒有試過
思考
其實這個教訓充分暴露了我對 git revert 和 git reset 的區別不甚了解,可以看下這篇文章《git revert 用法》,對它倆的區別解釋的很清楚,其實就是 git revert 的撤銷原理就是刪除掉之前的提交然后執行commit,這樣所有的commits都會保留下來,也就埋下了隱患,在我的場景里就是在git 合並的時候尋找的base節點就是develop revert之前的那個commit,如果是 git reset 就不會保留這個commit,也就不會把它作為base節點,合並的時候就不會有問題。我看了下網上還有另一種解釋就是執行完 git revert 后,git就認為你不在需要這些修改,以后再合並的時候如果有這樣的修改要合並,git就會忽略,我沒有驗證過這種說法的真實性,如果有人清楚的話,還請不吝賜教。