git将文件分成三个阶段

- working directory 
 - stage 
 - repository

要进入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 filename
2.若该文件已经在repository内:git reset head filename


git rm - -cached

先了解git rm --cached背后的原理:
1.若文件存在与stage和repository中时,会将文件从repository中删除,并且从stage中删除,但不会删除本地的实际文件,不过由于文件已经从repository中删除,文件会从tracked变成untracked
2.若文件存在与stage,却不存在与repository中,会将文件从stage中删除,但不会删除本地的实际文件。由于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会出现以下错误:

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变成modifed,符合我们的预期。
这解释了为什么文件不存在于repository时,不能用git rm --cached而得用git reset head