Git 進(jìn)階 - 衍合 rebase

將一個(gè)分支合并到另一個(gè)分支有兩種胖替,一種是大多都很熟悉的 merge(合并),另一種就是本篇要介紹的 rebase(衍合)。

看下本文綱要

已經(jīng)有 merge 了,為什么需要 rebase 怠肋?我們先跟著官方文檔學(xué)習(xí)下 rebase 的基本概念

rebase 是做什么的?

(如果你大概知道 rebase 是做什么的淹朋,可以直接跳到第二趴笙各,實(shí)戰(zhàn) rebase)

在了解 rebase 之前钉答,先溫習(xí)下 merge 的過(guò)程

假設(shè)現(xiàn)在基于 <master> 分支,檢出一個(gè) <hotfix> 分支

然后在這個(gè)分支 <hotfix> 做一些修改酪惭,生成兩個(gè)提交(C3 和 C4),同時(shí)也有其他人在 <master> 分支做了提交(C5 和 C6 )者甲,那么在同一位置(C2) <master> 和 hotfix 兩個(gè)分支分別前進(jìn)了

使用 merge 合并分支

此時(shí)拉取 <master> 分支內(nèi)容并合并到 <hotfix> 分支中春感,Git 會(huì)把兩個(gè)分支最新的快照(C3~C6)以及他們共同的祖先(C2)進(jìn)行合并,然后就形成了一次新的合并提交(C7)

<hotfix>: git merge master

如果想讓 <hotfix> 分支看起來(lái)沒(méi)有經(jīng)過(guò)任何合并一樣虏缸,就可以使用 git rebase

使用 rebase 合并

rebase 有的翻譯成衍合鲫懒,有的直接翻譯成變基,變基就很好理解了刽辙,就是重新設(shè)定基底窥岩。

$ git checkout hotfix
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: ***

git rebase 這個(gè)命令會(huì)將 <hotfix> 分支里的提交(C3、C4)取消宰缤,保存成臨時(shí)文件颂翼,然后把 <hotfix> 更新到最新的 <master> 分支,最后把保存的這些內(nèi)容應(yīng)用到 <master> 分支上慨灭,這個(gè)過(guò)程就是衍合朦乏。

rebase 原理 衍合前回到兩個(gè)分支(所在的分支和想要衍合的分支)的共同祖先,提取你所在分支每次提交時(shí)產(chǎn)生的差異(diff)氧骤,把這些差異分別保存到臨時(shí)文件里呻疹,然后從當(dāng)前分支轉(zhuǎn)換到你需要衍合的分支,依照順序使用每一個(gè)差異補(bǔ)丁文件筹陵。

當(dāng) <hotfix> 更新后刽锤,它會(huì)指向新創(chuàng)建的提交,而老的提交會(huì)被丟棄

使用 git rebase 之后產(chǎn)生的歷史會(huì)是如下

<hotfix>: git rebase master

還是有點(diǎn)亂嗎朦佩?再通俗點(diǎn)呢并思,原來(lái)的「基點(diǎn)」是 C2,現(xiàn)在把「基點(diǎn)」改變到要衍合的分支處语稠,就是把「基點(diǎn)」搞到 C6 哪兒去纺荧,然后把改變的內(nèi)容,應(yīng)用上去颅筋。然后分支歷史就被改寫啦宙暇。

實(shí)戰(zhàn)體驗(yàn) rebase

概念和效果清楚了,實(shí)戰(zhàn)一下议泵,推薦一個(gè)學(xué)習(xí) Git 實(shí)踐的網(wǎng)站占贫,這個(gè)工具非常有意思,可以用來(lái)學(xué)習(xí)模擬基本的 Git 操作先口,戳這里學(xué)習(xí)

模擬以下操作過(guò)程

  1. 在 <master>:C1 處創(chuàng)建 <b1> 和 <b2> 分支型奥,在三個(gè)分支分別產(chǎn)生提交 C2~C4
    <b1>:C2
    <b2>:C3
    <master>:C4

那么當(dāng)前的歷史節(jié)點(diǎn)如下圖:

2:衍合 b1 分支到 master 分支
<master>: git rebase b1

衍合過(guò)程分析:先回到 C1瞳收,提取當(dāng)前分支<master> 和 C1 的差異,形成 C4' (C4 和 C1 的差異) 保存起來(lái)厢汹,當(dāng)前分支<master> 轉(zhuǎn)換到要衍合的分支<b1>(C2處)螟深,再把 C4' 應(yīng)用進(jìn)去。

這里要注意一下:在命令里面可以看到 Git 提示衍合過(guò)程

$ git rebase b1
First, rewinding head to replay your work on top of it...
Applying: C4

