1. 程式人生 > >git reset soft,hard,mixed之區別深解

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

GIT reset命令,似乎讓人很迷惑,以至於誤解,誤用。但是事實上不應該如此難以理解,只要你理解到這個命令究竟在幹什麼。

首先我們來看幾個術語

  • HEAD

這是當前分支版本頂端的別名,也就是在當前分支你最近的一個提交

  • Index

index也被稱為staging area,是指一整套即將被下一個提交的檔案集合。他也是將成為HEAD的父親的那個commit

  • Working Copy

working copy代表你正在工作的那個檔案集

  • Flow

當你第一次checkout一個分支,HEAD就指向當前分支的最近一個commit。在HEAD中的檔案集(實際上他們從技術上不是檔案,他們是blobs(一團),但是為了討論的方便我們就簡化認為他們就是一些檔案)和在index中的檔案集是相同的,在working copy的檔案集和HEAD,INDEX中的檔案集是完全相同的。所有三者(HEAD,INDEX(STAGING),WORKING COPY)都是相同的狀態,GIT很happy。

當你對一個檔案執行一次修改,Git感知到了這個修改,並且說:“嘿,檔案已經變更了!你的working copy不再和index,head相同!”,隨後GIT標記這個檔案是修改過的。

然後,當你執行一個git add,它就stages the file in the index,並且GIT說:“嘿,OK,現在你的working copy和index區是相同的,但是他們和HEAD區是不同的!”

當你執行一個git commit,GIT就建立一個新的commit,隨後HEAD就指向這個新的commit,而index,working copy的狀態和HEAD就又完全匹配相同了,GIT又一次HAPPY了。

下面這一段是另外一個牛人的解釋:

總的來說,git reset命令是用來將當前branch重置到另外一個commit的,而這個動作可能會將index以及work tree同樣影響。比如如果你的master branch(當前checked out)是下面這個樣子:

- A - B - C (HEAD, master)

HEAD和master branch tip是在一起的,而你希望將master指向到B,而不是C,那麼你執行

git reset B以便移動master branch到B那個commit:

- A - B (HEAD, master)      # - C is still here, but there'
s no branch pointing to it anymore

注意:git reset和checkout是不一樣的。如果你執行git checkout B,那麼你講得到:

- A - B (HEAD) - C (master)

這時HEAD和master branch就不在一個點上了,你進入detached HEAD STATE. HEAD,work tree,index都指向了B,但是master branch卻依然指向C。如果在這個點上,你執行一個新的commit D,那麼你講得到下面(當然這可能並不是你想要的,你可能想要的是創一個branch做bug fix):

- A - B - C (master)
       \
        D (HEAD)

記住git reset不會產生commits,它僅僅更新一個branch(branch本身就是一個指向一個commit的指標)指向另外一個commit(Head和branch Tip同時移動保持一致).其他的僅剩對於index和work tree(working directory)有什麼影響。git checkout xxxCommit則隻影響HEAD,如果xxxCommit和一個branch tip是一致的話,則HEAD和branch相匹配,如果xxxCommit並不和任何branch tip相一致,則git進入detached HEAD 狀態

  • Reset

如果你仔細研究reset命令本身就知道,它本身做的事情就是重置HEAD(當前分支的版本頂端)到另外一個commit。假設我們有一個分支(名稱本身無所謂,所以我們就簡單稱為"super-duper-feature”分支吧),圖形化表示如下:

如果我們執行:

git reset HEAD

任何事情都不會發生,這是因為我們告訴GIT重置這個分支到HEAD,而這個正是它現在所在的位置。

git reset HEAD~1

當我們再執行上面的命令時(HEAD~1是“the commit right before HEAD”的別名,或者說:put differently "HEAD's parent"),我們的分支將會如下所示

如果我們執行git reset HEAD~2,則意味著將HEAD從頂端的commit往下移動兩個更早的commit。

  • Parameters
  1. soft

--soft引數告訴Git重置HEAD到另外一個commit,但也到此為止。如果你指定--soft引數,Git將停止在那裡而什麼也不會根本變化。這意味著index,working copy都不會做任何變化,所有的在original HEAD和你重置到的那個commit之間的所有變更集都放在stage(index)區域中。

  2.hard

--hard引數將會blow out everything.它將重置HEAD返回到另外一個commit(取決於~12的引數),重置index以便反映HEAD的變化,並且重置working copy也使得其完全匹配起來。這是一個比較危險的動作,具有破壞性,資料因此可能會丟失!如果真是發生了資料丟失又希望找回來,那麼只有使用:git reflog命令了。makes everything match the commit you have reset to.你的所有本地修改將丟失。如果我們希望徹底丟掉本地修改但是又不希望更改branch所指向的commit,則執行git reset --hard = git reset --hard HEAD. i.e. don't change the branch but get rid of all local changes.另外一個場景是簡單地移動branch從一個到另一個commit而保持index/work區域同步。這將確實令你丟失你的工作,因為它將修改你的work tree!

  3.mixed(default)

--mixed是reset的預設引數,也就是當你不指定任何引數時的引數。它將重置HEAD到另外一個commit,並且重置index以便和HEAD相匹配,但是也到此為止。working copy不會被更改。所有該branch上從original HEAD(commit)到你重置到的那個commit之間的所有變更將作為local modifications儲存在working area中,(被標示為local modification or untracked via git status),但是並未staged的狀態,你可以重新檢視然後再做修改和commit