實務上常會遇到的問題,卻要使用兩個完全不同的指令才能達成

git將檔案分成三個階段 : working directorystagerepository,要進repository之前,必須先進stage,但實務上可能下了git add之後,才反悔發現這個檔案不應該進stage,必須從stage移除,由於必須考慮該檔案是否已經存在於repository,所以必須下不同的git指令才能達成效果。

Version


git 2.6.4

將檔案commit進repository


我們知道在git裡,要將檔案commit進repository,需經過兩個步驟 :

  1. git add將檔案寫進stage。
  2. git commit將檔案寫進repository。

將檔案從stage移除


實務上常遇到一個狀況 : 當下了git add之後,才發現加錯檔案了,必須將檔案從stage移除。

一個看似很單純的需求,在git下卻要分兩個不同的狀況去思考:

  1. 若該檔案不在repository內 : git rm --cached 檔案名稱
  2. 若檔案已經在repository內 : git reset HEAD 檔案名稱
為什麼一個很簡單的需求,卻要使用完全不同的git指令呢?

git rm –cached


先了解git rm --cached的背後原理 :

  1. 若檔案存在於stage與repository時,會將檔案從repository刪除,並且從stage刪除,但不會刪除working directory的實際檔案,不過由於檔案已經從repository刪除,檔案會從tracked變成untracked
  2. 若檔案存在於stage,卻不存在於repository,會將檔案從stage刪除,但不會刪除working director的實際檔案,因為repository本來就沒有這個檔案,所以一樣是untracked不變。

回想我們的狀況 :

  1. 若該檔案不在repository內 : git rm --cached會幫我們從stage刪除,且檔案本來就是untracked,執行完還是untracked,符合我們的預期。

  2. 若檔案已經在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

  1. –soft : repository的檔案會被還原到HEAD,但stage與working directory檔案不變。
  2. –mixed : repository與stage的檔案都會被還原到HEAD,但working directory的檔案不變。
  3. –hard : repository、stage與working directory的檔案都會被還原到HEAD。

回想我們的狀況:

  1. 若該檔案不在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馬上給你錯誤訊息,,這並不是我們預期的。

  2. 若檔案已經在repository內 : git reset HEAD會幫我們將repository與stage還原到目前最新commit節點檔案,但working directory的檔案不會被還原,因為stage的檔案已經不是目前的檔案,所以檔案的狀態由原本的stage變成modified,符合我們的預期。

這解釋了為什麼當檔案已經在repository時,必須下git reset HEAD

Conclusion


  • 學習git時,最好要去了解每個指令背後的原理,否則只能死背指令,這樣很容易忘記,也無法活用。
2015-12-18