如何在Git中撤销最近的本地提交?

26051
我不小心提交了错误的文件到Git,但还没有将提交推送到服务器。
我该如何从本地仓库撤销这些提交?

684
你知道git需要什么?就是"git undo",这样一来,git对于我们这些凡人所犯下的错误的处理名声就消失了。实现的方法是在执行任何"git"命令之前将当前状态推送到git堆栈上。这会影响性能,所以最好增加一个配置标志来决定是否启用它。 - Yimin Rong
57
可以使用Git的“alias”功能来完成这个操作:https://git-scm.com/book/zh/v2/Git-基础-Git-别名 - Edric
134
针对VsCode用户,只需按下ctrl+shift+G,然后单击三个点,即“更多选项”,接着再点击“撤销上一次提交”。请注意,翻译过程中不会添加解释或其他任何额外信息。 - ashad
29
您要撤销的是什么?有很多不同的功能用例,其中“撤销”意味着完全不同的事情。我敢打赌,添加一个新的花哨的“魔法棒”只会更加混淆事情。 - Romain Valeri
50
不太相信,人们仍然会弄乱并撤销不应该撤销的事情。但更重要的是,git reflog 已经接近你所描述的内容,但它给用户更多的控制权来确定需要(撤销)做的事情。但是请注意,“撤销”在各个地方的工作方式不同,人们会期望这个功能实现许多不同的事情。撤销上一个提交?撤销上一个操作?如果最后一个操作是推送,如何撤销,(重置并推送)还是(撤销并推送)? - Romain Valeri
显示剩余10条评论
107个回答

142

要将内容重置为之前的版本,并永久删除所有未提交的更改:

git reset --hard HEAD~1

24
或许您可以添加一条注释/警告,说明该命令会立即删除提交和工作目录中的更改,而不会进一步询问。 - cr7pt0gr4ph7
6
如果您不小心这样做了,也不必担心。请参见https://dev59.com/K2kw5IYBdhLWcg3wJXQh、https://dev59.com/kG_Xa4cB1Zd3GeqP5M2P和https://dev59.com/A2s05IYBdhLWcg3wNPW7。 - cr7pt0gr4ph7
13
使用 --soft 选项可以将您的更改保留为“未提交更改”,使用 --hard 选项可以完全删除提交并还原到前一个状态。请记住,仅对尚未推送的更改执行此类操作。 - Yunus Nedim Mehel
@Zaz:你说得对,也许我应该澄清一下。只有已添加到索引(/staged)或已提交的文件/更改才可能被恢复。未提交的、未暂存的更改会被git reset --hard完全丢弃,就像你所说的那样。 - cr7pt0gr4ph7
1
顺便提一下:每次文件被暂存时,git 都会将其内容存储在对象数据库中。只有在执行垃圾回收时才会删除存储的内容。因此,在执行 git reset --hard 时,可以恢复最后一个已暂存但当前未暂存的文件版本(有关更多信息,请参见上面链接的帖子)。 - cr7pt0gr4ph7
这是一个糟糕的建议。这不仅会重置你的远程,而且会从你正在操作的文件夹中删除实际的文件。对我来说,这一点根本不清楚,我以为你只是在说你要重置git head,而不是将你当前的文件重置为存储库中的内容。如果昨天的笔记本不在我的RAM中,我就会失去我昨天所做的一切。 - stidmatt

135

使用SourceTree(Git的图形化工具)查看您的提交和树状结构。您可以通过右键单击手动重置它。


101
一个简单的逐步指南如下:
  • 撤销一个提交并丢弃任何未提交的更改

    git reset --hard HEAD~1
    
  • 撤销提交,但保留您的更改

    git reset HEAD~1
    
  • 保留您的文件,并自动将所有更改重新加入暂存区

    git reset --soft HEAD~1
    
  • 恢复您删除的提交

    git reflog # 查找提交哈希值
    

2
你说的“找到sh”是什么意思?你是指“找到SHA-1哈希值”吗?还是别的什么? - Peter Mortensen
是的,sh 也是一种东西... - mirekphd

99

假设我们有一个名为code.txt的文件。我们对它进行了一些更改并提交了这些更改。现在我们有三种方法可以撤销此次提交,但首先您需要知道什么是暂存文件...

一个暂存文件是指准备提交的文件,如果您运行 git status 命令,该文件将以绿色显示;如果未暂存以提交,则将以红色显示:

enter image description here

这意味着如果您提交更改,则对此文件进行的更改不会被保存。您可以使用 git add code.txt 将此文件添加到暂存区中,然后提交更改:

enter image description here

撤销上一次提交:

  1. 如果只想撤销提交而没有其他更改,可以使用

    git reset --soft HEAD^

    enter image description here

  2. 如果要撤销提交及其更改(这很危险,因为您的更改将会丢失),可以使用

    git reset --hard HEAD^

    enter image description here

  3. 如果要撤销提交并从暂存区中删除更改,可以使用

    git reset --mixed HEAD^ 或简写为 git reset HEAD^

    图片描述


