5.1 代码合并:Merge、Rebase的选择
BY 童仲毅(geeeeeeeeek@github)
這是一篇在原文(BY atlassian)基礎上演繹的譯文。除非另行注明,頁面上所有內容采用知識共享-署名(CC BY 2.5 AU)協議共享。
git rebase?這個命令經常被人認為是一種Git巫術,初學者應該避而遠之。但如果使用得當的話,它能給你的團隊開發省去太多煩惱。在這篇文章中,我們會比較git rebase和類似的git merge命令,找到Git工作流中rebase的所有用法。
概述
你要知道的第一件事是,git rebase?和git merge?做的事其實是一樣的。它們都被設計來將一個分支的更改并入另一個分支,只不過方式有些不同。
想象一下,你剛創建了一個專門的分支開發新功能,然后團隊中另一個成員在master分支上添加了新的提交。這就會造成提交歷史被Fork一份,用Git來協作的開發者應該都很清楚。
現在,如果master中新的提交和你的工作是相關的。為了將新的提交并入你的分支,你有兩個選擇:merge或rebase。
Merge
將master分支合并到feature分支最簡單的辦法就是用下面這些命令:
git checkout feature git merge master或者,你也可以把它們壓縮在一行里。
git merge master featurefeature分支中新的合并提交(merge commit)將兩個分支的歷史連在了一起。你會得到下面這樣的分支結構:
Merge好在它是一個安全的操作。現有的分支不會被更改,避免了rebase潛在的缺點(后面會說)。
另一方面,這同樣意味著每次合并上游更改時feature分支都會引入一個外來的合并提交。如果master非?;钴S的話,這或多或少會污染你的分支歷史。雖然高級的git log?選項可以減輕這個問題,但對于開發者來說,還是會增加理解項目歷史的難度。
Rebase
作為merge的替代選擇,你可以像下面這樣將feature分支并入master分支:
git checkout feature git rebase master它會把整個feature分支移動到master分支的后面,有效地把所有master分支上新的提交并入過來。但是,rebase為原分支上每一個提交創建一個新的提交,重寫了項目歷史,并且不會帶來合并提交。
rebase最大的好處是你的項目歷史會非常整潔。首先,它不像git merge?那樣引入不必要的合并提交。其次,如上圖所示,rebase導致最后的項目歷史呈現出完美的線性——你可以從項目終點到起點瀏覽而不需要任何的Fork。這讓你更容易使用git log?、git bisect?和gitk?來查看項目歷史。
不過,這種簡單的提交歷史會帶來兩個后果:安全性和可跟蹤性。如果你違反了Rebase黃金法則,重寫項目歷史可能會給你的協作工作流帶來災難性的影響。此外,rebase不會有合并提交中附帶的信息——你看不到feature分支中并入了上游的哪些更改。
交互式的rebase
交互式的rebase允許你更改并入新分支的提交。這比自動的rebase更加強大,因為它提供了對分支上提交歷史完整的控制。一般來說,這被用于將feature分支并入master分支之前,清理混亂的歷史。
把-i?傳入git rebase?選項來開始一個交互式的rebase過程:
git checkout feature git rebase -i master它會打開一個文本編輯器,顯示所有將被移動的提交:
pick 33d5b7a Message for commit #1 pick 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3這個列表定義了rebase將被執行后分支會是什么樣的。更改pick?命令或者重新排序,這個分支的歷史就能如你所愿了。比如說,如果第二個提交修復了第一個提交中的小問題,你可以用fixup?命令把它們合到一個提交中:
pick 33d5b7a Message for commit #1 fixup 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3保存后關閉文件,Git會根據你的指令來執行rebase,項目歷史看上去會是這樣:
忽略不重要的提交會讓你的feature分支的歷史更清晰易讀。這是git merge?做不到的。
Rebase的黃金法則
當你理解rebase是什么的時候,最重要的就是什么時候?不能?用rebase。git rebase?的黃金法則便是,絕不要在公共的分支上使用它。
比如說,如果你把master分支rebase到你的feature分支上會發生什么:
這次rebase將master分支上的所有提交都移到了feature分支后面。問題是它只發生在你的代碼倉庫中,其他所有的開發者還在原來的master上工作。因為rebase引起了新的提交,Git會認為你的master分支和其他人的master已經分叉了。
同步兩個master分支的唯一辦法是把它們merge到一起,導致一個額外的合并提交和兩堆包含同樣更改的提交。不用說,這會讓人非常困惑。
所以,在你運行git rebase?之前,一定要問問你自己“有沒有別人正在這個分支上工作?”。如果答案是肯定的,那么把你的爪子放回去,重新找到一個無害的方式(如git revert)來提交你的更改。不然的話,你可以隨心所欲地重寫歷史。
強制推送
如果你想把rebase之后的master分支推送到遠程倉庫,Git會阻止你這么做,因為兩個分支包含沖突。但你可以傳入--force?標記來強行推送。就像下面一樣:
# 小心使用這個命令! git push --force它會重寫遠程的master分支來匹配你倉庫中rebase之后的master分支,對于團隊中其他成員來說這看上去很詭異。所以,務必小心這個命令,只有當你知道你在做什么的時候再使用。
僅有的幾個強制推送的使用場景之一是,當你在想向遠程倉庫推送了一個私有分支之后,執行了一個本地的清理(比如說為了回滾)。這就像是在說“哦,其實我并不想推送之前那個feature分支的。用我現在的版本替換掉吧?!蓖瑯?#xff0c;你要注意沒有別人正在這個feature分支上工作。
工作流
rebase可以或多或少應用在你們團隊的Git工作流中。在這一節中,我們來看看在feature分支開發的各個階段中,rebase有哪些好處。
第一步是在任何和git rebase?有關的工作流中為每一個feature專門創建一個分支。它會給你帶來安全使用rebase的分支結構:
本地清理
在你工作流中使用rebase最好的用法之一就是清理本地正在開發的分支。隔一段時間執行一次交互式rebase,你可以保證你feature分支中的每一個提交都是專注和有意義的。你在寫代碼時不用擔心造成孤立的提交——因為你后面一定能修復。
調用git rebase?的時候,你有兩個基(base)可以選擇:上游分支(比如master)或者你feature分支中早先的一個提交。我們在“交互式rebase”一節看到了第一種的例子。后一種在當你只需要修改最新幾次提交時也很有用。比如說,下面的命令對最新的3次提交進行了交互式rebase:
git checkout feature git rebase -i HEAD~3通過指定HEAD~3作為新的基提交,你實際上沒有移動分支——你只是將之后的3次提交重寫了。注意它不會把上游分支的更改并入到feature分支中。
如果你想用這個方法重寫整個feature分支,git merge-base?命令非常方便地找出feature分支開始分叉的基。下面這段命令返回基提交的ID,你可以接下來將它傳給git rebase:
git merge-base feature master交互式rebase是在你工作流中引入git rebase?的的好辦法,因為它只影響本地分支。其他開發者只能看到你已經完成的結果,那就是一個非常整潔、易于追蹤的分支歷史。
但同樣的,這只能用在私有分支上。如果你在同一個feature分支和其他開發者合作的話,這個分支是公開的,你不能重寫這個歷史。
用帶有交互式的rebase清理本地提交,這是無法用git merge?命令代替的。
將上游分支上的更改并入feature分支
在概覽一節,我們看到了feature分支如何通過git merge?或git rebase?來并入上游分支。merge是保留你完整歷史的安全選擇,rebase將你的feature分支移動到master分支后面,創建一個線性的歷史。
git rebase?的用法和本地清理非常類似(而且可以同時使用),但之間并入了master上的上游更改。
記住,rebase到遠程分支而不是master也是完全合法的。當你和另一個開發者在同一個feature分之上協作的時候,你會用到這個用法,將他們的更改并入你的項目。
比如說,如果你和另一個開發者——John——往feature分支上添加了幾個提交,在從John的倉庫中fetch之后,你的倉庫可能會像下面這樣:
就和并入master上的上游更改一樣,你可以這樣解決這個Fork:要么merge你的本地分支和John的分支,要不把你的本地分支rebase到John的分支后面。
注意,這里的rebase沒有違反Rebase黃金法則,因為只有你的本地分支上的commit被移動了,之前的所有東西都沒有變。這就像是在說“把我的改動加到John的后面去”。在大多數情況下,這比通過合并提交來同步遠程分支更符合直覺。
默認情況下,git pull?命令會執行一次merge,但你可以傳入--rebase?來強制它通過rebase來整合遠程分支。
用Pull Request進行審查
如果你將pull request作為你代碼審查過程中的一環,你需要避免在創建pull request之后使用git rebase。只要你發起了pull request,其他開發者能看到你的代碼,也就是說這個分支變成了公共分支。重寫歷史會造成Git和你的同事難以找到這個分支接下來的任何提交。
來自其他開發者的任何更改都應該用git merge?而不是git rebase?來并入。
因此,在提交pull request前用交互式的rebase進行代碼清理通常是一個好的做法。
并入通過的功能分支
如果某個功能被你們團隊通過了,你可以選擇將這個分支rebase到master分支之后,或是使用git merge?來將這個功能并入主代碼庫中。
這和將上游改動并入feature分支很相似,但是你不可以在master分支重寫提交,你最后需要用git merge?來并入這個feature。但是,在merge之前執行一次rebase,你可以確保merge是一直向前的,最后生成的是一個完全線性的提交歷史。這樣你還可以加入pull request之后的提交。
如果你還沒有完全熟悉git rebase,你還可以在一個臨時分支中執行rebase。這樣的話,如果你意外地弄亂了你feature分支的歷史,你還可以查看原來的分支然后重試。
比如說:
git checkout feature git checkout -b temporary-branch git rebase -i master # [清理目錄] git checkout master git merge temporary-branch總結
你使用rebase之前需要知道的知識點都在這了。如果你想要一個干凈的、線性的提交歷史,沒有不必要的合并提交,你應該使用git rebase?而不是git merge?來并入其他分支上的更改。
另一方面,如果你想要保存項目完整的歷史,并且避免重寫公共分支上的commit, 你可以使用git merge。兩種選項都很好用,但至少你現在多了git rebase這個選擇。
這篇文章是『git-recipes』的一部分,點擊目錄查看所有章節。
from:?https://github.com/geeeeeeeeek/git-recipes/wiki/5.1-%E4%BB%A3%E7%A0%81%E5%90%88%E5%B9%B6%EF%BC%9AMerge%E3%80%81Rebase%E7%9A%84%E9%80%89%E6%8B%A9
總結
以上是生活随笔為你收集整理的5.1 代码合并:Merge、Rebase的选择的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux下ll命令与ls -l
- 下一篇: mybatis深入理解(一)之 # 与