如何使用git撤销已推送的提交?

1449

我有一个存储在远程仓库中的项目,已经与本地仓库(开发)和服务器仓库(生产)同步。我已经进行了一些提交并将其推送到远程,并从服务器进行了拉取。现在,我想撤销这些更改。因此,我可以只是使用git checkout到更改之前的提交并提交新的更改,但我猜想再次将它们推送到远程会出现问题。您对如何进行有任何建议吗?

15个回答

1685
你可以使用以下方法还原单个提交:
git revert <commit_hash>

这将创建一个新的提交,撤销您指定的提交的更改。请注意,它只会撤销特定的提交,而不是在其后的提交。如果您想撤销一系列的提交,可以像这样操作:
git revert <oldest_commit_hash>..<latest_commit_hash>

它将回滚从到的所有提交。Git的某些版本还会回滚本身,所以请仔细检查该提交是否被回滚。您始终可以使用g reset --hard HEAD~命令删除最新的回滚提交(即回滚最早的提交)。
要找出提交的哈希值,可以使用git log命令。
请查看git-revert man page以获取有关git revert命令的更多信息。此外,还可以查看this answer以获取有关回滚提交的更多信息。
重新应用已回滚的提交的额外说明:
通常情况下,你会撤销提交是因为你发现你推送的提交有问题。在继续修复问题之前,你首先想要将仓库恢复到稳定状态。因此,你会首先执行之前描述的git revert命令。然后推送这些撤销的提交,使远程仓库保持稳定。之后,你会想要在本地重新应用被撤销的提交,这样你本地仓库的文件就会回到撤销之前的状态。然后你可以修复问题。
要重新应用被撤销的提交,你需要"撤销"这些"撤销提交"。所以你要做的是再次执行git revert命令,但是这次是针对"撤销提交"的一系列提交范围。这样,你的新的撤销提交将撤销之前的撤销提交。然后你的文件就会回到第一次撤销之前的状态。然后你可以修复问题,创建一个包含修复的新提交,然后推送这些新的提交。

55
换句话说,它会回滚到最旧的提交哈希值 <oldest_commit_hash> ;) - David
4
在Git文档中,revert命令会撤销第一个和最后一个提交之间(包括第一个和最后一个)的所有提交。查看文档 - Onur Demir
7
@aod是正确的,这个答案需要更新。当前git API撤销操作中的<oldest_commit_hash>被包含在撤销列表中。 - JHixson
10
使用Git 2.17.2版本时,"git revert <old>..<new>"不会包含<old>,但会包含<new>。 - chingis
18
刚刚尝试了一下......最后一步漏掉了,在执行 "git revert <commit_hash>" 后,使用 "git push origin <your_branch>"</your_branch></commit_hash>。 - VicXj
显示剩余14条评论

777
一个不会留下“撤消”痕迹的解决方案。
注意:如果有人已经拉取了您的更改,则不要这样做(我仅在个人存储库上使用此功能)。
运行:
git reset <previous label or sha1>

注意:previous 意味着错误提交之前的提交。
这将重新检出所有本地更新(因此 git status 将列出所有已更新的文件,即您更改/添加/.. 并提交的文件)。
然后您可以“完成您的工作”并重新提交您的更改(注意:此步骤是可选的)。
git commit -am "blabla"

此时,您的本地树与远程树不同

git push -f <remote-name> <branch-name>

将强制远程分支接受此推送并删除先前的推送(不强制指定远程名称和分支名称,但建议这样做以避免使用更新标志更新所有分支)。

!! 注意,某些标签仍可能指向已删除的提交 !! 如何删除远程标签


15
-a 所有被追踪的文件将被提交 -m 提交信息如下 - jo_
17
正是我所寻找的。有人提交了一个错误的代码并且将其推送到了代码库,然后又将其还原了。由于此原因,分支无法合并,我希望能够使代码库回到正确的状态(并将这些错误的提交从历史记录中移除)。 - byemute
6
我应该使用哪个“之前的标签或sha1”?我是输入最后一个“正确”的还是它之前的一个,并重新执行最后一个正确的所做的所有更改? - elysch
24
在错误提交之前的那个。 - jo_
3
我需要的是,我不小心将一个分支推送到主分支上。结果,在所有提交历史记录中都有许多无用的提交。我只需对最后一个正确的提交进行 git push -f 操作,就可以清理远程历史记录!谢谢! - Anton Rybalko
显示剩余12条评论

476

