Git Step by Step – (8) Git的merge和rebase


前面一篇文章中提到了"git pull"等價於"git fetch"加上"git merge",然后還提到了pull命令支持rebase模式,這篇文章就介紹一下merge和rebase之間有什么差別。

由於我們主要是想看看merge跟rebase之間的區別,這里就是用本地倉庫的分支進行演示了。

merge

其實在介紹分支的那篇文章中已經介紹過了一些分支merge的內容,這里就進行一些補充和總結。

下面我們基於本地一個倉庫開始介紹,當前倉庫的分支情況如下:

其實,merge命令總結下來會有三種情況發生:

  1. merge命令不生效

    當目標分支是當前分支的祖先commit節點,也就是說當前分支已經是最新的了,在這種情況下merge命令沒有任何效果。

    在當前倉庫中,當我們把dev分支merge到master的時候,會得到"Already up-to-date."

  2. Fast-forward合並模式

    當前分支是目標分支的祖先commit節點時,會發生Fast-forward的merge,看下圖

    這時的對象模型就更新了,這里merge的操作只是把dev分支的HEAD引用進行更新,指向最新的commit對象

  3. 三方合並

    請參照"Git Step by Step – (5) Git分支(branch)"中分支合並的內容。如果沒有沖突,Git會幫我們完成分支的合並,如果有沖突,就需要我們手動解決沖突了。

Fast-forward合並模式

在前面我們看到了Fast-forward合並模式,這種合並模式很簡單,只是HEAD引用的更新。但是合並后,我們從"git log"中將看不到分支的信息。

在Git中,我們可以選擇在merge的時候禁止Fast-forward。現在,我們通過"git reset --hard HEAD~1"撤銷前面的Fast-forward合並。

然后,我們在merge命令中加上"--no-ff"進行合並操作。

cherry-pick

這里我們將插入介紹一個非常有用的命令,雖然它跟merge沒有什么關系。

在實際應用中,我們可能會經常碰到這種情況,在分支A上提交了一個更新,但是后來發現我們同樣需要在分支B上應用這個更新。那么這時cherry-pick就可以幫助你解決問題。

我們要做的就是通過"git reflog"找到A上那個更新的SHA1哈希值,然后切換到B分支上使用"git cherry-pick"。

當我們手動合並過沖突,然后繼續執行"cherry-pick"時候,Git會給出友好的交互界面,如果我們不需要更新這個提交的message,我們可以直接":wq"進行保存退出。到此,這個"cherry-pick"操作就成功了。

 

rebase

前面我們介紹了merge,現在來看看rebase。在merge的過程中,比較好的就是我們可以看到分支的歷史信息,但是,如果分支很多的時候,這個分支歷史可能就會變得很復雜了。如果我們使用rebase,那么提交的歷史會保持線性。

rebase的原理:先將當前分支的HEAD引用指向目標分支和當前分支的共同祖先commit節點,然后將當前分支上的commit一個個apply到目標分支上,apply完以后再將HEAD引用指向當前分支。是不是有點繞,下面我們看個實例。

下面就開始rebase的介紹,我們會基於master新建一個release-1.0的分支,並在該分支上提交一個更新。

這時,我們在release-1.0分支上執行"git rebase master",就會得到下面的對象關系圖。

根據rebase的工作原理進行分析:

  1. 把當前分支的HEAD引用指向"00abc3f"
  2. 然后將當前分支上的commit一個個apply到目標分支,這里就是把"ed53897"更新apply到master上;注意,如果沒有沖突,這里將直接生成一個新的更新
  3. 最后更新當前分支的HEAD引用執行最新的提交"8791e3b"

這個就是rebase操作,可以看到通過rebase操作的話,我們的commit歷史會一直保持線性。在這種情況下,當我們切換到master分支,然后進行"git merge release-1.0"分支合並時,master將會直接是Fast-forward合並模式,commit歷史也會是線性的。

當然rebase操作也會產生沖突,當一個沖突發生的時候,我們可以skip過當前的patch(一定要當心,不要隨便使用,以免丟失更新);也可以手動的解決沖突,然后繼續rebase操作

rebase交互模式

其實,rebase還有別的很強大功能,比如rebase交互模式,通過交互模式我們可以改變commit的信息,刪除commit,合並commit以及更改commit的順序等等。

假如我們現在想要更新一個commit的信息,我們就可以使用rebase交互模式,找到commit的哈希值,然后進入交互模式。


根據rebase的操作提示,我們選擇edit操作,然后保存退出。

這時,Git將會提示我們,是進行更改,還是可以繼續操作。這里我們通過"git commit --amend"進入編輯模式。

在編輯模式中對commit進行更新,然后保存退出,繼續rebase操作。

關於rebase交互模式的其他命令,這里就不做介紹了,有興趣的同學可以google一下。

 

總結

這篇文章主要對merge和rebase進行了介紹。對於最終的結果而言,rebase和merge是沒有區別的,不會發生rebase和merge導致最終更新不一致的情況。

rebase和merge的差別主要是:

  • rebase更清晰,因為commit歷史是線性的,但commit不一定按日期先后排,而是當前分支的commit總在后面(參考rebase原理)
  • merge之后commit歷史變得比較復雜,並且一定程度上反映了各個分支的信息,而且 commit按日期排序的。

大家可以根據自己的項目需求進行選擇使用哪種方式拉取遠程的更新。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM