Merge和Rebase概念概述
rebase 和 merge 相似,但又不完全相同,本質上都是用來合并分支的命令,區別如下
- merge合并分支會多出一條merge commit記錄,而rebase不會
- merge的提交樹是非線性的,會有分叉,而rebase的提交樹是線性的(通過重寫提交歷史),它是一條直線;
- rebase可以將多個commit合并,而merge不支持這個功能
- merge合并操作很友好,因為它沒有破壞性。現存的分支歷史不會發生什么改變,而rebase提交是線性的,雖然便于理解提交歷史,卻需要付出兩種代價:安全性和可追溯性;
1、分支描述
請考慮這個場景,當你開始在一個專有的feature分支開發新的功能時,另一位團隊成員更新了main分支的內容。這將會造成一個分叉的提交歷史,接下來,我們將用merge和rebase來合并分支,并解釋它們之間的區別;
2、使用merge合并
最簡單的方法就是把main分支合并到feature分支:
# 先切換到 feature 分支
git checkout feature
# 將main分支的內容合并到 feature 分支
git merge main
或者用下面這樣的單行命令:
git merge feature main
這會在feature分支中創建一個合并提交,這次提交會連結兩個分支的提交歷史,在分支圖示結構中看起來像下面這樣:
合并操作很友好,因為它沒有破壞性。現存的分支歷史不會發生什么改變。只是新增了一個commit記錄(綠色帶*
號的就是合并記錄);
但是另一方面來說,這也意味著每當feature分支需要應用上游分支的更改時,都會在提交歷史上增加一個無關的提交歷史。如果main分支的更新非常活躍,這種操作也會對功能分支的提交歷史產生相當程度的污染。雖然通過復雜的git log命令可以減輕這種提交歷史的混亂現狀,但仍然會讓其他開發者對于提交歷史感到費解。
3、使用rebase合并
為了替代merge操作,你也可以把feature分支的提交歷史rebase到main分支的提交歷史頂端:
# 先切換到 feature 分支
git checkout feature
# 將main分支的內容合并到 feature 分支
git rebase main
這些操作會把feature分支的起始歷史放到main分支的最后一次提交之上,也達成了使用main分支中新代碼的目的。但是,相對于merge操作中新建一個合并提交,rebase操作會通過為原始分支的每次提交創建全新的提交,從而重寫原始分支的提交歷史(綠色帶*
號部分就是被重寫提交歷史)。
使用rebase操作的最大好處在于你可以讓項目提交歷史變得非常干凈整潔。首先,它消除了git merge操作所需創建的沒有必要的合并提交。其次,正如上圖所示,rebase會造就一個線性的項目提交歷史——也就是說你可以從feature分支的頂部開始向下查找到分支的起始點,而不會碰到任何歷史分叉。這在使用git log,git bisect以及gitk等命令時更簡單。
不過為了獲得這種便于理解的提交歷史,卻需要付出兩種代價:安全性和可追溯性。如果不能遵循rebase的黃金法則,重寫項目提交歷史會為協作工作流程帶來潛在的災難性后果。再次,rebase操作丟失了合并提交能夠提供的上下文信息——所以你就無法知道功能分支是什么時候應用了上游分支的變更。
可交互式rebase (將多個commit合并為一個)
可交互式rebase讓你在把變更提交給其他分支之前有機會對提交記錄進行修改。這甚至比自動rebase操作更強大,畢竟它提供了對于分支提交歷史的完全掌控力。通常來說這一操作的使用場景在于合并功能分支到main分支之前,對于功能分支雜亂的提交記錄進行整理。
進行可交互式rebase操作,需要向git rebase命令傳遞i選項參數
git checkout feature
git rebase -i main
執行以上命令會打開一個文本編輯器,其中內容為分支中需要移動的所有提交列表:
pick 33d5b7a Message for commit #1
pick 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3
上面這樣的列表正表示了分支被rebase之后其歷史的長相。通過修改pick命令或者對提交歷史進行重新排序,你可以讓最終的提交歷史變成任何你希望的樣子。比如說,如果第二次提交修復了第一次提交的什么BUG,你可以使用fixup命令替代pick來把兩次提交壓縮在一起。
pick 33d5b7a Message for commit #1
fixup 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3
指令說明
- p,pick <提交>: 保留當前的提交內容。
- r,reword <提交>: 保留當前的提交內容。,但修改提交說明
- e,edit <提交>: 保留當前的提交內容,并允許你修改該提交的文件內容
- s,squash <提交> : 將當前提交與前一個提交合并,并將它們合并為一個提交,允許你合并提交信息。
- f,fixup <提交>: 與 squash 類似,但將當前提交合并到前一個提交中,并丟棄當前提交的提交信息。
- x,exec <命令>: 執行 shell 命令,可以在 rebase 過程中執行自定義操作
- b,break : 在當前提交之后創建一個新的提交點,將當前提交內容分成兩個獨立的提交。 (使用git rebase–continue’繼續變基)
- d,drop <提交>: 丟棄當前的提交內容,不保留該提交。
- l,label
當你保存并關閉這個文件之后,Git會根據你的調改結果執行rebase操作,根據上面的例子項目歷史會變成下圖這樣:
通過清除那些并不重要的提交歷史可以讓項目整體的歷史更易讀易懂。這一點是git merge操作所無法提供的。