88

通常情况下,您希望撤消(commit)一个提交,是因为您犯了一个错误并且想要修复它 - 这本质上就是提问者(OP)在问问题时所做的事情。实际上,您实际上想要重做(commit)一个提交。

这里大多数答案都集中在命令行上。虽然当你熟悉 Git 后,命令行是使用 Git 的最佳方式,但对于那些来自其他版本控制系统的人来说,它可能有点陌生。

以下是使用 GUI 完成此操作的方法。如果您已安装 Git,则已经拥有完成这些说明所需的所有内容。

注意: 我假设您在推送(push)之前意识到提交(commit)错误。 如果您不知道推送是什么意思,那么您可能还没有推送。 请继续遵循说明。 如果您已经推送了有问题的提交(commit),则最不冒险的方法就是跟随有问题的提交(commit)进行修复,就像在不允许修改历史记录的版本控制系统中一样。

话虽如此,以下是使用 GUI 修复最近一个错误提交(commit)的方法:

  1. 在命令行上导航到您的存储库(repository)并使用git gui启动 GUI。
  2. 选择“修改上一个提交(Amend last commit)”。 您将看到您最后的提交(commit)消息、您暂存的文件以及您没有暂存的文件。
  3. 现在更改它们,使其看起来符合您的要求,并单击“提交(Commit)”。

83
如果您想撤销最后一次提交,但仍希望在本地保留已进行的更改,请使用以下命令:
git reset HEAD~1 --mixed

13
不需要 Git 教程,直接给出正确答案。 - Ringo
2
简单易行,对我很有用。我重置了密码,登录后,在列表中寻找这个答案,因为它不是按照相同的顺序排列的,然后投了您一票。 - Brent Woodruff

80

您可以使用:

git reset HEAD@{1}

这条指令将在不使用Git日志的情况下删除您的错误提交。


75

撤销最后一次提交

有许多情况下,你可能真的想要撤销你在代码中进行的最后一次提交,例如因为你想要对其进行大量重构,甚至将其完全丢弃!

在这些情况下,“reset”命令是你最好的朋友:

$ git reset --soft HEAD~1

上述命令(reset)将把您当前的HEAD分支回到指定的修订版本。在上面的示例中,我们想返回到当前修订版本之前的版本 - 有效地撤销了我们最后一次提交。

请注意--soft标志:这可以确保撤消的修订更改被保留。运行命令后,您将在工作副本中找到这些更改作为未提交的本地修改。

如果您不想保留这些更改,只需使用--hard标志。请确保只有在确定不再需要这些更改时才执行此操作。

$ git reset --hard HEAD~1

这里输入图片描述


“Working copy”?这是Git的概念吗?不是SVN的概念吗? - Peter Mortensen
@PeterMortensen 是的,工作副本,这是一个 Git 的概念。 - Mohit

72

撤销最后一次提交:

git reset --soft HEAD~

或者撤销上上次提交的更改:

git reset --soft HEAD~2

或撤销任何以前的提交:

git reset --soft <commitID>

(您可以使用git reflog获取提交ID)

当您撤消先前的提交时,请记得使用以下命令清理工作区:

git clean

有关详细信息,请参阅文档:git-reset


67

在回答之前,让我们先解释一下什么是HEAD

首先,什么是HEAD?

HEAD简单来说是当前分支上最新的提交(commit)的引用。
任何时候只能有一个HEAD。(不包括git worktree

HEAD的内容存储在.git/HEAD中,其中包含当前提交的40个字节SHA-1值。


分离头指针

如果您不在最新的提交上 - 意味着 HEAD 指向历史记录中的先前提交,那么它被称为分离头指针

enter image description here

在命令行中,它看起来像这样- SHA-1代替分支名称,因为HEAD没有指向当前分支的末端

enter image description here

enter image description here

如何从分离的HEAD中恢复的几种选项:


git checkout

git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits t go back

这将检出一个指向所需提交的新分支。
此命令将检出到给定的提交。
此时,您可以创建一个分支并从此点开始工作。
# Checkout a given commit. 
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>

# create a new branch forked to the given commit
git checkout -b <branch name>

git reflog

您总是可以使用reflog
git reflog会显示更新HEAD的任何更改,并检出所需的reflog条目将HEAD设置回此提交。

每次修改HEAD时,都会在reflog中创建一个新条目

git reflog
git checkout HEAD@{...}

这将使您返回到所需的提交

enter image description here


git reset --hard <commit_id>

将您的 HEAD 移回到所需的提交。

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts if you've modified things which were
# changed since the commit you reset to.

git revert <sha-1>

"撤销"指定的提交或提交范围。
重置命令将“撤消”给定提交中所做的任何更改。
一个带有撤消补丁的新提交将被提交,而原始提交也将保留在历史记录中。

# add new commit with the undo of the original one.
# the <sha-1> can be any commit(s) or commit range
git revert <sha-1>

这个模式说明了每个命令的作用。
正如您所看到的,reset && checkout 修改了 HEAD

enter image description here


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