git rebase VS git merge? 更優雅的 git 合併方式值得擁有

寫在前面

若是你不能很好的應用 Git,那麼這裏爲你提供一個很是棒的 Git 在線練習工具 Git Online (回覆公衆號「工具」,獲取更多內容) ,你能夠更直觀的看到你所使用的命令會產生什麼效果 git

另外,你在使用 Git 合併分支時只會使用 git merge 嗎?有時使用 git rebase 能夠比 git merge 作出更優雅的操做


合併與變基

Merge 與 Rebase

不知怎麼,git rebase 命令被賦予了一個神奇的污毒聲譽,初學者應該遠離它,但它實際上可讓開發團隊在使用時更加輕鬆。面試

你能夠將它理解成「七傷拳」,「七傷拳」並非不能練,只是練「七傷拳」有一個先訣條件,那就是內功境界必定要很是高,即你要充分理解 git rebase 命令安全

在本文中,咱們將 git rebasegit merge 命令進行比較。在 Git 工做流中,說明全部可使用 rebase 的場景bash

概念概述

關於 git rebase ,首先要理解的是它解決了和 git merge 一樣的問題。這兩個命令都旨在將更改從一個分支合併到另外一個分支,但兩者的合併方式卻有很大的不一樣。服務器

當你在專用分支上開發新 feature 時,而後另外一個團隊成員在 master 分支提交了新的 commits,這會發生什麼?這會致使分叉的歷史記錄,對於這個問題,使用 Git 做爲協做工具的任何人來講都應該很熟悉。編輯器

分叉提交歷史記錄

如今,假設在 master 分支上的新提交與你正在開發的 feature 相關。須要將新提交合併到你的 feature 分支中,你能夠有兩個選擇:merge 或者 rebase。工具

Merge 方式

最簡單的方式是經過如下命令將 master 分支合併到 feature 分支中:學習

git checkout feature
git merge master
複製代碼

或者,你能夠將其濃縮爲一行命令:fetch

git merge feature master
複製代碼

這會在 feature 分支中建立一個新的 merge commit,它將兩個分支的歷史聯繫在一塊兒,請看以下所示的分支結構:this

將master合併到功能分支中

使用 merge 是很好的方式,由於它是一種 非破壞性的 操做。現有分支不會以任何方式被更改。這避免了 rebase 操做所產生的潛在缺陷(下面討論)。

另外一方面,這也意味着 feature 分支每次須要合併上游更改時,它都將產生一個額外的合併提交。若是master 提交很是活躍,這可能會嚴重污染你的 feature 分支歷史記錄。儘管可使用高級選項 git log 緩解此問題,但它可能使其餘開發人員難以理解項目的歷史記錄

Rebase 方式

做爲 merge 的替代方法,你可使用如下命令將 master 分支合併到 feature分支上:

git checkout feature
git rebase master
複製代碼

這會將整個 feature 分支移動到 master 分支的頂端,從而有效地整合了全部 master 分支上的提交。可是,與 merge 提交方式不一樣,rebase 經過爲原始分支中的每一個提交建立全新的 commits 來 重寫 項目歷史記錄。

將功能分支從新映射到主服務器上

rebase 的主要好處是能夠得到更清晰的項目歷史。首先,它消除了 git merge 所需的沒必要要的合併提交;其次,正如你在上圖中所看到的,rebase 會產生完美線性的項目歷史記錄,你能夠在 feature分支上沒有任何分叉的狀況下一直追尋到項目的初始提交。這樣能夠經過命令 git loggit bisectgitk 更容易導航查看項目。

可是,針對這樣的提交歷史咱們須要權衡其「安全性」和「可追溯性」。若是你不遵循 [Rebase 的黃金法則](## Rebase 的黃金法則),重寫項目歷史記錄可能會對你的協做工做流程形成災難性後果。並且,rebase 會丟失合併提交的上下文, 你也就沒法看到上游更改是什麼時候合併到 feature 中的。

交互式 Rebase

交互式 rebase 使你有機會在將 commits 移動到新分支時更改這些 commits。這比自動 rebase 更強大,由於它提供了對分支提交歷史的徹底控制。一般,這用於在合併 feature 分支到 master 以前清理其雜亂的歷史記錄。

要使用交互式 rebase,須要使用 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 命令或從新排序條目,你可使分支的歷史記錄看起來像你想要的任何內容。例如,若是第二次提交 fix 了第一次提交中的一個小問題,您可使用如下 fixup 命令將它們濃縮爲一個提交:

pick 33d5b7a Message for commit #1
fixup 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3
複製代碼

保存並關閉文件時,Git將根據您的指示執行 rebase,從而產生以下所示的項目歷史記錄:

使用交互式rebase來壓縮提交

消除這種無心義的提交使你的功能歷史更容易理解。這是 git merge 根本沒法作到的事情。至於 commits 條目前的 pickfixupsquash 等命令,在 git 目錄執行 git rebase -i 便可查看到,你們按需重排或合併提交便可,註釋說明很是清晰,在此不作過多說明:

Rebase 的黃金法則

一旦你理解了什麼是 rebase,最重要的是要學習何時不能使用它。git rebase 的黃金法則是永遠不要在公共分支上使用它。

例如,想一想若是你 rebase master 分支到 feature 分支之上會發生什麼:

從新分配主分支

rebase 將全部 master 分支上的提交移動 feature 分支的頂端。問題是這隻發生在 你本身 的存儲庫中。全部其餘開發人員仍在使用原始版本的 master。因爲 rebase 致使全新 commit,Git 會認爲你的 master 分支歷史與其餘人的歷史不一樣。

此時,同步兩個 master 分支的惟一方法是將它們合併在一塊兒,可是這樣會產生額外的合併提交和兩組包含相同更改的提交(原始提交和經過 rebase 更改的分支提交)。不用說,這是一個使人很是困惑的狀況。

所以,在你運行 git rebase 命令以前,老是問本身,還有其餘人在用這個分支嗎? 若是答案是確定的,那就把你的手從鍵盤上移開,開始考慮採用非破壞性的方式進行改變(例如,git revert 命令)。不然,你能夠爲所欲爲地重寫歷史記錄。

Force Push

若是你嘗試將 rebase 了的 master 分支推送回 remote repository,Git 將阻止你這樣作,由於它會與遠程master 分支衝突。可是,你能夠經過傳遞 --force 標誌來強制推送,以下所示:

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

這樣你本身 repository 的內容將覆蓋遠程 master分支的內容,但這會使團隊的其餘成員感到困惑。所以,只有在確切知道本身在作什麼時纔要很是當心地使用此命令。

若是沒有人在 feature branch 上做出更改,你可使用 force push 將本地內容推送到 remote repository 作清理工做

工做流程演練

rebase 能夠根據你所在團隊的須要方便的整合到現有的 Git 工做流程中。在本節中,咱們將瞭解 rebase 在功能開發的各個階段能夠提供的好處。

在任何工做流程中,利用 git rebase 是爲每一個功能建立專用分支。這爲你提供了必要的分支,以安全地利用 rebase:

在專用分支中開發功能

本地清理

將 rebase 歸入工做流程的最佳方法之一是清理本地正在進行的功能。經過按期執行交互式 rebase,你能夠確保功能中的每一個提交都具備針對性和意義。這可使你在編寫代碼時無需擔憂將其分解爲隔離的提交(多個提交),你能夠在過後修復整合它。

使用 git rebase 時,有兩種狀況:feature 父分支(例如 master )的提交,或在 feature 中的早期提交。咱們在 交互式 Rebase 部分已經介紹了第一種狀況的示例。咱們來看後一種狀況,當你只須要修復最後幾回提交時,如下命令僅作最後 3 次提交的交互式 rebase。

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

經過指定 HEAD~3 ,你實際上並無移動分支,你只是交互式地重寫其後的3個提交。請注意,這不會將上游更改合併到 feature 分支中。

從新上頭~3

若是要使用此方法重寫整個功能,git merge-base 命令可用於查找 feature 分支的原始 base。如下內容返回原始 base 的提交ID,而後你能夠將其傳遞給 git rebase

git merge-base feature master
複製代碼

交互式 rebase 的使用是引入git rebase 工做流的好方法,由於它隻影響本地分支。其餘開發人員惟一能看到的就是你提交的最終版,這應該是一個簡潔易懂易跟蹤的分支歷史記錄。

但一樣,這僅適用於 私有 feature分支。若是你經過相同的功能分支(公共分支)與其餘開發人員協做,那麼你是 不被容許 重寫其歷史記錄的。

將上游更改合併到功能分支中

概念概述 部分中,咱們瞭解了 feature 分支可使用 git mergegit rebase 合併 master 分支的上游更改 。merge 是一個安全的方式,能夠保留存 git repository 的整個歷史記錄,而 rebase 則是經過將 feature 分支移動到 master 頂端來建立線性歷史記錄。

這種使用 git rebase 相似於本地清理,但在此過程當中它包含了那些來自 master 上游提交。

請記住,將當前提交 rebase 到遠程 branch(非 master 分支)同樣是合法的。當與另外一個開發人員協做使用相同的功能而且你須要將他們的更改合併到你的 repository 時,就會發生這種狀況。

例如,若是你和另外一個名爲 John 的開發人員添加了對 feature 分支的提交,在你 fetch (注意 fetch 並不會自動 merge )來自 John 的遠程 feature分支後,你的 repository 可能以下所示:

![在同一個功能分支上進行協做]

你能夠整合上來自上游的分叉:要麼用 john/feature merge 本地 feature ,要麼 rebase 本地featurejohn/feature 的頂部。

合併與從新定位到遠程分支

請注意,此 rebase 不違反 Rebase 黃金規則,由於只有你的本地 feature 提交被移動, 以前的全部內容都不會受到影響。這就像是說 "將個人更改添加到 John 已經完成的工做中"。在大多數狀況下,這比經過合併提交與遠程分支同步更直觀。

默認狀況下,使用 git pull 命令執行合併,但你能夠經過向其傳遞 --rebase 選項來強制它將遠程分支 以 rebase 方式集成。

git pull --rebase
複製代碼

使用 Pull 請求 Review Feature

若是你在代碼審查過程當中使用 pull 請求,在使用了 pull 請求以後你應該避免使用 git rebase 。一旦你發出 pull 請求,其餘開發人員就會查看你的提交,這意味着它是一個 公共 分支。重寫其歷史記錄將使 Git 和你的隊友沒法跟蹤添加到該功能的任何後續提交。

其餘開發人員的任何更改都須要合併 git merge 而不是 git rebase

所以,在提交拉取請求以前,一般使用交互式 rebase 清理代碼一般是個好的辦法。注意使用順序

集成已批准的功能

在你的團隊批准某項 feature 後,你能夠選擇將該功能 rebase 到 master 分支的頂端,而後git merge再將該功能集成到主代碼庫中。

這與將上游更改合併到 feature 分支中的狀況相似,但因爲你不容許在 master 分支中重寫提交,所以你必須最終使用 git merge 該功能進行集成。可是,經過在 merge 以前執行 rebase,你能夠確保會以 fast-forward 方式 merge,從而產生完美的線性歷史記錄。

使用和不使用rebase將功能集成到master中

若是您不熟悉 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
複製代碼

總結

若是你更喜歡沒有沒必要要的乾淨的合併提交,線性歷史記錄,你就須要開始瞭解使用 rebase 功能。同時你應該會使用 git rebase 而不是 git merge 集成來自另外一個分支的更改。

另外一方面,若是你想保留項目的完整歷史記錄並避免重寫公共提交的風險,你能夠堅持下去git merge。這兩種選擇都是徹底有效的,但至少如今你能夠選擇利用 git rebase 的好處 。

靈魂追問

  1. 你有使用過 git rebase 嗎?這樣清晰的線形歷史是否是能夠嘗試一下呢?
  2. 交互式 rebase 提交條目前的命令 fixup 等你能靈活使用嗎
  3. 在 feature 分支上開發時,試試 git pull -rebase?

帶着疑問去思考,而後串聯,進而概括總結,不斷追問本身,進行自我辯證,像偵查嫌疑案件同樣看待技術問題,漆黑的街道,你我一塊兒尋找線索,你就是技術界大偵探福爾摩斯


提升效率工具


推薦閱讀


歡迎持續關注公衆號:「日拱一兵」

  • 前沿 Java 技術乾貨分享
  • 高效工具彙總
  • 面試問題分析與解答
  • 技術資料領取

持續關注,帶你像讀偵探小說同樣輕鬆趣味學習 Java 技術棧相關知識

相關文章
相關標籤/搜索