Git 中的回退操作:reset 和 revert

2022-07-08 09:00:43

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 常見操作梳理