假設當前從 master 分別切出了兩個分支:learn-rebase 和 learn-merge,它倆分別都進行了兩次 commit,咱們但願把兩個分支的內容都合併到 master 上html
使用 merge 能夠合併多個歷史記錄的流程。git
首先咱們合併 learn-merge 分支,只看藍色的部分,learn-merge 分支與 master 分支的最新 commit 在同一條線上,這說明 learn-merge 分支的歷史記錄包含 master 分支全部的歷史記錄,那麼這個合併是很是簡單的。只須要經過把 master 分支的位置移動到 learn-merge 的最新分支上,Git 就會合並。這樣的合併被稱爲 fast-forward(快進)合併。(fast-forward 模式下是不可能出現衝突的)vim
操做以下:segmentfault
➜ git branch learn-merge learn-rebase * master ➜ git merge learn-merge Updating 594a6c9..c2b4ae3 Fast-forward myfile.txt | 2 ++ 1 file changed, 2 insertions(+)
結果以下:bash
接下來須要合併 learn-rebase 分支,此時能夠看到 master 的最新 commit 已經和 learn-merge 保持一致了,與 learn-rebase 分支不在一條直線上,那此時就不能進行 fast-forward 合併了。app
在這種狀況下使用 merge 方法,能夠合併兩個修改並生成一個新的提交。(此時可能會出現衝突的狀況,由於 learn-rebase 分支可能與 learn-merge 分支修改同一個文件)工具
操做以下:this
➜ 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
結果以下:spa
執行合併時,若是設定了non fast-forward 選項,即便在可以 fast-forward 合併的狀況下也會生成新的提交併合併。3d
設置方法:添加 -no-ff
選項
與 merge 會保留修改內容的歷史記錄不一樣,rebase 是在原有提交的基礎上將差別內容反映進去。讓咱們回到 merge learn-rebase 以前的樣子。
rebase 的步驟分爲兩步,第一步將 learn-rebase 分支的歷史記錄添加在 master 分支的後面。這個時候可能會有衝突,當出現衝突時,解決衝突後的提交不是使用 commit
命令,而是執行 rebase
命令指定 --continue
選項。若要取消 rebase,指定 --abort
選項。
操做以下:
➜ 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
結果以下:
而後再使用 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 後的歷史記錄簡單,是在原有提交的基礎上將差別內容反映進去。
奇妙的變基也並不是天衣無縫,要用它得遵照一條準則:
不要對在你的倉庫外有副本的分支執行變基。
變基操做的 實質是丟棄一些現有的提交,而後相應地新建一些內容同樣但實際上不一樣的提交。 若是你已經將提交推送至某個倉庫,而其餘人也已經從該倉庫拉取提交併進行了後續工做,此時,若是你用 git rebase 命令從新整理了提交併再次推送,你的同伴所以將不得再也不次將他們手頭的工做與你的提交進行整合,若是接下來你還要拉取並整合他們修改過的提交,事情就會變得一團糟。
到底該使用 merge 方式開發仍是使用 rebase 方法開發是有爭議的
有一種觀點認爲,倉庫的提交歷史便是 記錄實際發生過什麼。 它是針對歷史的文檔,自己就有價值,不能亂改。 從這個角度看來,改變提交歷史是一種褻瀆,你使用謊話掩蓋了實際發生過的事情。 若是由合併產生的提交歷史是一團糟怎麼辦? 既然事實就是如此,那麼這些痕跡就應該被保留下來,讓後人可以查閱。
另外一種觀點則正好相反,他們認爲提交歷史是 項目過程當中發生的事。 沒人會出版一本書的初版草稿,軟件維護手冊也是須要反覆修訂才能方便使用。 持這一觀點的人會使用 rebase 及 filter-branch 等工具來編寫故事,怎麼方便後來的讀者就怎麼寫。
總的原則是,只對還沒有推送或分享給別人的本地修改執行變基操做清理歷史,從不對已推送至別處的提交執行變基操做,這樣,你才能享受到兩種方式帶來的便利。
若是使用 merge 進行合併,可使用 revert 命令對 merge 的內容進行撤銷操做(參考 revert),而使用 rebase 則不行(已經沒有 merge commit 了),而須要使用 rebase -i
對提交進行從新編輯(參考 交互式 rebase)。