細說git reset和git checkout的不同之處

Those who want to read English version to understand the difference between git reset and git checkout, please go to check the reference [1] at the bottom.

git是一個非常好用的版本控制軟體,但是在使用上偶爾會碰到使用git checkout和git reset,這時候就會想說到底要用哪個才是正確的,簡單的結論是,除非你清楚知道你在做什麼,不然不要使用git reset,大部分的情況都可以不用使用它,它就跟Linux中的 rm指令一樣,不可以隨便亂用,因為一旦使用了就會造成不可還原的影響。

在詳細介紹之前首先必須知道什麼叫做工作目錄、索引和HEAD。工作目錄(Working Tree)是保存目前正在處理檔案的目錄,Git 相關的操作都會在這個目錄下完成。索引(Index )位於工作目錄和數據庫之間,是為了向數據庫提交作準備的暫存區域,又被稱為staging area。最後HEAD是指到現在我們在操作哪一個commit,當我們checkout的時候其實就是移動HEAD就會跳到另外一個commit或branch上了,HEAD被移動的時候會造成工作目錄的檔案內容改變,所以如果有檔案被修改過但是沒有commit或stash的檔案存在時會不能夠移動HEAD,也就是不能使用git checkout。

  • git checkout

checkout的意思在字典中可以查到為 to depart from a place; record one’s departure from work,也就是說這是一個切換並且記錄現在的情況的指令,使用checkout的時候最重要的是不會修改master的位置,而且不會修改索引,只會更改目前目錄內的資料(也就是HEAD會被修改到該commit或branch),也就是說不會破壞git所儲存的管理資訊,只是目前工作目錄下面的檔案會被替換掉而已,如下所示。

原本的master長這樣子

- A - B - C (HEAD, master)

如果使用了git checkout B則會變成

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

如果在這時候創建新的branch,git checkout -b new-branch,則會變成

- A - B - C (master)
       \
        D (HEAD, new-branch)

基本上checkout是一個很安全的行為,常常會被用於想要取得之前commit的某個檔案來比較更改之前和更改之後差在哪裡,或著想要回到以前的commit,也可以跳到另外一個branch也可以開新分支

  • git reset

reset相對的就不一樣了,字典中可以查到意義為to set again, to clear,如上圖所示一旦使用git reset不論是用什麼模式都會更改到master所指的commit依照現在處於哪個branch會有不同,如果不是在master而是在test這個branch上的只會更改到test所指的commit,為求方便這邊都以master為例。

如果使用git reset,基本上會是在想要更改master或索引,也就是更改git所儲存的管理資訊才會使用,例如有一個commit因為打錯想要撤銷,這時因為他已經被存入索引,必須要使用git reset來把剛剛的commit撤銷掉,這個動作可以有幾個選擇如下所示,不管哪一種模式都會變更到master,這點要特別注意,另外如果沒有加任何參數時會使用預設的mixed模式。

模式名稱 master的位置 索引 工作目錄
soft 修改 不修改 不修改
mixed 修改 修改 不修改
hard 修改 修改 修改

一旦使用了git reset,依照接下來做了什麼事情而定,對不熟悉的人很可能會導致這個repository再也無法push回去git server(例如Github),因為HEAD的位置和資訊和原本的不一致,導致他判斷你有做過更改,所以不給push,這時候就會很麻煩了,這也是為什麼盡量不要使用git reset,唯二建議使用git reset的只有兩個地方,第一個是git add加到不想要的檔案到索引想要取消掉,第二個是自己commit之後發現有東西想要修改或著有問題想要修改,可以使用git reset –soft HEAD^。

如下範例,原本的master長這樣子

- A - B - C (HEAD, master)

如果使用了git reset B則會變成

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

以下比較使用不同模式的時候看到的status不同之處來應證上面所說的HEAD、index(staging area)和工作目錄的不同之處
使用git reset B之後git status會得到

On branch master
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)

Changes not staged for commit:
  (use "git add/rm ..." to update what will be committed)
  (use "git checkout -- ..." to discard changes in working directory)

	modified:   runfair
	deleted:    runservice
	modified:   runsim
	modified:   runstride
	deleted:    src/scheduler-service.c


no changes added to commit (use "git add" and/or "git commit -a")

使用git reset –soft B之後git status會得到

On branch master
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)

Changes to be committed:
  (use "git reset HEAD ..." to unstage)

	modified:   runfair
	deleted:    runservice
	modified:   runsim
	modified:   runstride
	deleted:    src/scheduler-service.c

使用git reset –hard B之後git status會得到

On branch master
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)

nothing to commit, working directory clean
  • 總結

如果是想要切換HEAD(工作目錄的內容)或 branch,請使用git checkout,想要開啟新branch也是使用checkout,checkout就是一個負責移動HEAD指到不同地方的指令

如果想要清除過去做過的壞事,就使用git reset,會幫你把紀錄都抹掉,消除掉,使用時請謹慎使用,reset是一個負責移動HEAD和master的指令,前者為指向當前commit,後者為歷史記錄的最新一筆commit,一旦master被移動了,雖然還可以找得回來被拋棄的記錄,但是做這件事情的時候基本上就是想要消去記錄,請謹慎使用。

  • 參考
  1. http://stackoverflow.com/questions/2530060/can-you-explain-what-git-reset-does-in-plain-english
  2. http://backlogtool.com/git-guide/tw/intro/intro1_4.html
  3. http://backlogtool.com/git-guide/tw/stepup/stepup6_3.html

One thought on “細說git reset和git checkout的不同之處

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s