git - “git checkout —。”和“git reset HEAD --hard”之间有什么区别?

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

这不是一个关于“--”做什么的一般性问题,如在标记的副本中。这是一个特定于git的问题,要求澄清上述命令之间的操作差异。
如果要清除当前目录而不隐藏或提交,通常使用以下命令:

git reset HEAD --hard
git clean -fd

一位同事还提到使用此命令:
git checkout -- .

对google来说,这是一个困难的命令,从git文档中我不清楚这个命令到底做了什么。这似乎是手册中后来提到的用法之一。
据猜测,它会复制git reset HEAD --hard,但与我已经使用的命令相比,它到底能做什么呢?
它是复制其中一个或两个命令,还是相似但又有细微的不同?

最佳答案

首先,让我们只解决双连字符或双破折号,以摆脱它的方式(特别是因为这个问题不再有标记的重复)。
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中的六个单独调用!)。关于Git糟糕的“用户体验”模型,有各种各样的论调(质量参差不齐:Steve Bennet's在我看来实际上是有用的,尽管我自然不同意它100%:-),包括git checkout有太多的操作模式。
特别是,您可以git checkout一个分支(切换分支),或git checkout一个或多个文件。后者从特定提交或索引中提取文件。当git从提交中提取文件时,它首先将它们复制到索引中,然后从索引中将它们复制到工作树中。
这个序列有一个潜在的实现原因,但它显示出来的事实是一个关键元素。我们需要对git的索引了解很多,因为git checkoutgit reset都使用它,有时使用的方式也不同。
我认为,最好绘制一个三向图或表,说明当前或HEAD-commit、索引和工作树。假设:
有两个普通的提交文件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”头、索引和工作树有七种可能的组合(第八种组合是“not in all three”,在这种情况下,我们首先谈论的是什么文件?!)。
checkoutreset命令
您询问的两个命令git checkoutgit reset都可以执行许多操作。但是,每一个的具体调用都将“完成的事情”减少到两个中的一个,我将在其中再添加几个:
git checkout -- .:仅从索引复制到工作树
git checkout HEAD -- .:从head复制到index,然后复制到work tree
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复制到索引(现在它回来了),然后从索引复制到工作树(现在它也回来了)。
在没有路径名参数的情况下使用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存储库顶部”不同:
$ 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 - “git checkout —。”和“git reset HEAD --hard”之间有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44953037/

相关文章:

git - Ubuntu git : fatal: unable to access gnutls_handshake() failed: A TLS fatal alert has been received

mysql - 在 gitlab CE 捆绑服务器上安装 phpmyadmin

git - 如何回滚(重置)单个文件上 git 中提交的更改

git - 在不推送本地分支的情况下重置远程分支

git - git reset --hard 和 git clean 的区别

git - `go get` 和 `git clone` 之间的区别?

git - 如何将存储库变成 GitHub 页面?

git - 暂停文件观察器直到 git checkout 完成

git - 在与 main 相同的提交上分离 HEAD?

git - 只需一个命令即可获取和 checkout 远程 git 分支