git 還原、恢復、回退


通過git revert來實現線主干代碼的回滾。如下命令

  • 對於 merge類型的commit對象,還需要“-m”參數

git revert -m 1  commit-id

  • 對於普通的commtit對象

git revert commit-id

1 問題描述

一個同事不小把自己代碼合並 到了master生成C2。但是在該同事還沒有回滾之前,又有其他同事合並到master生成了 C3。現在問題是我們想要回滾到C1應該怎么做?

1

2 解決問題

1、選擇git revert還是git reset?

git revert是生成新的commit對象,而git reset是刪除commit對象,為了保留記錄,使用git revert命令。

2、解決

按時間順序,依次回滾每一個commit對象,直到自己想要的那個commit對象為准。如下:

  • 回滾C3 ,c3-id是C3對應的那個commit-id

git revert -m  1  c3-id

  • 回滾C2。其中c2-id是C2對應的那個commit-id

git  revert  -m  1  c2-id

3、上面的命令為什么有“-m 1”?

這是因為上面的C2和C3不是普通的commit對象,都是merge生成的commit對象。如下圖,如果需要通過git revert回滾M3,那么此時會在M3后面生成一個新的commit節點R,那么這個新節點R是屬於M分支(M1->M2->M3->R)還是D分支(D1->D2->D3->R)呢?此時可以通過“-m” 來指定,如果是1,表示的是當前所在的分支,如果是2表示的是另外的分支。

2

對於上面的問題,由於我們當前分支是master,我們希望revert之后生成的commit也在master這條分支上,所以指定“-m 1”。

這里需要注意的是,如果只是普通的commit 對象,不是merge類型commit對象(由merge產生commit對象),就不需要”-m 1″了

3 問題總結

后續再遇到代碼回滾,通過哪些步驟來做呢?這里總結了一些步驟。

1、假設在master上C0后面有C1、C2、C3三次代碼提交,此時需要回滾到C0。

3

2、回滾步驟如下

(1)第一步  切到master代碼,使用git log,如下圖,獲取到每一個commit對象對應的commit-id和commit對象類型(是普通類型還是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) 第二步  按時間倒序,依次執行”git revert”回滾每一個commit對象

  • 對於 merge類型的commit對象,需要“-m”參數
    git revert -m 1  commit-id

  • 對於普通的commtit對象
    git revert commit-id

所以回滾命令如下

  • 回滾C3,merge類型commit對象,需要-m

git revert  -m 1   ee1389bba4bfcaa0ddb850c6e58d1e982fdfcb4d

  • 回滾C2,普通commit對象

git revert  2f315650dff28e94d654309ed3230c34d32f1000

  • 回滾C1,merge類型commit對象,需要-m

git revert -m 1  7dc873cd34b8bba1fab68635ddf8331ab2babc74

后續問題

比如說當M3執行了git revet生成RM3,此時在master上又有其他人提交了M4,我們還需要在dev上進行開發D4和D5,如下圖

Snip20171203_2

此時怎么合並D5到master的M4

(1)第一步  master合並到D5

因為master執行了git revert撤銷操作,所以此時D1和D2的代碼會被刪除?

  • 在master上執行git revert撤銷 RM3
  • 將master合並到dev的D5

(2)第二步 將dev的合並到master

附1 其他方法-通過覆蓋方法來實現回滾主干

步驟如下:

  • 第一步 分別拉兩份代碼。第一份代碼上面基於master新建一個分支F1,作為發布分支;第二份代碼可以通過git checkout切到自己想要回滾的那個版本上(切到某個commit對象上)。
  • 第二步 刪除分支F1對應的代碼,把第二份代碼拷貝到F1上面,即使用第二份代碼覆蓋F1的代碼。。
  • 第三步  提交F1代碼,並合並到master。

附2 git revert和git reset區別

1、git revert 。只是撤銷某一次commit的操作,並沒有刪除commit對象;並且會生成一個新的commit對象。

假設分支為m1->m2->m3。當我們”git revet m2″生成m4,此時在m4里面只是把m2的操作撤銷了而已,並沒有撤銷m2和m3對象。此時分支為“m1->m2->m3->m4”

2、git reset。刪除某一個commit之后所有commit提交對象;不會產生新的commit對象。

假設m1->m2->m3,當我們git reset m2,此時並沒有生成新的commit對象,並且刪除了m2和m3兩個commit對象,此時分分支為”m1->m2″

