之前的幾篇,談了 GIT 的 merge、rebase 及 cherry-pick提到的例子因為有先做過設計,所以合併或移動通常都會很順利,但實務中當共同使用儲存庫的使用者們一同變更同一個檔案,多少會遇到合併失敗的問題,但,應該要怎麼解決合併失敗呢?
我們回到樂高積木的例子,我們現在有兩個版本的樂高手冊,預計要合而為一,其中 A 版本,在右邊放上一塊兩格的粉紅色積木如下:

B 版本,同樣的也在右邊放上一塊兩格的蘋果綠色積木,如下:

現在要把 A 版本及 B 版本合併在一起,請問,這樣的步驟合併有幾種可能?應該怎麼合併?方案一:把兩個重疊在一起的樂高,就繼續疊在一起,但誰上誰下?

方案二:把兩個樂高併排在一起,但誰左誰右?

方案三:只保留其中一個步驟的積木,但留粉紅色還是蘋果綠色?

其實在這個案例中可以發現,最好的方法是:找到兩個版本的作者來討論,這塊積木應該要怎麼合併比較好。
在原始碼的創作上,當兩個 commit,同時都在同一個原始碼區塊做了變更,在合併的時候,就會發生衝突;就如上面提到的樂高一樣,都在同一個格子做了變更。
舉例來說,同樣的從 master 開始,分離了兩個版本,分別為 apple 及 banana,同時都編輯了一個檔案 fruit.txt:
apple 分支的 fruit.txt 的檔案內容:
I am a apple.

banana 分支的 fruit.txt 的檔案內容:
I am a banana.

這時候要把兩個分支進行合併的動作,就會發生衝突。
目前所在位置在 apple 分支,執行 git merge banana,CLI 的提示中,出現了 merge 的過程中有發生衝突的情況,衝突的檔案為 fruit.txt。

這時候透過編輯工具,如 vim 進入編輯 fruit.txt,可以看到畫面中提示,呈現 <<<<<<< HEAD 一直到 ======= 所包圍起來的區段,為目前所在的 HEAD 變更的內容。而從 ======= 直到 >>>>>>> banana,所提示的,則是合併來源 banana 分支的內容。

解決衝突的方法,除了上面的例子提到的,要先找兩個版本的關係人來討論,應該怎麼處理外,討論後,只需要把 <<<<<<< HEAD、=======、>>>>>>> banana 等標示符號移除,並且把原始碼修改為討論後的樣子,儲存,離開編輯器,即可完成衝突修改。假設修改後的內容如下:

把發生衝突的檔案做對應的修改之後,必須要跟 GIT 告知,已經完成,讓 merge 的動作繼續進行,這時候有兩個步驟,一、再把剛剛修改後的檔案加入到舞台區(stage area)。

二、進行 git commit 編輯 commit message,說明如何解決衝突等相關訊息。

兩個步驟完成後,即完成 git merge 的衝突解決。

目前所在位置在 apple 分支,執行 git rebase banana,CLI 的提示中,提醒 rebase 的過程中有發生衝突的情況,衝突的檔案為 fruit.txt,並且在訊息末段提醒:
git add 衝突檔案,而後執行 git rebase --continue 繼續 rebase。git rebase --skip
git rebase --abort,執行完之後,就像什麼事情都還沒發生一樣。
git add fruit.txt
git rebase --continue
git rebase --skip:可以看到,原本的 「I am a apple.」commit 直接消失了,且目前 apple 分支跟 banana 分支指在同一個 commit 上。

在 GIT 的介面上訊息幾乎都會提醒解決衝突的方法,當發生衝突的時候,只要仔細的查看訊息,按照訊息提示內容進行,也就可以完成解衝突的程序。比較重要的還是,解決衝突必須找到兩版本對應的重要關係人作討論,而後再進行發生衝突的原始碼編輯。
嗨,這邊我有一個疑問想要請教。
在最後一個例子中使用了git rebase --skip,進行略過commit的動作
為什麼會指向同一個hash code呢?
我在 apple 分支上執行 git rebase banana 可以想像把 apple 上的分支與 banana 共同起點起的 commit ,疊到 banana 上。
apple 分支目前的 commit 為 aa4f23c,而 banana 分支上的 commit 為 fe8a348。在 apple 上執行 git rebase banana 也就是把 aa4f23c 基於 fe8a348 開始堆疊。在開始疊 aa4f23c 的時候發生衝突,我們選擇 skip 跳過,因此 apple 的分支會留在 fe8a348 上,所以會 指向同一個 hash code。
這樣我想到另外一個問題,如此一來 apple這個分支所變更的內容不就不見了。
因為apple指向banana的fe8a348 hash code 上。
如此一來不論我切到apple分支或是banana分支應該都是看到一樣的內容。
對。無論你切到 apple 或 banana 分支上,都可以看到一樣的內容。但接著,你新增 commit 到儲存庫上,會是新增到你所處的分支上。