如何將檔案從stage移除 ?
git將檔案分成三個階段 : working directory、stage與repository,要進repository之前,必須先進stage,但實務上可能下了git add
之後,才反悔發現這個檔案不應該進stage,必須從stage移除,由於必須考慮該檔案是否已經存在於repository,所以必須下不同的git指令才能達成效果。
Version
git 2.6.4
將檔案commit進repository
我們知道在git裡,要將檔案commit進repository,需經過兩個步驟 :
- 下
git add
將檔案寫進stage。 - 下
git commit
將檔案寫進repository。
將檔案從stage移除
實務上常遇到一個狀況 : 當下了git add
之後,才發現加錯檔案了,必須將檔案從stage移除。
一個看似很單純的需求,在git下卻要分兩個不同的狀況去思考:
- 若該檔案不在repository內 :
git rm --cached 檔案名稱
- 若檔案已經在repository內 :
git reset HEAD 檔案名稱
git rm –cached
先了解git rm --cached
的背後原理 :
- 若檔案存在於stage與repository時,會將檔案從repository刪除,並且從stage刪除,但不會刪除working directory的實際檔案,不過由於檔案已經從repository刪除,檔案會從
tracked
變成untracked
。 - 若檔案存在於stage,卻不存在於repository,會將檔案從stage刪除,但不會刪除working director的實際檔案,因為repository本來就沒有這個檔案,所以一樣是
untracked
不變。
回想我們的狀況 :
若該檔案不在repository內 :
git rm --cached
會幫我們從stage刪除,且檔案本來就是untracked,執行完還是untracked,符合我們的預期。若檔案已經在repository內 :
git rm --cached
會幫我們從repository刪除,並且從stage刪除,因為已經從repository刪除檔案,檔案會從tracked
變成untracked
,這並不是我們預期的。
這解釋了為什麼當檔案不在repository時,必須下git rm --cached
。
git reset HEAD
先了解git reset HEAD
的背後原理:
HEAD為目前最新的commit節點,git reset HEAD
表示將檔案還原到目前最新的commit,若沒下任何參數,預設為--mixed
:
- –soft : repository的檔案會被還原到HEAD,但stage與working directory檔案不變。
- –mixed : repository與stage的檔案都會被還原到HEAD,但working directory的檔案不變。
- –hard : repository、stage與working directory的檔案都會被還原到HEAD。
回想我們的狀況:
若該檔案不在repository內 :
git reset HEAD
會出現以下錯誤:1
fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree. Use '--' to separate paths from revisions, like this: 'git <command> [<revision>...] -- [<file>...]'
因為檔案根本還沒進repository,也就是還沒有commit過,哪來的HEAD呢?git馬上給你錯誤訊息,,這並不是我們預期的。
若檔案已經在repository內 :
git reset HEAD
會幫我們將repository與stage還原到目前最新commit節點檔案,但working directory的檔案不會被還原,因為stage的檔案已經不是目前的檔案,所以檔案的狀態由原本的stage
變成modified
,符合我們的預期。
這解釋了為什麼當檔案已經在repository時,必須下git reset HEAD
。
Conclusion
- 學習git時,最好要去了解每個指令背後的原理,否則只能死背指令,這樣很容易忘記,也無法活用。