Git Reset 三種模式hard,soft,mix

有時候,咱們用Git的時候有可能commit提交代碼後,發現這一次commit的內容是有錯誤的,那麼有兩種處理方法:
一、修改錯誤內容,再次commit一次 二、使用git reset 命令撤銷這一次錯誤的commit
第一種方法比較直接,但會屢次一次commit記錄。
而我我的更傾向第二種方法,錯誤的commit不必保留下來。
那麼今天來講一下git reset。它的一句話歸納git

git-reset - Reset current HEAD to the specified state

意思就是可讓HEAD這個指針指向其餘的地方。例如咱們有一次commit不是否是很滿意,須要回到上一次的Commit裏面。那麼這個時候就須要經過reset,把HEAD指針指向上一次的commit的點。
它有三種模式,soft,mixed,hard,具體的使用方法下面這張圖,展現的很全面了。spa

git各個區域和命令關係指針

 

這三個模式理解了,對於使用這個命令頗有幫助。在理解這三個模式以前,須要略微知道一點Git的基本流程。正如上圖,Git會有三個區域:code

  • Working Tree 當前的工做區域
  • Index/Stage 暫存區域,和git stash命令暫存的地方不同。使用git add xx,就能夠將xx添加近Stage裏面
  • Repository 提交的歷史,即便用git commit提交後的結果

文件存入Repository流程索引

如下簡單敘述一下把文件存入Repository流程:ci

  1. 剛開始 working tree 、 index 與 repository(HEAD)裏面的內容都是一致的開發

     

    階段1it

  2. 當git管理的文件夾裏面的內容出現改變後,此時 working tree 的內容就會跟 index 及 repository(HEAD)的不一致,而Git知道是哪些文件(Tracked File)被改動過,直接將文件狀態設置爲 modified (Unstaged files)。file

     

    階段2方法

  3. 當我們執行 git add 後,會將這些改變的文件內容加入 index 中 (Staged files),因此此時working tree跟index的內容是一致的,但他們與repository(HEAD)內容不一致。

     

    階段3

  4. 接着執行 git commit 後,將Git索引中全部改變的文件內容提交至 Repository 中,創建出新的 commit 節點(HEAD)後, working tree 、 index 與與repository(HEAD)區域的內容 又會保持一致。

     

    階段4

實戰演示

reset --hard:重置stage區和工做目錄:

reset --hard 會在重置 HEAD 和branch的同時,重置stage區和工做目錄裏的內容。當你在 reset 後面加了 --hard 參數時,你的stage區和工做目錄裏的內容會被徹底重置爲和HEAD的新位置相同的內容。換句話說,就是你的沒有commit的修改會被所有擦掉。

例如你在上次 commit 以後又對文件作了一些改動:把修改後的ganmes.txt文件addstage區,修改後的shopping list.txt保留在工做目錄

git status

 

最初狀態


而後,你執行了reset並附上了--hard參數:

 

git reset --hard HEAD^

你的 HEAD 和當前 branch 切到上一條commit 的同時,你工做目錄裏的新改動和已經add到stage區的新改動也一塊兒全都消失了:

git status

 

reset --hard head^以後


能夠看到,在 reset --hard 後,全部的改動都被擦掉了。

 

reset --soft:保留工做目錄,並把重置 HEAD 所帶來的新的差別放進暫存區

reset --soft 會在重置 HEAD 和 branch 時,保留工做目錄和暫存區中的內容,並把重置 HEAD 所帶來的新的差別放進暫存區。

什麼是「重置 HEAD 所帶來的新的差別」?就是這裏:

 

因爲 HEAD 從 4 移動到了 3,並且在 reset 的過程當中工做目錄和暫存區的內容沒有被清理掉,因此 4 中的改動在 reset 後就也成了工做目錄新增的「工做目錄和 HEAD 的差別」。這就是上面一段中所說的「重置 HEAD 所帶來的差別」。

此模式下會保留 working tree工做目錄的內容,不會改變到目前全部的git管理的文件夾的內容;也會
保留 index暫存區的內容,讓 index 暫存區與 working tree 工做目錄的內容是一致的。就只有 repository 中的內容的更變須要與 reset 目標節點一致,所以原始節點與reset節點之間的差別變動集合會存在與index暫存區中(Staged files),因此咱們能夠直接執行 git commit 將 index暫存區中的內容提交至 repository 中。當咱們想合併「當前節點」與「reset目標節點」之間不具太大意義的 commit 記錄(多是階段性地頻繁提交)時,能夠考慮使用 Soft Reset 來讓 commit 演進線圖較爲清晰點。

 

因此在一樣的狀況下,仍是老樣子:把修改後的ganmes.txt文件addstage區,修改後的shopping list.txt保留在工做目錄

git status

 

最初狀態


假設此時當前 commit 的改動內容是新增了 laughters.txt 文件:

 

git show --stat

git show --stat

若是這時你執行:

git reset --soft HEAD^

那麼除了 HEAD 和它所指向的 branch1 被移動到 HEAD^ 以外,原先 HEAD 處 commit 的改動(也就是那個 laughters.txt 文件)也會被放進暫存區:

git status

 

使用git reset --soft HEAD^後


這就是--soft 和 --hard 的區別:--hard 會清空工做目錄和暫存區的改動,*而 --soft則會保留工做目錄的內容,並把由於保留工做目錄內容所帶來的新的文件差別放進暫存區

 

