协慌网

登录 贡献 社区

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

我看到了一些有趣的帖子,它们解释了有关git reset细微差别。

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

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

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

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

答案

通常, 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可能会导致您(某种程度上)丢失提交。在上面的玩具示例中,我们丢失了提交C它仍然在仓库中,您可以通过查看git reflog show HEADgit reflog show master来找到它,但实际上不再可以从任何分支访问它。

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

争论

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

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

主要工作树和索引选项

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

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

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

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

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

  • --merge是最近添加的,旨在帮助您中止失败的合并。这是必要的,因为git merge实际上将允许您尝试与脏工作树(具有本地修改的工作树)进行合并,只要这些修改位于不受合并影响的文件中即可。 git reset --merge重置索引(例如 --mixed - 所有更改都显示为本地修改),并重置受合并影响的文件,但其他文件则--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视为 “n 在 HEAD 之前提交” 或 “HEAD 的第 n 代祖先”。
  • HEAD^ (或HEAD^1 )也表示提交的第一父级。 HEAD^2表示提交的第二个父级。请记住,普通的合并提交有两个父级 - 第一个父级是 merged-into 提交,第二个父级是已合并的提交。通常,合并实际上可以有任意多个父母(章鱼合并)。
  • 所述^~运算符可以串成,如在HEAD~3^2 ,第三代祖先的第二个亲本HEADHEAD^^2 ,的第一个亲本的第二个亲本HEAD ,甚至HEAD^^^ ,相当于HEAD~3

尖号和代字号

请记住, 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清除所有内容,并在新提交时重新开始。

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

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

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

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

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