用简单的英语来说,“ git reset”有什么作用?


674

我看到了一些有趣的帖子,它们解释了有关的微妙之处git reset

不幸的是,我对它的了解越多,似乎我对它的理解就越不充分。我来自SVN背景,而Git是一个全新的范例。我很容易上手,但是Git更具技术性。

我认为git reset已经接近hg revert,但似乎存在差异。

那到底是git reset怎么做的呢?请提供有关以下内容的详细说明:

  • 选项--hard--soft--merge;
  • 您使用的HEAD诸如HEAD^和的奇怪符号HEAD~1
  • 具体的用例和工作流程;
  • 对工作副本,HEAD您和您的整体压力水平的影响。

17
我认为《 Visual Git参考》可以很好地了解使用常见git命令时会发生什么。

Answers:


992

通常,git reset的功能是获取当前分支并将其重置为指向其他位置,并可能将索引和工作树一起使用。更具体地说,如果您的master分支(当前已签出)是这样的:

- A - B - C (HEAD, master)

并且您意识到您希望master指向B,而不是C,您将使用git reset B它来移动它:

- A - B (HEAD, master)      # - C is still here, but there's no branch pointing to it anymore

离题:这与结帐不同。如果您运行git checkout B,则会得到以下信息:

- A - B (HEAD) - C (master)

您最终处于分离的HEAD状态。HEAD,工作树,索引全部匹配B,但master分支留在C。如果此时进行新的提交D,您将得到此信息,这可能不是您想要的:

- A - B - C (master)
       \
        D (HEAD)

请记住,reset不会提交提交,它只会更新一个分支(这是指向提交的指针)以指向另一个提交。剩下的只是索引和工作树发生的细节。

用例

git reset在下一节中,我将在各种选项的描述中介绍许多主要用例。它确实可以用于各种各样的事物。共同的线程是所有这些都涉及重置分支,索引和/或工作树以指向/匹配给定的提交。

要注意的事情

  • --hard会导致您真的失去工作。它修改了您的工作树。

  • git reset [options] commit可能导致您(某种程度上)丢失提交。在上面的玩具示例中,我们丢失了commit C。它仍在存储库中,您可以通过查看git reflog show HEAD或来找到它git reflog show master,但实际上不再可以从任何分支访问它。

  • Git在30天后会永久删除此类提交,但是在此之前,您可以通过再次将分支指向C来恢复它(git checkout C; git branch <new branch name>)。

争论

解释手册页,最常见的用法是形式git reset [<commit>] [paths...],它将把给定路径从给定提交重置为其状态。如果未提供路径,则会重置整个树,如果未提供提交,则将其视为HEAD(当前提交)。这是整个git命令的通用模式(例如,checkout,diff,log,尽管确切的语义有所不同),所以这并不奇怪。

例如,将git reset other-branch path/to/foopath / to / foo中的所有内容git reset -- .重置为other-branch中的状态,将当前目录重置为HEAD中的状态,简单地将git reset所有内容重置为HEAD中的状态。

主要工作树和索引选项

有四个主要选项可以控制重置期间工作树和索引的处理方式。

