"git reset HEAD"和"git reset HEAD"有什么区别。

  • 本文关键字:reset HEAD git 区别 git
  • 更新时间 :
  • 英文 :


这是一个类似于git add -A的简写吗;git add -u

请参阅此问题了解上下文https://stackoverflow.com/a/22207257/7680918#

事实证明,Git足够聪明,如果不丢弃存储,就不会丢弃干净地涂抹。我用以下步骤:

  1. 要取消合并冲突的分级:git reset HEAD .(注意后面的点)
  2. 保存冲突合并(以防万一):git stash
  3. 返回主机:git checkout master
  4. 提取最新更改:git fetch upstream; git merge upstream/master
  5. 更正我的新分支:git checkout new-branch; git rebase master
  6. 要应用正确的隐藏更改(现在是堆栈上的第二个):git stash apply stash@{1}

TL;DR

链接的答案可能不应该建议git reset HEAD .。它是有效的,但前提是你在工作树的顶端。一个简单的git reset(没有选项,也没有额外的参数)可能是一个更好的主意。

Long

以下是在阅读git reset文档之前需要了解的一些背景知识:

  • pathspec是一个文件名,可能包括路径组件,如dir/filea/b/file.ext,或仅包括file.ext或其他任何组件。

  • git reset命令最多可以影响三件事:通过名称HEAD看到的当前分支、Git的索引以及工作树中的文件。

  • CCD_ 18命令过于复杂:;操作模式";,事实上。

要讨论git reset如何影响git add .0,我们需要讨论分支名称如何工作1以及HEAD如何附加到分支名称2但是我们将在这里特别讨论的git reset类型不会影响HEAD本身,所以除了脚注,我们不会打扰。不过,它确实会影响Git的索引,所以在继续之前,让我们先谈谈Git的指数。


1简而言之,分支名称指向提交(通过存储原始提交哈希ID)。名称HEAD是附在分支名称上的;移动你的CCD_ 25〃;,git reset更改分支名称指向的提交。也就是说,Git在分支名称中写入一个不同的提交哈希ID。

2git checkout命令和new-in-Git-2.23git switch命令,这样做";附加";同时使用某个分支名称签出提交。


Git的索引

Git的索引是一个持久的(存储在磁盘上)数据结构,在大多数情况下,它记录了Git需要知道的内容,以便Git进行下一次提交。我喜欢这样说的方式是,索引保存建议的下一次提交,或者至少是它的快照。通过这种方式,索引实现了其作为暂存区的角色,这就是为什么它也有这个名称的原因。但索引所做的不仅仅是,它只是存储建议的下一次提交。特别是,当您正在进行合并并且合并冲突时,索引将扮演一个扩展的角色。

不过,首先,让我们多谈谈这个舞台区域的角色。作为暂存区,索引保存的是每个源文件的完整副本。它不仅包含已更改的文件。git status命令特别提到更改的文件,特别是没有提到未更改的文件。但是暂存区包含所有文件。

这些文件最初来自当前提交。如果您没有替换暂存区中的文件,并且它仍然是当前提交中的文件,则它与当前提交匹配。所以git status就是不提,它还在那里!只是没有被提及但如果你确实更改了一个工作树文件,然后运行git add,你会告诉Git:弹出你现在拥有的文件,并使用相同的名称放入另一个文件中所以现在暂存的文件与提交的文件不同

对于我们的特定目的来说,这并不重要,但值得注意的是,每个文件的暂存副本已经是Git的压缩和重复数据消除格式。这就是文件在提交时的样子:名称存储在其他地方——就像它在索引中一样——内容被压缩和消除重复存储,就像它们在Git的索引中一样。工作树中的文件未压缩,未消除重复,3因此git add必须在将其存储到Git的索引中之前对其进行压缩和消除重复,以便为新提交暂存。

所有这些的目的是让你意识到这些事情:

  • Git索引中的内容是可提交的格式
  • 将在下一次提交中的每个文件都是始终暂存的。从暂存区删除一个文件意味着整个文件不会在下一次提交
  • git status不会麻烦告诉您当前提交中与索引中相同的文件。当这些文件在某种程度上不同时,它只表示为提交而暂存的。这样,即使您的提交每个都包含10000个文件,您也只需要注意您更改的两三个文件

