git reset 和 git revert

今天恰巧遇到了須要回退分支的問題,我以前一直在用的都是git reset,自認爲清楚明瞭,也就沒去看還有其餘的回退方法。今天突然被問到,特地查了查。git

git reset

develop ----1      3-----
             \   /
branch a       a
複製代碼

develop將a分支合併後,想要不留痕跡的撤回合併。這個時候用git reset就是很好的選擇了。github

具體的操做步驟以下:緩存

  1. 切換分支到develop
  2. git log 查看當前分支日誌

例如個人日誌是這樣的:bash

commit 3
Merge 1 2
Author: admin <admin@163.com>
Date: Wed May 30 15:00:00 2018 +0800

    Merge branch 'feature/a' into 'develop'
    
    close a
    
    See merge request !20
    
commit 2
Author: admin <admin@163.com>
Date: Wed May 30 14:00:00 2018 +0800

    close a
    
commit 1
Author: admin <admin@163.com>
Date: Wed May 30 13:00:00 2018 +0800
    init project
複製代碼

我要將develop回退到合併以前的狀態,那就是退到 commit 1這了,將commit號複製下來。退出編輯界面。服務器

  1. 講一下git reset參數定義,具體見官網
    --soft 回退後a分支修改的代碼被保留並標記爲add的狀態(git status 是綠色的狀態)
    --mixed 重置索引,但不重置工做樹,更改後的文件標記爲未提交(add)的狀態。默認操做。
    --hard 重置索引和工做樹,而且a分支修改的全部文件和中間的提交,沒提交的代碼都被丟棄了。
    --merge 和--hard相似,只不過若是在執行reset命令以前你有改動一些文件而且未提交,merge會保留你的這些修改,hard則不會。【注:若是你的這些修改add過或commit過,merge和hard都將刪除你的提交】
    --keep 和--hard相似,執行reset以前改動文件若是是a分支修改了的,會提示你修改了相同的文件,不能合併。若是不是a分支修改的文件,會移除緩存區。git status仍是能夠看到保持了這些修改。
    
    複製代碼

a分支的代碼我不須要了,之後應該也不須要了。所以:測試

git reset 1(粘貼過來的commit號) --hardspa

a分支代碼我仍是想要的,只是這個提交我不想要了: git reset 1日誌

  1. git log查看一下:
commit 1
Author: admin <admin@163.com>
Date: Wed May 30 13:00:00 2018 +0800
    init project
複製代碼

是我想要的沒錯了,能夠push到遠端去了。(這一步很危險,必定要確認好reset的結果確實是你想要的結果,不然,丟失了代碼就不要怨我了)code

git push origin develop索引

![rejected] develop -> develop (non-fast-forward)
error: 沒法推送一些引用到 'git@github.cn:...'
提示:更新被拒絕,由於您當前分支的最新提交落後於其對應的遠程分支。
。。。
複製代碼

好吧,個人分支確實落後於遠程的develop分支。我須要--force

git push origin develop --force

Total 0 (delta 0), reused 0 (delta 0)
To git@...
+ 83***...23***** develop -> develop (forced update)
複製代碼

能夠了,回到github查看network。嗯,就是想要這樣的。

git revert

仍是這個例子

develop ----1      3-----
             \   /
branch a       a
複製代碼

仍是以前的需求,不想要合併a,只想要沒合併a時的樣子。

操做步驟:

  1. 切分支到develop:git checkout develop
  2. 查看日誌:git log,仍是上面的日誌:
commit 3
Merge 1 2
Author: admin <admin@163.com>
Date: Wed May 30 15:00:00 2018 +0800

    Merge branch 'feature/a' into 'develop'
    
    close a
    
    See merge request !20
    
commit 2
Author: admin <admin@163.com>
Date: Wed May 30 14:00:00 2018 +0800

    close a
    
commit 1
Author: admin <admin@163.com>
Date: Wed May 30 13:00:00 2018 +0800
    init project
複製代碼

此次和git reset 不一樣的是我不能複製 commit 1這個commit號了,我須要複製的是commit 2的commit號。由於revert後面跟的是具體須要哪一個已經合併了的分支,而並非須要會退到哪的commit號。

  1. 開始回退:git revert 2
Revert "close a"
This reverts commit 2
#.......
複製代碼

這是至關於又新增了一個commit,把a分支上的全部修改又改了回去。

  1. Ctrl+X離開編輯commit信息頁面。

git log查看一下是否是我想的那樣

commit 4
Author: admin <admin@163.com>
Date: Wed May 30 17:00:00 2018 +0800
    Revert "close a"
    This reverts commit 2
commit 3
Merge 1 2
Author: admin <admin@163.com>
Date: Wed May 30 15:00:00 2018 +0800

    Merge branch 'feature/a' into 'develop'
    
    close a
    
    See merge request !20
    
commit 2
....
複製代碼

確實是新增長了一個commit,查看代碼發現a分支的修改都不存在了,也達到了我想要的效果。

  1. push的遠程服務器上git push origin develop

查看network,是這樣的:

develop ----1      3-----revert a------
             \   /
branch a       a
複製代碼

如此看來,git resetgit revert都能實現我如今的需求,那這兩個到底有什麼區別呢,在網上查了這個問題,我以爲說的有些抽象,看的不是很明白,因而本身實踐了以後才明白。

區別

  1. git revert是用一次新的commit來回滾以前的commit,git reset是直接刪除指定的commit。

    這個很好理解,在剛纔的操做中咱們看日誌已經能夠看到這個現象。

    git reset操做以後,咱們查看上面例子的network已經能夠看到network中只有commit 1,分支a合併分支後的commit 3都消失了;

    git revert操做以後,network中仍是能夠看到a分支和合並a分支的操做,只不過在其基礎上又增長了一個revert的commit而已。

  2. git reset 是把HEAD向後移動了一下,而git revert是HEAD繼續前進,只是新的commit的內容和要revert的內容正好相反,可以抵消要被revert的內容。

    這個也是能夠清晰明瞭的看到,我就不作過多的解釋了

  3. 在回滾這一操做上看,效果差很少。可是在往後繼續merge之前的老版本時有區別。由於git revert是用一次逆向的commit「中和」以前的提交,所以往後合併老的branch時,致使這部分改變不會再次出現,可是git reset是之間把某些commit在某個branch上刪除,於是和老的branch再次merge時,這些被回滾的commit應該還會被引入。

    git revert

    如今的需求是我以前已經把a分支revert了,可是我如今又須要a分支的代碼了,我以前都寫過一遍總不能再從新寫一遍了。我首先想到的方法,把a分支再merge到develop不就行了。

    git merge a
    複製代碼

    結果

    Already up-to-date
    複製代碼

    啥?由於咱們以前提交合並的a分支的代碼還在,所以咱們並不能在從新合併a分支。

    解決辦法:使用revert以前revert的commit號。在上面的例子中就是git revert 4。因而又新增了一個commit,把以前revert的代碼又從新revert回來了。具體參考

    git reset

    依舊是上面的需求。

    develop -----1-----4-------5------6
                  \   / \     / \    /
    feature         b      c       d
    複製代碼

    如今我將develop reset到commit 4這裏,繼續提交我之後的代碼

    develop -----1-----4-------7------8
                  \   / \\     /
    feature         b     \ e
                            \------5----d
                              \-c-/
    複製代碼

    我如今想從新合併d分支的代碼

    develop -----1-----4-------7------8------9
                  \   / \\     /             /
    feature         b     \ e              /
                            \------5----d/
                              \-c-/
    複製代碼

    合併過來大概是這個樣子的。也就是說d分支以前已經合併過c分支,所以若是還想要合併d分支的話,c分支的代碼就會同步的帶過來。

應用場景

講到這裏大部分經常使用的應用場景也就應該是比較清晰的了。

  1. 若是回退分支的代碼之後還須要的話用git revert就再好不過了; 若是分支我就是提錯了沒用了還不想讓別人發現我錯的代碼,那就git reset
  2. 例如:develop分支已經合併了a、b、c、d四個分支,我突然發現b分支沒用啊,代碼也不必,這個時候就不能用reset了,由於使用reset以後c和d的分支也一樣消失了。這時候只能用git revert b分支commit號,這樣c和d的代碼依然還在。

總結

我能想到的大概就是這些了,固然在具體項目上咱們會遇到不少沒遇到過的情形。個人建議是不要盲目的查找相似問題後就去修改,由於他的問題並不必定就等同於你的問題。我建議去建立測試的分支,等真正整明白了,再去整線上的分支也不遲。

第一次寫了這麼詳細的一篇文章,主要也是由於網上查到的知識,對git知識體系通常的人來講,看的比較費解。

相關文章
相關標籤/搜索