Git代碼回滾:Reset、Checkout、Revert 的選擇

一、詳細學習Gitgit

https://github.com/geeeeeeeeek/git-recipes/wikigithub

二、git reset、git checkout、git revert 學習緩存

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安全

git resetgit checkout 和 git revert 是你的 Git 工具箱中最有用的一些命令。它們都用來撤銷代碼倉庫中的某些更改,而前兩個命令不只能夠做用於提交,還能夠做用於特定文件。工具

由於它們很是類似,因此咱們常常會搞混,不知道什麼場景下該用哪一個命令。在這篇文章中,咱們會比較 git resetgit checkout 和 git revert 最多見的用法。但願你在看完後能遊刃有餘地使用這些命令來管理你的倉庫。學習

[此處有圖]spa

Git 倉庫有三個主要組成——工做目錄,緩存區和提交歷史。這張圖有助於理解每一個命令到底產生了哪些影響。當你閱讀的時候,牢記這張圖。code

提交層面的操做

你傳給 git reset 和 git checkout 的參數決定了它們的做用域。若是你沒有包含文件路徑,這些操做對全部提交生效。咱們這一節要探討的就是提交層面的操做。注意,git revert 沒有文件層面的操做。ip

Reset

在提交層面上,reset 將一個分支的末端指向另外一個提交。這能夠用來移除當前分支的一些提交。好比,下面這兩條命令讓 hotfix 分支向後回退了兩個提交。ci

git checkout hotfix
git reset HEAD~2

hotfix 分支末端的兩個提交如今變成了懸掛提交。也就是說,下次 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 resetgit checkout 和 git revert 命令比較容易混淆,但當你想起它們對工做目錄、緩存區和提交歷史的不一樣影響,就會容易判斷如今應該用哪一個命令。

下面這個表格總結了這些命令最經常使用的使用場景。記得常常對照這個表格,由於你使用 Git 時必定會常常用到。

命令 做用域 經常使用情景
git reset 提交層面 在私有分支上舍棄一些沒有提交的更改
git reset 文件層面 將文件從緩存區中移除
git checkout 提交層面 切換分支或查看舊版本
git checkout 文件層面 捨棄工做目錄中的更改
git revert 提交層面 在公共分支上回滾更改
git revert 文件層面 (然而並無)
相關文章
相關標籤/搜索