在使用 Git 的進行代碼版本控制的時候,往往會發現在 log 中出現 "Merge branch 'master' of ..." 這句話,如下圖所示。日志中記錄的一般為開發過程中對代碼的改動信息,如果出現過多例如上述描述的信息會造成日志的污染。
閱讀了一些外文的博客,下面就來一探究竟。
產生原因分析
當多人合作開發一個項目時,本地倉庫落后於遠程倉庫是一個非常正常的事情,可參考下圖。
A-B-C(master) \ D(origin/master)
具體情境如下:
- 我當前拉取的遠端版本為
B
,此時修改了代碼,並在本地倉庫 commit 一次,但並未 push 到遠端倉庫。 - 另一位開發者在
B
的基礎上,同樣 commit 了一次並 push 到遠端倉庫。那么這個時候,我再 push 自己的代碼就會發生錯誤,如下。
To github.com:maoqyhz/usegit.git ! [rejected] master -> master (fetch first) error: failed to push some refs to 'git@github.com:maoqyhz/usegit.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.
這個時候我們會選擇,先 pull,再 push。Ok,push 成功,但是此時我們查看 log 就會發現除了我們自己提交的那條日志之外,會多出一條 "Merge branch 'master' of ..."。
那么,為什么會出現這種現象呢?其實是與 Git 的工作原理有關,對 Git 比較了解的人應該會知道,無論是 pull
、push
亦或是 merge
操作,其實背后都是有很多的不同的模式的。
在進行 pull 操作的同時,其實就是 fetch+merge 的一個過程。我們從 remote 分支中拉取新的更新,然后再合並到本地分支中去。
- 如果 remote 分支超前於本地分支,並且本地分支沒有任何 commit 的,直接從 remote 進行 pull 操作,默認會采用
fast-forward
模式,這種模式下,並不會產生合並節點,也就是說不會產生多余的那條 log 信息 - 如果想之前那樣,本地先 commit 后再去 pull,那么此時,remote 分支和本地會分支會出現分叉,這個時候使用 pull 操作拉取更新時,就會進行分支合並,產生合並節點和 log 信息。這兩種狀態分別如下圖所示:
# fast-forword A-B-D(origin/master) \ C'(master) # merge A-B-C-E(master) \ / D(origin/master)
如何避免
為了去除自動生成的 log 信息,有以下幾種解決方案:
- 如果你使用的是 Git Bash,直接使用
git pull --rebase(即git pull -r)
。如果拉取不產生沖突,會直接 rebase,不會產生分支合並操作,如果有沖突則需要手動 fix 后,自行合並。 - 如果使用的是 GUI,例如 TortoiseGit,可以先 fetch,再手動 rebase 就可以了。
關於 rebase 和 merge
關於什么時候使用 rebase,什么時候使用 merge,開發者總結了幾條規則:
- 從 remote 分支拉取更新到本地時,使用 rebase。
- 當完成 bug 修復或新功能時,使用 merge 將子分支合並到主分支。
- 沒有人應該 rebase 一根共享的分支。
有關這兩者具體的操作,可以參考我在文章最后列出的博客。