当您进行新的提交时,例如,通过运行git commit,Git所做的是使用文件的索引副本作为快照进行新的提交。由于它们已经采用了用于提交的格式,所以执行速度非常快。

不过,在这种特殊情况下,我们关注的是合并冲突后的索引。因此,我们需要了解扩展的索引,而不仅仅是普通的日常建议的下一个快照索引。


3从技术上讲,是否消除工作树文件的重复取决于您的操作系统。例如,在Linux或FreeBSD上使用ZFS,可以将文件系统本身配置为执行重复数据消除。这与Git无关,Git自己独立进行重复数据消除。


扩展索引

在正常的索引设置中,每个文件都处于Git所称的阶段零。我们可以使用git ls-files --stage看到这一点,以及分段编号。以下是Git:的Git存储库的Git索引中的内容片段

100644 908330a0a3d5d1c1bad56544ba5bb18c3b783c84 0       .travis.yml
100644 5ba86d68459e61f87dae1332c7f2402860b4280c 0       .tsan-suppressions
100644 65651beada79b6267b1d0bda518a88269374cfdf 0       CODE_OF_CONDUCT.md
100644 536e55524db72bd2acf175208aef4f3dfc148d42 0       COPYING
100644 ddb030137d54ef3fb0ee01d973ec5cee4bb2b2b3 0       Documentation/.gitattributes
100644 9022d4835545cbf40c9537efa8ca9a7678e42673 0       Documentation/.gitignore
100644 45465bc0c98f5d88cfe1ade092d29b5dc32c1e23 0       Documentation/CodingGuidelines

(完整的索引内容运行了近4000行:每个文件一个条目,几乎有4000个文件)。分段号是每行中的第三个字段,正如您所看到的,每一个分段号在这里都是零这意味着没有正在进行的合并冲突最后一个字段保存文件名,如索引中所示;请注意,许多名称都嵌入了斜杠(即使在Windows上,这也总是一个正斜杠)。

当你有合并冲突时,Git所做的是在索引中留下三个——或者更准确地说,最多三——每个文件的版本4这三个文件在三个非零的分段索引号处。阶段编号1表示索引中的文件来自合并基提交。分段号2表示该文件来自";我们的";或者HEAD提交,并且分段号3表示该文件来自";他们的";犯罪在非零阶段存在任何条目表明该特定文件存在合并冲突。

你可以这样想的一种方式是,每个文件最多有四个";槽";。如果文件不冲突,则使用插槽零。否则,时隙1、2和/或3被占用。因此,如果一个文件处于合并冲突状态,那么它的槽号为非零(同一个文件可能有更多条目,还有其他槽号)。否则,它的插槽号为零(并且该文件没有更多条目)。

每当任何文件处于合并冲突状态时,Git都会拒绝进行新的提交。它实际上不能,因为只有槽零条目可以进入提交:提交中没有槽号的空间。因此,在提交之前,必须解决冲突

具体如何解决冲突取决于您。Git需要的东西很简单:它只需要你告诉Git:完全去掉槽1、2和/或3个条目你可以通过多种方式做到这一点:

  • git rm将删除这些条目
  • git add将把工作树中的一个文件复制到索引的槽零中,删除非零条目
  • CCD_ 39和CCD_

最后一个将是答案的关键。


4最初的索引设计本应允许三个以上,但实际上似乎没有利用这一点。


让我们再次回顾这个问题

引发这一切的最初问题是,之间有什么区别(如果有的话)

git reset HEAD

和:

git reset HEAD .

然后在另一个StackOverflow答案(包括运行六个列出的命令)中如何使用它。

git reset HEAD .命令最好写成:

git reset HEAD -- .

以明确这里的CCD_ 42是路径规范。现在我们转向git reset文档,特别是SYNOPSIS部分,内容如下:

概要

git reset[-q] [<tree-ish>] [--] <pathspec>…​ git reset[-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>] git reset(--patch | -p) [<tree-ish>] [--] [<pathspec>…​] git reset[--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]