這里其實(shí)應(yīng)用的并不是 C4 那次 commit烫葬,而是 C4 和 C1 比較后的 diff界弧,C4'。所以 C4 和 C4' 是兩個(gè)不同的提交(會(huì)產(chǎn)生不同的歷史搭综,但是內(nèi)容是一樣的)垢箕。

  1. 再做一次衍合,將 b2 也合并過(guò)來(lái)
    <master>: git rebase b2
$ git rebase b2
First, rewinding head to replay your work on top of it...
Applying: C2
Applying: C4

還是會(huì)提取 <master> 分支 和 C1 的 差異兑巾,產(chǎn)生了 C2' 和 C4''条获,轉(zhuǎn)到 <b2>分支再把 diff 應(yīng)用進(jìn)去。


  1. 此時(shí)如果 b2 merge master 的話 實(shí)際就是快速跟進(jìn)了蒋歌。
    <b2>: git merge master

同樣的過(guò)程帅掘,把上面的 rebase 都換成 merge

小結(jié)

可以看到不論用 rebase 還是 merge 得到的結(jié)果是沒(méi)有區(qū)別的,但是衍合能產(chǎn)生一個(gè)更為整潔的提交歷史堂油。如果視察一個(gè)衍合過(guò)的分支的歷史記錄锄开,看起來(lái)更清楚:仿佛所有修改都是先后進(jìn)行的,盡管實(shí)際上它們?cè)瓉?lái)是同時(shí)發(fā)生的称诗。

你可以經(jīng)常使用衍合萍悴,確保在遠(yuǎn)程分支里的提交歷史更清晰。比方說(shuō)寓免,某些項(xiàng)目自己不是維護(hù)者癣诱,但想幫點(diǎn)忙,就應(yīng)該盡可能使用衍合:先在一個(gè)分支里進(jìn)行開(kāi)發(fā)袜香,當(dāng)準(zhǔn)備向主項(xiàng)目提交補(bǔ)丁的時(shí)候撕予,再把它衍合到 origin/master 里面。這樣蜈首,維護(hù)者就不需要做任何整合工作实抡,只需根據(jù)你提供的倉(cāng)庫(kù)地址作一次快進(jìn),或者采納你提交的補(bǔ)丁欢策。

請(qǐng)注意吆寨,合并結(jié)果中最后一次提交所指向的快照,無(wú)論是通過(guò)一次衍合還是一次三方合并踩寇,都是同樣的快照內(nèi)容啄清,只是提交的歷史不同罷了。衍合按照每行改變發(fā)生的次序重演發(fā)生的改變俺孙,而合并是把最終結(jié)果合在一起辣卒。

rebase 的其他操作

前面用了大篇幅來(lái)說(shuō)明 rebase 的概念及實(shí)踐掷贾,看下 rebase 的其他操作

onto 選項(xiàng)

--onto 剪切指定范圍內(nèi)提交節(jié)點(diǎn),并在指向的分支上對(duì)這些節(jié)點(diǎn)執(zhí)行變基操作
git rebase --onto base from to
將 (from,to] 范圍內(nèi)所有提交的節(jié)點(diǎn)在 base 指向的節(jié)點(diǎn)之后重建

看官方的例子了解下這個(gè) onto 選項(xiàng)

你創(chuàng)建了一個(gè)特性分支 <server> 來(lái)給服務(wù)器端代碼添加一些功能荣茫,然后提交 C3 和 C4想帅。然后從 C3 的地方再增加一個(gè) <client> 分支來(lái)對(duì)客戶端代碼進(jìn)行一些修改,提交 C8 和 C9啡莉。最后港准,又回到 <server> 分支提交了 C10。

假設(shè)在接下來(lái)的一次軟件發(fā)布中票罐,你決定把客戶端的修改先合并到主線中叉趣,而暫緩并入服務(wù)端軟件的修改(因?yàn)檫€需要進(jìn)一步測(cè)試)泞边。你可以僅提取對(duì)客戶端的改變(C8 和C9)该押,然后通過(guò)使用 git rebase--onto 選項(xiàng)來(lái)把它們?cè)?<master> 分支上重演:

$ git rebase --onto master server client

這基本上等于在說(shuō)“檢出 <client> 分支,找出 <client> 分支和 <server> 分支的共同祖先之后的變化阵谚,然后把它們?cè)?<master> 上重演一遍”蚕礼。是不是有點(diǎn)復(fù)雜?不過(guò)它的結(jié)果梢什,非车斓牛酷:

