git rebase VS git merge梗肝? 更優(yōu)雅的 git 合并方式值得擁有

寫在前面

如果你不能很好的應用 Git桑谍,那么這里為你提供一個非常棒的 Git 在線練習工具 Git Online ,(回復公眾號「工具」洗显,了解更多內(nèi)容)你可以更直觀的看到你所使用的命令會產(chǎn)生什么效果

image

另外外潜,你在使用 Git 合并分支時只會使用 git merge 嗎?有時使用 git rebase 可以比 git merge 做出更優(yōu)雅的操作


image

Merge 與 Rebase

不知怎么挠唆,git rebase 命令被賦予了一個神奇的污毒聲譽处窥,初學者應該遠離它,但它實際上可以讓開發(fā)團隊在使用時更加輕松玄组。

你可以將它理解成「七傷拳」滔驾,「七傷拳」并不是不能練,只是練「七傷拳」有一個先訣條件巧勤,那就是內(nèi)功境界一定要非常高嵌灰,即你要充分理解 git rebase 命令

在本文中,我們將 git rebasegit merge 命令進行比較颅悉。在 Git 工作流中沽瞭,說明所有可以使用 rebase 的場景

概念概述

關(guān)于 git rebase ,首先要理解的是它解決了和 git merge 同樣的問題剩瓶。這兩個命令都旨在將更改從一個分支合并到另一個分支驹溃,但二者的合并方式卻有很大的不同城丧。

當你在專用分支上開發(fā)新 feature 時,然后另一個團隊成員在 master 分支提交了新的 commits豌鹤,這會發(fā)生什么亡哄?這會導致分叉的歷史記錄,對于這個問題布疙,使用 Git 作為協(xié)作工具的任何人來說都應該很熟悉蚊惯。

image

現(xiàn)在,假設(shè)在 master 分支上的新提交與你正在開發(fā)的 feature 相關(guān)灵临。需要將新提交合并到你的 feature 分支中截型,你可以有兩個選擇:merge 或者 rebase。

Merge 方式

最簡單的方式是通過以下命令將 master 分支合并到 feature 分支中:

git checkout feature
git merge master

或者儒溉,你可以將其濃縮為一行命令:

git merge feature master

這會在 feature 分支中創(chuàng)建一個新的 merge commit宦焦,它將兩個分支的歷史聯(lián)系在一起,請看如下所示的分支結(jié)構(gòu):

image

使用 merge 是很好的方式顿涣,因為它是一種 非破壞性的 操作〔郑現(xiàn)有分支不會以任何方式被更改。這避免了 rebase 操作所產(chǎn)生的潛在缺陷(下面討論)涛碑。

另一方面精堕,這也意味著 feature 分支每次需要合并上游更改時,它都將產(chǎn)生一個額外的合并提交锌唾。如果master 提交非吵耄活躍,這可能會嚴重污染你的 feature 分支歷史記錄晌涕。盡管可以使用高級選項 git log 緩解此問題,但它可能使其他開發(fā)人員難以理解項目的歷史記錄

Rebase 方式

作為 merge 的替代方法痛悯,你可以使用以下命令將 master 分支合并到 feature分支上:

git checkout feature
git rebase master

這會將整個 feature 分支移動到 master 分支的頂端余黎,從而有效地整合了所有 master 分支上的提交。但是载萌,與 merge 提交方式不同惧财,rebase 通過為原始分支中的每個提交創(chuàng)建全新的 commits 來 重寫 項目歷史記錄。

image

rebase 的主要好處是可以獲得更清晰的項目歷史扭仁。首先垮衷,它消除了 git merge 所需的不必要的合并提交;其次乖坠,正如你在上圖中所看到的搀突,rebase 會產(chǎn)生完美線性的項目歷史記錄,你可以在 feature分支上沒有任何分叉的情況下一直追尋到項目的初始提交熊泵。這樣可以通過命令 git log仰迁,git bisectgitk 更容易導航查看項目甸昏。

