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