reset 不加參數(mixed):保留工做目錄,並清空暫存區

reset 若是不加參數,那麼默認使用 --mixed 參數。它的行爲是:保留工做目錄,而且清空暫存區。也就是說,工做目錄的修改、暫存區的內容以及由 reset 所致使的新的文件差別,都會被放進工做目錄。簡而言之,就是「把全部差別都混合(mixed)放在工做目錄中」。

還以一樣的狀況爲例:

git status

最初狀態

修改了 的games.txt 和 shopping list.txt,並把 games.txt 放進了暫存區。

git show --stat

git show --stat

最新的 commit 中新增了 laughters.txt 文件。

這時若是你執行無參數reset或者帶--mixed參數:

git reset HEAD^
git reset --mixed HEAD^

工做目錄的內容和 --soft 同樣會被保留,但和 --soft 的區別在於,它會把暫存區清空,並把原節點和reset節點的差別的文件放在工做目錄,總而言之就是,工做目錄的修改、暫存區的內容以及由 reset 所致使的新的文件差別,都會被放進工做目錄

git status

git reset HEAD^以後

總結

reset 的本質:移動 HEAD 以及它所指向的 branch

實質上,reset 這個指令雖然能夠用來撤銷 commit ,但它的實質行爲並非撤銷,而是移動 HEAD ,而且「捎帶」上 HEAD 所指向的 branch(若是有的話)。也就是說,reset 這個指令的行爲其實和它的字面意思 "reset"(重置)十分相符:它是用來重置 HEAD 以及它所指向的 branch 的位置的。

而 reset --hard HEAD^ 之因此起到了撤銷 commit 的效果,是由於它把 HEAD 和它所指向的 branch 一塊兒移動到了當前 commit 的父 commit 上,從而起到了「撤銷」的效果:

git reset

Git 的歷史只能往回看,不能向將來看,因此把 HEAD 和 branch 往回移動,就能起到撤回 commit 的效果。

因此同理,reset --hard 不只能夠撤銷提交,還能夠用來把 HEAD 和 branch 移動到其餘的任何地方。

git reset --hard branch2

git reset --hard branch2

reset三種模式區別和使用場景

區別:

  1. --hard:重置位置的同時,直接將 working Tree工做目錄、 index 暫存區及 repository 都重置成目標Reset節點的內容,因此效果看起來等同於清空暫存區和工做區。

  2. --soft:重置位置的同時,保留working Tree工做目錄index暫存區的內容,只讓repository中的內容和 reset 目標節點保持一致,所以原節點和reset節點之間的【差別變動集】會放入index暫存區中(Staged files)。因此效果看起來就是工做目錄的內容不變,暫存區原有的內容也不變,只是原節點和Reset節點之間的全部差別都會放到暫存區中。

  3. --mixed(默認):重置位置的同時,只保留Working Tree工做目錄的內容,但會將 Index暫存區 和 Repository 中的內容更改和reset目標節點一致,所以原節點和Reset節點之間的【差別變動集】會放入Working Tree工做目錄中。因此效果看起來就是原節點和Reset節點之間的全部差別都會放到工做目錄中。

使用場景:

  1. --hard:(1) 要放棄目前本地的全部改變時,即去掉全部add到暫存區的文件和工做區的文件,能夠執行 git reset -hard HEAD 來強制恢復git管理的文件夾的內容及狀態;(2) 真的想拋棄目標節點後的全部commit(可能以爲目標節點到原節點之間的commit提交都是錯了,以前全部的commit有問題)。

  2. --soft:原節點和reset節點之間的【差別變動集】會放入index暫存區中(Staged files),因此假如咱們以前工做目錄沒有改過任何文件,也沒add到暫存區,那麼使用reset --soft後,咱們能夠直接執行 git commit 將 index暫存區中的內容提交至 repository 中。爲何要這樣呢?這樣作的使用場景是:假如咱們想合併「當前節點」與「reset目標節點」之間不具太大意義的 commit 記錄(多是階段性地頻繁提交,就是開發一個功能的時候,改或者增長一個文件的時候就commit,這樣作致使一個完整的功能可能會好多個commit點,這時假如你須要把這些commit整合成一個commit的時候)時,能夠考慮使用reset --soft來讓 commit 演進線圖較爲清晰。總而言之,可使用--soft合併commit節點

  3. --mixed(默認):(1)使用完reset --mixed後,我們能夠直接執行 git add 將這些改變果的文件內容加入 index 暫存區中,再執行 git commit 將 Index暫存區 中的內容提交至Repository中,這樣同樣能夠達到合併commit節點的效果(與上面--soft合併commit節點差很少,只是多了git add添加到暫存區的操做);(2)移除全部Index暫存區中準備要提交的文件(Staged files),咱們能夠執行 git reset HEAD 來 Unstage 全部已列入 Index暫存區 的待提交的文件。(有時候發現add錯文件到暫存區,就可使用命令)。(3)commit提交某些錯誤代碼,或者沒有必要的文件也被commit上去,不想再修改錯誤再commit(由於會留下一個錯誤commit點),能夠回退到正確的commit點上,而後全部原節點和reset節點之間差別會返回工做目錄,假若有個不必的文件的話就能夠直接刪除了,再commit上去就OK了。

相關文章
相關標籤/搜索