Git Rebase教程: 用Git Rebase讓時(shí)光倒流
想象一下你正在開(kāi)發(fā)一個(gè)激進(jìn)的新功能。這將是很燦爛的但它需要一段時(shí)間。您這幾天也許是幾個(gè)星期一直在做這個(gè)。
你的功能分支已經(jīng)超前master有6個(gè)提交了。你是一個(gè)優(yōu)秀的開(kāi)發(fā)人員并做了有意義的語(yǔ)義提交。但有一件事情:你開(kāi)始慢慢意識(shí)到,這個(gè)瘋狂的東西仍需要更多的時(shí)間才能真的做好準(zhǔn)備被合并回主分支。
- m1-m2-m3-m4 (master)
- \
- f1-f2-f3-f4-f5-f6(feature)
你也知道的是,一些地方實(shí)際上是交叉不大的新功能。它們可以更早地合并到主分支。不幸的是,你想將部分合并到主分支的內(nèi)容存在于你六個(gè)提交中的某個(gè) 地方。更糟糕的是,它也包含了依賴于你的功能分支的之前的提交。有人可能會(huì)說(shuō),你應(yīng)該在***處地方做兩次提交,但沒(méi)有人是***的。
- m1-m2-m3-m4 (master)
- \
- f1-f2-f3-f4-f5-f6(feature)
- ^
- |
- mixed commit
在你準(zhǔn)備提交的時(shí)間,你沒(méi)有預(yù)見(jiàn)到,你可能要逐步把該功能合并入主分支。哎呀!你不會(huì)想到這件事會(huì)有這么久。
你需要的是一種方法可以回溯歷史,把它并分成兩次提交,這樣就可以把代碼都安全地分離出來(lái),并可以移植到master分支。
用圖說(shuō)話,就是我們需要這樣。
- m1-m2-m3-m4 (master)
- \
- f1-f2-f3a-f3b-f4-f5-f6(feature)
在將工作分成兩個(gè)提交后,我們就可以cherry-pick出前面的部分到主分支了。
原來(lái)Git自帶了一個(gè)功能強(qiáng)大的命令git rebase -i ,它可以讓我們這樣做。它可以讓我們改變歷史。改變歷史可能會(huì)產(chǎn)生問(wèn)題,作為一個(gè)經(jīng)驗(yàn),應(yīng)盡快避免歷史與他人共享。不過(guò)在我們的例子中,我們只是改變我們 的本地功能分支的歷史。沒(méi)有人會(huì)受到傷害。就這么做了!
好吧,讓我們來(lái)仔細(xì)看看f3提交究竟修改了什么。原來(lái)我們共修改了兩個(gè)文件:userService.js和 wishlistService.js。比方說(shuō),userService.js的更改可以直接合入主分支而wishlistService.js不能。因 為wishlistService.js甚至不存在在主分支里面。它是f1提交中引入的。
專家提示:即使是在一個(gè)文件中更改,git也可以搞定。但這篇博客中我們先簡(jiǎn)化情況。
我們已經(jīng)建立了一個(gè)公眾演示倉(cāng)庫(kù),我們將使用這個(gè)來(lái)練習(xí)。為了便于跟蹤,每一個(gè)提交信息的前綴是在上面的圖表中使用的假的SHA。以下是git在分開(kāi)提交f3時(shí)的分支圖。
現(xiàn)在,我們要做的***件事就是使用git的checkout功能checkout出我們的功能分支。用git rebase -i master開(kāi)始做rebase。
現(xiàn)在接下來(lái)git會(huì)用所配置的編輯器打開(kāi)(默認(rèn)為Vim)一個(gè)臨時(shí)文件。
該文件為您提供一些rebase選擇,它帶有一個(gè)提示(藍(lán)色文字)。對(duì)于每一個(gè)提交,我們可以選擇的動(dòng)作有pick、rwork、edit、 squash、fixup和exec。每一個(gè)動(dòng)作也可以通過(guò)它的縮寫(xiě)形式p、r、e、s、f和e引用。描述每一個(gè)選項(xiàng)超出了本文范疇,所以讓我們專注于我 們的具體任務(wù)。
我們要為f3提交選擇edit選項(xiàng),因此我們把內(nèi)容改變成這樣。
現(xiàn)在我們保存文件(在Vim中是按下后輸入:wq,***是按下回車)。接下來(lái)我們注意到git在編輯選項(xiàng)中選擇的提交處停止了rebase。
這意味這git開(kāi)始將f1、f2、f3生效仿佛它就是常規(guī)的rebase,但是在f3生效之后停止。事實(shí)上,我們可以看一眼停止的地方的日志就可以證明這一點(diǎn)。
要將f3分成兩個(gè)提交,我們所要做的是重置git的指針到先前的提交(f2)而保持工作目錄和現(xiàn)在一樣。這就是git reset在混合模式在做的。由于混合模式是git reset的默認(rèn)模式,我們可以直接用git reset head~1。就這么做并在運(yùn)行后用git status看下發(fā)生了什么。
git status告訴我們userService.js和wishlistService.js被修改了。如果我們運(yùn)行 git diff 我們就可以看見(jiàn)在f3里面確切地做了哪些更改。
如果我們看一眼日志我們會(huì)發(fā)現(xiàn)f3已經(jīng)消失了。
現(xiàn)在我們有了準(zhǔn)備提交的先前的f3提交,而原先的f3提交已經(jīng)消失了。記住雖然我們?nèi)耘f在rebase的中間過(guò)程。我們的f4、f5、f6提交還沒(méi)有缺失,它們會(huì)在接下來(lái)回來(lái)。
讓我們創(chuàng)建兩個(gè)新的提交:首先讓我們?yōu)榭梢蕴峤坏街鞣种У膗serService.js創(chuàng)建一個(gè)提交。運(yùn)行g(shù)it add userService.js 接著運(yùn)行 git commit -m "f3a: add updateUser method"。
太棒了!讓我們?yōu)閣ishlistService.js的改變創(chuàng)建另外一個(gè)提交。運(yùn)行g(shù)it add wishlistService.js,接著運(yùn)行g(shù)it commit -m "f3b: add addItems method".
讓我們?cè)诳匆谎廴罩尽?/p>
這就是我們想要的,除了f4、f5、f6仍舊缺失。這是因?yàn)槲覀內(nèi)栽趓ebase交互的中間,我們需要告訴git繼續(xù)rebase。用下面的命令繼續(xù):git rebase --continue。
讓我們?cè)俅螜z查一下日志。
就是這樣。我們現(xiàn)在已經(jīng)得到我們想要的歷史了。先前的f3提交現(xiàn)在已經(jīng)被分割成兩個(gè)提交f3a和f3b。剩下的***一件事是cherry-pick出f3a提交到主分支上。
為了完成***一步,我們首先切換到主分支。我們用git checkout master?,F(xiàn)在我們就可以用cherry-pick命令來(lái)拾取f3a commit了。本例中我們可以用它的SHA值bd47ee1來(lái)引用它。
現(xiàn)在f3a這個(gè)提交就在主分支的最上面了。這就是我們需要的!
這篇文章的長(zhǎng)度看起來(lái)需要花費(fèi)很大的功夫,但實(shí)際上對(duì)于一個(gè)git高級(jí)用戶而言這只是一會(huì)會(huì)。
注:Christoph目前正在與Pascal Precht寫(xiě)一本關(guān)于Git rebase的書(shū),您可以在leanpub訂閱它并在準(zhǔn)備出版時(shí)獲得通知。
本文作者 Christoph Burgdorf自10歲時(shí)就是一名程序員,他是HannoverJS Meetup網(wǎng)站的創(chuàng)始人,并且一直活躍在AngularJS社區(qū)。他也是非常了解gti的內(nèi)內(nèi)外外,在那里他舉辦一個(gè)thoughtram的工作室來(lái)幫助初學(xué)者掌握該技術(shù)。
本的教程最初發(fā)表在他的blog。
via: https://www.codementor.io/git-tutorial/git-rebase-split-old-commit-master
作者:cburgdorf 譯者:geekpi 校對(duì):wxy
本文由 LCTT 原創(chuàng)翻譯,Linux中國(guó) 榮譽(yù)推出