首先,让我们只处理双连字符或双破折号,以使其不碍事(特别是因为这个问题不再有标记的重复项)。
Git 主要以POSIX 认可的方式使用它(参见指南 10),以指示选项参数和非选项参数之间的分界线。由于git checkout
接受分支名称(如git checkout master
)和文件(路径)名称(如 )git checkout README.txt
,因此您可以使用--
强制 Git 将 之后的任何内容解释--
为文件名,即使它是有效的分支名称。也就是说,如果你有一个分支和一个名为的文件master
:
git checkout master
将检查分支,但是:
git checkout -- master
将检查文件(令人困惑的是,从当前索引)。
接下来,我们需要解决git checkout
. 从文档中可以看出,有许多“模式” git checkout
(文档在概要中列出了六个单独的调用!)。关于 Git 糟糕的“用户体验”模型,包括有太多模式的事实,有各种各样的咆哮(质量参差不齐:Steve Bennet 的实际上很有用,在我看来,虽然我自然不同意 100% :-))git checkout
的操作。
特别是,你可以git checkout
一个分支(切换分支),或者git checkout
一个或多个文件。后者从特定提交或索引中提取文件。当 Git 从提交中提取文件时,它首先将它们复制到索引,然后将它们从索引复制到工作树。
这个序列有一个潜在的实现原因,但它完全显示出来的事实是一个关键要素。我们需要对 Git 的索引有很多了解,因为两者都git checkout
使用git reset
它,而且有时以不同的方式使用它。
我认为,绘制一个三向图或表格来说明当前或HEAD
提交、索引和工作树是个好主意。假设:
- 有两个普通的提交文件
README.md
和file.txt
;
- 有一个新的,
git add
-ed 但未提交new.txt
;
- 有一个名为的文件
rmd.txt
已被git rm
-ed 但未提交;
- 并且有一个名为
untr.txt
.
每个实体(HEAD
提交、索引和工作树)现在都包含三个文件,但每个实体都包含一组不同的文件。整个状态的表格如下所示:
HEAD index work-tree
-------------------------------
README.md README.md README.md
file.txt file.txt file.txt
new.txt new.txt
rmd.txt
untr.txt
除了这些之外,还有更多可能的状态:事实上,对于每个文件名,“in/not-in” HEAD、索引和工作树有七种可能的组合(第八种组合是“不在所有三个“,在这种情况下,我们首先谈论的是什么文件?!)。
checkout
和reset
命令_
您要询问的两个命令git checkout
和git reset
都可以做很多事情。然而,每一个的具体调用将“完成的事情”减少到两个之一,我将在其中添加几个:
git checkout -- .
:仅从索引复制到工作树
git checkout HEAD -- .
:从HEAD复制到索引,然后到工作树
git reset --mixed
:从 HEAD 重置索引(然后单独离开工作树)
git reset --hard
:从 HEAD 重置索引,然后从索引重置工作树
这些重叠很多,但有几个至关重要的不同部分。
让我们特别考虑上面命名的文件new.txt
。它现在在索引中,所以如果我们从索引复制到工作树,我们用索引副本替换工作树副本。例如,这就是这样git checkout -- new.txt
做的。
相反,如果我们首先从 from 复制HEAD
到索引,则索引中不会发生任何事情new.txt
:new.txt
不存在于HEAD
. 因此,明确的git checkout HEAD -- new.txt
只是失败,而git checkout HEAD -- .
复制其中的文件HEAD
并使两个现有new.txt
版本不受干扰。
该文件从索引rmd.txt
中消失git checkout -- .
了,所以如果我们,Git 看不到它并且什么也不做。但是如果我们git checkout HEAD -- .
,Gitrmd.txt
从索引复制HEAD
到索引(现在它回来了),然后从索引复制到工作树(现在它也回来了)。
git reset
当使用没有路径名参数时,该命令有一个关键区别。在这里,它从字面上重新设置索引以匹配提交。这意味着对于new.txt
,它注意到文件不在 中HEAD
,因此它删除了索引条目。如果与 一起使用--hard
,它也会因此删除工作树条目。同时rmd.txt
在中HEAD
,因此它将其复制回索引,并与--hard
, 一起复制到工作树。
如果存在未暂存的,即仅工作树,则对其他两个文件进行更改,README.md
并且 和的file.txt
两种形式都会清除这些更改。git checkout
--hard
git reset
如果对这些文件进行了暂存更改(已复制到索引中的更改),则git reset
取消暂存它们。git checkout
你给它命名的地方的变体也是如此HEAD
。但是,git checkout
将索引文件复制回工作树的位置的变体使这些分阶段的更改分阶段进行!
顶级与当前目录
最后,值得注意的是.
,表示当前目录,在任何时候都可能与“Git 存储库顶部”不同:
$ git rev-parse --show-toplevel
/home/torek/src/kernel.org/git
$ pwd
/home/torek/src/kernel.org/git/Documentation
$ git rev-parse --show-cdup
../
在这里,我在Documentation
顶级目录的子目录中git
,因此.
意味着所有内容Documentation
及其子目录。例如,使用git checkout -- .
将检查(从索引中)所有Documentation
和Documentation/RelNotes
文件,但不检查任何../builtin
文件。但是git reset
,当不使用路径名时,将重置所有条目,包括那些 for..
和../builtin
.