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

M_M*_*M_M 3 git git-checkout git-reset git-clean

这不是关于' - '的一般问题,如标记的副本.这是一个特定于git的问题,要求明确所提到的命令之间的操作差异.

如果我想清除当前目录而不进行存储或提交,我通常会使用以下命令:

git reset HEAD --hard
git clean -fd
Run Code Online (Sandbox Code Playgroud)

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

git checkout -- .
Run Code Online (Sandbox Code Playgroud)

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

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

tor*_*rek 9

首先,让我们解决双连字符或双短划线,以使其不受影响(特别是因为这个问题不再有明显的重复).

Git主要以POSIX批准的方式使用它(参见指南10),以指示选项参数和非选项参数之间的分界线.既然git checkout接受分支名称,例如git checkout master文件(路径)名称,git checkout README.txt也可以使用--强制Git来解释--作为文件名后面的内容,即使它本来是有效的分支名称.也就是说,如果您同时拥有一个分支和一个名为的文件master:

git checkout master
Run Code Online (Sandbox Code Playgroud)

将检查分支,但:

git checkout -- master
Run Code Online (Sandbox Code Playgroud)

将检出文件(令人困惑的是,从当前索引).

分支,索引和文件,哦,我的

接下来,我们需要解决一个奇怪的问题git checkout.从文档中可以看出,有许多"模式" git checkout(文档列出了在synposis中的六个单独的调用!).有各种各样的咆哮(不同的质量:史蒂夫班纳特的实际上很有用,在我看来,虽然我自然不同意它100%:-))关于Git糟糕的"用户体验"模型,包括git checkout有太多模式的事实的操作.

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

这个序列有一个潜在的实现原因,但它显示的事实是一个关键元素.我们需要知道很多关于Git的指数,因为两者git checkoutgit reset以不同的方式使用它,有时.

我认为,绘制一个三维图表或表格来说明当前 - 或 - HEAD委托,索引和工作树是一个好主意.假设:

  • 有两个普通的,提交的文件README.mdfile.txt;
  • 有一个新的,git add但未提交的new.txt;
  • 有一个名为rmd.txthas 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
Run Code Online (Sandbox Code Playgroud)

除了这些之外还有更多可能的状态:实际上,对于每个文件名,有七种可能的组合"in/not-in"HEAD,index和work-tree(第八种组合"不是全部三种" ",在这种情况下,我们甚至在第一时间谈论什么文件?!).

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索引(现在它回来)复制,然后从索引复制到工作树(现在它也回到那里).

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索引文件复制回工作树的变体可以保持这些分阶段的更改!

顶级vs当前目录

最后,值得注意的是.,意味着当前目录,可能在任何时候都与"顶级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
../
Run Code Online (Sandbox Code Playgroud)

在这里,我位于Documentation顶级目录的子目录中git,因此.意味着所有内容Documentation及其子目录.例如,使用git checkout -- .将检出(从索引)所有DocumentationDocumentation/RelNotes文件,但不检出任何../builtin文件.但是git reset,在没有路径名的情况下使用时,将重置所有条目,包括for ..../builtin.