在这些情况下我通常会做:

  • In the server, move the cursor back to the last known good commit:

    git push -f origin <last_known_good_commit>:<branch_name>
    
  • Locally, do the same:

    git reset --hard <last_known_good_commit>
    #         ^^^^^^
    #         optional
    
请查看我为此创建的分支 my_new_branch 上的完整示例:
$ git branch
my_new_branch

在将一些东西添加到 myfile.py 后,这是最近的历史记录:

$ git log
commit 80143bcaaca77963a47c211a9cbe664d5448d546
Author: me
Date:   Wed Mar 23 12:48:03 2016 +0100

    Adding new stuff in myfile.py

commit b4zad078237fa48746a4feb6517fa409f6bf238e
Author: me
Date:   Tue Mar 18 12:46:59 2016 +0100

    Initial commit

我想撤销已经推送的最后一次提交,所以我运行:

$ git push -f origin b4zad078237fa48746a4feb6517fa409f6bf238e:my_new_branch
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:me/myrepo.git
 + 80143bc...b4zad07 b4zad078237fa48746a4feb6517fa409f6bf238e -> my_new_branch (forced update)

不错!现在我看到那个提交(myfile.py)中更改了的文件显示为“未暂存提交”:
$ git status
On branch my_new_branch
Your branch is up-to-date with 'origin/my_new_branch'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   myfile.py

no changes added to commit (use "git add" and/or "git commit -a")

因为我不想要这些更改,所以我也将光标在本地后移:

$ git reset --hard b4zad078237fa48746a4feb6517fa409f6bf238e
HEAD is now at b4zad07 Initial commit

现在HEAD已经回到了之前的一次提交,无论是在本地还是远程都是如此:

$ git log
commit b4zad078237fa48746a4feb6517fa409f6bf238e
Author: me
Date:   Tue Mar 18 12:46:59 2016 +0100

    Initial commit

32
这是正确的答案!这正是我需要做的,在我的情况下感谢你,今天学到了新东西 :) - Egli Becerra
3
这是正确的答案!它涉及到推送的提交(!)。 - powtac
13
如果您想真正还原更改(就像从未推送过一样),这确实是正确的答案。 - Jacob
1
完美的答案。按预期运行良好! - RafiAlhamd
1
git push -f origin <last_known_good_commit>:<branch_name> 对于 DevOps/Lead Dev 来说,不断进行热修复的疯狂人士(比如我)来说,这绝对是我见过的最棒的 git 命令,感谢! - alex
显示剩余11条评论

282

这将删除您推送的提交

git reset --hard 'xxxxx'

git clean -f -d

git push -f

为了以防万一,请备份数据。

从评论中提取的两点:

  • 'xxxxx'是您希望保留的最后一次提交的哈希代码。

  • clean很危险。它会删除不在版本控制下的文件!


26
这种方法可以正确地重写历史记录以删除提交。了不起。 - Jesse Reza Khorasanee
8
这个答案太棒了! - daparic
5
为了明确一点(我不确定):此次操作后,提交将是当前提交,而不是最后一个被删除的提交。 - user1199624
2
如果您想在本地保留更改,请使用 git reset --soft 'xxxxx' 并继续使用剩下的命令! - Ikbel
7
clean 命令很危险。它会删除那些没有被版本控制的文件! - Friedrich -- Слава Україні
显示剩余11条评论

131

