iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 17
1
Software Development

用樂高玩轉 GIT 版本控制系列 第 17

Day 17 - 能在一起無憂無慮多幸運,談 GIT 解衝突

之前的幾篇,談了 GIT 的 mergerebasecherry-pick提到的例子因為有先做過設計,所以合併或移動通常都會很順利,但實務中當共同使用儲存庫的使用者們一同變更同一個檔案,多少會遇到合併失敗的問題,但,應該要怎麼解決合併失敗呢?

先談談樂高

我們回到樂高積木的例子,我們現在有兩個版本的樂高手冊,預計要合而為一,其中 A 版本,在右邊放上一塊兩格的粉紅色積木如下:

A 版本樂高

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

B 版本樂高

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

誰上誰下

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

誰左誰右?

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

B 版本樂高

其實在這個案例中可以發現,最好的方法是:找到兩個版本的作者來討論,這塊積木應該要怎麼合併比較好

在原始碼上,怎麼發生衝突?

在原始碼的創作上,當兩個 commit,同時都在同一個原始碼區塊做了變更,在合併的時候,就會發生衝突;就如上面提到的樂高一樣,都在同一個格子做了變更。

舉例來說,同樣的從 master 開始,分離了兩個版本,分別為 apple 及 banana,同時都編輯了一個檔案 fruit.txt:
apple 分支的 fruit.txt 的檔案內容:

I am a apple.

Apple 分支

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

I am a banana.

Banana 分支

這時候要把兩個分支進行合併的動作,就會發生衝突。

當 merge 的時候發生衝突怎麼處理?

目前所在位置在 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,說明如何解決衝突等相關訊息。

編輯 commit message

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

完成 git merge 的衝突解決

當 rebase 的過程中發生衝突該怎麼處理?

目前所在位置在 apple 分支,執行 git rebase banana,CLI 的提示中,提醒 rebase 的過程中有發生衝突的情況,衝突的檔案為 fruit.txt,並且在訊息末段提醒:

  1. 衝突解決後,可以透過 git add 衝突檔案,而後執行 git rebase --continue 繼續 rebase。
  2. 略過這個 commit 的 rebase,執行:git rebase --skip
  3. 停止這次的 rebase,執行:git rebase --abort,執行完之後,就像什麼事情都還沒發生一樣。

rebase 發生衝突的訊息內容

解決衝突:

  1. 如上一個例子 merge 解衝突一樣,把有衝突的原始碼內容進行討論編輯之後,儲存內容。
  2. 進行 git add fruit.txt
  3. 使 rebase 的動作繼續進行:git rebase --continue
    進行 git add fruit.txt
  4. 解決衝突後,如前幾天所說的 apple 分支移動到 banana 分支之後。
    apple 分支移動到 banana 分支之後

衝突過程如執行 git rebase --skip

可以看到,原本的 「I am a apple.」commit 直接消失了,且目前 apple 分支跟 banana 分支指在同一個 commit 上。

且目前 apple 分支跟 banana 分支指在同一個 commit 上

總結:

在 GIT 的介面上訊息幾乎都會提醒解決衝突的方法,當發生衝突的時候,只要仔細的查看訊息,按照訊息提示內容進行,也就可以完成解衝突的程序。比較重要的還是,解決衝突必須找到兩版本對應的重要關係人作討論,而後再進行發生衝突的原始碼編輯。


上一篇
Day 16 - 把製作步驟歷程整理好讓人更好理解,談 git rebase -i 互動模式
下一篇
Day 18 - 讓我們一起來,談把儲存庫發佈到網路上 Push Remote
系列文
用樂高玩轉 GIT 版本控制30

1 則留言

0
jjlayyl
iT邦新手 5 級 ‧ 2019-10-23 11:08:40

嗨,這邊我有一個疑問想要請教。
在最後一個例子中使用了git rebase --skip,進行略過commit的動作
為什麼會指向同一個hash code呢?

墨嗓 iT邦新手 3 級 ‧ 2019-10-23 11:57:01 檢舉

我在 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。

jjlayyl iT邦新手 5 級 ‧ 2019-10-23 12:07:41 檢舉

這樣我想到另外一個問題,如此一來 apple這個分支所變更的內容不就不見了。

因為apple指向bananafe8a348 hash code 上。

如此一來不論我切到apple分支或是banana分支應該都是看到一樣的內容。

墨嗓 iT邦新手 3 級 ‧ 2019-10-23 12:31:19 檢舉

對。無論你切到 apple 或 banana 分支上,都可以看到一樣的內容。但接著,你新增 commit 到儲存庫上,會是新增到你所處的分支上。

我要留言

立即登入留言