简单地英语,“git reset”是做什么的?

hc8w905p  于 2023-04-19  发布在  Git
关注(0)|答案(7)|浏览(97)

我看过interesting posts解释git reset的微妙之处。
不幸的是,我读得越多,就越觉得我没有完全理解它。我来自SVN背景,而Git是一个全新的范式。我很容易就学会了mercurial,但Git的技术性要强得多。
我认为git reset接近hg revert,但似乎有差异。
那么git reset到底是做什么的呢?请详细解释一下:

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

nhaq1z211#

博客Pro Git中的帖子Reset Demystified给出了一个关于git resetgit checkout的非常简单的解释。
在那篇文章的顶部进行了所有有益的讨论之后,作者将规则简化为以下简单的三个步骤:
基本上就是这样,reset命令以特定的顺序覆盖这三个树,并在您告诉它时停止。
1.移动HEAD指向的任何分支(如果--soft则停止)
1.然后,使索引看起来像这样(在这里停止,除非--hard
1.然后,使工作目录看起来像这样
还有--merge--keep选项,但我现在更希望简化一些-这将在另一篇文章中讨论。

ne5o7dgx

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

jtoj6r0c

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的更改(就好像它们从未存在过一样)

wmtdaxz3

wmtdaxz34#

Checkout将head指向特定的提交。
Reset将分支指向特定的提交。(分支是指向提交的指针。)
顺便说一句,如果你的head没有指向一个也被分支指向的commit,那么你就有一个分离的head。(结果是错误的。见注解...)

13z8s7eq

13z8s7eq5#

我并不总是使用git reset,但是当我使用git reset的时候,我会看一下这个:

* 444668f (HEAD -> main) C
|
* c3739b7 B
|
* 207e8a1 A
|
* 38fab46 Initial commit

git reset --hard 207e8

* 207e8a1 (HEAD -> main) A
|
* 38fab46 Initial commit

To retrieve the changes, use --soft instead

HEAD移动到Amain也是,因为HEAD指向main)。git reset不会“重置”BC。您仍然可以使用git log--reflog选项看到BC

git log --graph --oneline --all --reflog

警告

在执行git reset之前,

  • 如果您有未暂存的更改
  • 使用--hard,它将被丢弃
  • 使用--mixeddefault),它将mixedstaged changes和检索到的commits changes
  • 如果您有暂存更改
  • 使用--hard,它将被丢弃
  • 使用--mixed,它将混合未暂存的更改和检索到的提交更改
  • 使用--soft,它将与检索到的提交的更改混合

为了摆脱它们,你可以使用git stash,但我更喜欢创建一个新的分支,并为那里的暂存和未暂存的更改创建一个单独的提交。然后当我需要它们时,使用git rebase + git reset

lvjbypge

lvjbypge6#

一般来说,git reset的功能是获取当前分支并将其重置为指向其他地方,并可能带来索引和工作树。更具体地说,如果您的主分支(当前已 checkout )是这样的:

- A - B - C (HEAD, master)

如果你想让master指向B,而不是C,你可以使用git reset B将它移动到B:

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

题外话:这与 checkout 不同。如果你运行git checkout B,你会得到这样的结果:

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

你已经结束了一个分离的HEAD状态。HEAD,工作树,索引都匹配B,但是master分支被留在了C。如果你在这个时候做一个新的提交D,你会得到这样的结果,这可能不是你想要的:

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

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

使用案例

我将在下一节对各种选项的描述中介绍git reset的许多主要用例。共同的思路是所有这些都涉及重置分支、索引和/或工作树以指向/匹配给定提交。

Things to be careful of

  • --hard可能会导致你真正失去工作。它修改了你的工作树。
  • git reset [options] commit可能会导致你丢失提交。在上面的玩具例子中,我们丢失了提交C。它仍然在仓库中,你可以通过查看git reflog show HEADgit 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将所有内容重置为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^2HEAD的第三代祖先的第二个父代,HEAD^^2HEAD的第一个父代的第二个父代,甚至HEAD^^^,它等效于HEAD~3

mbzjlibv

mbzjlibv7#

记住在git中你有:

    • HEAD指针 *,它告诉您正在处理的提交
    • 工作树 *,表示系统上文件的状态
  • staging area(也称为 index),用于“暂存”更改,以便稍后可以一起提交

请详细说明:
--hard--soft--merge;
按危险性递增顺序:

  • --soft移动HEAD,但不接触临时区域或工作树。
  • --mixed移动HEAD并更新临时区域,但不更新工作树。
  • --merge移动HEAD,重置暂存区,并尝试将工作树中的所有更改移动到新的工作树中。
  • --hardHEAD * 和 * 移动到新的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清除所有内容,并在新提交时重新开始。

相关问题