之前的幾篇,談了 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 到儲存庫上,會是新增到你所處的分支上。