记住,索引是git的“临时区域”-当您git add准备提交时,它就是这里的去处。

  • --hard使所有内容都与您已重置的提交匹配。这可能是最容易理解的。您所有的本地更改都会受到破坏。一种主要用途是浪费您的工作,但不切换提交:git reset --hardmeans git reset --hard HEAD,即,不更改分支,但摆脱所有本地更改。另一个只是将分支从一个位置移动到另一个位置,并使索引/工作树保持同步。这确实会使您失去工作,因为它修改了您的工作树。非常非常确定要在运行任何操作之前先放弃本地工作reset --hard

  • --mixed是默认值,即git resetmeans git reset --mixed。它重置索引,但不重置工作树。这意味着您的所有文件都是完整的,但是原始提交和您重置为的所有提交之间的任何差异都将以git状态显示为本地修改(或未跟踪的文件)。当您意识到自己做了一些错误的提交,但是想要保留所有已完成的工作,以便对其进行修复和重新提交时,请使用此功能。为了提交,您必须再次将文件添加到索引(git add ...)。

  • --soft不触及索引工作树。和一样--mixed,您的所有文件均完好无损,但所有更改都显示为changes to be committedgit status(即已签入,准备提交)。当您意识到自己做了一些错误的提交,但工作很顺利时,请使用此功能-您要做的就是以不同的方式重新提交它。索引未更改,因此您可以根据需要立即提交-所生成的提交将具有与重置之前相同的内容。

  • --merge是最近添加的,旨在帮助您中止失败的合并。这是必要的,因为git merge只要这些修改在不受合并影响的文件中,实际上就可以让您尝试与脏工作树(具有本地修改的工作树)进行合并。git reset --merge重置索引(例如--mixed-所有更改都显示为本地修改),并重置受合并影响的文件,但将其他文件保留下来。希望这将使一切恢复到错误合并之前的状态。通常将其用作git reset --merge(含义git reset --merge HEAD),因为您只想重置合并,而不实际移动分支。(HEAD由于合并失败,尚未更新)

    更具体地说,假设您已经修改了文件A和B,并尝试在一个分支中合并了修改文件C和D的文件。由于某种原因合并失败,因此您决定中止该文件。您使用git reset --merge。它使C和D恢复它们的状态HEAD,但由于A和B并不是合并尝试的一部分,因此您只需对A和B进行修改。

想知道更多?

我确实认为man git reset这确实非常有用-也许您确实需要git的工作方式让他们真正陷入困境。特别是,如果您花时间仔细阅读它们,则这些表详细列出了所有各种选项和案例的索引和工作树中的文件状态,这些非常有用。(但是,是的,它们非常密集-他们以非常简洁的形式传达了很多上述信息。)

奇怪的符号

您提到的“奇怪记号”(HEAD^HEAD~1)只是用于指定提交的简写形式,而不必使用诸如的哈希名称3ebe3f6。在git-rev-parse的手册页的“指定修订”部分中有完整的文档,并提供了许多示例和相关语法。插入符号和波浪号实际上表示不同的含义:

  • HEAD~的缩写HEAD~1,表示提交的第一父级。HEAD~2表示提交的第一父级的第一父级。可以认为HEAD~n是“在HEAD之前提交了n次提交”或“ HEAD的第n代祖先”。
  • HEAD^(或HEAD^1)也表示提交的第一父级。HEAD^2表示提交的第二个父级。请记住,普通的合并提交有两个父级-第一个父级是merged-into提交,第二个父级是已合并的提交。通常,合并实际上可以有任意多个父母(章鱼合并)。
  • ^~运营商可以被串在一起,如在HEAD~3^2,第三代祖先的第二亲本HEADHEAD^^2,的第一个亲本的第二个亲本HEAD,或甚至HEAD^^^,这相当于HEAD~3

尖号和代字号


“您将使用git reset将其移动到那里。” 您为什么不使用git checkout这样做呢?
e-satis

5
@ e-satis:git checkout将移动HEAD,但将分支保留在原处。当您要移动分支时。
卡斯卡贝尔

因此,如果我了解得很好,则重置B可以:-A-B-C-B(主),而结帐B可以-A-B(主)?
e-satis 2010年

32
该文档非常好,即使需要花费大量时间阅读并且文档非常密集,也需要花费大量时间来验证它们是否像您已经知道其工作原理一样有效。听起来文档对我没有好处……
Kirby 2012年

4
这样的答案给出了一个非常简单易懂的解释:stackoverflow.com/questions/3528245/whats-the-difference-between-git-reset-mixed-soft-and-hard
Nitin Bansal

80

请记住,git您拥有:

  • HEAD指针,它告诉你什么承诺你的工作
  • 工作树,这代表你的系统上的文件的状态
  • 临时区域(也称为指数),其中“分期”的变化,让他们以后可以一起提交

请提供有关以下内容的详细说明:

--hard--soft--merge;

按危险性升序排列:

  • --soft移动HEAD但不接触暂存区或工作树。
  • --mixed移动HEAD并更新暂存区域,而不是工作树。
  • --merge移动HEAD,重置临时区域,然后尝试将工作树中的所有更改移到新的工作树中。
  • --hard移动HEAD 调整暂存区和工作树以适应新的需求HEAD,并丢弃所有内容。

