在講git的reset和checkout的區別以前,不得不說說HEAD、Index、Working Directory三個區域。git
Git裏有三個區域很重要緩存
下圖解釋了這三個區域的狀態的變化過程:spa
當你checkout分支的時候,git作了這麼三件事情code
因此你能夠發現,HEAD、Index、Working Directory這個時候裏的內容都是如出一轍的。blog
注意:通常會誤解爲,Index中的內容是空的,只有git add後纔會有東西。實際上不是,Index裏一直是有東西的。圖片
因此,Git的全部操做就是對這三個區域的狀態(或內容)的操做。開發
若是你在Working Directory裏修改了文件,git會發現Working Directory裏的內容和Index區域裏的內容不一致了。get
這個時候git status的結果是:同步
# Changes not staged for commit:
一個文件僅僅changed是不能被commit的,Git要求只能提交Index裏的東西。it
因此須要git add。這個命令的意思是,把Changed的文件的內容同步到Index區域裏。這樣Working Directory和Index區域的內容就一致了。這個過程被稱之爲stage
這個時候git status的結果是:
# Changes to be committed:
最後,你就能夠提交了
git commit
這樣,就把HEAD的狀態和Index以及Working Directory造成一致了。
reset是用來修改提交歷史的,想象這種狀況,若是你在2天前提交了一個東西,忽然發現此次提交是有問題的。
這個時候你有兩個選擇,要麼使用git revert(推薦),要麼使用git reset。
上圖能夠看到git reset是會修改版本歷史的,他會丟棄掉一些版本歷史。
而git revert是根據那個commit逆向生成一個新的commit,版本歷史是不會被破壞的。
上面已經講了,git reset是會丟棄掉commit的。
若是commit已經被push到遠程倉庫上了,也就意味着其餘開發人員就可能基於這個commit造成了新的commit,這時你去reset,就會形成其餘開發人員的提交歷史莫名其妙的丟失,或者其餘災難性的後果。
所以,一旦commit已經被push到遠程倉庫,那麼是堅定不容許去reset它的。
前面章節已經說道Git有三個區域,Git的全部操做其實是在操做這三個區域的狀態(或內容)。
git reset配合不一樣的參數,對這三個區域會產生不一樣的影響。
reset實際上有3個步驟,根據不一樣的參數能夠決定執行到哪一個步驟(--soft
, --mixed
, --hard
)。
--soft
)--mixed
)--hard
)注意
–mixed
是默認參數,也就是說執行reset的時候不給就認爲是--mixed
。
下表說明了三種形式的git reset所產生的不一樣效果。
target表明想要將git指向到哪一個commit
working index HEAD target working index HEAD ---------------------------------------------------- A B C D --soft A B D --mixed A D D --hard D D D --merge (disallowed) working index HEAD target working index HEAD ---------------------------------------------------- A B C C --soft A B C --mixed A C C --hard C C C --merge (disallowed)
上面講到的git reset實際上不帶參數的,若是帶上文件參數,那麼效果會是怎樣的?
須要注意的是帶文件參數的git reset沒有--hard, --soft這兩個參數。只有--mixed參數。
下面這兩個命令是同樣的,都是reset到HEAD上。
git reset file.txt git reset --mixed HEAD file.txt
這個例子的意義在於,unstage file,仔細想想是否是這樣?當你把一個文件stage到Index區域裏後後悔了,那麼只須要把Index區域裏的這個文件恢復到最近一次commit的狀態(也就是HEAD),那就至關於unstage了。
下面這個命令就是將某個文件恢復到歷史版本上。
reset eb43bf file.txt
這個例子的意思在於,把某個文件恢復到Index區域裏,而後直接commit,這樣就等於把這個文件恢復到歷史版本了,這樣依賴你都不須要去改動Working Directory了。
前面講到checkout是會修改HEAD的指向,變動Index區域裏的內容,修改Working Directory裏的內容。
這看上去很像reset --hard
,但和reset --hard
相比有兩個重要的差異
第二個區別可能有點難以理解,舉例來講:假設你有兩個分支master和develop,這兩個分支指向不同的commit,咱們如今在develop分支上(HEAD指向的地方)
若是咱們git reset master
,那麼develop就會指向master所指向的那個commit。
若是咱們git checkout master
,那麼develop不會動,只有HEAD會移動。HEAD會指向master。看圖:
當執行git checkout [branch] file時,checkout幹了這件事情:
head index work dir wd safe Commit Level reset --soft [commit] REF NO NO YES reset [commit] REF YES NO YES reset --hard [commit] REF YES YES NO checkout [commit] HEAD YES YES YES File Level reset (commit) [file] NO YES NO YES checkout (commit) [file] NO YES YES NO
「head」一列中的「REF」表示該命令移動了HEAD指向的分支引用,而「HEAD」則表示只移動了HEAD自身。 特別注意 「wd safe?」 一列,YES表示不會懂你在work dir的修改,NO表明會動你在work dir的修改。