这里有一些语法技巧需要理解:方括号[]中的东西是可选的,尖括号<>中的东西要用一些非空字符串代替,省略号(…​)表示";我们刚才说的一个或多个";。(在这种情况下,这是一个或多个路径规范。)括号围绕着用竖条|分隔的备选方案,因此(--patch | -p)意味着您可以在此处编写--patch-p。这些语法技巧在大多数Unix/Linux文档的SYNOPSIS部分中都是标准的。

这里还有更多针对Git的技巧:单词tree-ish的意思是根据gitrevisions文档可以接受的任何东西,只要Git可以将其转换为内部树对象说明符。在这种情况下,这意味着任何指定提交的操作都有效。HEAD指定了一个提交——当前提交——所以git reset HEAD与这个git reset <tree-ish>匹配。

第一个概要条目需要git reset,允许可选的-q,允许可选<tree-ish>,允许可选--,然后需要<pathspec>。因此:

git reset HEAD

与此表单不匹配,因为缺少路径规范。但是:

git reset HEAD .

与此表单匹配:它省略了--,但这是允许的。

大纲部分中的第二个表单需要git reset部分,像以前一样有一个可选的-q,允许一个可选--pathspec-from-file——如果使用了CCD_64,也可以使用--pathspec-file-nul——然后有一个可选择的<tree-ish>。因此:

git reset HEAD

与此表单匹配。(这是文档中的一个小故障!)

第三种形式需要--patch-p,因此这两个命令都不匹配。

最后一个表单需要git reset(一如既往),然后允许选项--soft--mixed--hard--merge--keep、可选-q和可选<commit>之一(请注意,这不是<tree-ish>,而是提交)。:

git reset HEAD

命令也与此表单匹配。所以git reset HEAD,没有一个点作为路径规范,可能就是其中之一。

正如我在上面所建议的,这是文档中的一个小故障:git reset HEAD应该进行两个允许的匹配中的哪一个?我们只能通过阅读文档来了解这一点(即使这样,我们也必须猜测一点,或者尝试测试重置)。

下一部分是DESCRIPTION部分。它说使用路径规范git reset类型

。。。将匹配CCD_ 81的所有路径的索引条目重置为它们在CCD_。(它不会影响工作树或当前分支。)

这意味着指定提交中文件的状态(在本例中为HEAD)被复制到索引中。没有完全提到的是(尽管在下一段中通过引用git restore至少部分涵盖了这一点),这与git add具有清除非零阶段条目的副作用相同。

所以这回答了什么:

git reset HEAD -- .

does:它重置,就像在中一样,清除冲突,并复制的HEAD提交副本,每个文件都与路径规范.匹配。

这给我们留下了一个问题:路径规范.匹配哪些文件当前重置文档中有一个缺陷。它指的是gitglossary页面,该页面告诉我们路径规范可以是相对于工作树的顶部,也可以是相对于当前工作目录,并且每个更特定的页面(即git reset的页面)都应该说。它没有说。事实上,这里的.是相对于当前工作目录的。所以,如果你而不是在树的顶部:

git reset HEAD -- .

表示仅此目录中及下的文件。

实验(一些现有文件的git rm --cached和一些新文件的git add)表明,git reset HEAD -- .将由于git rm而丢失的任何文件恢复到索引中,并从索引中删除任何不在HEAD中的新文件。如果文件对此更清楚可能会很好,但也许我们可以将实验结果视为明确/目标行为。

让我们转到另一个命令:

git reset HEAD

这是指没有--pathspec-from-file选项但有<tree-ish>git reset,还是指没有--hard--mixed或除<commit>之外的任何git reset?好吧,如果前者,它根本不会提供任何路径规范。--pathspec-from-file背后的想法是在文件中提供路径规范,而不是在命令行中,但这样会有一些路径规范。如果Git将其视为前者,则根本没有路径规范。

我们可以在这里尝试一个测试:

$ git reset HEAD^{tree}
error: object fcb94a429496c28fa7f95926e9d46840671d0d88 is a tree, not a commit
fatal: Could not parse object 'HEAD^{tree}'.

