如何撤销使用"git commit --amend"代替"git commit"的操作

1887

我不小心修改了之前的提交。这个提交应该是独立的,以便保留我对特定文件所做更改的历史记录。

有没有办法撤销上一个提交?如果我执行类似于git reset --hard HEAD^的操作,第一个提交也会被撤销。

(我还没有推送到任何远程目录)


如果你想确认你所采取的每一步骤的效果(无论是在尝试下面的答案之前还是在执行某个操作时头脑开始晕眩),可以尝试使用 git log --reflog -p -- {{name-of-the-dir-or-file-in-question}} 命令。它会显示每个操作的实际更改和提交消息。 - Kay V
14个回答

3360

你需要做的是创建一个新的提交记录,它与当前的HEAD提交记录具有相同的细节,但其父提交记录是HEAD的上一个版本。执行git reset --soft将移动分支指针,使得下一次提交记录发生在当前分支头部所在的不同提交记录之上。

# Move the current head so that it's pointing at the old commit
# Leave the index intact for redoing the commit.
# HEAD@{1} gives you "the commit that HEAD pointed at before 
# it was moved to where it currently points at". Note that this is
# different from HEAD~1, which gives you "the commit that is the
# parent node of the commit that HEAD is currently pointing to."
git reset --soft HEAD@{1}

# commit the current tree using the commit details of the previous
# HEAD commit. (Note that HEAD@{1} is pointing somewhere different from the
# previous command. It's now pointing at the erroneously amended commit.)
# The -C option takes the given commit and reuses the log message and
# authorship information.
git commit -C HEAD@{1}

52
非常酷,加一。我甚至使用倒数第二条修订视图进入git reflog来查找正确的编号,例如{2} - JJD
255
为了明确起见,第一个命令是真正的“撤销”。它会产生在执行 git commit --amend 命令之前的 HEAD、工作目录(未更改)和索引状态。第二个命令是将操作重做到一个新的提交中。这些命令适用于任何 git commit,而不仅仅是 --amend - cdunn2001
83
如果您没有修改需要挽救的新提交消息,那么第二部分可以只是一个普通的 git commit - Matt Montag
18
由于某种原因,当我运行 git reset --soft HEAD@{1} 时出现错误:fatal: ambiguous argument 'HEAD@1': unknown revision or path not in the working tree. Use '--' to separate paths from revisions。当我用在 git reflog 中显示的等效提交哈希替换了 HEAD@{1} 时(感谢JJD!),这个答案非常有效! - Tim Arnold
32
根据您使用的shell不同,需要用单引号或双引号来包括 HEAD@{1}。例如,在tcsh中运行命令 echo HEAD@{1},输出结果会是 HEAD@1,因为tcsh会解释花括号。如果使用单引号,则花括号将得到保留。 - Kelvin
显示剩余22条评论

195
使用ref-log
git branch fixing-things HEAD@{1}
git reset --soft fixing-things

你应该在你的工作副本中拥有所有之前修改过的更改,并将它们提交到一个新的提交中。
要查看先前的所有头提交的完整列表,请键入git reflog
如果你正在寻找一个单一的命令来实现这个,并且不怕低级的管道命令,你可能会发现我的其他答案有帮助。

9
这也会清除索引 - 仍然有用,但超出了简单的“撤消”。 - cdunn2001
23
@neaumusic: 是的!HEAD~1HEAD^完全相同,都指代当前提交的_父节点_。而另一方面,HEAD@{1}指的是在此之前HEAD所指向的提交,也就是说当你切换到一个不同的分支或修补一个提交时,它们意味着不同的提交。 - knittl
15
第一步是多余的。简单的 git reset HEAD@{1} 就足够了。 - dwelle
1
创建一个临时分支有助于为提交命名,避免进一步混乱 :) - knittl
11
“reflog” 意为 “引用日志”而不是“重新鞭打”?这样就更有意义了。 - byxor
显示剩余2条评论

185

我尝试了使用HEAD@{1}的所有答案,但都无效,这是我的解决方案:

git reflog

d0c9f22 HEAD@{0}: commit (amend): [Feature] - ABC Commit Description 
c296452 HEAD@{1}: commit: [Feature] - ABC Commit Description 

git reset --soft c296452

你的暂存区现在包含了你不小心与c296452提交合并的所有更改。


11
我在已经推送的提交上执行了git commit --amend,因此其他建议都不起作用了。但这个方法有效。谢谢。 - stackomatiker
好的。所以我刚刚执行了 git reset --soft HEAD@{1} 来撤销最新的修改。 - Noam Manos
3
如果您在PowerShell中,git reset --soft "HEAD@{1}"需要加上引号。{1}是一种特殊的语法。 - TankorSmash
1
谢谢,因为接受的解决方案在我的代码库中完全没有起作用。 - undefined

138

通过以下方式查找您的修改后提交记录:

git log --reflog

注意:您可以添加--patch以查看提交的主体内容,以便更清晰地了解。与git reflog相同。

然后通过以下方式将HEAD重置为之前任何一次提交的状态:

git reset SHA1 --hard

注意: 用实际的提交哈希替换 SHA1。还要注意,此命令将丢失任何未提交的更改,因此您可能需要在执行之前将它们存储。或者,改为使用 --soft 以保留最新更改,然后再提交。

然后在其上方cherry-pick其它需要的提交:

git cherry-pick SHA1

