http://www.heartthinkdo.com/?p=1791
http://blog.psjay.com/posts/git-revert-merge-commit/
通過(guò)git revert來(lái)實(shí)現(xiàn)線主干代碼的回滾次慢。如下命令
- 對(duì)于 merge類型的commit對(duì)象,還需要“-m”參數(shù)
git revert -m 1 commit-id
- 對(duì)于普通的commtit對(duì)象
git revert commit-id
1 問(wèn)題描述
一個(gè)同事不小把自己代碼合并 到了master生成C2。但是在該同事還沒(méi)有回滾之前暑刃,又有其他同事合并到master生成了 C3∷夏纾現(xiàn)在問(wèn)題是我們想要回滾到C1應(yīng)該怎么做吉懊?
2 解決問(wèn)題
1拖吼、選擇git revert還是git reset授滓?
git revert是生成新的commit對(duì)象琳水,而git reset是刪除commit對(duì)象,為了保留記錄般堆,使用git revert命令在孝。
2、解決
按時(shí)間順序淮摔,依次回滾每一個(gè)commit對(duì)象私沮,直到自己想要的那個(gè)commit對(duì)象為準(zhǔn)。如下:
- 回滾C3 ,c3-id是C3對(duì)應(yīng)的那個(gè)commit-id
git revert -m 1 c3-id
- 回滾C2和橙。其中c2-id是C2對(duì)應(yīng)的那個(gè)commit-id
git revert -m 1 c2-id
3仔燕、上面的命令為什么有“-m 1”造垛?
這是因?yàn)樯厦娴腃2和C3不是普通的commit對(duì)象,都是merge生成的commit對(duì)象涨享。如下圖筋搏,如果需要通過(guò)git revert回滾M3,那么此時(shí)會(huì)在M3后面生成一個(gè)新的commit節(jié)點(diǎn)R,那么這個(gè)新節(jié)點(diǎn)R是屬于M分支(M1->M2->M3->R)還是D分支(D1->D2->D3->R)呢厕隧?此時(shí)可以通過(guò)“-m” 來(lái)指定奔脐,如果是1,表示的是當(dāng)前所在的分支吁讨,如果是2表示的是另外的分支髓迎。
對(duì)于上面的問(wèn)題,由于我們當(dāng)前分支是master建丧,我們希望revert之后生成的commit也在master這條分支上排龄,所以指定“-m 1”。
這里需要注意的是翎朱,如果只是普通的commit 對(duì)象橄维,不是merge類型commit對(duì)象(由merge產(chǎn)生commit對(duì)象),就不需要”-m 1″了
3 問(wèn)題總結(jié)
后續(xù)再遇到代碼回滾拴曲,通過(guò)哪些步驟來(lái)做呢争舞?這里總結(jié)了一些步驟。
1澈灼、假設(shè)在master上C0后面有C1竞川、C2、C3三次代碼提交叁熔,此時(shí)需要回滾到C0委乌。
2、回滾步驟如下
(1)第一步 切到master代碼荣回,使用git log遭贸,如下圖,獲取到每一個(gè)commit對(duì)象對(duì)應(yīng)的commit-id和commit對(duì)象類型(是普通類型還是merge類型)心软。
commit ee1389bba4bfcaa0ddb850c6e58d1e982fdfcb4d
Merge: 8397201 7dc873c
Author: jie01 <jie01@qq.com>
Date: Thu Oct 12 20:27:33 2017 +0800
Merge branch 'fweb_1-0-806_BRANCH' into master
commit 2f315650dff28e94d654309ed3230c34d32f1000
Author: shan03 <shan03@qq.com>
Date: Tue Oct 17 14:33:16 2017 +0800
commit 7dc873cd34b8bba1fab68635ddf8331ab2babc74
Merge: cbaf7d9 d24f133
Author: xu01 <xu01@qq.com>
Date: Thu Oct 12 15:59:13 2017 +0800
Merge branch 'web_1-0-790_BRANCH' into financeweb_1-0-806_BRANCH
(2) 第二步 按時(shí)間倒序革砸,依次執(zhí)行”git revert”回滾每一個(gè)commit對(duì)象
- 對(duì)于 merge類型的commit對(duì)象,需要“-m”參數(shù)
git revert -m 1 commit-id
- 對(duì)于普通的commtit對(duì)象
git revert commit-id
所以回滾命令如下
- 回滾C3糯累,merge類型commit對(duì)象,需要-m
git revert -m 1 ee1389bba4bfcaa0ddb850c6e58d1e982fdfcb4d
- 回滾C2,普通commit對(duì)象
git revert 2f315650dff28e94d654309ed3230c34d32f1000
- 回滾C1册踩,merge類型commit對(duì)象泳姐,需要-m
git revert -m 1 7dc873cd34b8bba1fab68635ddf8331ab2babc74
后續(xù)問(wèn)題
比如說(shuō)當(dāng)M3執(zhí)行了git revet生成RM3,此時(shí)在master上又有其他人提交了M4,我們還需要在dev上進(jìn)行開(kāi)發(fā)D4和D5暂吉,如下圖
此時(shí)怎么合并D5到master的M4
(1)第一步 master合并到D5
因?yàn)閙aster執(zhí)行了git revert撤銷操作胖秒,所以此時(shí)D1和D2的代碼會(huì)被刪除缎患?
- 在master上執(zhí)行g(shù)it revert撤銷 RM3
- 將master合并到dev的D5
(2)第二步 將dev的合并到master
附1 其他方法-通過(guò)覆蓋方法來(lái)實(shí)現(xiàn)回滾主干
步驟如下:
- 第一步 分別拉兩份代碼。第一份代碼上面基于master新建一個(gè)分支F1阎肝,作為發(fā)布分支挤渔;第二份代碼可以通過(guò)git checkout切到自己想要回滾的那個(gè)版本上(切到某個(gè)commit對(duì)象上)。
- 第二步 刪除分支F1對(duì)應(yīng)的代碼风题,把第二份代碼拷貝到F1上面判导,即使用第二份代碼覆蓋F1的代碼。沛硅。
- 第三步 提交F1代碼眼刃,并合并到master。
附2 git revert和git reset區(qū)別
1摇肌、git revert 擂红。只是撤銷某一次commit的操作,并沒(méi)有刪除commit對(duì)象围小;并且會(huì)生成一個(gè)新的commit對(duì)象昵骤。
假設(shè)分支為m1->m2->m3。當(dāng)我們”git revet m2″生成m4肯适,此時(shí)在m4里面只是把m2的操作撤銷了而已变秦,并沒(méi)有撤銷m2和m3對(duì)象。此時(shí)分支為“m1->m2->m3->m4”
2疹娶、git reset伴栓。刪除某一個(gè)commit之后所有commit提交對(duì)象;不會(huì)產(chǎn)生新的commit對(duì)象雨饺。
假設(shè)m1->m2->m3钳垮,當(dāng)我們git reset m2,此時(shí)并沒(méi)有生成新的commit對(duì)象额港,并且刪除了m2和m3兩個(gè)commit對(duì)象饺窿,此時(shí)分分支為”m1->m2″
撤銷提交
Git 的 revert 命令可以用來(lái)撤銷提交(commit),對(duì)于常規(guī)的提交來(lái)說(shuō)移斩,revert 命令十分直觀易用肚医,相當(dāng)于做一次被 revert 的提交的「反操作」并形成一個(gè)新的 commit,但是當(dāng)你需要撤銷一個(gè)合并(merge)的時(shí)候向瓷,事情就變得稍微復(fù)雜了一些肠套。
Merge Commit
在描述 merge commit 之前,先來(lái)簡(jiǎn)短地描述一下常規(guī)的 commit猖任。每當(dāng)你做了一批操作(增加你稚、修改、或刪除)之后,你執(zhí)行 git commit 便會(huì)得到一個(gè)常規(guī)的 Commit刁赖。執(zhí)行 git show <commit> 將會(huì)輸出詳細(xì)的增刪情況搁痛。
Merge commit 則不是這樣。每當(dāng)你使用 git merge 合并兩個(gè)分支宇弛,你將會(huì)得到一個(gè)新的 merge commit鸡典。執(zhí)行 git show <commit> 之后,會(huì)有類似的輸出:
commit 19b7d40d2ebefb4236a8ab630f89e4afca6e9dbe
Merge: b0ef24a cca45f9
......
其中枪芒,Merge 這一行代表的是這個(gè)合并 parents彻况,它可以用來(lái)表明 merge 操作的線索。
舉個(gè)例子病苗,通常疗垛,我們的穩(wěn)定代碼都在 master 分支,而開(kāi)發(fā)過(guò)程使用 dev 分支硫朦,當(dāng)開(kāi)發(fā)完成后贷腕,再把 dev 分支 merge 進(jìn) master 分支:
a -> b -> c -> f -- g -> h (master)
\ /
d -> e (dev)
上圖中,g 是 merge commit咬展,其他的都是常規(guī) commit泽裳。g 的兩個(gè) parent 分別是 f 和 e。
Revert a Merge Commit
當(dāng)你使用 git revert 撤銷一個(gè) merge commit 時(shí)破婆,如果除了 commit 號(hào)而不加任何其他參數(shù)涮总,git 將會(huì)提示錯(cuò)誤:
git revert g
error: Commit g is a merge but no -m option was given.
fatal: revert failed
在你合并兩個(gè)分支并試圖撤銷時(shí),Git 并不知道你到底需要保留哪一個(gè)分支上所做的修改祷舀。從 Git 的角度來(lái)看瀑梗,master 分支和 dev 在地位上是完全平等的,只是在 workflow 中裳扯,master 被人為約定成了「主分支」抛丽。
于是 Git 需要你通過(guò) m 或 mainline 參數(shù)來(lái)指定「主線」。merge commit 的 parents 一定是在兩個(gè)不同的線索上饰豺,因此可以通過(guò) parent 來(lái)表示「主線」亿鲜。m 參數(shù)的值可以是 1 或者 2,對(duì)應(yīng)著 parent 在 merge commit 信息中的順序冤吨。
以上面那張圖為例蒿柳,我們查看 commit g 的內(nèi)容:
git show g
commit g
Merge: f e
那么,$ git revert -m 1 g 將會(huì)保留 master 分支上的修改漩蟆,撤銷 dev 分支上的修改垒探。
撤銷成功之后,Git 將會(huì)生成一個(gè)新的 Commit怠李,提交歷史就成了這樣:
a -> b -> c -> f -- g -> h -> G (master)
\ /
d -> e (dev)
其中 G 是撤銷 g 生成的 commit叛复。通過(guò) $ git show G 之后仔引,我們會(huì)發(fā)現(xiàn) G 是一個(gè)常規(guī)提交,內(nèi)容就是撤銷 merge 時(shí)被丟棄的那條線索的所有 commit 的「反操作」的合集褐奥。
Recover a Reverted Merging
上面的提交歷史在實(shí)踐中通常對(duì)應(yīng)著這樣的情況:
工程師在 master 分支切出了 dev 分支編寫新功能,開(kāi)發(fā)完成后合并 dev 分支到 master 分支并上線翘簇。上線之后撬码,發(fā)現(xiàn)了 dev 分支引入了嚴(yán)重的 bug,而其他人已經(jīng)在最新的 master 上切出了新的分支并進(jìn)行開(kāi)發(fā)版保,所以不能簡(jiǎn)單地在 master 分支上通過(guò)重置(git reset )來(lái)回滾代碼呜笑,只能選擇 revert 那個(gè) merge commit。
但是事情還沒(méi)有結(jié)束彻犁。工程師必須切回 dev 分支修復(fù)那些 bug叫胁,于是提交記錄變成了這個(gè)樣子:
a -> b -> c -> f -- g -> h -> G -> i (master)
\ /
d -> e -> j -> k (dev)
工程師返回 dev 分支通過(guò) j,k 兩個(gè) commit 修復(fù)了 bug汞幢,其他工程師在 master 上有了新的提交 i⊥斩欤現(xiàn)在到了 dev 分支的內(nèi)容重新上線的時(shí)候了。
直覺(jué)上來(lái)說(shuō)森篷,還是和之前一樣输钩,把 dev 分支合并到 master 分支就好了。于是:
$ git checkout master
$ git merge dev
得到的提交記錄變成了這樣:
a -> b -> c -> f -- g -> h -> G -> i -- m (master)
\ / /
d -> e -> j -> k --------- (dev)
m 是新的 merge commit仲智。需要注意的是买乃,這不能得到我們期望的結(jié)果。因?yàn)?d 和 e 兩個(gè)提交曾經(jīng)被丟棄過(guò)钓辆,如此合并到 master 的代碼剪验,并不會(huì)重新包含 d 和 e 兩個(gè)提交的內(nèi)容,相當(dāng)于只有 dev 上的新 commit 被合并了進(jìn)來(lái)前联,而 dev 分支之前的內(nèi)容功戚,依然是被 revert 掉了。
所以蛀恩,如果想恢復(fù)整個(gè) dev 所做的修改疫铜,應(yīng)該:
$ git checkout master
$ git revert G
$ git merge dev
于是,提交歷史變成了這樣:
a -> b -> c -> f -- g -> h -> G -> i -> G' -- m (master)
\ / /
d -> e -> j -> k --------------- (dev)
其中 G' 是這次 revert 操作生成的 commit双谆,把之前撤銷合并時(shí)丟棄的代碼恢復(fù)了回來(lái)壳咕,然后再 merge dev 分支,把解 bug 寫的新代碼合并到 master 分支顽馋。
現(xiàn)在谓厘,工程師可以放心地上線沒(méi)有 bug 的新功能了。