7

这不是关于'-' 做什么的一般问题,如标记的副本中那样。这是一个特定于 git 的问题,要求明确上述命令之间的操作差异。

如果我想在不存储或提交的情况下清理当前目录,我通常使用以下命令:

git reset HEAD --hard
git clean -fd

一位同事也提到使用此命令:

git checkout -- .

对于谷歌来说,这是一个困难的命令,从 git 文档中我不清楚这个命令实际上做了什么。这似乎是手册中后面提到的用法之一。

猜测它会复制git reset HEAD --hard,但与我已经使用的命令相比,它到底做了什么?
它是复制一个或两个命令,还是相似但略有不同?

4

1 回答 1

14

首先,让我们只处理双连字符或双破折号,以使其不碍事(特别是因为这个问题不再有标记的重复项)。

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.mdfile.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、索引和工作树有七种可能的组合(第八种组合是“不在所有三个“,在这种情况下,我们首先谈论的是什么文件?!)。

checkoutreset命令_

您要询问的两个命令git checkoutgit reset都可以做很多事情。然而,每一个的具体调用将“完成的事情”减少到两个之一,我将在其中添加几个:

  • git checkout -- .:仅从索引复制工作树
  • git checkout HEAD -- .:从HEAD复制索引,然后工作树
  • git reset --mixed:从 HEAD 重置索引(然后单独离开工作树)
  • git reset --hard:从 HEAD 重置索引,然后从索引重置工作树

这些重叠很多,但有几个至关重要的不同部分。

让我们特别考虑上面命名的文件new.txt。它现在在索引中,所以如果我们索引复制工作树,我们用索引副本替换工作树副本。例如,这就是这样git checkout -- new.txt做的。

相反,如果我们首先从 from 复制HEAD到索引,则索引中不会发生任何事情new.txtnew.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--hardgit 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 -- .将检查(从索引中)所有DocumentationDocumentation/RelNotes文件,但不检查任何../builtin文件。但是git reset,当不使用路径名时,将重置所有条目,包括那些 for..../builtin.

于 2017-07-10T17:46:10.483 回答