現(xiàn)在你決定把 <server> 分支的變化也包含進(jìn)來(lái)∥宋纾可以直接把 <server> 分支衍合到 <master> 而不用手工轉(zhuǎn)到 <server> 分支再衍合囤躁。git rebase [主分支] [特性分支] 命令會(huì)先檢出特性分支 <server>,然后在主分支 <master> 上重演

$ git rebase master server

rebase 沖突處理

在 rebase 的過(guò)程中荔睹,也許也會(huì)出現(xiàn)沖突狸演,這時(shí)候 Git 會(huì)停止 rebase 讓你解決沖突(這個(gè)過(guò)程和 merge 是一樣的)
手動(dòng)處理沖突之后,通過(guò) add 命令暫存沖突文件
可以使用 --continue 選項(xiàng)僻他,繼續(xù)本次操作

git rebase --continue

或者使用 --abort 選項(xiàng) 放棄本次衍合操作

git rebase --abort

在進(jìn)行衍合或合并操作時(shí)宵距,Git 類似新建了一個(gè)匿名分支,當(dāng)使用 --abort 選項(xiàng)時(shí)吨拗, Git 會(huì)切回原分支满哪,丟棄匿名分支,放棄本次操作劝篷。

如果使用 Git 管理工具哨鸭,當(dāng) merge 或者 rebase 操作有沖突需要處理時(shí),都會(huì)有相關(guān)提示娇妓,比如有哪些文件有沖突兔跌,也有 continue 和 abort 操作供選擇。

rebase 還有其他的一些選項(xiàng)峡蟋,比如 -i坟桅,后面學(xué)習(xí)重寫歷史的時(shí)候再做補(bǔ)充

rebase 的風(fēng)險(xiǎn)和使用場(chǎng)景

永遠(yuǎn)不要衍合哪些已經(jīng)推送到公共倉(cāng)庫(kù)的更新
衍合的時(shí)候华望,實(shí)際上拋棄了一些已經(jīng)存在的 commit 而創(chuàng)建了一些類似的但是不同的新 commit。如果把這個(gè) commit(假設(shè)是 C6)推送到遠(yuǎn)程端仅乓,其他人在其基礎(chǔ)上工作赖舟,然后你使用 git rebase 重寫了C6 推送了 C6',那么別人不得不重新合并夸楣,而這次合并的內(nèi)容和之前已經(jīng)獲取到的 C6 是一樣宾抓,而再獲取的時(shí)候就可能是 C6-C7-C6'-C8 ( C6 和 C6' 有著相同的內(nèi)容(包括作者、提交說(shuō)明等)豫喧,C7 是其他人的提交石洗,C8是其他人合并 C6' 產(chǎn)生的提交),這個(gè)歷史記錄會(huì)變的非常令人費(fèi)解紧显。

如果把衍合當(dāng)成一種在推送之前清理提交歷史的手段讲衫,而且僅僅衍合那些永遠(yuǎn)不會(huì)公開(kāi)的 commit,那就不會(huì)有任何問(wèn)題孵班。如果衍合那些已經(jīng)公開(kāi)的commit涉兽,而與此同時(shí)其他人已經(jīng)用這些 commit 進(jìn)行了后續(xù)的開(kāi)發(fā)工作,那就很麻煩了篙程。

參考:Git 分支 - 變基枷畏;rebase

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市虱饿,隨后出現(xiàn)的幾起案子拥诡,更是在濱河造成了極大的恐慌,老刑警劉巖氮发,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渴肉,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡折柠,警方通過(guò)查閱死者的電腦和手機(jī)宾娜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)扇售,“玉大人前塔,你說(shuō)我怎么就攤上這事〕斜” “怎么了华弓?”我有些...
    開(kāi)封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)困乒。 經(jīng)常有香客問(wèn)我寂屏,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任迁霎,我火速辦了婚禮吱抚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘考廉。我一直安慰自己秘豹,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布昌粤。 她就那樣靜靜地躺著既绕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪涮坐。 梳的紋絲不亂的頭發(fā)上凄贩,一...
    開(kāi)封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音袱讹,去河邊找鬼疲扎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛廓译,可吹牛的內(nèi)容都是我干的评肆。 我是一名探鬼主播债查,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼非区,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了盹廷?” 一聲冷哼從身側(cè)響起征绸,我...
    開(kāi)封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎俄占,沒(méi)想到半個(gè)月后管怠,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缸榄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年渤弛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甚带。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡她肯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鹰贵,到底是詐尸還是另有隱情晴氨,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布碉输,位于F島的核電站籽前,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜枝哄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一肄梨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧挠锥,春花似錦峭范、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至菜秦,卻和暖如春甜害,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背球昨。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工尔店, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人主慰。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓嚣州,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親共螺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子该肴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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