用简单的英语来说,“git reset”是做啥的?
Posted
技术标签:
【中文标题】用简单的英语来说,“git reset”是做啥的?【英文标题】:In plain English, what does "git reset" do?用简单的英语来说,“git reset”是做什么的? 【发布时间】:2011-02-01 13:41:54 【问题描述】:我看到interesting posts 解释了关于git reset
的微妙之处。
不幸的是,我读得越多,就越觉得我不完全理解它。我来自 SVN 背景,Git 是一个全新的范例。我很容易变得善变,但 Git 技术性更强。
我认为git reset
与hg revert
接近,但似乎有区别。
那么git reset
究竟做了什么?请详细说明:
--hard
、--soft
和 --merge
选项;
与HEAD
一起使用的奇怪符号,例如HEAD^
和HEAD~1
;
具体用例和工作流程;
工作副本、HEAD
和您的整体压力水平的后果。
【问题讨论】:
截至 2021 年 4 月 24 日,A Visual Git Reference 的工作链接。 【参考方案1】:一般来说,git reset
的功能是获取当前分支并将其重置为指向其他地方,并可能将索引和工作树一起带上。更具体地说,如果您的主分支(当前已签出)是这样的:
- 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
,但主分支留在C
。如果此时你进行新的提交 D
,你会得到这个,这可能不是你想要的:
- A - B - C (master)
\
D (HEAD)
请记住,reset 不会进行提交,它只是更新一个分支(它是一个提交的指针)以指向不同的提交。其余的只是您的索引和工作树发生的情况的详细信息。
用例
我在下一节对各种选项的描述中介绍了git reset
的许多主要用例。它真的可以用于各种各样的事情;共同点是所有这些都涉及重置分支、索引和/或工作树以指向/匹配给定的提交。
注意事项
--hard
会导致你真的丢掉工作。它会修改您的工作树。
git reset [options] commit
可能会导致您(在某种程度上)丢失提交。在上面的玩具示例中,我们丢失了提交 C
。它仍在 repo 中,您可以通过查看 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/foo
将 path/to/foo 中的所有内容重置为其在 other-branch 中的状态,git reset -- .
将当前目录重置为其在 HEAD 中的状态,而简单的 git reset
将所有内容重置为其在其中的状态头。
主要工作树和索引选项
有四个主要选项可以控制在重置期间您的工作树和索引会发生什么。
记住,索引是 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
并带有git 状态(即签入以准备提交)。当你意识到你做了一些错误的提交时使用它,但工作都很好——你需要做的就是以不同的方式重新提交它。索引未受影响,因此您可以根据需要立即提交 - 生成的提交将具有与重置前相同的所有内容。
--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,因为它们不是尝试合并的一部分。
想了解更多?
我确实认为man git reset
确实非常适合这一点 - 也许您确实需要对 git 的工作方式有所了解才能真正融入其中。特别是,如果您花时间仔细阅读它们,那些详细说明所有各种选项和案例的索引和工作树中文件状态的表格非常有帮助。 (但是,是的,它们非常密集——它们以非常简洁的形式传达了上述大量信息。)
奇怪的符号
您提到的“奇怪符号”(HEAD^
和 HEAD~1
)只是指定提交的简写,而不必使用像 3ebe3f6
这样的哈希名称。它在 git-rev-parse 的手册页的 "specifying revisions" section 中有完整的记录,其中包含大量示例和相关语法。插入符号和波浪号实际上表示different things:
HEAD~
是 HEAD~1
的缩写,表示提交的第一个父级。 HEAD~2
表示提交的第一个父级的第一个父级。将HEAD~n
视为“HEAD 之前的 n 次提交”或“HEAD 的第 n 代祖先”。
HEAD^
(或HEAD^1
)也表示提交的第一个父级。 HEAD^2
表示提交的 second 父级。请记住,正常的合并提交有两个父级 - 第一个父级是合并到的提交,第二个父级是合并的提交。一般来说,合并实际上可以有任意多个父级(章鱼合并)。
^
和~
运算符可以串在一起,如HEAD~3^2
,HEAD
的第三代祖先的第二个父代,HEAD^^2
,@ 的第一个父代的第二个父代987654383@,甚至是HEAD^^^
,相当于HEAD~3
。
【讨论】:
"你将使用 git reset 将它移到那里。"你为什么不使用 git checkout 来做呢? @e-satis: git checkout 将移动 HEAD,但将分支留在原来的位置。这是在您想要移动分支时使用的。 这些文档很好,尽管阅读它们需要很长时间,而且它们非常密集,并且需要很长时间才能验证他们说它是否像您已经知道它是如何工作的那样工作。听起来文档对我来说不太好...... @Kirby 我花了更长的时间才能读到这样的东西。文档完整、正确且简洁,这意味着它们很密集。这是重要的信息;如果没有总结和近似,你永远无法在短时间内传达它。 这个 SO 答案给出了一个非常简单易懂的解释:***.com/questions/3528245/whats-the-difference-between-git-reset-mixed-soft-and-hard跨度> 【参考方案2】:请记住,在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
清除所有内容并在新提交时重新开始。
【讨论】:
这不是reset --merge
的预期用例。它不执行三向合并。如文档中所述,它实际上仅用于重置冲突合并。你会想要使用checkout --merge
来做你正在谈论的事情。如果您也想移动分支,我认为唯一的方法是跟进一些结帐/重置以将其拖动。
@Jefromi » 是的,我的措辞不是很好。我所说的“新地点”是指“没有冲突合并的新地点”。
啊,我明白了。我认为这里重要的是,除非您真的知道自己在做什么,否则您可能永远不想将reset --merge
与除(默认)HEAD
之外的任何目标一起使用,因为除了中止冲突合并之外的情况下,它会丢弃您本来可以保存的信息。
我发现这个答案最简单也最有帮助
请添加有关这些命令的信息:git reset
和 git reset -- .
。【参考方案3】:
博客Pro Git 中的帖子Reset Demystified 对git reset
和git checkout
给出了非常不费吹灰之力的解释。
在该帖子顶部的所有有益讨论之后,作者将规则简化为以下简单的三个步骤:
基本上就是这样。
reset
命令以特定顺序覆盖这三棵树,当您告诉它时停止。移动 HEAD 指向的任何分支(如果
--soft
则停止) 那么,让索引看起来像这样(除非--hard
,否则停在这里) 那么,让工作目录看起来像这样还有
--merge
和--keep
选项,但我宁愿暂时让事情变得更简单——这将是另一篇文章。
【讨论】:
我只花了 13 年的时间编写代码,终于坐下来理解这些概念【参考方案4】:当您向 git 提交某些内容时,您首先必须暂存(添加到索引)您的更改。这意味着在 git 将它们视为提交的一部分之前,您必须 git add 所有要包含在此提交中的文件。让我们先看一下 git repo 的图像:
所以,现在很简单。我们必须在工作目录中工作,创建文件、目录等等。这些更改是未跟踪的更改。为了使它们被跟踪,我们需要使用 git add 命令将它们添加到 git index 中。一旦它们被添加到 git 索引中。如果我们想将其推送到 git 存储库,我们现在可以提交这些更改。
但突然我们在提交时发现我们在索引中添加的一个额外文件不需要推送到 git 存储库。这意味着我们不希望该文件在索引中。 现在的问题是如何从 git index 中删除该文件,既然我们使用 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 或 git commit -a。如果我们使用 git commit -a 提交的内容超出预期,我们有时会使用它,我们可以使用 git reset --mixed 取消提交,添加我们想要提交的内容并提交这些内容。
git revert 和 git reset 的区别 :-
简单来说,git reset是“修复未提交的错误”的命令,git revert是的命令“修复提交的错误”。
这意味着如果我们在某些更改中犯了一些错误并提交并将其推送到 git repo,那么 git revert 是解决方案。如果我们在推送/提交之前发现了同样的错误,我们可以使用 git reset 来解决这个问题。
我希望它能帮助你摆脱困惑。
【讨论】:
这是 OP 要求的一个很好的简单英文答案。 尽管我可能在您的回答中错过了这一点。git reset HEAD
默认是什么? --hard
,--soft
或 --mixed
?顺便说一句,答案很好。
很好的答案,但我会更清楚地说明git reset --hard
会导致您丢失一些数据。还有一点可能是错误的(尽管我不是 100% 确定......仍在学习!):谈论 --mixed
你说“如果我们使用 git commit 的承诺超出了我们的预期,我们有时会使用它 -一种”。您的意思是:“如果我们上演的次数超出了git stage .
的预期”?如果你真的犯了,我认为为时已晚(正如你最后所说, git reset 是“修复未提交的错误”的命令)【参考方案5】:
TL;DR
git reset
将 Staging 重置为最后一次提交。使用--hard
也可以将工作目录中的文件重置为最后一次提交。
加长版
但这显然过于简单,因此有许多相当冗长的答案。在撤消更改的背景下阅读git reset
对我来说更有意义。例如。看到这个:
如果 git revert 是撤消更改的“安全”方式,您可以考虑 git 重置为危险的方法。当您使用 git reset 撤消时(以及 提交不再被任何 ref 或 reflog 引用),有 无法检索原始副本 - 这是永久撤消。关心必须 使用此工具时请注意,因为它是仅有的可能会丢失您工作的 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
【讨论】:
【参考方案6】:请注意,这是一个简化的解释,旨在作为寻求理解这一复杂功能的第一步。
对于希望在执行以下每个命令后可视化项目状态的视觉学习者可能会有所帮助:
对于那些使用已打开颜色的终端的用户 (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 的变化(就好像它们从未存在过一样)
或者对于那些使用“Tower”或“SourceTree”等 GUI 程序的人
git reset --soft A
你会在“暂存文件”区域看到 B 和 C 的东西准备提交
git reset --mixed A
(或git reset A
),您将在“未暂存文件”区域中看到 B 和 C 的内容已准备好移至暂存文件,然后提交
git reset --hard A
并且您将不再在任何地方看到 B 和 C 的更改(就好像它们从未存在过一样)
【讨论】:
【参考方案7】:Checkout 将头部指向特定的提交。
Reset 将分支指向特定提交。 (分支是指向提交的指针。)
顺便说一句,如果你的 head 没有指向一个分支也指向的提交,那么你就有了一个分离的 head。 (结果是错误的。参见 cmets.. .)
【讨论】:
不要吹毛求疵,但是(是的,实际上它是吹毛求疵,但让我们添加它以完成)您的第三句话在技术上是错误的。假设您的 HEAD 指向分支 B,而分支 B 又指向提交 abc123。如果您现在签出提交 abc123,您的 HEAD 和分支 B 都指向提交 abc123,并且您的 HEAD 已分离。此时提交将不会更新分支 B 的位置。你可以说“如果你的头不指向一个分支,那么你就有一个分离的头” @RomainValeri 在这种情况下提交会做什么? 提交会创建不被任何分支引用的提交,并且分支 B 将继续指向同一个提交 abc123,即使在您之后提交多次之后也是如此。这意味着当 HEAD 停止指向此“狂野”系列提交中的最后一个提交时,这些提交将成为垃圾收集的候选对象。以上是关于用简单的英语来说,“git reset”是做啥的?的主要内容,如果未能解决你的问题,请参考以下文章