Git 小教室「在 Merge 之前想試試看有沒有衝突?」

當你開了一個新的 branch,然後做了幾個 commit,進度做得差不多之後,下一步就是準備使用 git merge 指令來進行合併。但你手邊的專案可能有好一陣子沒跟線上的同步,這個 merge 執行下去可能噴一堆的衝突要解決。

不久前在社群分享就有朋友問到,有沒有辦法可以在進行 merge 之前先看看會不會發生衝突發生?

除了直接合併下去,衝突再解掉或是再 git reset 回來就好的方法之外, git merge 指令目前並沒有類似 git commit --dry-run 的乾跑參數。

假設想要合併分支 cat,想做到乾跑效果可以這樣做:

$ git merge cat --no-commit --no-ff

根據 Git 的說明手冊,對於 --no-commit 參數的說明如下:

With --no-commit perform the merge but pretend the merge failed and do not autocommit, to give the user a chance to inspect and further tweak the merge result before committing.

這個參數會假裝這次的合併失敗,並且不會產生新的 commit,讓使用者有機會可以在 commit 前再做一些事。

而後面再加上 --no-ff 參數則是不希望 Git 使用 Fast Forward 方式合併,如果想了解 Fast Forward 是怎麼回事,歡迎參閱「為什麼我的分支都沒有『小耳朵』」章節。

那,就讓我們實際來操作一次:

目前專案裡有 mastercat 兩個分支,cat 分支是從 master 分出去的,狀態如下:

Git Merge Dryrun

$ git merge cat --no-commit --no-ff
Automatic merge went well; stopped before committing as requested

Good! 沒發生衝突,但也沒產生 Commit 或 Fast Forward。看一下目前的 Git 狀態:

$ git status
On branch master
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:

  new file:   cat1.html
  new file:   cat2.html

的確就停在 Commit 之前的狀態,cat1.htmlcat2.html 都被放到暫存區了。

要注意的是,這個地方如果沒有加上 --no-ff 的話,雖然不會產生 Commit,但還是因為 Fast forward 而完成合併。

剛剛這個例子太順利的,沒有每天在過年的,讓我們再來看一個會衝突的例子:

Git Merge Dryrun

在這個例子裡,payment 分支跟 member 因為剛好改到同一個檔案,所以 merge 應該會發生衝突:

$ git merge member --no-commit --no-ff
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

果然發生衝突了。看一下狀態:

$ git status
On branch payment
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Changes to be committed:

  new file:   member.html

Unmerged paths:
  (use "git add <file>..." to mark resolution)

  both modified:   index.html

回到過去

在上面這兩個範例中,git reset --merge 指令來回到合併之前的狀態,而在 Git 1.7.4 版本之後,可使用 git merge --abort 也可以跟上面這個指令一樣的效果:

$ git merge --abort

再看一下狀態:

$ git status
On branch payment
nothing to commit, working tree clean

這樣就回到合併之前的狀態了。

來看一下這個 --abort 參數的手冊說明:

The second syntax ("git merge --abort") can only be run after the merge has resulted in conflicts. git merge --abort will abort the merge process and try to reconstruct the pre-merge state.

簡單的說,如果合併沒有順利完成的話,這個參數可以讓專案會回到合併之前的狀態。

歡迎來聊聊

有任何跟 Git 有關的疑難雜症或是應用情境,都歡迎來信或留言,如果討論的篇幅夠多就會另外整理一篇文章供大家參考。