撤銷提交

Git 的 revert 命令可以用來撤銷提交(commit),對於常規的提交來說,revert 命令十分直觀易用,相當於做一次被 revert 的提交的「反操作」並形成一個新的 commit,但是當你需要撤銷一個合並(merge)的時候,事情就變得稍微復雜了一些。

Merge Commit
在描述 merge commit 之前,先來簡短地描述一下常規的 commit。每當你做了一批操作(增加、修改、或刪除)之后,你執行 git commit 便會得到一個常規的 Commit。執行 git show 將會輸出詳細的增刪情況。

Merge commit 則不是這樣。每當你使用 git merge 合並兩個分支,你將會得到一個新的 merge commit。執行 git show 之后,會有類似的輸出:

commit 19b7d40d2ebefb4236a8ab630f89e4afca6e9dbe
Merge: b0ef24a cca45f9
......
其中,Merge 這一行代表的是這個合並 parents,它可以用來表明 merge 操作的線索。

舉個例子,通常,我們的穩定代碼都在 master 分支,而開發過程使用 dev 分支,當開發完成后,再把 dev 分支 merge 進 master 分支:

a -> b -> c -> f -- g -> h (master)
           \      /
            d -> e  (dev)

上圖中,g 是 merge commit,其他的都是常規 commit。g 的兩個 parent 分別是 f 和 e。

Revert a Merge Commit
當你使用 git revert 撤銷一個 merge commit 時,如果除了 commit 號而不加任何其他參數,git 將會提示錯誤:

git revert g
error: Commit g is a merge but no -m option was given.
fatal: revert failed
在你合並兩個分支並試圖撤銷時,Git 並不知道你到底需要保留哪一個分支上所做的修改。從 Git 的角度來看,master 分支和 dev 在地位上是完全平等的,只是在 workflow 中,master 被人為約定成了「主分支」。

於是 Git 需要你通過 m 或 mainline 參數來指定「主線」。merge commit 的 parents 一定是在兩個不同的線索上,因此可以通過 parent 來表示「主線」。m 參數的值可以是 1 或者 2,對應着 parent 在 merge commit 信息中的順序。

以上面那張圖為例,我們查看 commit g 的內容:

git show g
commit g
Merge: f e
那么,$ git revert -m 1 g 將會保留 master 分支上的修改,撤銷 dev 分支上的修改。

撤銷成功之后,Git 將會生成一個新的 Commit,提交歷史就成了這樣:


a -> b -> c -> f -- g -> h -> G (master)
           \      /
            d -> e  (dev)

其中 G 是撤銷 g 生成的 commit。通過 $ git show G 之后,我們會發現 G 是一個常規提交,內容就是撤銷 merge 時被丟棄的那條線索的所有 commit 的「反操作」的合集。

Recover a Reverted Merging
上面的提交歷史在實踐中通常對應着這樣的情況:

工程師在 master 分支切出了 dev 分支編寫新功能,開發完成后合並 dev 分支到 master 分支並上線。上線之后,發現了 dev 分支引入了嚴重的 bug,而其他人已經在最新的 master 上切出了新的分支並進行開發,所以不能簡單地在 master 分支上通過重置(git reset )來回滾代碼,只能選擇 revert 那個 merge commit。

但是事情還沒有結束。工程師必須切回 dev 分支修復那些 bug,於是提交記錄變成了這個樣子:

a -> b -> c -> f -- g -> h -> G -> i (master)
           \      /
            d -> e -> j -> k (dev)

工程師返回 dev 分支通過 j,k 兩個 commit 修復了 bug,其他工程師在 master 上有了新的提交 i。現在到了 dev 分支的內容重新上線的時候了。

直覺上來說,還是和之前一樣,把 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。需要注意的是,這不能得到我們期望的結果。因為 d 和 e 兩個提交曾經被丟棄過,如此合並到 master 的代碼,並不會重新包含 d 和 e 兩個提交的內容,相當於只有 dev 上的新 commit 被合並了進來,而 dev 分支之前的內容,依然是被 revert 掉了。

所以,如果想恢復整個 dev 所做的修改,應該:

$ 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,把之前撤銷合並時丟棄的代碼恢復了回來,然后再 merge dev 分支,把解 bug 寫的新代碼合並到 master 分支。

現在,工程師可以放心地上線沒有 bug 的新功能了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM