Git Merging vs. Rebasing(譯文)

littleBird發表於2018-01-31

原文地址

Git Merging vs. Rebasing(譯文)
git reabase有這樣一個聲譽,那就是初學者要遠離這個命令,但是當開發團隊能小心謹慎的使用它時,將會使我們的生活更加美麗。在這篇文章裡我們將深度比較git rebase和與之相關的git merge命令並且找到能將其納入我們的git工作流的潛在機會。

###概述 首先要明白的是git reabase命令和git merge命令解決了相同的問題,這兩個命令都是設計用來整合一個分支上的更改到另一個分支-只不過是用了不同的方式。 首先思考一下當你為一個工作分支新增新特性時發生了什麼,接下來另一個團隊成員通過提交commitg更新了master分支。這會產生分叉歷史記錄,任何使用Git作為協作工具的人都應該熟悉這一點。

Git Merging vs. Rebasing(譯文)
現在,假設master中的新提交與您正在處理的功能相關,為了合併這個新提交到你的工作分支,你有兩個選擇merging或者rebasing。 ###合併選項 最簡單的合併marster分支到本地分支的辦法就是使用下面的命令

git checkout feature
git merge master
複製程式碼

或者寫成一行

git merge master feature
複製程式碼

這樣將會在你的工作分支上建立一個merge commit將兩個分支的歷史聯絡在一起,分支結構如下圖所示: ![IMAGE](quiver-image-url/3464B87C2B36C0C3B8A72C5B623355C6.jpg =561x366) 合併操作相當完美因為他是無害操作,現有的分支沒有任何改變,這樣可以避免所有的rebasing隱患,(下面討論) 另一方面,這也意味著每次需要合併上游更改時,工作分支都會有一個無關的合併提交。 如果master分支改動非常頻繁,這會讓你的工作分支的提交記錄看起來非常的醜陋,儘管使用高階的git log選項可以緩解這個問題,但是這這可能會讓其他開發者難以理解專案的歷史。

###變基選項 作為合併的替代方法,您可以使用以下命令將工作分支重新繫結到master分支上:

git checkout feature
git rebase master
複製程式碼

這會將整個工作分支移到master分支的前面,有效的將所有新提交合併到master中。但是,相比於使用合併,變基操作通過為原始分支中的每個提交建立全新的提交來重新編寫專案歷史記錄。

Git Merging vs. Rebasing(譯文)
變基操作的一個好處就是能得到更清晰的專案提交歷史。第一,他消除了git merge所需的不必要的合併提交。第二,如上圖所示,變基操作可以產出完美的線性提交歷史記錄,你可以在沒有任何分叉的情況下沿著專案的開頭一直到最新的提交。這使得使用git loggit bisectgitk等命令輕鬆導航專案。 但是為了獲得簡介的歷史提交歷史,有兩個地方需要權衡:安全性和可追溯性,如果你不遵守變基操作的黃金法則,那麼重寫專案的歷史記錄對你的git工作流來說可能是災難性的。而且,變基操作會失去合併提交提供的上下文 - 你無法看到上游更改何時合併到你的工作分支中來。

###互動式的變基操作 互動變基讓你有機會在提交到新分支時更改提交,這比自動變基更加強大,因為它提供了對分支的提交歷史的完全控制,通常情況下,這是在合併到主幹分支之前清理混亂的歷史提交。 要開始互動式重新繫結會話,在git rebase命令後加上i選項

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,執行完成後提交歷史如下圖所示:

Git Merging vs. Rebasing(譯文)
消除這樣的微不足道的提交使得你的特性的歷史更容易理解。這是git合併根本做不到的事情。 ###變基的黃金法則 當你明白變基能幹什麼後,你要學習一件非常重要的事就是什麼時候不去使用它。 法則一:永遠不要再公共分支上用他 例如,想象一下當你將master分支變基到開發分支上時會發生什麼?
Git Merging vs. Rebasing(譯文)
變基操作將會把master分支上的提交移動到當前分支的最前面,問題是現在這個分支只存在於你自己的倉庫中。其他的開發者仍然在原來的master分支上工作,由於變基產生全新的提交,Git會認為你的主分支的歷史與其他人的不同。 同步兩個主分支的唯一方法是將它們合併回去,這將導致一個額外的合併提交和兩組提交包含相同的更改(原來的和來自你的reabae後的分支)不用說,這是一個非常混亂的情況。 所以,在你執行git rebase之前,總是問自己:“還有其他人在看這個分支嗎?”如果答案是肯定的,那就把手放在鍵盤上,開始思考一個非破壞性的方法來做出你的改變(例如, git revert命令)。 否則的話,您可以隨心所欲地重寫歷史記錄。 ###強制提交 如果你嘗試將變基後的master分支推送到遠端倉庫,Git將不允許你這樣子幹因為本地master現在和遠端倉庫的master分支衝突了。但是你可以再push命令後新增--force選項來強制提交。就像這樣:

# Be very careful with this command!
git push --force
複製程式碼

這個操作將會根據本地倉庫的master分支來重寫遠端的master分支,將導致專案中的其他成員感到困惑。所以執行這個操作前必須三思而後行。 你唯一應該強制推送的情況是,當你將私有特性分支推送到遠端倉庫之後,你執行了一次本地清理(例如,為了備份目的) 這就像是說,“糟糕,我不是真的想把這個原始版本的特性分支推出去。而是用後面的替代。 同樣重要的是,沒有人在原始分支上提交程式碼。 ###工作流程演練 變基操作可以將現有的Git工作流程整合到您的團隊所熟悉的程度,在本節中,我們將介紹變基在功能開發的各個階段可以提供的優勢。 任何利用git rebase的工作流程的第一步是為每個功能建立一個專用的分支。這將給你必要的分支結構,以安全利用rebasing:

