如何撤消git commit --amend

Jwa*_*622 42 git

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

tor*_*rek 107

PetSerAl的评论是关键.这是两个命令序列,可以满足您的需求:

git reset --soft @{1}
git commit -C @{1}
Run Code Online (Sandbox Code Playgroud)

并解释这是如何工作的.

描述

当你犯了一个新的提交,Git的通常1使用事件的顺序:

  1. 读取a123456...当前提交的ID(SHA-1哈希等)(via HEAD,它为我们提供当前分支).我们称这个ID为C(对于当前).请注意,此当前提交具有​​父提交; 让我们称它为ID P(对于Parent).
  2. 将索引(也称为临时区域)转换为树.这会产生另一个ID; 让我们称这个ID为T(对于Tree).
  3. 使用parent = C和tree = T编写新的提交.这个新提交获得另一个ID.我们称之为N(对于新).
  4. 使用新提交ID N更新分支.

使用--amendGit时会稍微改变一下这个过程.它仍然像以前一样写一个新的提交,但是在步骤3中,它不是用parent = C写新的提交,而是用parent = P写它.

图片

从图像上看,我们可以画出这种方式.我们从一个以下结尾的提交图开始P--C,指向branch:

...--P--C   <-- branch
Run Code Online (Sandbox Code Playgroud)

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

...--P--C--N   <-- branch
Run Code Online (Sandbox Code Playgroud)

当我们使用时--amend,我们得到这个:

       C
      /
...--P--N   <-- branch
Run Code Online (Sandbox Code Playgroud)

需要注意的是承诺C仍然在仓库中 ; 它只是被推到一边,一路走开,所以新的提交N可以指向旧的父母P.

目标

在你之后你意识到你想要git commit --amend的链条看起来像是:

...--P--C--N   <-- branch
Run Code Online (Sandbox Code Playgroud)

我们不能相当做到这一点,我们不能改变N; 一旦存储在repo中,Git永远不会更改任何提交(或任何其他对象),但请注意...--P--C链仍然在那里,完整无缺.您可以C通过reflog 找到提交,这就是@{1}语法的作用.(具体来说,这是2的缩写currentbranch@{1},意思是" 前一步指向",即"提交".)currentbranchC

所以,我们现在运行git reset --soft @{1},这样做:

       C   <-- branch
      /
...--P--N
Run Code Online (Sandbox Code Playgroud)

现在branch指向C,指向回到P.

怎么了NC之前发生过同样的事情:它通过reflog保存了一段时间.

我们并不真的需要它(尽管它可能会派上用场),因为保持索引/暂存区域的--soft标志git reset不受影响(与工作树一起).这意味着我们现在可以通过运行另一个来再次进行新的提交git commit.它将经历相同的四个步骤(从中读取ID HEAD,创建树,创建新提交,并更新分支):

       C--N2   <-- branch
      /
...--P--N
Run Code Online (Sandbox Code Playgroud)

N2我们新的(第二个新的?)提交将在哪里.

我们甚至可以git commit从提交中重用提交消息N.该git commit命令有一个--reuse-message参数,也是拼写的-C; 我们所要做的就是给它一些东西,让它找到原始的新提交N,从中复制消息N2.我们怎么做?答案是:它在reflog中,就像C我们需要做的那样git reset.

事实上,它是一样的@{1}!

请记住,@{1}意思是"刚才的地方",并且git reset只是更新它,将其C移至N.我们还没有做出新的承诺N2.(一旦我们这样做,N将会@{2},但我们还没有.)

所以,把它们放在一起,我们得到:

git reset --soft @{1}
git commit -C @{1}
Run Code Online (Sandbox Code Playgroud)

1此描述分解的位置包括修改合并,何时使用分离的HEAD以及使用备用索引时.尽管如此,如何修改描述也很明显.

2如果HEAD已断开,以便有没有当前分支,意思变成HEAD@{1}.请注意,@它本身就是简称HEAD,因此引用当前分支而不是本身的事实有点不一致.@{n}HEAD

要了解它们之间的差异,请考虑git checkout develop后面的git checkout master(假设两个分支都存在).第一个checkout更改HEAD指向develop,第二个更改HEAD指向master.这意味着在上次更新之前指向的master@{1}是任何提交; 但是现在的提交点 - 可能是其他一些提交.mastermasterHEAD@{1}develop

(回顾:在这两个git checkout命令之后,@{1}意味着master@{1}现在,HEAD@{1}意味着与develop现在相同的提交,@意味着HEAD.如果你感到困惑,那么我也是,显然我并不孤单:看到评论.)

  • @8bitjunkie:不要盲目地使用“@{1}”,因为如果你在“git commit --amend”之后做了任何事情,它就不再是“1”了。如果需要,运行“git reflog”,为“@{...}”语法查找正确的哈希 ID 或数字。 (2认同)