如何撤消git commit --amend

这个问题已经在这里有了答案:

  • 如何撤消已完成的“ git commit --amend”而不是“ git commit” 10个答案

我不小心输入了git commit --amend。 这是一个错误,因为我意识到提交实际上是全新的,应该使用新消息来提交。 我想重新提交。 我该如何撤消呢?

Jwan622 asked 2020-08-08T17:39:58Z
1个解决方案
105 votes

PetSerAl的评论是关键。 这是执行所需操作的两个命令序列:

git reset --soft @{1}
git commit -C @{1}

并解释其工作原理。

描述

当您进行新的提交时,Git通常使用以下事件序列:

  1. 读取当前提交的ID(SHA-1哈希,例如a123456...)(通过HEAD,这为我们提供了当前分支)。 我们将此ID称为C(针对当前)。 注意,此当前提交有一个父提交。 我们将其称为ID P(针对“父对象”)。
  2. 将索引(又称临时区域)变成一棵树。 这将产生另一个ID; 我们将此ID称为T(对于Tree)。
  3. 用parent = C和tree = T编写一个新的提交。这个新的提交获得另一个ID。 我们称其为N(对于New)。
  4. 使用新的提交ID N更新分支。

使用git checkout时,Git会稍微改变该过程。 它仍然像以前一样写一个新的提交,但是在第3步中,它不是使用parent = C来编写新的提交,而是使用parent = P来编写它。

图片

在图形上,我们可以绘制出这种情况。 我们从以git checkout指向的git checkout结尾的提交图开始:

...--P--C   <-- branch

当我们进行新的提交git checkout时,我们得到:

...--P--C--N   <-- branch

当我们使用git checkout时,我们得到的是:

       C
      /
...--P--N   <-- branch

请注意,提交git checkout仍在存储库中。 它只是被推到了一边,所以新的提交@{1}可以指向老父级master@{1}

目标

git checkout之后,您意识到想要的是使链看起来像这样:

...--P--C--N   <-- branch

我们不能完全做到这一点-我们不能更改git checkout; 一旦将提交(或任何其他对象)存储在存储库中,Git就永远无法更改它,但请注意,@{1}链仍在其中,并且完好无损。 您可以通过引用日志找到提交master@{1},这就是HEAD@{1}语法的作用。 (具体来说,这是@{1},2表示“ git checkout指向一步之前”,即“提交@”。)

因此,我们现在运行git checkout,它执行以下操作:

       C   <-- branch
      /
...--P--N

现在git checkout指向@{1},后者指向master@{1}

git checkout会发生什么? 之前发生在@{1}上的事情相同:它通过reflog保存了一段时间。

我们真的不需要它(尽管它可能会派上用场),因为git checkout@{1}标志可以使索引/暂存区域保持不变(以及工作树)。 这意味着我们现在可以通过运行另一个master@{1}再次进行新的提交。它将执行相同的四个步骤(从HEAD@{1}读取ID,创建树,创建新的提交,并更新分支):

       C--N2   <-- branch
      /
...--P--N

其中git checkout将是我们的新新提交(第二个新提交?)。

我们甚至可以使git checkout再次使用来自提交@{1}的提交消息。master@{1}命令具有HEAD@{1}自变量,也拼写为develop; 我们要做的就是给它一些东西,让它找到要复制消息的原始新提交@,从而使HEAD可以使用。 我们该怎么做? 答案是:它在reflog中,就像master@{1}在需要执行master时一样。

实际上,这是相同的git checkout

请记住,git checkout的意思是“刚才的位置”,而@{1}刚刚对其进行了更新,将其从master@{1}移至30026300982322800800。我们尚未进行新的提交develop。(一旦这样做,@将为HEAD,但是我们 还没有。)

因此,将所有内容放在一起,我们得到:

git reset --soft @{1}
git commit -C @{1}

1此描述所细分的地方包括:当您修改合并时,在分离的HEAD上以及使用其他索引时。 即使这样,如何修改描述还是很明显的。

2如果将git checkout拆开,则没有当前分支,则含义变为@{1}。请注意,master@{1}本身是HEAD@{1}的缩写,因此事实n}指的是当前分支,而不是@本身,有点不一致。

要了解它们之间的区别,请考虑git checkout,然后考虑@{1}(假设两个分支都存在)。 第一个master@{1}HEAD@{1}指向develop,第二个将@指向HEAD。这意味着master@{1}就是master所指向的内容,而最后一次更新为master; 但是HEAD@{1}develop指向现在的提交,可能是其他提交。

(回顾:在这两个git checkout命令之后,@{1}表示现在3002630098232280080066,HEAD@{1}表示与develop现在相同的提交,而@表示HEAD意味着HEAD。如果您感到困惑,我也是,显然我并不孤单:请参阅注释 )

torek answered 2020-08-08T17:42:35Z
translate from https://stackoverflow.com:/questions/38001038/how-to-undo-a-git-commit-amend