但是,針對這樣的提交歷史我們需要權(quán)衡其「安全性」和「可追溯性」徐许。如果你不遵循 [Rebase 的黃金法則](## Rebase 的黃金法則)施蜜,重寫項目歷史記錄可能會對你的協(xié)作工作流程造成災難性后果。而且雌隅,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

此列表準確定義了執(zhí)行 rebase 后分支的外觀仇箱。通過更改 pick 命令或重新排序條目,你可以使分支的歷史記錄看起來像你想要的任何內(nèi)容东羹。例如剂桥,如果第二次提交 fix 了第一次提交中的一個小問題,您可以使用以下 fixup 命令將它們濃縮為一個提交:

pick 33d5b7a Message for commit #1
fixup 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3

保存并關(guān)閉文件時属提,Git將根據(jù)您的指示執(zhí)行 rebase权逗,從而產(chǎn)生如下所示的項目歷史記錄:


image

消除這種無意義的提交使你的功能歷史更容易理解。這是 git merge 根本無法做到的事情冤议。至于 commits 條目前的 pick斟薇、fixupsquash 等命令恕酸,在 git 目錄執(zhí)行 git rebase -i 即可查看到堪滨,大家按需重排或合并提交即可,注釋說明非常清晰蕊温,在此不做過多說明:

Rebase 的黃金法則

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

例如发笔,想想如果你 rebase master 分支到 feature 分支之上會發(fā)生什么:

image

rebase 將所有 master 分支上的提交移動 feature 分支的頂端。問題是這只發(fā)生在 你自己 的存儲庫中凉翻。所有其他開發(fā)人員仍在使用原始版本的 master了讨。由于 rebase 導致全新 commit,Git 會認為你的 master 分支歷史與其他人的歷史不同。

此時量蕊,同步兩個 master 分支的唯一方法是將它們合并在一起铺罢,但是這樣會產(chǎn)生額外的合并提交和兩組包含相同更改的提交(原始提交和通過 rebase 更改的分支提交)。不用說残炮,這是一個令人非常困惑的情況韭赘。

因此,在你運行 git rebase 命令之前势就,總是問自己泉瞻,還有其他人在用這個分支嗎? 如果答案是肯定的苞冯,那就把你的手從鍵盤上移開袖牙,開始考慮采用非破壞性的方式進行改變(例如,git revert 命令)舅锄。否則鞭达,你可以隨心所欲地重寫歷史記錄。

Force Push

如果你嘗試將 rebase 了的 master 分支推送回 remote repository皇忿,Git 將阻止你這樣做畴蹭,因為它會與遠程master 分支沖突。但是鳍烁,你可以通過傳遞 --force 標志來強制推送叨襟,如下所示:

# Be very careful with this command!
git push --force

這樣你自己 repository 的內(nèi)容將覆蓋遠程 master分支的內(nèi)容,但這會使團隊的其他成員感到困惑幔荒。因此糊闽,只有在確切知道自己在做什么時才要非常小心地使用此命令。

如果沒有人在 feature branch 上作出更改爹梁,你可以使用 force push 將本地內(nèi)容推送到 remote repository 做清理工作

工作流程演練

rebase 可以根據(jù)你所在團隊的需要方便的整合到現(xiàn)有的 Git 工作流程中右犹。在本節(jié)中,我們將了解 rebase 在功能開發(fā)的各個階段可以提供的好處卫键。

在任何工作流程中傀履,利用 git rebase 是為每個功能創(chuàng)建專用分支。這為你提供了必要的分支莉炉,以安全地利用 rebase:

image

本地清理

將 rebase 納入工作流程的最佳方法之一是清理本地正在進行的功能。通過定期執(zhí)行交互式 rebase碴犬,你可以確保功能中的每個提交都具有針對性和意義絮宁。這可以使你在編寫代碼時無需擔心將其分解為隔離的提交(多個提交),你可以在事后修復整合它服协。

使用 git rebase 時绍昂,有兩種情況:feature 父分支(例如 master )的提交,或在 feature 中的早期提交。我們在 交互式 Rebase 部分已經(jīng)介紹了第一種情況的示例窘游。我們來看后一種情況唠椭,當你只需要修復最后幾次提交時,以下命令僅做最后 3 次提交的交互式 rebase忍饰。

git checkout feature
git rebase -i HEAD~3

通過指定 HEAD~3 贪嫂,你實際上并沒有移動分支,你只是交互式地重寫其后的3個提交艾蓝。請注意力崇,這不會將上游更改合并到 feature 分支中。

image

如果要使用此方法重寫整個功能赢织,git merge-base 命令可用于查找 feature 分支的原始 base亮靴。以下內(nèi)容返回原始 base 的提交ID,然后你可以將其傳遞給 git rebase

git merge-base feature master

交互式 rebase 的使用是引入git rebase 工作流的好方法于置,因為它只影響本地分支茧吊。其他開發(fā)人員唯一能看到的就是你提交的最終版,這應該是一個簡潔易懂易跟蹤的分支歷史記錄八毯。

但同樣搓侄,這僅適用于 私有 feature分支。如果你通過相同的功能分支(公共分支)與其他開發(fā)人員協(xié)作宪彩,那么你是 不被允許 重寫其歷史記錄的休讳。

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

概念概述 部分中,我們了解了 feature 分支可以使用 git mergegit rebase 合并 master 分支的上游更改 尿孔。merge 是一個安全的方式俊柔,可以保留存 git repository 的整個歷史記錄,而 rebase 則是通過將 feature 分支移動到 master 頂端來創(chuàng)建線性歷史記錄活合。

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

請記住白指,將當前提交 rebase 到遠程 branch(非 master 分支)一樣是合法的留晚。當與另一個開發(fā)人員協(xié)作使用相同的功能并且你需要將他們的更改合并到你的 repository 時,就會發(fā)生這種情況告嘲。

例如错维,如果你和另一個名為 John 的開發(fā)人員添加了對 feature 分支的提交,在你 fetch (注意 fetch 并不會自動 merge )來自 John 的遠程 feature分支后橄唬,你的 repository 可能如下所示:

image

你可以整合上來自上游的分叉:要么用 john/feature merge 本地 feature 赋焕,要么 rebase 本地featurejohn/feature 的頂部。

image

請注意仰楚,此 rebase 不違反 Rebase 黃金規(guī)則隆判,因為只有你的本地 feature 提交被移動犬庇, 之前的所有內(nèi)容都不會受到影響。這就像是說 "將我的更改添加到 John 已經(jīng)完成的工作中"侨嘀。在大多數(shù)情況下臭挽,這比通過合并提交與遠程分支同步更直觀。

默認情況下咬腕,使用 git pull 命令執(zhí)行合并欢峰,但你可以通過向其傳遞 --rebase 選項來強制它將遠程分支 以 rebase 方式集成。

git pull --rebase

使用 Pull 請求 Review Feature

如果你在代碼審查過程中使用 pull 請求郎汪,在使用了 pull 請求之后你應該避免使用 git rebase 赤赊。一旦你發(fā)出 pull 請求,其他開發(fā)人員就會查看你的提交煞赢,這意味著它是一個 公共 分支抛计。重寫其歷史記錄將使 Git 和你的隊友無法跟蹤添加到該功能的任何后續(xù)提交。

其他開發(fā)人員的任何更改都需要合并 git merge 而不是 git rebase照筑。

因此吹截,在提交拉取請求之前,通常使用交互式 rebase 清理代碼通常是個好的辦法凝危。注意使用順序

集成已批準的功能

在你的團隊批準某項 feature 后波俄,你可以選擇將該功能 rebase 到 master 分支的頂端,然后git merge再將該功能集成到主代碼庫中蛾默。

這與將上游更改合并到 feature 分支中的情況類似懦铺,但由于你不允許在 master 分支中重寫提交,因此你必須最終使用 git merge 該功能進行集成支鸡。但是冬念,通過在 merge 之前執(zhí)行 rebase,你可以確保會以 fast-forward 方式 merge牧挣,從而產(chǎn)生完美的線性歷史記錄急前。

image

如果您不熟悉 git rebase,可以隨時在臨時分支中執(zhí)行 rebase瀑构。這樣裆针,如果你不小心弄亂了功能的歷史記錄,可以查看原始分支寺晌,然后重試世吨。例如:

git checkout feature
git checkout -b temporary-branch
git rebase -i master
# [Clean up the history]
git checkout master
git merge temporary-branch

總結(jié)

如果你更喜歡沒有不必要的干凈的合并提交,線性歷史記錄呻征,你就需要開始了解使用 rebase 功能另假。同時你應該會使用 git rebase 而不是 git merge 集成來自另一個分支的更改。

另一方面怕犁,如果你想保留項目的完整歷史記錄并避免重寫公共提交的風險,你可以堅持下去git merge。這兩種選擇都是完全有效的奏甫,但至少現(xiàn)在你可以選擇利用 git rebase 的好處 戈轿。

靈魂追問

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

帶著疑問去思考,然后串聯(lián)挠进,進而歸納總結(jié)色乾,不斷追問自己,進行自我辯證领突,像偵查嫌疑案件一樣看待技術(shù)問題暖璧,漆黑的街道,你我一起尋找線索君旦,你就是技術(shù)界大偵探福爾摩斯


提高效率工具

image

推薦閱讀


歡迎持續(xù)關(guān)注公眾號:「日拱一兵」

  • 前沿 Java 技術(shù)干貨分享
  • 高效工具匯總
  • 面試問題分析與解答
  • 技術(shù)資料領(lǐng)取

持續(xù)關(guān)注恕稠,帶你像讀偵探小說一樣輕松趣味學習 Java 技術(shù)棧相關(guān)知識

image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末琅绅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鹅巍,更是在濱河造成了極大的恐慌千扶,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昆著,死亡現(xiàn)場離奇詭異县貌,居然都是意外死亡,警方通過查閱死者的電腦和手機凑懂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門煤痕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人接谨,你說我怎么就攤上這事摆碉。” “怎么了脓豪?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵巷帝,是天一觀的道長。 經(jīng)常有香客問我扫夜,道長楞泼,這世上最難降的妖魔是什么驰徊? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮堕阔,結(jié)果婚禮上棍厂,老公的妹妹穿的比我還像新娘。我一直安慰自己超陆,他們只是感情好牺弹,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著时呀,像睡著了一般张漂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谨娜,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天航攒,我揣著相機與錄音,去河邊找鬼瞧预。 笑死屎债,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的垢油。 我是一名探鬼主播盆驹,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼滩愁!你這毒婦竟也來了躯喇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤硝枉,失蹤者是張志新(化名)和其女友劉穎廉丽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妻味,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡正压,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了责球。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片焦履。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖雏逾,靈堂內(nèi)的尸體忽然破棺而出嘉裤,到底是詐尸還是另有隱情,我是刑警寧澤栖博,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布屑宠,位于F島的核電站,受9級特大地震影響仇让,放射性物質(zhì)發(fā)生泄漏典奉。R本人自食惡果不足惜躺翻,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望秋柄。 院中可真熱鬧获枝,春花似錦、人聲如沸骇笔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽笨触。三九已至,卻和暖如春雹舀,著一層夾襖步出監(jiān)牢的瞬間芦劣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工说榆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留虚吟,地道東北人。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓签财,卻偏偏與公主長得像串慰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子唱蒸,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容