" git checkout - 。"有什么区别?和" git reset HEAD --hard"?

时间:2017-07-06 15:21:14

标签: git git-checkout git-reset git-clean

这不是关于什么' - '与标记的副本一样。这是一个特定于git的问题,要求澄清所提到的命令之间的操作差异。

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

git reset HEAD --hard
git clean -fd

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

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

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

1 个答案:

答案 0 :(得分:7)

首先,让我们只解决双连字符或双连字符,以使其不受影响(特别是因为此问题不再有明显重复)。

Git主要在the POSIX-approved fashion (see Guideline 10)中使用它来表示选项参数和非选项参数之间的分界线。由于git checkout接受分支名称,如git checkout master,以及文件(路径)名称,如git checkout README.txt,您可以使用--强制Git解释任何来在--之后作为文件名,即使它是一个有效的分支名称。也就是说,如果您同时拥有名为master分支文件

git checkout master

将检出分支,但是:

git checkout -- master

将检出文件(令人困惑的是,来自当前的索引)。

Branches, index, and files, oh my

接下来,我们需要解决git checkout的怪癖。从the documentation可以看出,有很多"模式" git checkout的文档(文档列出了在synposis中的六个单独的调用!)。有各种各样的咆哮(不同的质量:Steve Bennet's实际上是有用的,在我看来,虽然我自然不同意它100%:-))关于Git的糟糕"用户体验&# 34;模型,包括git checkout有太多操作模式的事实。

特别是,您可以git checkout 分支(切换分支)或git checkout一个或多个文件。后者从特定提交或索引中提取文件。当Git从提交中提取文件时,它首先将它们复制到索引,然后将索引复制到工作树

这个序列有一个潜在的实现原因,但它显示的事实是一个关键元素。我们需要了解很多关于Git的索引,因为git checkoutgit reset都使用它,有时候会以不同的方式使用它。

我认为,绘制一个说明当前或HEAD - 提交,索引和工作树的三向图表或表格是一个好主意。假设:

  • 有两个普通的已提交文件README.mdfile.txt;
  • 有一个新的git add - 但未提交new.txt;
  • 有一个名为rmd.txt的文件已被git rm - 但尚未提交;
  • 并且有一个名为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就是这样做的。

相反,如果我们首先从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 -- .,Git会将rmd.txtHEAD复制到索引(现在它已经回来),然后从索引复制到工作树(现在它就是&#39} ; s也回到那里。)

git reset命令与没有路径名参数一起使用时有一个关键区别。在这里,它实际上重新设置索引以匹配提交。这意味着,对于new.txt,它会注意到该文件不在HEAD中,因此删除了索引条目。如果与--hard一起使用,则它也删除工作树条目。同时rmd.txt HEAD中的,因此它会将其复制回索引,并使用--hard复制到工作树中。

如果存在未分级的,即仅限工作树,则更改其他两个文件README.mdfile.txt,这两种形式的git checkout--hard形式git reset消除了这些变化。

如果暂存对这些文件进行了更改 - 已经复制到工作树中的更改 - 则git reset取消对它们进行分级。 git checkout的变体也是如此,您可以在其中为其命名HEAD。但是,您将索引文件复制回工作树的git checkout变体会保留这些分阶段更改!

顶级与当前目录

最后,值得注意的是.,意味着当前目录,可能在任何时候都与Git存储库的顶部不同而且#34;:

$ 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的{​​{1}}子目录中,因此git表示 .及其子目录中的所有内容。例如,使用Documentation将(从索引中)检出所有git checkout -- .Documentation文件,但不检出任何Documentation/RelNotes文件。但../builtin在没有路径名的情况下使用时,会重置所有条目,包括git reset..的条目。