Git 之 merge 與 rebase 的區別


首先看下面舉例:

  1. 假定 1-2 是現在的master分支狀態;
  2. 這個時候從 master 分支 checkout 出來一個 Dev01 分支;
  3. 然后 master 提交了 3、4,Dev01 提交了5、6、7;
  4. 這個時候 master 分支狀態就是 1-2-3-4 ,Dev01 分支狀態變成 1-2-5-6-7
  5. 如果在 Dev01 分支上,用 rebase master,Dev01 分支狀態就成了 1-2-3-4-5-6-7
  6. 如果在 Dev01 分支上,用 merge:

1-2-5-6-7-8

........ |3-4|

    7.會出來一個 8,這個 8 的提交就是把 3-4 合進來的提交。

2者區別:

假設當前從 master 分別切出了兩個分支:learn-rebase 和 learn-merge,它倆分別都進行了兩次 commit,我們希望把兩個分支的內容都合並到 master 上

clipboard.png

Merge(合並)

fast-forward

使用 merge 可以合並多個歷史記錄的流程。

首先我們合並 learn-merge 分支,只看藍色的部分,learn-merge 分支與 master 分支的最新 commit 在同一條線上,這說明 learn-merge 分支的歷史記錄包含 master 分支所有的歷史記錄,那么這個合並是非常簡單的。只需要通過把 master 分支的位置移動到 learn-merge 的最新分支上,Git 就會合並。這樣的合並被稱為 fast-forward(快進)合並。(fast-forward 模式下是不可能出現沖突的)

操作如下:

➜  git branch
  learn-merge
  learn-rebase
* master
➜  git merge learn-merge
Updating 594a6c9..c2b4ae3
Fast-forward
 myfile.txt | 2 ++
 1 file changed, 2 insertions(+)

結果如下:

clipboard.png

非 fast-forward

接下來需要合並 learn-rebase 分支,此時可以看到 master 的最新 commit 已經和 learn-merge 保持一致了,與 learn-rebase 分支不在一條直線上,那此時就不能進行 fast-forward 合並了。

在這種情況下使用 merge 方法,可以合並兩個修改並生成一個新的提交。(此時可能會出現沖突的情況,因為 learn-rebase 分支可能與 learn-merge 分支修改同一個文件)

操作如下:

➜  git merge learn-rebase
Auto-merging myfile.txt
CONFLICT (content): Merge conflict in myfile.txt Automatic merge failed; fix conflicts and then commit the result. ➜ vim myfile.txt # 解決沖突 ➜ git add myfile.txt ➜ git commit -m 'fix conflicts' [master 2a6899b] fix conflicts

結果如下:

clipboard.png

設置 non fast-forward

執行合並時,如果設定了non fast-forward 選項,即使在能夠 fast-forward 合並的情況下也會生成新的提交並合並。

clipboard.png

設置方法:添加 -no-ff 選項

rebase(變基)

與 merge 會保留修改內容的歷史記錄不同,rebase 是在原有提交的基礎上將差異內容反映進去。讓我們回到 merge learn-rebase 之前的樣子。

clipboard.png

rebase 的步驟分為兩步,第一步將 learn-rebase 分支的歷史記錄添加在 master 分支的后面。這個時候可能會有沖突,當出現沖突時,解決沖突后的提交不是使用 commit 命令,而是執行 rebase 命令指定 --continue 選項。若要取消 rebase,指定 --abort 選項。

clipboard.png

操作如下:

➜  git checkout learn-rebase
Switched to branch 'learn-rebase' ➜ git rebase master First, rewinding head to replay your work on top of it... Applying: add rebase Using index info to reconstruct a base tree... M myfile.txt Falling back to patching base and 3-way merge... Auto-merging myfile.txt CONFLICT (content): Merge conflict in myfile.txt error: Failed to merge in the changes. Patch failed at 0001 add rebase The copy of the patch that failed is found in: .git/rebase-apply/patch When you have resolved this problem, run "git rebase --continue". If you prefer to skip this patch, run "git rebase --skip" instead. To check out the original branch and stop rebasing, run "git rebase --abort". 1 merge 與 rebase 的差別 ➜ vim myfile.txt # 解決沖突 ➜ git add myfile.txt ➜ git rebase --continue Applying: add a new line

結果如下:

clipboard.png

然后再使用 fast-forward 合並即可

➜  git checkout master
Switched to branch 'master' ➜ git merge learn-rebase Updating 0b9e66f..1fc71b9 Fast-forward myfile.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)

對比 merge 和 rebase 最終的歷史記錄,可以發現 merge 保持了修改內容的歷史記錄,但是歷史記錄會很復雜;而 rebase 后的歷史記錄簡單,是在原有提交的基礎上將差異內容反映進去。

clipboard.png

rebase 黃金定律

奇妙的變基也並非完美無缺,要用它得遵守一條准則:

不要對在你的倉庫外有副本的分支執行變基。

變基操作的 實質是丟棄一些現有的提交,然后相應地新建一些內容一樣但實際上不同的提交。 如果你已經將提交推送至某個倉庫,而其他人也已經從該倉庫拉取提交並進行了后續工作,此時,如果你用 git rebase 命令重新整理了提交並再次推送,你的同伴因此將不得不再次將他們手頭的工作與你的提交進行整合,如果接下來你還要拉取並整合他們修改過的提交,事情就會變得一團糟。

merge vs rebase

到底該使用 merge 方式開發還是使用 rebase 方法開發是有爭議的

有一種觀點認為,倉庫的提交歷史即是 記錄實際發生過什么。 它是針對歷史的文檔,本身就有價值,不能亂改。 從這個角度看來,改變提交歷史是一種褻瀆,你使用謊言掩蓋了實際發生過的事情。 如果由合並產生的提交歷史是一團糟怎么辦? 既然事實就是如此,那么這些痕跡就應該被保留下來,讓后人能夠查閱。

另一種觀點則正好相反,他們認為提交歷史是 項目過程中發生的事。 沒人會出版一本書的第一版草稿,軟件維護手冊也是需要反復修訂才能方便使用。 持這一觀點的人會使用 rebase 及 filter-branch 等工具來編寫故事,怎么方便后來的讀者就怎么寫。

總的原則是,只對尚未推送或分享給別人的本地修改執行變基操作清理歷史,從不對已推送至別處的提交執行變基操作,這樣,你才能享受到兩種方式帶來的便利。

撤銷提交的不同

如果使用 merge 進行合並,可以使用 revert 命令對 merge 的內容進行撤銷操作(參考 revert),而使用 rebase 則不行(已經沒有 merge commit 了),而需要使用 rebase -i 對提交進行重新編輯(參考 交互式 rebase)。

轉自:時間被海綿吃了 https://segmentfault.com/a/1190000012897637

參考

 


免責聲明!

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



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