Git 版本控制系統- 分支合併衝突與解決辦法 - Roya's Blog

文章推薦指數: 80 %
投票人數:10人

這一次Push 就成功了,之前我們有說過 git pull 主要會將 git fetch 的內容直接執行 git merge ,這也才導致直接跳出合併的訊息視窗,這樣子看起來是不是 ... 0% 前言在之前我們有提到Git主要被使用在多人協作開發,每個人各自完成屬於自己的工作,最後透過合併即可完成應用,但想的簡單,實作起來卻有些困難,過程中難免會有意外發生,比如說A同事與B同事先前在初始檔案各開了一個分支,但A同事發現這一份檔案存在Bug,於是做了修復的動作,而B同事正好也發現了這一個Bug也做了修復,此時如果進行合併,就會造成所謂的合併衝突,此狀況不只會發生在本地分支,遠端分支也同樣會發生,該如何解決此類的衝突,也就是本篇的主題。

筆記重點 本地分支合併衝突 遠端分支合併衝突 GitHub保護機制 Git指令回顧 本地分支合併衝突讓我們先來模擬衝突發生的情境: 1234567891011mkdirprojectcdprojectgitinittouchindex.htmlgitadd.gitcommit-m'addindex.html' 修改index.html檔案: 123456789Document

再次提交commit: 123gitadd.gitcommit-m'updateindex.html' 到這邊已經完成初始化的動作了,假設目前有一位工程人員開了一個分支並提交了一次commit紀錄: 1234567gitcheckout-bdogtouchall.cssgitadd.gitcommit-m'addall.css' 此時他發現原本的index.html檔案標題打錯了,進行了修改: 123456789dog

並提交了commit紀錄: 123gitadd.gitcommit-m'editindex.html>title' 假設又有一位工程人員開了一個分支並修復title這個問題: 123456789gitcheckoutmastergitcheckout-bcat...editindex.htmltitlegitadd.gitcommit-m'editindex.html>title' 讓我們來看目前的日誌: 你會發現在dog分支與cat分支同時修改了index.html檔案的標題,這邊要注意,並不是修改同一份檔案就會發生衝突,而是修改同一份檔案的同一行代碼才會發生衝突,基本上Git有自己判定的標準,讓我們繼續來看衝突是如何發生的: 123gitcheckoutcatgitmergedog 此時會跳出合併發生衝突的警告: 此時千萬不要慌張,讓我們先用gitstatus壓壓驚: 從上面可以得知,目前all.css已經被提交至索引區,代表這一個檔案沒有發生衝突,而index.html這個檔案就不一樣了,出現了Unmergedpaths的狀態,且提示bothmodified字樣,代表兩個分支同時修改到了同份檔案的同行代碼,這時候Git提示可執行以下命令還原到未合併前的狀態: 1gitmerge--abort 這很明顯是半途而廢的行為!遇到問題,解決它不就行了?讓我們先打開發生衝突的這一個檔案: 12345678910111213<<<<<<cat=======dog>>>>>>>dog

神奇的事情發生了!發生衝突的這個檔案居然出現了奇怪的符號,其實這是Git用來告訴我們何處發生了衝突,上半部是HEAD,也就是請求合併的cat分支,中間是分隔線,接下是dog分支的內容,這時請去與dog分支的人討論究竟該用誰的修改,假設我們要用dog分支的人修改好了,請把cat內容與其餘標記都給它刪除: 123456789dog

在這邊補充一點,如果你的編輯器是VSCode,它會有選擇的提示喔!挺方便的,效果如下: 修改完後,老樣子,目前這個檔案還是存在於工作目錄,將它提交至索引區吧: 1gitadd. 這時我們來看狀態長什麼樣子: 你會發現提示改變了,告訴我們所有衝突都已被修復,但此時還沒有結束喔,我們目前還是處於索引區,使用以下指令提交至本地數據庫: 1gitcommit 這時你可能會想,為什麼沒有加-m參數呢?事實上,在這種情況下,我習慣使用預設的訊息,如果你跟我一樣單純使用gitcommit,此時會跳出預設編輯器請你輸入提交訊息,而分支衝突本身就有預設的訊息內容了,也就代表直接關閉視窗即可,但如果你想要自訂訊息內容,那就照往常的加入-m'message'即可,這時候讓我們來看究竟合併成功了沒: 大功告成!分支已被成功合併,事實上,本地分支發生衝突的機率確實是挺高的,但只要跑過一次流程,就沒什麼好害怕的了,接下來進入到遠端分支合併衝突的部分。

遠端分支合併衝突在這邊我們需要先釐清遠端數據庫是怎麼處理我們的上傳檔案的,你可能會認為遠端是以“更新”的方式進行處理,但這個觀念是錯的,事實上遠端與本地端同樣都是使用合併的方式處理檔案,這也就導致可能發生與本地端相同的合併衝突問題。

請先在GitHub隨便開一個遠端數據庫,並將本地端內容推上去: [email protected]:awdr74100/conflict-demo.gitgitpush-uorigin--all 此時的日誌應該為: 在這邊補充一個指令: 1gitcommit--amend-m'mergedogbranch' 這個指令主要可用來修改最後一次提交的commit訊息,假設你不小心在提交commit時打錯字,這個指令就很用好,但不建議使用在以推至遠端數據庫的commit節點,必定會發生衝突: 你會發現原本的6aa88f4節點目前只剩遠端的cat分支指著,本地的cat分支反而指向了一個全新的commit節點,這邊你可能會有所誤會,修改訊息對於Git來說也算是一次全新的commit紀錄,但假設你是使用在本地尚未推至遠端的commit節點,原有的6aa88f4應該是會被“隱藏”才對,使用gitlog是看不到這一個節點的,上面為什麼看的到是因為遠端的cat分支指著,才導致這個節點被顯示出來。

此時如果將本地推至遠端,就會產生所謂的遠端合併分支衝突: 對於遠端分支來說,應該是存在6aa88f4這一個節點的,但我們透過修改形成了一個全新的節點,原有的6aa88f4就被隱藏了,只要合併前的舊有紀錄有被更改的情形,就有可能發生衝突,因為版本對不上阿! 有沒有發現non-fast-forward字樣?這不就是之前介紹的取消快轉合併嗎?這也證實了遠端是以合併的方式處理推上來的檔案,讓我們先來看目前的檔案狀態: Git也直接跟你表明目前有衝突發生,請將遠端內容下載到本地端並進行合併,我們可以依造它指示的來做: 1gitpull 此時會跳出請你輸入此次合併提交的訊息: 再次執行gitpush: 這一次Push就成功了,之前我們有說過gitpull主要會將gitfetch的內容直接執行gitmerge,這也才導致直接跳出合併的訊息視窗,這樣子看起來是不是使用commit--amend挺麻煩的?我建議此命令不要用在以推至遠端的commit紀錄上,以免造成自己與夥伴的困擾。

跑過一次上面的流程你大概就知道怎麼修復遠端分支的衝突了,你也可以嘗試使用Fetch來跑上面的流程,之後再透過與本地端發生衝突的修復方法來解決這一個衝突,兩者的原理是一模一樣的。

GitHub保護機制如果你是乖乖依照上面方法去修復衝突,倒是不必動用到GitHub的保護機制,但如果你是使用以下指令可就麻煩了: 1gitpush-f 這個-f等同於--force,表示強制的意思,這個指令主要用在遠端分支發生衝突時,可以強迫上傳,並且覆蓋掉遠端的分支,你可以把它想像成最高權限的覆蓋動作,如果以我們剛才的例子來講,遠端發生衝突時,就可以直接使用這個指令,免去修復的困擾,但建議這個指令只用在自己身上,你可以想像,團隊裡有人沒有先知會大家就突然使用這個指令,此時會發生什麼事?回家吃自己吧! 也因為這個指令帶來的後果太過於可怕,像是GitHub網站就有提供所謂的保護機制,可以避免某個分支被ForcePush,以下為示範: 路徑:Settings>Branches>Branchprotection 點擊Addrule並挑選適合的選項: master分支已被保護: 這樣就完成囉,根據你挑選的保護選項,在每次推送前都會觸發,避免可能發生的可怕後果。

Git指令回顧1234567891011#還原至合併前狀態gitmerge--abort#修改最後一次commit訊息gitcommit--amend-m'message'#強制推送遠端分支gitpush-f#強制推送遠端分支(同上)gitpush--force 文章目錄 本站概要 1.前言2.筆記重點3.本地分支合併衝突4.遠端分支合併衝突5.GitHub保護機制6.Git指令回顧 Roya Front-EndWebDeveloper學會運用比學會使用還要來的有用 69 文章 9 分類 28 標籤 RSS GitHub E-Mail



請為這篇文章評分?