如果您遵循以下git命令行步骤,您可以在本地和远程同时REVERT(也可以称为DELETE Git Commit。

运行以下命令以查看要还原的提交ID:

git log --oneline --decorate --graph

你将会得到类似下面这张截图的内容 在此输入图片描述

如果你勾选了远程 (通过Web界面),那么你会看到如下所示

在此输入图片描述

根据截图,你现在位于提交ID为e110322,但是你想要本地和远程都回退到030bbf6

执行以下步骤以本地+远程删除/回滚提交


首先本地回滚到提交ID为030bbf6

git reset --hard 030bbf6

紧随其后

git clean -f -d

这两个命令将强制清除并重置提交阶段030bbf6,如下面快照所示。

enter image description here

现在,如果你运行 git status 命令,你会看到你落后于远程分支两个提交,如下面所示。

enter image description here

运行以下命令来更新你的索引(如果有任何更新)。建议你要求所有开发人员不要接受主远程分支上的任何拉取请求。

git fetch --all
一旦完成后,您需要使用“+”符号强制将此提交推送到分支。如下所示,我在此处使用了“master”分支,您可以将其替换为任何分支。

输入图像描述代码

git push -u origin +master

现在,如果您看到远程的Web界面,则提交应该也被还原。

enter image description here


1
这是这里最好的答案,因为它实际上通过示例进行了解释!感谢您的回答。但是,我遇到了以下错误:! [remote rejected] master -> master (pre-receive hook declined) error: failed to push some refs to 'gitlab.com:information-provision/collectors/deribit-collector.git' 尽管我有维护者状态等。 - user6400946
1
@user6400946,你是否像最后一个命令中所示一样使用了“+”(加号)符号?git push -u origin +YourBrandName - vibs2006
2
是的,不过我已经解决了。看起来我的主分支受到了强制推送保护,我需要在 GitLab 仓库设置中暂时取消保护它。 - user6400946
1
2022年运行良好 :D - danu
1
这些步骤在2022年仍然很容易实现。不错!谢谢。 - V P
显示剩余2条评论

120

假设61234是您想要保留的最新可靠提交的sha编号。

git reset --hard 61234
git push -f

会完全删除所有错误的提交记录,不留痕迹。

注意:如果您想将(重置后的提交)推送到特定分支,则应使用git push -f origin branch-name


2
谢谢!提交历史是无辜的!为了方便获取SHA编号,我使用了:$ git log --oneline <feature-branch-name>。 - InaFK
2
这应该是被接受的答案! - Stone
我在运行 git push -f origin master 时遇到了 remote: GitLab: You are not allowed to force push code to a protected branch on this project. 错误。我认为我需要在我的GitLab服务器上更改一些配置。 - Brian
因为我真正想要重置分支上的提交,所以我编辑了你的答案,这样就避免了我几乎运行“git push -f origin master”的风险 - 很高兴它没有运行! - Ryan Taylor
1
我和你一样,总是需要推回代码,可以使用以下方式: git reset --hard <最新的可用提交哈希值>,然后再使用 git push -f origin 分支名称。 - Esmaeil Ahmadipour

59

2020 简单方法:

git reset <commit_hash>

(你想要保留的最后提交的哈希值)。

你将保留本地的未提交更改。

如果您想再次推送,您必须执行:

git push -f

1
它并不总是有效,因为管理员可以阻止重写历史记录,这在行业中非常普遍。因此,@gitaarik提到的解决方案将起作用。 git revert <commit_hash> git push origin <branch_name> - tejp124
这很简短、简单,而且正好满足我的需求。谢谢! - RedGuy11

36
git revert HEAD -m 1

在上述代码行中,“最后一个参数表示”

  • 1- 撤销一个提交。

  • 2- 撤销最近的两个提交。

  • n - 撤销最近的n个提交。

执行此命令后,需要推送以对远程产生影响。您可以选择其他选项,例如指定要撤消的提交范围。这是其中的一种选择。


稍后使用git commit -am“COMMIT_MESSAGE”,然后git pushgit push -f


9
这不是正确的 -m参数指定要还原到的父级数(通常为1,如果要还原传入的“theirs”更改,而不是2个合并到的更改,“ours” - 用于合并2个提交)。它与还原的提交数无关 - 如果要还原提交范围,请使用git revert ref1..ref2 - Null Reference
2
没有达到预期效果。 - Sam Tuke

27

这是我的方法:

假设分支名称为 develop

# Checkout a new temp branch based on one history commit
# (command to get history commit hashes: git log --oneline)
git checkout <last_known_good_commit_hash>

# Delete the original develop branch 
git branch -D develop
# Create a new develop branch based on the temp branch
git checkout -b develop

# Force update the remote with the new branch
git push -f origin develop


3
对我来说,这个答案有很多值得学习的东西,所以我选择了它。 - AUR
我在Mac上遇到了非法符号,只有这个解决方案帮助我保存了项目。 - storenth
删除develop分支来撤销提交似乎太冒险了。如果这是一个非常繁忙的项目,那该怎么办? - Nikita128
@Nikita128 你说得对,如果这是一个非常繁忙的项目,使用这种方法肯定会删除其他人的提交。 - backslash112
@Nikita128 它可以保留旧的历史记录。而且你可以备份一个分支,以防万一。但对我来说,这个方法非常有效。其他答案会在历史记录中保留已删除的提交,我必须再次拉取它们。 - Dr. MAF

19

重置硬件对我起作用了: 感谢 @Mo D Genensis 和 @vibs2006


git reset --hard '你上一个可工作的提交哈希值'
git clean -f -d
git push -f

2
这将覆盖已更改的文件并删除已添加的文件。 - Sahin
1
这似乎是Mo D Genesis答案的重复。 - Rui F Ribeiro

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