我看过interesting posts解释git reset
的微妙之处。
不幸的是,我读得越多,就越觉得我没有完全理解它。我来自SVN背景,而Git是一个全新的范式。我很容易就学会了mercurial,但Git的技术性要强得多。
我认为git reset
接近hg revert
,但似乎有差异。
那么git reset
到底是做什么的呢?请详细解释一下:
- 选项
--hard
、--soft
和--merge
; - 您对
HEAD
使用的奇怪符号,例如HEAD^
和HEAD~1
; - 具体的用例和工作流程;
- 对工作副本、
HEAD
和您的全局压力水平的影响。
7条答案
按热度按时间nhaq1z211#
博客Pro Git中的帖子Reset Demystified给出了一个关于
git reset
和git checkout
的非常简单的解释。在那篇文章的顶部进行了所有有益的讨论之后,作者将规则简化为以下简单的三个步骤:
基本上就是这样,
reset
命令以特定的顺序覆盖这三个树,并在您告诉它时停止。1.移动HEAD指向的任何分支(如果
--soft
则停止)1.然后,使索引看起来像这样(在这里停止,除非
--hard
)1.然后,使工作目录看起来像这样
还有
--merge
和--keep
选项,但我现在更希望简化一些-这将在另一篇文章中讨论。ne5o7dgx2#
TL;DR
git reset
将暂存重置为最后一次提交。使用--hard
还可以将工作目录中的文件重置为最后一次提交。加长版
但这显然是过于简单化了,因此有许多相当冗长的答案。对我来说,在撤销更改的上下文中阅读
git reset
更有意义。例如,请参阅以下内容:如果git revert是一种“安全”的撤销更改的方法,那么你可以把git reset看作是一种危险的方法。当你使用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
jtoj6r0c3#
对于希望在以下每个命令之后可视化其项目状态的视觉学习者可能有帮助:
对于那些使用终端并打开颜色的用户(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的更改(就好像它们从未存在过一样)wmtdaxz34#
Checkout将head指向特定的提交。
Reset将分支指向特定的提交。(分支是指向提交的指针。)
顺便说一句,如果你的head没有指向一个也被分支指向的commit,那么你就有一个分离的head。(结果是错误的。见注解...)
13z8s7eq5#
我并不总是使用git reset,但是当我使用git reset的时候,我会看一下这个:
HEAD
移动到A
(main
也是,因为HEAD
指向main
)。git reset
不会“重置”B
和C
。您仍然可以使用git log
的--reflog
选项看到B
和C
:警告
在执行
git reset
之前,--hard
,它将被丢弃--mixed
(default),它将mixedstaged changes和检索到的commits changes--hard
,它将被丢弃--mixed
,它将混合未暂存的更改和检索到的提交更改--soft
,它将与检索到的提交的更改混合为了摆脱它们,你可以使用
git stash
,但我更喜欢创建一个新的分支,并为那里的暂存和未暂存的更改创建一个单独的提交。然后当我需要它们时,使用git rebase
+git reset
。lvjbypge6#
一般来说,
git reset
的功能是获取当前分支并将其重置为指向其他地方,并可能带来索引和工作树。更具体地说,如果您的主分支(当前已 checkout )是这样的:如果你想让master指向B,而不是C,你可以使用
git reset B
将它移动到B:题外话:这与 checkout 不同。如果你运行
git checkout B
,你会得到这样的结果:你已经结束了一个分离的HEAD状态。
HEAD
,工作树,索引都匹配B
,但是master分支被留在了C
。如果你在这个时候做一个新的提交D
,你会得到这样的结果,这可能不是你想要的:记住,reset不会提交,它只是更新一个分支(指向一个提交的指针)以指向另一个提交。剩下的只是索引和工作树发生的细节。
使用案例
我将在下一节对各种选项的描述中介绍
git reset
的许多主要用例。共同的思路是所有这些都涉及重置分支、索引和/或工作树以指向/匹配给定提交。Things to be careful of
--hard
可能会导致你真正失去工作。它修改了你的工作树。git reset [options] commit
可能会导致你丢失提交。在上面的玩具例子中,我们丢失了提交C
。它仍然在仓库中,你可以通过查看git reflog show HEAD
或git reflog show master
找到它,但它实际上不再可以从任何分支访问。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 add ...
).--soft
不会触及index * 或 * work树。所有文件都与--mixed
一样完好无损,但所有更改都显示为带有git状态的changes to be committed
当你意识到你做了一些错误的提交,但工作都很好时,使用这个方法-你需要做的就是重新提交不同的索引。所以如果你愿意的话,你可以立即提交--结果提交的内容将与你重置之前的内容完全相同。--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手册页的“指定修订版本”部分有完整的文档,其中有很多示例和相关语法。插入符号和波浪号实际上是指different things:HEAD~
是HEAD~1
的缩写,表示提交的第一个父提交。HEAD~2
表示提交的第一个父提交的第一个父提交。可以将HEAD~n
视为“HEAD之前的n次提交”或“HEAD的第n代祖先”。HEAD^
(或HEAD^1
)也表示提交的第一个父提交。HEAD^2
表示提交的 * 第二个 * 父提交。记住,一个普通的合并提交有两个父提交-第一个父提交是被合并的提交,第二个父提交是被合并的提交。一般来说,合并实际上可以有任意多个父提交(octopus合并)。^
和~
运算符可以串在一起,如HEAD~3^2
,HEAD
的第三代祖先的第二个父代,HEAD^^2
,HEAD
的第一个父代的第二个父代,甚至HEAD^^^
,它等效于HEAD~3
。mbzjlibv7#
记住在
git
中你有:HEAD
指针 *,它告诉您正在处理的提交请详细说明:
--hard
、--soft
和--merge
;按危险性递增顺序:
--soft
移动HEAD
,但不接触临时区域或工作树。--mixed
移动HEAD
并更新临时区域,但不更新工作树。--merge
移动HEAD
,重置暂存区,并尝试将工作树中的所有更改移动到新的工作树中。--hard
将HEAD
* 和 * 移动到新的HEAD
,并将您的临时区域和工作树调整为新的HEAD
,丢弃所有内容。具体的用例和工作流程;
--soft
。--
--
--mixed
(默认值)。--merge
。--hard
清除所有内容,并在新提交时重新开始。