代码回滚:Reset、Checkout、Revert 的选择
BY 童仲毅(geeeeeeeeek@github)
這是一篇在原文(BY atlassian)基礎上演繹的譯文。除非另行注明,頁面上所有內容采用知識共享-署名(CC BY 2.5 AU)協議共享。
git reset、git checkout?和?git revert?是你的 Git 工具箱中最有用的一些命令。它們都用來撤銷代碼倉庫中的某些更改,而前兩個命令不僅可以作用于提交,還可以作用于特定文件。
因為它們非常相似,所以我們經常會搞混,不知道什么場景下該用哪個命令。在這篇文章中,我們會比較?git reset、git checkout?和?git revert?最常見的用法。希望你在看完后能游刃有余地使用這些命令來管理你的倉庫。
Git 倉庫有三個主要組成——工作目錄,緩存區和提交歷史。這張圖有助于理解每個命令到底產生了哪些影響。當你閱讀的時候,牢記這張圖。
提交層面的操作
你傳給?git reset?和?git checkout?的參數決定了它們的作用域。如果你沒有包含文件路徑,這些操作對所有提交生效。我們這一節要探討的就是提交層面的操作。注意,git revert?沒有文件層面的操作。
Reset
在提交層面上,reset 將一個分支的末端指向另一個提交。這可以用來移除當前分支的一些提交。比如,下面這兩條命令讓 hotfix 分支向后回退了兩個提交。
git checkout hotfix git reset HEAD~2hotfix 分支末端的兩個提交現在變成了懸掛提交。也就是說,下次 Git 執行垃圾回收的時候,這兩個提交會被刪除。換句話說,如果你想扔掉這兩個提交,你可以這么做。reset 操作如下圖所示:
如果你的更改還沒有共享給別人,git reset?是撤銷這些更改的簡單方法。當你開發一個功能的時候發現「糟糕,我做了什么?我應該重新來過!」時,reset 就像是 go-to 命令一樣。
除了在當前分支上操作,你還可以通過傳入這些標記來修改你的緩存區或工作目錄:
- --soft – 緩存區和工作目錄都不會被改變
- --mixed – 默認選項。緩存區和你指定的提交同步,但工作目錄不受影響
- --hard – 緩存區和工作目錄都同步到你指定的提交
把這些標記想成定義?git reset?操作的作用域就容易理解多了。
這些標記往往和 HEAD 作為參數一起使用。比如,git reset --mixed HEAD?將你當前的改動從緩存區中移除,但是這些改動還留在工作目錄中。另一方面,如果你想完全舍棄你沒有提交的改動,你可以使用?git reset --hard HEAD。這是?git reset?最常用的兩種用法。
當你傳入 HEAD 以外的其他提交的時候要格外小心,因為 reset 操作會重寫當前分支的歷史。正如 rebase 黃金法則所說的,在公共分支上這樣做可能會引起嚴重的后果。
Checkout
你應該已經非常熟悉提交層面的?git checkout。當傳入分支名時,可以切換到那個分支。
git checkout hotfix上面這個命令做的不過是將HEAD移到一個新的分支,然后更新工作目錄。因為這可能會覆蓋本地的修改,Git 強制你提交或者緩存工作目錄中的所有更改,不然在 checkout 的時候這些更改都會丟失。和?git reset?不一樣的是,git checkout?沒有移動這些分支。
除了分支之外,你還可以傳入提交的引用來 checkout 到任意的提交。這和 checkout 到另一個分支是完全一樣的:把 HEAD 移動到特定的提交。比如,下面這個命令會 checkout 到當前提交的祖父提交。
git checkout HEAD~2這對于快速查看項目舊版本來說非常有用。但如果你當前的 HEAD 沒有任何分支引用,那么這會造成 HEAD 分離。這是非常危險的,如果你接著添加新的提交,然后切換到別的分支之后就沒辦法回到之前添加的這些提交。因此,在為分離的 HEAD 添加新的提交的時候你應該創建一個新的分支。
Revert
Revert 撤銷一個提交的同時會創建一個新的提交。這是一個安全的方法,因為它不會重寫提交歷史。比如,下面的命令會找出倒數第二個提交,然后創建一個新的提交來撤銷這些更改,然后把這個提交加入項目中。
git checkout hotfix git revert HEAD~2如下圖所示:
相比?git reset,它不會改變現在的提交歷史。因此,git revert?可以用在公共分支上,git reset?應該用在私有分支上。
你也可以把?git revert?當作撤銷已經提交的更改,而?git reset HEAD?用來撤銷沒有提交的更改。
就像?git checkout?一樣,git revert?也有可能會重寫文件。所以,Git 會在你執行 revert 之前要求你提交或者緩存你工作目錄中的更改。
文件層面的操作
git reset?和?git checkout?命令也接受文件路徑作為參數。這時它的行為就大為不同了。它不會作用于整份提交,參數將它限制于特定文件。
Reset
當檢測到文件路徑時,git reset?將緩存區同步到你指定的那個提交。比如,下面這個命令會將倒數第二個提交中的?foo.py?加入到緩存區中,供下一個提交使用。
git reset HEAD~2 foo.py和提交層面的?git reset?一樣,通常我們使用HEAD而不是某個特定的提交。運行?git reset HEAD foo.py?會將當前的?foo.py?從緩存區中移除出去,而不會影響工作目錄中對?foo.py?的更改。
--soft、--mixed?和?--hard?對文件層面的?git reset?毫無作用,因為緩存區中的文件一定會變化,而工作目錄中的文件一定不變。
Checkout
Checkout 一個文件和帶文件路徑?git reset?非常像,除了它更改的是工作目錄而不是緩存區。不像提交層面的 checkout 命令,它不會移動 HEAD引用,也就是你不會切換到別的分支上去。
比如,下面這個命令將工作目錄中的?foo.py?同步到了倒數第二個提交中的?foo.py。
git checkout HEAD~2 foo.py和提交層面相同的是,它可以用來檢查項目的舊版本,但作用域被限制到了特定文件。
如果你緩存并且提交了 checkout 的文件,它具備將某個文件回撤到之前版本的效果。注意它撤銷了這個文件后面所有的更改,而?git revert?命令只撤銷某個特定提交的更改。
和?git reset?一樣,這個命令通常和 HEAD 一起使用。比如?git checkout HEAD foo.py?等同于舍棄?foo.py?沒有緩存的更改。這個行為和?git reset HEAD --hard?很像,但只影響特定文件。
總結
你現在已經掌握了 Git 倉庫中撤銷更改的所有工具。git reset、git checkout?和?git revert?命令比較容易混淆,但當你想起它們對工作目錄、緩存區和提交歷史的不同影響,就會容易判斷現在應該用哪個命令。
下面這個表格總結了這些命令最常用的使用場景。記得經常對照這個表格,因為你使用 Git 時一定會經常用到。
| git reset | 提交層面 | 在私有分支上舍棄一些沒有提交的更改 |
| git reset | 文件層面 | 將文件從緩存區中移除 |
| git checkout | 提交層面 | 切換分支或查看舊版本 |
| git checkout | 文件層面 | 舍棄工作目錄中的更改 |
| git revert | 提交層面 | 在公共分支上回滾更改 |
| git revert | 文件層面 | (然而并沒有) |
?
from:?https://github.com/geeeeeeeeek/git-recipes/wiki/5.2-%E4%BB%A3%E7%A0%81%E5%9B%9E%E6%BB%9A%EF%BC%9AReset%E3%80%81Checkout%E3%80%81Revert-%E7%9A%84%E9%80%89%E6%8B%A9
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的代码回滚:Reset、Checkout、Revert 的选择的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: git reset revert 回退回
- 下一篇: git reset --hard、git