49
如果你运行git reset SHA1 --soft,你可以保留最新的更改并将它们提交。 - pravj
还有一个方便的技巧:如果在操作之前,你想确认每一步所产生的影响,请尝试使用git log --reflog -p -- {{name-of-the-dir-or-file-in-question}}命令。它会显示实际的更改和提交信息。 - Kay V
只需使用“git reset SHA1”,默认为“--mixed”模式,不会重置工作树,允许您立即提交。 git reset SHA1 && git commit -m“abc” - Meuko
@Meuko 但是 reset --soft 的好处(在我看来在这种情况下)是将更改放回索引中(而不是工作树)。这样,如果您再次提交,您将提交与刚刚重置的提交相同的更改。当我错误地修改提交时,这对我很方便。 - Felipe Romero
1
完整命令:git reset --soft 'HEAD@{1}' - Felipe Romero

35
如果您已经将提交推送到远程,然后错误地修改了该提交的更改,则可以使用此方法解决问题。发出git log命令以查找提交之前的SHA。(假设远程命名为origin)。现在使用该SHA发出以下命令。
git reset --soft <SHA BEFORE THE AMMEND>
#you now see all the changes in the commit and the amend undone

#save ALL the changes to the stash
git stash

git pull origin <your-branch> --ff-only
#if you issue git log you can see that you have the commit you didn't want to amend

git stash pop
#git status reveals only the changes you incorrectly amended

#now you can create your new unamended commit

4
这是更一般问题的一个特例,但正好满足了我的紧急需求。 - dmckee --- ex-moderator kitten
1
对我来说也是一样。我有时在 git rebase 过程中会遇到冲突,有时候我会选择 "amend" 而不是 "rebase --continue" ... 而这个工具简直就是救命稻草! - GhostCat

28

值得注意的是,如果您仍然在编辑器中输入提交消息,您可以删除该提交消息,这将中止git commit --amend命令。


哇!谢谢你,我在搜索如何取消它时一直让我的vim保持打开状态。 - krehwell

26

您可以随时拆分一个提交记录,

来自手册
  • 使用git rebase -i commit^启动交互式rebase,其中commit是您想要拆分的提交。实际上,只要包含该提交的任何提交范围都可以。
  • 将您想要拆分的提交标记为“edit”操作。
  • 在编辑该提交时,执行git reset HEAD^。效果是HEAD向后倒退了一步,索引也跟着变化了。但是,工作树保持不变。
  • 现在将您想要包含在第一个提交中的更改添加到索引中。您可以使用git add(可能是交互式的)或git-gui(或两者都用)来完成此操作。
  • 使用适当的提交消息提交当前索引。
  • 重复最后两个步骤,直到您的工作树干净为止。
  • 继续rebase,使用git rebase --continue命令。

32
太过复杂了。你只需要用 git reflog 即可。 - knittl
2
很多步骤,但每一步都简单易行。这对我有效,并且得到了我的支持。 - OzBandit
5
此外,这个答案使您能够有选择地挑选您意外“修改”的更改,从而为git reset --soft HEAD@{1}的方法提供了一些额外的价值(顺便说一句,这种方法确实解决了我的问题)。 - Wiebe Tijsma
2
你也可以使用reflog方法有选择地挑选更改。只需执行git reset而不是git reset --soft,然后执行git add --patch即可。 - geekofalltrades
太过复杂,提供的控制比仅撤销错误修订所需的要多,这也是问题所在 - 不是如何手动拆分提交。如果我错误地进行了修订,我不想去选择我想要在每个提交中包含哪些更改!git reflog才是解决之道。 - corinnaerin
2
这仍然会重写历史并需要强制推送。根据您的情况,这可能是一个问题或不是问题。 - Pajn

23

或许可以使用 git reflog 命令获取修改之前和修改之后的两个提交记录。

然后,使用 git diff before_commit_id after_commit_id > d.diff 命令获取修改前后的差异,并将其保存在d.diff文件中。

接着,使用 git checkout before_commit_id 回到修改前的状态。

最后,使用 git apply d.diff 应用你真正做出的更改。

这样就解决了我的问题。


15

以下是撤销 git commit —amend 的操作步骤:

  1. git reset --soft HEAD^
  2. git checkout files_from_old_commit_on_branch
  3. git pull origin your_branch_name

====================================

现在您的更改已恢复为之前的状态,撤销 git commit —amend 操作已完成。

现在可以执行 git push origin <your_branch_name>,将更改推送到分支。


+1. 撤销“修改”操作的简单解决方案。为了更好地理解,在第二步(git checkout files)之前,运行git restore --staged <YOUR_FILES>以取消暂存的更改。 - Ravi Patel

8

虽然晚了近9年,但是没有看到提到这种方法的变化(这是几个方法的组合,类似于顶部答案 (https://dev59.com/hHM_5IYBdhLWcg3wQQpd#1459264))。

搜索分支上的所有分离头

git reflog show origin/BRANCH_NAME --date=relative

然后找到SHA1哈希值

回退到旧的SHA1

git reset --hard SHA1

然后将其推送回去。

git push origin BRANCH_NAME

完成。

这将完全恢复您回到旧的提交状态。

(包括之前被覆盖的分离提交头的日期)


4
好的,但通常我希望重置 --soft 以保留我的更改。我只想将其单独提交。 - Ruan Mendes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接