ChatGPT解决这个技术问题 Extra ChatGPT

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

我看到 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 的工作链接。

C
Cascabel

一般来说,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 <新分支名称>)。

论据

解释手册页,最常见的用法是 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 一样完好无损,但所有更改都显示为要使用 git 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 的修改单独保留,因为它们不是尝试合并的一部分。

想知道更多?

我确实认为 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 表示提交的第二个父级。请记住,正常的合并提交有两个父级 - 第一个父级是合并到的提交,第二个父级是合并的提交。一般来说,合并实际上可以有任意多个父级(章鱼合并)。

^ 和 ~ 运算符可以串在一起,如 HEAD~3^2,HEAD 的第三代祖先的第二个父代,HEAD^^2,HEAD 的第一个父代的第二个父代,甚至 HEAD^^ ^,相当于 HEAD~3。

https://i.stack.imgur.com/J73jv.png


@e-satis: git checkout 将移动 HEAD,但将分支留在原来的位置。这是在您想要移动分支时使用的。
好的。我刚刚得到了一件非常重要的事情。你说“没有指向它的分支”,这让我很烦。现在我懂了。分支不是更改列表,它只是指向历史中某处的指针,不是吗?这就是为什么 SVN 家伙不明白的原因,我们没有以正确的方式看待它。非常有用的帖子,希望你能从中得到很多代表。
这些文档很好,即使阅读它们需要很长时间并且它们非常密集并且需要很长时间才能验证他们说它是否像您已经知道它是如何工作的那样工作。听起来文档对我来说不是很好...
@Kirby 我需要更长的时间才能阅读这样的内容。文档完整、正确且简洁,这意味着它们很密集。这是重要的信息;如果没有总结和近似,你永远无法在短时间内传达它。
这个SO答案给出了一个非常简单易懂的解释:stackoverflow.com/questions/3528245/whats-the-difference-between-git-reset-mixed-soft-and-hard
J
John Feminella

请记住,在 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 resetgit reset -- .
s
suspectus

博客 Pro Git 中的帖子 Reset Demystifiedgit resetgit checkout 给出了非常不费吹灰之力的解释。

在该帖子顶部的所有有用讨论之后,作者将规则简化为以下简单的三个步骤:

基本上就是这样。重置命令以特定顺序覆盖这三个树,当你告诉它时停止。移动 HEAD 指向的任何分支(如果 --soft 则停止)然后,使索引看起来像那样(除非 --hard 在这里停止)然后,使工作目录看起来像那样 还有 --merge 和 --keep 选项,但我现在宁愿让事情变得更简单——那将是另一篇文章。


我只花了 13 年的时间写代码,终于坐下来理解这些概念
l
love

https://i.stack.imgur.com/qwGOi.png

所以,现在很简单。我们必须在工作目录中工作,创建文件、目录等等。这些更改是未跟踪的更改。为了使它们被跟踪,我们需要使用 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'ed 或 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 是“修复未提交的错误”的命令)
S
Snowcrash

TL;博士

git reset 将 Staging 重置为最后一次提交。使用 --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


t
timhc22

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

对于希望在以下每个命令之后可视化其项目状态的视觉学习者可能会有所帮助:

对于那些使用终端打开颜色的人(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 的更改(就好像它们从未存在过一样)


N
Neuron

Checkout 将头部指向特定的提交。

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

顺便说一句,如果你的头没有指向一个分支也指向的提交,那么你就有了一个分离的头。 (原来是错的。见评论......)


不是吹毛求疵,但是(是的,实际上它是吹毛求疵,但让我们添加它以完成)您的第三句话在技术上是错误的。假设您的 HEAD 指向分支 B,而分支 B 又指向提交 abc123。如果您现在签出提交 abc123,您的 HEAD 和分支 B 都指向提交 abc123,并且您的 HEAD 已分离。此时提交不会更新分支 B 的位置。你可以说“如果你的头不指向一个分支,那么你就有一个分离的头”
@RomainValeri 在这种情况下承诺会做什么?
提交将创建未被任何分支引用的提交,并且分支 B 将继续指向同一个提交 abc123,即使在您之后提交多次之后也是如此。这意味着当 HEAD 停止指向此“狂野”系列提交中的最后一个提交时,这些提交将成为垃圾收集的候选者。
M
M Imam Pratama

我并不总是做 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,它将被丢弃使用--mixed(默认),它将与暂存的更改和检索到的提交更改混合

使用--hard,它将被丢弃

使用 --mixed (默认),它将与分阶段更改和检索到的提交更改混合

如果您已暂存更改:使用--hard,它将被丢弃使用--mixed,它将与未暂存的更改和检索到的提交更改混合使用--soft,它将与检索到的提交的更改混合

使用--hard,它将被丢弃

使用--mixed,它将与未暂存的更改和检索到的提交更改混合在一起

使用--soft,它将与检索到的提交的更改混合在一起

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