具体的用例和工作流程;

  • 使用--soft时要移动到另一个承诺和修补的东西没有“失去你的地方。” 您很少需要这个。

-

# git reset --soft example
touch foo                            // Add a file, make some changes.
git add foo                          // 
git commit -m "bad commit message"   // Commit... D'oh, that was a mistake!
git reset --soft HEAD^               // Go back one commit and fix things.
git commit -m "good commit"          // There, now it's right.

-

  • --mixed当您想查看另一次提交时的情况时使用(这是默认设置),但又不想丢失已经拥有的任何更改。

  • 使用--merge时要移动到一个新的地方,但包括你已经有了成工作树的变化。

  • 使用--hard擦拭的一切,并开始一个新的石板在新的提交。


2
那不是的预期用例reset --merge。它不执行三向合并。如文档中所述,它实际上仅用于重置冲突合并。您将想要使用checkout --merge您正在谈论的内容。如果您也想移动分支,我认为唯一的方法是跟进一些检出/重置操作以将其拖动。
卡斯卡贝尔

@Jefromi»是的,我说得不太好。“一个新位置”是指“一个没有冲突合并的新鲜地方”。
John Feminella

1
知道了 我认为这里重要的是,除非您真的知道自己在做什么,否则您可能永远都不想reset --merge与(默认值)之外的任何目标一起使用HEAD,因为在某些情况下,除了中止冲突的合并之外,它还会被丢弃。您可以另外保存的信息。
卡斯卡贝尔

2
我发现这个答案最简单,最有用
Jazzepi

请添加有关以下命令的信息:git resetgit reset -- .
Flimm 2013年

35

Pro Git博客中的Reset Demystified帖子在and 上给出了非常清晰的解释。git resetgit checkout

在文章顶部进行了所有有益的讨论之后,作者将规则简化为以下三个简单步骤:

基本上就是这样。该reset命令将以特定顺序覆盖这三棵树,并在您告诉它时停止。

  1. 移动任何分支HEAD指向的位置(如果,则停止--soft
  2. 然后,使索引看起来像这样(除非在此处停止,否则--hard
  3. 然后,使工作目录如下所示

也有--merge--keep选项,但我现在想使事情更简单-那将是另一篇文章。


25

当您向git提交内容时,首先必须暂存(添加到索引中)您的更改。这意味着您必须git添加要包含在此提交中的所有文件,然后git才将它们视为提交的一部分。首先让我们看一下git repo的图像: 在此处输入图片说明

所以,现在很简单。我们必须在工作目录中工作,创建文件,目录等。这些更改是未跟踪的更改。为了跟踪它们,我们需要使用git add命令将它们添加到git index中。一旦将它们添加到git索引中。如果我们想将其推送到git仓库,我们现在可以提交这些更改。

但是突然间,我们在提交承诺时知道了我们有一个额外的文件,我们不需要将它添加到索引中来推送git存储库。这意味着我们不希望该文件包含在索引中。现在的问题是如何从git索引中删除该文件,因为我们使用git add将它们放入索引中,所以使用git rm是合乎逻辑的?错误!git rm只会删除文件并将删除内容添加到索引中。那么现在该怎么办:

采用:-

git重置

它清除您的索引,使您的工作目录保持不变。(只需取消所有内容的暂存)。

它可以与许多选项一起使用。git reset可使用三个主要选项:--hard,--soft和--mixed。这些会影响您重置时除了HEAD指针之外的重置内容。

首先,-- hard重置所有内容。如果您一直关注该分支,则当前目录将与该目录完全相同。工作目录和索引更改为该提交。这是我最常使用的版本。git reset --hard类似于svn revert

接下来,完全相反的符号—soft,不会重置工作树或索引。它仅移动HEAD指针。这将使您的当前状态具有与您要在目录中切换到的提交不同的任何更改,并“暂存”用于提交。如果您在本地进行提交但尚未将提交推送到git服务器,则可以重置为先前的提交,并使用良好的提交消息重新提交。

最后,-- mixed重置索引,但不重置工作树。因此更改仍然存在,但是是“未暂存”的,需要进行git add'ed或git commit -a。如果有时我们承诺的投入比git commit -a所承诺的要多,我们有时会用到这一点,我们可以使用git reset --mixed撤消承诺,添加我们要承诺的内容,然后只提交这些内容。

git revert和git reset之间的区别:-


在简单的话,git的复位就是命令“修复未提交的错误”混帐复归就是命令“修复COMMITED错误”

这意味着如果我们在某些更改中犯了一些错误,并将其提交并推送到git repo,那么git revert是解决方案。如果在推送/提交之前发现了相同的错误,可以使用git reset修复问题。

我希望它能帮助您摆脱混乱。


2
OP要求的,这是一个很好的普通英语答案。
Episodex

1
即使在您的回答中我可能会错过这一点。git reset HEAD默认情况下是什么?--hard--soft还是--mixed?好答案顺便说一句。
giannis christofakis

1
很好的答案,但我会更清楚地指出,这git reset --hard会导致您丢失一些数据。有一点可能是错误的(尽管我不是100%确信...还在学习!):谈论--mixed您时说的是:“如果我们比git commit -a承诺更多,我们有时会使用它”。您的意思是:“如果我们上演的节目超出了我们的预期git stage .”?如果您真的犯了,我认为为时已晚(正如您在最后所说,git reset是“修复未提交的错误”的命令)
Fabio说恢复Monica的

6

TL; DR

git reset将登台重置为最后一次提交。使用--hard在你的工作目录还重置文件上次提交。

更长的版本

但这显然很简单,因此有许多相当冗长的答案。对于我来说,git reset在撤消更改的情况下继续阅读更有意义。例如:

如果git revert是撤消更改的“安全”方法,则可以将git reset视为危险的方法。当您使用git reset撤消(并且提交不再被任何引用或引用日志引用)时,将无法检索原始副本,这是永久性的撤消。使用此工具时必须小心,因为它是可能会丢失工作的仅有的Git命令之一。

来自https://www.atlassian.com/git/tutorials/undoing-changes/git-reset

和这个

在提交级别,重置是一种将分支的尖端移动到其他提交的方法。这可用于从当前分支中删除提交。

来自https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting/commit-level-operations


2

请注意,这是一个简化的解释,旨在作为寻求了解此复杂功能的第一步。

对于希望通过以下每个命令来形象化其项目状态的视觉学习者可能会有所帮助:


对于那些使用启用了颜色的终端(git config --global color.ui auto)的用户:

git reset --soft A 然后您会看到B和C的内容为绿色(已暂存并准备提交)

git reset --mixed A(或git reset A),您将看到B和C的内容为红色(未暂存并准备暂存(绿色),然后提交)

git reset --hard A 并且您将不再在任何地方看到B和C的更改(就好像它们从未存在过一样)


或对于使用GUI程序(例如“ Tower”或“ SourceTree”)的用户

git reset --soft A 并且您将在“暂存文件”区域中看到B和C的东西准备提交

git reset --mixed A(或git reset A),您将在“未暂存的文件”区域中看到B和C的内容准备移至暂存,然后提交

git reset --hard A 并且您将不再在任何地方看到B和C的更改(就好像它们从未存在过一样)


1

Checkout将头部指向特定提交。

重置将分支指向特定提交。(分支是指向提交的指针。)

顺便说一句,如果您的头没有指向分支也指向的提交,那么您的头就会分离。 (结果是错误的。请参阅评论...)


1
不是要挑剔,而是(是的,实际上挑剔,但让我们添加它以完成操作)您的第三句话在技术上是错误的。假设您的HEAD指向分支B,分支B又指向提交abc123。如果现在检出commit abc123,则HEAD和分支B都指向commit abc123,并且HEAD被分离。此时提交将不会更新分支B的位置。您可能已经说过“如果您的头没有指向分支,那么您就拥有一个分离的头”
RomainValeri

@RomainValeri在这种情况下会做什么?
伊恩·沃伯顿

1
提交将创建任何分支都未引用的提交,并且分支B即使在此之后多次提交,也将继续指向同一提交abc123。这意味着当HEAD停止指向该“野生”提交系列中的最后一个提交时,这些提交将成为垃圾回收的候选对象。
RomainValeri
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.