Git: 更新分支+解衝突

Git: 同一分支合併+解衝突

事情是這樣的…

A、B 兩位工程師皆修改同一隻檔案 hello_world.html 的同一行程式碼。

$ git push origin master
To github.com:cythilya/git_test.git
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to 'git@github.com:cythilya/git_test.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

來看 Source Tree 的線圖,這表示本地端的修改尚未提交至遠端,必須先把遠端的進度更新至本地端才行,因此 A 需要做更新的動作。

看 Source Tree 的線圖

備註:Source Tree 可以設定自動更新,就不需要手動打指令 git fetch(Settings > Advanced > 勾選 Automatically refresh)。

方法一:Rebase

承 Step 4,做完 git fetch 就來做 Rebase。Rebase 意即「重新定義分支的參考基準」。

$ git rebase origin/master

表示本地端的 master 將以本地的遠端分支 origin/master 為參考基準,此時本地分支 origin/master 已更新過,進度同遠端倉儲分支 master。

出現以下訊息。

First, rewinding head to replay your work on top of it...
Applying: 修改文章段落
Using index info to reconstruct a base tree...
M hello_world.html
Falling back to patching base and 3-way merge...
Auto-merging hello_world.html
CONFLICT (content): Merge conflict in hello_world.html
error: Failed to merge in the changes.
Patch failed at 0001 修改文章段落
The copy of the patch that failed is found in: .git/rebase-apply/patch

Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".

對於程式碼的修改,Git 是可以自動合併不同行的部份,但若是更改同一行的程式碼,就需要人工手動解決衝突——在這裡,由於檔案 hello_world.html 都被 A、B 兩人同時更改了同一行程式碼,自動合併失敗,因此需要做人工解衝突的動作。

在有衝突的檔案看到這樣的記號…

<<<<<<< HEAD
<p>How are you?</p>
=======
<p>Hello World (*´∀`)~♥</p>
>>>>>>> 修改文章段落

決定使用下方(即本次)的修改,刪除標記衝突的記號。

<p>Hello World (*´∀`)~♥</p>

修改好有衝突的檔案後,將變更標記為已解決,然後繼續 Rebase 的操作,讓 Git 重新計算 <sha-1> 的值。

$ git add hello_world.html
$ git rebase --continue

Applying: 修改文章段落

最後推到遠端上。

$ git push origin master

來看 Source Tree 的線圖,這表示成功推到遠端上,同步遠端分支 origin/master 和遠端倉儲分支 master 的進度。

看 Source Tree 的線圖

備註:以上歷程等同於 git pull --rebase; git push。意即,將遠端更新的內容抓下來後,用 Rebase 的方式合併,最後推到遠端上。看更多-git ready » pull with rebase

方法二:Merge

承 Step 4,做完 git fetch 就來做 Merge。

$ git merge

Auto-merging hello_world.html
CONFLICT (content): Merge conflict in hello_world.html
Automatic merge failed; fix conflicts and then commit the result.

手動解衝突,在有衝突的檔案看到這樣的記號…

<<<<<<< HEAD
<p>Hello World (*´∀`)~♥</p>
=======
<p>How are you?</p>
>>>>>>> XXXX.......

決定使用上方(即本次)的修改,刪除標記衝突的記號。

<p>Hello World (*´∀`)~♥</p>

將變更加入暫存區、提交、推至遠端。

$ git add hello_world.html
$ git commit -m "解決衝突"
$ git push origin master

備註:git fetch + git merge 等同於 git pull

來看 Source Tree 的線圖,這表示成功推到遠端上,同步遠端分支 origin/master 和遠端倉儲分支 master 的進度。

看 Source Tree 的線圖

注意,使用 git merge 會多一個合併的提交記錄,因此會有一個小耳朵(紅線部份)。

總結:Rebase vs Merge

Rebase

Rebase 很像是把本次修改直接貼到新的基準點的後面。 在解衝突方面,提交紀錄是一個一個 apply 後來解衝突,所以如果本次修改有很多的提交紀錄,就要做很多次 apply & 解衝突的動作。

在指令的使用上

$ git fetch
$ git rebase <remote>/<branch>
$ git push <remote> <branch>

說明

等同於

$ git pull —-rebase
$ git push <remote> <branch>

Merge

Merge 是將遠端的更新加入本次修改中,所以遠端的提交紀錄會在本次修改提交紀錄之後。並且會多一個合併的提交紀錄(小耳朵),用來記錄合併的來源與合併後的修改,如果沒有修改而只是選擇用哪一個版本,合併時的修改檔案就是空白。

在指令的使用上

$ git pull <remote> <branch>
$ git push <remote> <branch>

說明

等同於

$ git fetch
$ git merge <remote>/<branch>
$ git push <remote> <branch>

補充

推薦閱讀


git rebase git merge git Sourcetree