Git 中的回退操作:reset 和 revert

Git 中回退有 resetrevert,這兩個的區別就是是否保留更改記錄

假設當前的提交情況是:A <- B <- C <- D <- HEAD,如下圖:

當前是 D,希望回退到 A,那我們可以使用 reset 命令,reset 後再看 git log 就會發現:B <- C <- D 宛如沒有出現過,這適用於想完全捨棄 A 之後的修改

但是如果我們想保留 B <- C <- D 的修改記錄,可能這三個 commit 的功能只是暫時用不到,以後可能還用到,或者可能當前分支是一個公共分支,B <- C <- D 可能已經被同步到了其他小夥伴電腦上,為了盡量避免程式碼衝突。這些情況就需要使用 revert 命令,這樣會重新生成新的 commit,其中包含回退的記錄(假設 D 這個 commit 是添加了一些程式碼,那麼 revert D 的 commit 就是刪除這些程式碼)

reset

使用 git reset A ,reset 會修改 head 的指向,這樣可以回滾到 A,默認使用的參數是 --mixed,這個參數決定了 reset 時 Git 該如何處理工作區和暫存區

一般地,我們對於程式碼的修改會體現在 working tree(工作區)的變動,通過 git add 添加即將要提交的文件到 index(暫存區),通過 git commit 再提交到 repository(本地倉庫),如下圖:

查看幫助:git help reset

 git reset [<mode>] [<commit>]
     This form resets the current branch head to <commit> and possibly updates the index (resetting it to the
     tree of <commit>) and the working tree depending on <mode>. If <mode> is omitted, defaults to --mixed. The
     <mode> must be one of the following:

--soft
    Does not touch the index file or the working tree at all
--mixed
    Resets the index but not the working tree
--hard
    Resets the index and working tree

假設我們現在處在 D,那麼分別會有三種 reset 的情況

  • 我們執行 git reset --soft A,字面意思,輕柔地 reset,只將 repository 回滾到了 A,而 working treeindex 維持 reset 之前的狀態,保持不變,這個時候直接執行 commit,這時候會得到一個和 D 修改內容相同的 commit D'(二者的 commit id 是不相同的),--soft 很適合呈現多次 commit 的修改的疊加效果,假設 BCD 都是針對某一個功能的修改,其中的 commit 可能修改了同一個文件,想整合這些 commit 都修改了哪些內容,就可以使用 --softD reset 到 A,那麼 BCD 的修改都會出現在 index

  • 我們執行 git reset --mixed Arepositoryindex 會回滾,working tree 維持 reset 之前的狀態,這個時候直接 commit,將無法提交,因為 repositoryindex 都被回滾了,二者是相同的,沒有變化則無法提交 commit

  • 我們執行 git reset --hard A,按照參數 hard 的字面意思,reset 的非常強硬,repositoryindexworking tree 都會回滾到 A,因為 working tree 工作區也回滾了,所以本地的所有修改也將丟失,--hard 相對來說比較危險,需要確保工作區沒有需要保留的程式碼,--hard 適合的情況是對於當前的即將要提交的程式碼失去信心,準備推倒重來

revert

如果想在回滾的同時保留 commit 記錄,就需要使用 revert,revert 就是生成原 commit 逆向修改的 commit,從而實現會滾。當前是 D,希望回退到 A,就需要按順序依次 revert DCB

git revert D

git revert C

git revert B

每一次 revert 都會生成新的 commit,需要依次手動輸入 commit message,也可以先 revert 最後集中 commit

git revert --no-commit D
git revert --no-commit C
git revert --no-commit B
git commit -m " Revert D C B"

使用 revert 需要注意,如果即將 revert 的 commit 是一個 merge commit,那麼會出現錯誤

使用 reset 方式,創建回滾的 commit

如果需要保留回滾記錄,但是需要 revert 的 commit 是 merge commit,那就必須手動指定 mainline,比較麻煩,可以用 reset 的方式創建回滾 commit,這種方式不受 merge 方式的影響:

git reset --hard A
git reset --soft D 

git commit -m " Revert D C B"

通過這種方式也能回滾回 A,並且生成一個新的 commit,其中包括了 DCB 的逆向修改:

  1. reset --hardA,這時 repositoryindexworking tree 都會回滾到 A

  2. reset --softD,這時 repository 指向了 D,但是 indexworking tree 還保持在 A,當前即將 commit 的就是 BCD 逆向修改的疊加

參考資料

Pretty Git branch graphs

git revert 用法

git reset soft,hard,mixed之區別深解

What’s the difference between git reset –mixed, –soft, and –hard?

How can I revert multiple Git commits?

Understanding Git — Index


相關閱讀:Git 常見操作梳理

Tags: