Git三路合併的隱患

我以爲最近碰到的這個狀況應該不算少見,作一點總結。git

這個repository的流程是 remote master 分支受保護,不直接接受push,新增feature經過pull request合併到master分支。web

場景再現

某個組員本身的A分支中的P文件是舊版本,提交了pull request,因爲種種緣由沒有發現P文件的問題,結果合併到master分支後,master分支的P文件部分的代碼倒退回了N天前。我嘗試經過B分支(最新P版本)發起一個pull request,合併到master試圖將P從新恢復到最新版本。但結果出乎意料,P仍然是舊版本。vim

分析

問題出在git自身的三路合併(three-way merge)機制,若是base和X分支的某一部分代碼相同,但和Y分支不一樣,合併的結果就會採用Y分支不一樣的那部分
再加上以前我對git合併的一個誤區,覺得 merge conflicts 給出的結果涵蓋了兩個分支全部變更。事實上僅屬於單一分支的變更不會顯示。(本地除了用外部合併工具,還能夠git difftool --ours or git difftool --theirs or git difftool --base)bash

如今用圖例來展現下合併的時間軸:工具

圖示:假設P5版本比P2新, M1是引入舊版本的合併, M2是我嘗試修復的合併:fetch

B:                          P5-----------------P5
                           /                    \
master: ---P5(M1 base)---P5(M2 base)---P2(M1)---P2(M2)
            \                         /
A:           P2----------------------P2

在M2合併的時候,Git(包括GitHub上處理合並衝突的webIDE)生成的衝突文件不會顯示僅由P2改動的區域code

目前我發現的解決方法只能經過外部合併工具(git mergetool)好比Kaleidoscope, Beyond Compare, vimdiff或者JetBrains家自帶的衝突處理工具才能完整看到哪些是僅僅被P2改動的部分。
但彷佛GitHub的pull request不支持用外部合併工具處理衝突。除非在GitHub處理衝突的webIDE上直接粘貼P5,想經過M2更新到P5無解。若是P2不單單包含舊版本,還有新增的feature,人工處理就會很麻煩。three

後來個人解決方法是:在本地用外部合併工具解決衝突後,再提交pull request。
具體操做:從M1 pull到了B分支(這樣一來base就變成M1),在本地經過外部合併工具修復回P5(或者git checkout --ours path/to/P),再發pull request合併到master
還有種方法就是 pull master後,在本地把B分支合併到master(經過外部合併工具修復回P5),用管理員權限push到 remote masterrem

總結

GitHub上pull request的Resolve conflicts慎用
本地git生成的衝突文件慎用
鑑於以上幾點,從此pull request出現衝突時,能夠採用以下流程:
(假設pull request對應的分支是featureget

  • 把 remote feature fetch到本地,把 remote master合併到該分支(能夠先fetch master,再合併到本地feature,或者直接把 remote master pull到本地的feature),再用外部合併工具解決衝突,而後push,這時候pull request上顯示能夠合併,直接點Merge按鈕便可。
  • 若是master不受保護或者可使用管理員權限強行push,則還能夠在本地 pull master後,把feature合併到master,再 push master到GitHub。

Reference

https://www.git-tower.com/lea...
https://stackoverflow.com/que...
https://git-scm.com/docs/git-...

相關文章
相關標籤/搜索