[英]What is the difference between “git checkout — .” and “git reset HEAD --hard”?
This is not a general question about what '--' does, as in the marked duplicate.这不是关于“--”做什么的一般问题,如标记的重复项。 This is a git-specific question asking for clarity on what the operational differences are between the mentioned commands.
这是一个特定于 git 的问题,要求明确上述命令之间的操作差异。
If I want to clean out my current directory without stashing or committing, I usually use these commands:如果我想在不存储或提交的情况下清理当前目录,我通常使用以下命令:
git reset HEAD --hard
git clean -fd
A co-worker also mentioned using this command:一位同事也提到使用此命令:
git checkout -- .
It's a difficult command to google, and it's not clear to me from the git documentation what this command actually does.对谷歌来说这是一个困难的命令,我从 git 文档中不清楚这个命令实际上做了什么。 It seems to be one of the later-mentioned usages in the manual.
这似乎是手册中后面提到的用法之一。
At a guess it replicates git reset HEAD --hard
, but what exactly does it do as compared to the commands I'm already using?猜测它复制了
git reset HEAD --hard
,但与我已经使用的命令相比,它究竟做了什么?
Does it replicate one or both of the commands, or is it similar yet subtly different?它是否复制了一个或两个命令,或者它是否相似但略有不同?
First, let's just address the double hyphen or double dash, to get it out of the way (especially since this question no longer has a marked duplicate).首先,让我们解决双连字符或双破折号,以摆脱它(特别是因为这个问题不再有标记重复)。
Git mostly uses this in the POSIX-approved fashion (see Guideline 10) , to indicate a dividing line between option arguments and non-option arguments. Git 主要以POSIX 认可的方式使用它(参见指南 10) ,以指示选项参数和非选项参数之间的分界线。 Since
git checkout
accepts branch names, as in git checkout master
, and also file (path) names, as in git checkout README.txt
, you can use the --
to force Git to interpret whatever comes after the --
as a file name, even if it would otherwise be a valid branch name.由于
git checkout
接受分支名称(如git checkout master
)和文件(路径)名称(如git checkout README.txt
,您可以使用--
强制 Git 将--
后面的任何内容解释为文件名,即使它是一个有效的分支名称。 That is, if you have both a branch and a file named master
:也就是说,如果你有一个分支和一个名为
master
的文件:
git checkout master
will check out the branch , but:将检查分支,但是:
git checkout -- master
will check out the file (confusingly, from the current index ).将检出文件(令人困惑的是,从当前索引)。
Next, we need to address a quirk of git checkout
.接下来,我们需要解决
git checkout
一个怪癖。 As one can see from the documentation , there are many "modes" of git checkout
(the documentation lists six separate invocations in the synposis!).从文档中可以看出,
git checkout
有许多“模式”(文档在概要中列出了六个单独的调用!)。 There are various rants (of varying quality: Steve Bennet's is actually useful, in my opinion, though naturally I do not agree with it 100% :-) ) about Git's poor "user experience" model, including the fact that git checkout
has too many modes of operation.关于 Git 糟糕的“用户体验”模型,有各种各样的咆哮(质量各不相同: Steve Bennet 的实际上很有用,在我看来,虽然我自然 100% 不同意它:-)),包括
git checkout
也有多种操作模式。
In particular, you can git checkout
a branch (to switch branches), or git checkout
one or more files .特别是,您可以
git checkout
一个分支(切换分支),或git checkout
一个或多个文件。 The latter extracts the files from a particular commit, or from the index.后者从特定提交或索引中提取文件。 When Git extracts files from a commit, it first copies them to the index, and then copies them from the index, to the work-tree.
当 Git 从提交中提取文件时,它首先将它们复制到索引,然后将它们从索引复制到工作树。
There is an underlying implementation reason for this sequence, but the fact that it shows through at all is a key element.这个序列有一个潜在的实现原因,但它完全显示出来的事实是一个关键因素。 We need to know a lot about Git's index, because both
git checkout
and git reset
use it, and sometimes in different ways.我们需要了解很多关于 Git 的索引,因为
git checkout
和git reset
使用它,而且有时使用的方式不同。
It's a good idea, I think, to draw a three-way diagram or table illustrating the current—or HEAD
—commit, the index, and the work-tree.我认为,绘制一个三向图或表格来说明当前(或
HEAD
提交、索引和工作树是个好主意。 Suppose that:假设:
README.md
and file.txt
;README.md
和file.txt
;git add
-ed but uncommitted new.txt
;git add
-ed 但未提交的new.txt
;rmd.txt
that has been git rm
-ed but not committed;rmd.txt
的文件已被git rm
-ed 但未提交;untr.txt
.untr.txt
的未跟踪文件。 Each entity—the HEAD
commit, the index, and the work-tree—holds three files right now, but each holds a different set of files.每个实体
HEAD
提交、索引和工作树——现在都持有三个文件,但每个都持有不同的文件集。 The table of the entire state then looks like this:整个状态的表看起来像这样:
HEAD index work-tree
-------------------------------
README.md README.md README.md
file.txt file.txt file.txt
new.txt new.txt
rmd.txt
untr.txt
There are many more possible states than just these: in fact, for each file-name, there are seven possible combinations of "in/not-in" HEAD, index, and work-tree (the eighth combination is "not in all three", in which case, what file are we even talking about in the first place?!).可能的状态远不止这些:事实上,对于每个文件名,“在/不在”HEAD、索引和工作树有七种可能的组合(第八种组合是“不在所有三个中” “,在这种情况下,我们甚至首先谈论的是什么文件?!)。
checkout
and reset
commands checkout
和reset
命令The two commands you're asking about, git checkout
and git reset
, are both able to do many things.您询问的两个命令
git checkout
和git reset
都可以做很多事情。 The specific invocations of each, however, reduce the "things done" to one of two, to which I will add several more:然而,每个的具体调用将“完成的事情”减少到两个之一,我将添加更多:
git checkout -- .
: copies from index, to work-tree, only git checkout HEAD -- .
: copies from HEAD, to index, and then to work-tree git reset --mixed
: resets index from HEAD (and then leaves work-tree alone) git reset --mixed
:从 HEAD 重置索引(然后单独留下工作树)git reset --hard
: resets index from HEAD, then resets work-tree from index git reset --hard
:从 HEAD 重置索引,然后从索引重置工作树These overlap a lot, but there are several crucially-different parts.这些重叠很多,但有几个关键不同的部分。
Let's consider the file named new.txt
above in particular.让我们特别考虑上面名为
new.txt
的文件。 It's in the index right now, so if we copy from the index, to the work-tree, we replace the work-tree copy with the index copy.它现在在索引中,所以如果我们从索引复制到工作树,我们用索引副本替换工作树副本。 This is what
git checkout -- new.txt
does, for instance.例如,这就是
git checkout -- new.txt
所做的。
If, instead, we start by copying from HEAD
to the index, nothing happens to new.txt
in the index: new.txt
doesn't exist in HEAD
.相反,如果我们通过复制开始
HEAD
到索引中,没有任何反应new.txt
索引: new.txt
不存在HEAD
。 Hence an explicit git checkout HEAD -- new.txt
just fails, while a git checkout HEAD -- .
因此,显式
git checkout HEAD -- new.txt
失败,而git checkout HEAD -- .
copies the files that are in HEAD
and leaves the two existing new.txt
versions undisturbed.复制
HEAD
的文件,并使两个现有的new.txt
版本不受干扰。
The file rmd.txt
is gone from the index, so if we git checkout -- .
文件
rmd.txt
从索引中消失了,所以如果我们git checkout -- .
, Git does not see it and does nothing about it. ,Git 看不到它,也不做任何处理。 But if we
git checkout HEAD -- .
但是如果我们
git checkout HEAD -- .
, Git copies rmd.txt
from HEAD
into the index (now it's back) and then from the index to the work-tree (and now it's back there, too). , Git 将
rmd.txt
从HEAD
复制到索引中(现在它回来了),然后从索引复制到工作树(现在它也回到了那里)。
The git reset
command has a key difference when used with no path name arguments. git reset
命令在没有路径名参数的情况下使用时有一个关键的区别。 Here, it literally re-sets the index to match the commit.在这里,它从字面上重新设置索引以匹配提交。 That means that for
new.txt
, it notices that the file is not in HEAD
, so it removes the index entry.这意味着对于
new.txt
,它注意到该文件不在HEAD
,因此它删除了索引条目。 If used with --hard
, it therefore also removes the work-tree entry.如果与
--hard
,它也会因此删除工作树条目。 Meanwhile rmd.txt
is in HEAD
, so it copies that back to the index, and with --hard
, to the work-tree as well.同时
rmd.txt
是HEAD
,所以它会复制回索引,并与--hard
,对工作树为好。
If there are unstaged, ie, work-tree only, changes to the other two files README.md
and file.txt
, both forms of git checkout
and the --hard
form of git reset
wipe out those changes.如果有未暂存的,即只有工作树,对其他两个文件
README.md
和file.txt
更改,两种形式的git checkout
和--hard
形式的git reset
清除这些更改。
If there are staged changes to those files—changes that have been copied into the index—then git reset
un-stages them.如果对这些文件进行了暂存更改(已复制到索引中的更改),则
git reset
取消git reset
它们。 So does the variant of git checkout
where you give it the name HEAD
. git checkout
的变体也是如此,您将其命名为HEAD
。 However, the variant of git checkout
where you copy the index files back to the work-tree keeps those staged changes staged!但是,将索引文件复制回工作树的
git checkout
变体使这些暂存更改保持暂存状态!
Last, it's worth noting that .
最后,值得注意的是
.
, meaning the current directory , may at any time be different from "top of Git repository": ,表示当前目录,可能随时与“顶部 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
../
Here, I am in the Documentation
sub-directory of the top level directory git
, so .
在这里,我在顶级目录
git
的Documentation
子目录中,所以.
means everything in Documentation
and its subdirectories .表示
Documentation
及其子目录中的所有内容。 Using git checkout -- .
使用
git checkout -- .
will check out (from the index) all the Documentation
and Documentation/RelNotes
files, but not any of the ../builtin
files, for instance.例如,将检出(从索引中)所有
Documentation
和Documentation/RelNotes
文件,但不会../builtin
任何../builtin
文件。 But git reset
, when used without path names, will reset all entries, including those for ..
and ../builtin
.但是
git reset
,当在没有路径名的情况下使用时,将重置所有条目,包括..
和../builtin
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.