merge 與 rebase 的區別
merge
現在假設我們有一個主分支 master 及一個開發分支 deve,倉庫歷史就像這樣:
初始倉庫歷史
現在如果在 master 分支上 git merge deve
:Git 會自動根據兩個分支的共同祖先即 e381a81
這個 commit 和兩個分支的最新提交即 8ab7cff
和 696398a
進行一個三方合併,然後將合併中修改的內容生成一個新的 commit,即下圖的 78941cb
merge 合併圖
rebase
rebase 是什麼情況呢?還是一個初始的倉庫歷史圖:
rebase初始倉庫歷史
如果是在 master 分支上 git rebase deve
:Git 會從兩個分支的共同祖先 3311ba0
開始提取 master 分支(當前所在分支)上的修改,即 85841be
、a016f64
與 e53ec51
,再將 master 分支指向 deve 的最新提交(目標分支)即 35b6708
處,然後將剛剛提取的修改依次應用到這個最新提交後面。操作會捨棄 master 分支上提取的 commit,同時不會像 merge 一樣生成一個合併修改內容的 commit,相當於把 master 分支(當前所在分支)上的修改在 deve 分支(目標分支)上原樣複製了一遍,操作完成後的版本歷史就像這樣:
rebase 合併圖
可以看見 master 分支從 deve 分支最新提交 35b6708
開始依次提交了自己的三個 commit(由於是提取修改後重新依次提交,故 commit 的 hash 碼與上面的85841be
、a016f64
、e53ec51
不同)
rebase -i
rebase 操作加上 -i
選項可以更直觀的看見被提取的 commit 信息。
仍然在 master 分支上 rebase deve 分支,不過這次要加上 -i
選項,即 git rebase -i deve
,然後我們可以得到這樣一個文本信息框
rebase -i信息
- A 區域內的信息說明了這次 rebase 操作提取了哪些 commit 記錄(
f9a7673
與edb2ba2
),會連接到目標分支的哪個 commit (9c86a5c
)後面。可以根據 B 區域中的命令說明修改pick
爲其他命令,對該次提取出來的 commit 做額外的操作 - B 區域內說明了本次 rebase 操作可以選用的命令
- 通過
:wq
保存退出後,就會按照剛剛在 A 區域內設定的命令處理 commit 並 rebase。
衝突處理策略的不同
- merge 遇見衝突後會直接停止,等待手動解決衝突並重新提交 commit 後,才能再次 merge
- rebase 遇見衝突後會暫停當前操作,開發者可以選擇手動解決衝突,然後
git rebase --continue
繼續,或者--skip
跳過(注意此操作中當前分支的修改會直接覆蓋目標分支的衝突部分),亦或者--abort
直接停止該次 rebase 操作
merge --no-ff
與 merge --ff-only
的區別
上面對 merge 的講述都是基於其默認操作即 --no-ff
(git merge xxx
= git merge --no-ff xxx
)的說明,但是 merge 還有一種常用的選項 --ff-only
,那麼這兩種有什麼區別呢?
--no-ff
是 merge 的默認操作,三方合併並提交修改;而 --ff-only
會判斷當前分支可否根據目標分支快速合併,就像下面這樣
快速合併
此時 deve 分支就可與 master 分支快速合併。
在 deve 分支上 git merge --ff-only master
,便得到合併完成後的版本歷史圖
快速合併完成
可以發現 --ff-only
生成的歷史記錄和 rebase 十分相似,但是本質上 --ff-only
仍然是合併操作,但 rebase 並沒有做合併,僅僅是提取修改到目標分支後面。
總結:選擇 merge 還是 rebase?
- merge 是一個合併操作,會將兩個分支的修改合併在一起,默認操作的情況下會提交合並中修改的內容
- merge 的提交歷史忠實地記錄了實際發生過什麼,關注點在真實的提交歷史上面
- rebase 並沒有進行合併操作,只是提取了當前分支的修改,將其複製在了目標分支的最新提交後面
- rebase 的提交歷史反映了項目過程中發生了什麼,關注點在開發過程上面
- merge 與 rebase 都是非常強大的分支整合命令,沒有優劣之分,使用哪一個應由項目和團隊的開發需求決定
- merge 和 rebase 還有很多強大的選項,可以使用
git help <command>
查看
最後:一些注意點
- 使用 merge 時應考慮是採用
--no-ff
默認操作,生成一個對回顧提交歷史並不友好的合併記錄,還是採用--ff-only
方式 - rebase 操作會丟棄當前分支已提交的 commit,故不要在已經 push 到遠程,和其他人正在協作開發的分支上執行 rebase 操作
- 與遠程倉庫同步時,使用 pull 命令默認進行了
git fetch + git merge --no-ff
兩個操作,可以通過加上--rebase
命令將 fetch 後的 merge 操作改爲 rebase 操作,或者僅僅 'git fetch remoteName',然後才思考採取哪種整合策略git merge(or rebase) origin/master
- 開發與 commit 時注意自己此時在哪個分支上
- 當有修改未 commit 時,不能進行 rebase 操作,此時可以考慮先用
git stash
命令暫存
作者:不在服務區
鏈接:http://www.jianshu.com/p/c17472d704a0
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。