Git Merging vs. Rebasing(譯文)
###本地清理 把rebasing融入工作流程的最好方法之一是清理本地真在開發的特性。通過定期執行互動式底圖,可以確保您的功能中的每個提交都是專注和有意義的。這可以讓你編寫你的程式碼,而不用擔心把它分解成孤立的提交 - 你可以在事後修復它。 當呼叫git rebase時,對於新的基礎有兩個選項:特性的父分支(例如master)或者在你的工作分支中之前的提交。我們在“互動式重新繫結”部分看到了第一個選項的示例。後面的選項是很好的,當你只需要修復最後幾個提交。例如,以下命令僅開始最後3次提交的互動式重新分頁。

git checkout feature
git rebase -i HEAD~3
複製程式碼

通過指定HEAD〜3作為新的基礎,你實際上並沒有移動分支 - 你只是互動地重寫它後面的3個提交。請注意,這不會將上游更改合併到功能分支中。

Git Merging vs. Rebasing(譯文)
如果你想用這種方法重寫整個工作分支,那麼git merge-base命令可以用來查詢工作分支的原始基礎。 以下內容返回原始基礎的提交ID,然後您可以將其傳遞給git rebase:

git merge-base feature master
複製程式碼

這種使用互動式重新繫結是將git rebase引入到工作流中的好方法,as it only affects local branches. 其他開發者唯一會看到的就是你的成品,這應該是一個乾淨,容易遵循的功能分支歷史。 但是,這隻能用於私人功能分支。如果你通過同一個功能分支與其他開發人員合作,該分支是公開的,n那麼你不允許重寫其歷史記錄。 沒有git merge的替代方案來清理本地提交的互動式rebase。

###將上游變更納入特性分支 在概述部分,我們看到了一個功能分支如何使用git mergegit rebase合併來自master的上游變更.合併是一個安全的選項,可以保留儲存庫的整個歷史記錄,而變基操作通過將特徵分支移動到master分支上建立線性歷史記錄。 這種使用git rebase類似於本地清理(可以同時執行),但是在這個過程中它包含了來自master的上游提交。 請記住,將其分配到遠端分支而不是master分支是完全合法的。當與其他開發人員在同一功能上進行協作時,可能會發生這種情況,並且需要將其更改合併到儲存庫中。 例如,如果您和另一位名為John的開發人員向功能分支新增了提交,則在從John的遠端倉庫獲取遠端功能分支後,您的倉庫可能如下所示:

Git Merging vs. Rebasing(譯文)
您可以使用完全相同的方式來解決這個問題,就像您整合來自主伺服器的上游更改一樣:將本地功能與john / feature合併,或將您的本地功能重新繫結到john / feature的頂端。
Git Merging vs. Rebasing(譯文)
請注意,這個rebase並沒有違反Rebasing的黃金規則,因為只有你的本地特性提交正在被移動 - 在這之前的所有內容都是不變的。這就好像在說:“把我的改變新增到John已經完成的工作中”。在大多數情況下,這比通過合併提交與遠端分支同步更直觀。 預設情況下,git pull命令執行合併,但可以強制它通過傳遞--rebase選項來將遠端分支與rebase整合。 ###使用合併請求檢視功能 如果你使用pull請求作為你的程式碼審查過程的一部分,你需要避免在建立pull請求之後使用git rebase。只要您提出拉取請求,其他開發人員就會檢視您的提交,這意味著它是一個公共分支。重寫它的歷史將使Git和你的隊友無法追蹤任何後續提交的功能。 其他開發人員的任何更改都需要使用git merge而不是git rebase。 出於這個原因,在提交您的請求之前,清理互動式底圖程式碼通常是一個好主意。 ###整合已釋出的特性 在你的團隊釋出了一個新功能之後,你可以選擇將這個功能rebaseing到主分支的頂端,然後使用git merge將這個功能整合到主程式碼庫中。 這與將上游更改合併到功能分支中的情況類似,但由於您不允許在主分支中重寫提交,你必須最終使用git merge來整合這個功能。但是,通過在合併之前執行rebase,可以確保合併將被快速轉發,從而產生完美的線性歷史記錄。這也讓你有機會壓縮在拉取請求期間新增的任何後續提交。
Git Merging vs. Rebasing(譯文)
如果你對git rebase不太滿意,你可以在一個臨時的分支上執行rebase。這樣,如果您不小心搞亂了您的功能的歷史記錄,您可以檢視原始分支並重試。例如:

git checkout feature
git checkout -b temporary-branch
git rebase -i master
# [Clean up the history]
git checkout master
git merge temporary-branch
複製程式碼

###總結 這就是你真正需要知道的開始重新分配你的分支機構。如果你更喜歡一個乾淨的,線性的歷史,沒有不必要的合併提交,你應該達到git rebase而不是git merge從另一個分支整合更改。 另一方面,如果你想儲存你的專案的完整歷史,並避免重寫公共提交的風險,你可以堅持使用git merge。這兩個選項都是完全有效的,但至少現在你可以選擇利用git rebase的好處。

相關文章