这使用gitreviews语法来确保我们为git reset提供树式的ish,而不是提交。结果是一个即时错误。运行git reset HEAD是有效的,所以这个特定的git reset必须将其与第四种语法匹配,而不是第二种语法。(文件应省略--pathspec-from-file周围的方括号。)

第四种语法由描述:

git reset[<mode>] [<commit>]

部分,其中写道:

此表单将当前分支头重置为<commit>,并可能根据<mode>更新索引(将其重置为树<commit>)和工作树。如果省略了<mode>,则默认为--mixed。[狙击]

所以这是一个--mixed重置,因为我们省略了<mode>参数(这是大纲中列举的四个选项之一)。我们选择的提交——导致当前分支名称将移动到该提交的——是HEAD选择的提交。但是HEAD是由当前分支名称选择的提交。因此,这一举措来自于某种承诺——让我们称之为"承诺";提交X"--如果你从现在的位置跳下,但安排在现在的位置降落,那真的不是什么大动作,是吗?:-)

无论如何,这意味着重置当前分支头短语变得无关紧要:当前分支头只是停留在原地。我们继续,并可能更新索引git reset是否更新索引取决于<mode>,我们刚才说它是--mixed,文档接着说:

重置索引,但不重置工作树。。。

因此,这会将当前提交复制回索引中。这有点像使用匹配每个文件的路径规范,只是我们根本不需要路径规范,而且事实上禁止使用路径规范。(使用路径规范可以让我们进入第一个语法,而不是第四个语法。)

与具有路径规范的git reset一样,这具有撤销任何合并冲突状态的副作用:索引中的任何非零阶段条目都将被提交时的阶段零副本所替换。

虽然没有很好的文档记录,但标准--mixed重置一直都是这样,这种git reset将从Git的索引中删除所选提交中任何而非的文件。正如我通过实验发现的,git reset HEAD -- .命令在这里的行为方式相同。

摘要

让我在这里再次复制:

事实证明,Git足够聪明,如果应用不干净,就不会丢弃隐藏。

这部分意味着要解决的问题是:

<do some hacking>
<realize that this is the wrong commit>
$ git stash
$ git checkout somebranch
$ git stash pop
<receive merge conflict messages>

git stash pop操作在其第一(git stash apply)步骤的中间停止,并且在此完全省略并且将不会运行其第二(git stash drop)步骤。

我能够通过以下步骤达到所需状态:

  1. 要取消合并冲突的分级:git reset HEAD .(注意后面的点)

这就是问题的来源。git reset --mixed HEAD,可以拼写为git reset,可能是更好的方法。这使得索引回到git stash apply甚至启动之前的状态。

(由于我们绝对不需要这个合并的结果——我们可以在以后任何时候重新创建它,只要我们还有两个存储提交——我们本可以运行git reset --hard。但这个家伙没有这么做。)

  1. 保存冲突合并(以防万一):git stash

这是完全没有必要的。它又进行了两次提交——每个存储条目都是两次或三次提交;看看我的这个老答案——其中一个是当前提交的副本,另一个保存工作树中的跟踪文件;则它运行CCD_ 134(我们本可以更早地这样做)。

  1. 返回主控:git checkout master

这当然正是我们所期望的。由于git stash末尾的git reset --hard,所以没有暂存或未暂存的更改:当前提交、Git的索引和工作树都匹配,git status会说nothing to commit, working tree clean

  1. 获取最新更改:git fetch upstream; git merge upstream/master

这从名为upstream的远程获取新的提交,然后执行git merge upstream/master将执行的任何操作;这显然取决于在git fetch中获得的对upstream/master的提交与对当前(master)分支的提交。

如果我们做一些假设——master通常与upstream/master同步,除非上游Git的用户向upstream添加新的提交——这将是一个简单的快进操作。

  1. 要更正我的新分支:git checkout new-branch; git rebase master

这将进行通常的重新基础工作。

  1. 要应用正确的隐藏更改(现在是堆栈上的第二个):git stash apply stash@{1}

如果我们早些时候跳过了额外的(不必要的)存储,git stash apply将在这里完成所需的操作。

最新更新