如何永久删除远程分支中的几个提交

1057

我知道重写历史是不好的,但如何永久地从远程分支中删除几个提交?


108
我知道这很愚蠢,但有时候会发生意外——比如在你的代码中测试登录并使用明文密码,这些是真正的登录凭据。然后哎呀…… - frhd
5
如果你是唯一的开发者,那就不会有冲突。即使有2-3名开发者,如果他们都在同一个房间里,你也可以很容易地解决问题。 - Mark Lilback
71
我已经厌倦了这些学术说辞,关于这是多么危险,永远不应该这样做之类的废话。有时候,从Git历史记录中删除一些东西并处理其他开发人员的冲突/错误会更好得多。问题就是这么简单。忽视这一点的人可能从未在课堂以外的环境中工作过。 - crush
11
大多数情况下,所谓“聪明”的人并未考虑到所有可能的情况,并且缺乏透视能力,因为他们只是机械地重复某些东西,却没有思考到他们完全没有考虑的某种可能性。有时候规则必须被打破。这让我想起了语法纳粹。 - Alfonso Fernandez-Ocampo
4
我刚刚提交了代码,里面明文包含了生产数据库的密码。否则我必须更改数据库密码,这会导致多个服务连接问题,因为它们都使用相同的凭据连接相同的数据库。我知道这也是不好的做法,但它能运行且我并不在意。 - TheMisir
显示剩余4条评论
11个回答

707
你可以使用git reset --hard重置本地分支以从工作树和索引中删除更改,然后使用git push --force(或git push --force-with-lease)将修改后的本地分支推送到远程。
(其他解决方案, 包括删除远程分支并重新推送它)

这个SO答案说明了这种命令的危险性,尤其是如果人们依赖于远程历史记录来管理自己的本地存储库。
您需要准备好指出人们到git rebase手册的RECOVERING FROM UPSTREAM REBASE部分。

此外,正如ringo评论中指出的那样,如果远程分支受到强制推送保护,那么像这个答案中所示的git revert可能更可取。


在Git 2.23(2019年8月,九年后),您将使用新命令git switch
即:git switch -C mybranch origin/mybranch~n
(将n替换为要删除的提交数)

这将恢复索引和工作树,就像git reset --hard一样。
文档添加

-C <new-branch>
--force-create <new-branch>

Similar to --create except that if <new-branch> already exists, it will be reset to <start-point>.
This is a convenient shortcut for:

$ git branch -f <new-branch>
$ git switch <new-branch>

2
奇怪,感觉我已经尝试过了。再加上一些变基 - 就像魔法一样有效。谢谢。 - Arnis Lapsa
12
@Arnis: 非常好;) 可以使用push --force - VonC
2
@MoshFeu 真的:在远程端,git gc 并不总是经常运行。例如在 GitHub 上:https://twitter.com/githubhelp/status/387926738161774592?lang=es - VonC
2
@VonC,您能否澄清一下您的回答:新命令是否更好或实现了相同的最终结果?(您提到旧命令存在一些危险!) - Porcupine
1
@jon_two git push --force-with-lease 可以更加安全一些。 - VonC
显示剩余11条评论

690

在回滚不可用的提交时,请注意使用last_working_commit_id

git reset --hard <last_working_commit_id>

所以我们不能将代码重置到我们不想要的 commit_id

然后,确保我们必须推送到远程分支:

git push --force

31
完美、优雅、简洁的回答。我只是在远程和本地都恢复到了我需要的最后一个稳定提交。 - lauWM
16
这导致我也失去了本地的修改。我没有预料到这一点。但是,这总比将你的个人密码提交到工作存储库中要好。 - Airwavezx
10
你可以使用 git stash 命令保存你的本地修改......做一些其他操作......然后用 git stash pop 命令恢复你的修改。 - MonTea
这个错误提示为“remote: error: denying non-fast-forward refs/heads/master(应该先进行拉取操作)”。 - Saurabhcdt
1
@Airwavezx,这就是git reset --hard应该做的事情。 - Luke
你应该明确指出,这将使你失去在远程要删除的提交中所做的所有更改。我刚刚失去了所有的工作,谢谢你啊。 - Gonzalo

180

重要提示:在使用“git push -f”命令时,一定要指定要修改的分支,否则可能会意外地修改其他分支![*]

这篇教程中介绍了三种选项。如果链接失效了,我会在这里列出主要步骤。

  1. 撤销整个提交
  2. 删除最后一个提交
  3. 从列表中删除提交

1 撤销整个提交

git revert dd61ab23

2 删除最后一次提交

git push <<remote>> +dd61ab23^:<<BRANCH_NAME_HERE>>

或者,如果该分支在本地可用

git reset HEAD^ --hard
git push <<remote>> -f

在这里,+dd61...是您的提交哈希值,git将x^解释为x的父提交,+表示强制非快进式推送。

3 从列表中删除提交

git rebase -i dd61ab23^

这将打开一个编辑器,显示所有提交的列表。删除您想要摆脱的那个提交。完成变基并强制推送到仓库。

git rebase --continue
git push <remote_repo> <remote_branch> -f

5
确保在执行“git push <remote_repo> <remote_branch> -f”时指定要修改的分支,否则可能会意外地修改其他分支! - Nigel Sheridan-Smith
只有步骤1和2完成了任务并回答了原始问题。(不要运行步骤3) - Saad Benbouzid
1
这些是不同场景的选项,而不是需要采取的步骤。在我的情况下,交互式变基(选项3)完成了我所需的操作。 - Steve Blackwell
1
我必须完成第三步。为什么你不应该运行这个@Saad?幸运的是,我的<<remote>>只是默认的“origin”,<remote_branch>是默认的“master”。 - Bart
我喜欢这个教程,它考虑并清晰地描述了重置与还原方法。 - robhem
更倾向于第三个选项。因为有时候,你真的不想要那种错误的分支历史记录。 - RY_ Zheng

168
如果想删除最后3次提交,可以运行以下命令来从本地分支的文件系统(工作树)和提交历史(索引)中删除更改:
git reset --hard HEAD~3

然后在您的本地计算机上运行以下命令,强制远程分支重写其历史记录:

git push --force

恭喜!全部完成!
一些注意事项:
你可以通过运行以下命令来获取所需的提交ID:
git log

然后,您可以像这样使用 <desired-commit-id> 替换 HEAD~N

git reset --hard <desired-commit-id>

如果你想保留文件系统中的更改并仅修改索引(提交历史记录),请使用--soft标志,例如git reset --soft HEAD~3。然后您有机会检查最新更改并保留或丢弃它们的全部或部分内容。在后一种情况下,运行git status会显示自<desired-commit-id>以来已更改的文件。如果使用了--hard选项,则git status将告诉您本地分支与远程分支完全相同。如果既不使用--hard也不使用--soft,则使用默认模式,即--mixed。在此模式下,git help reset说: 重置索引但不重置工作树(即保留更改的文件但不标记为提交)并报告未更新的内容。

25

简述:

  1. git switch -C branch_name origin/branch_name~n:使用 git switch 命令,将分支还原到前 n 次提交的状态。选项 -C 强制创建一个同名的新分支。
  2. git push --force:强制推送本地更改到远程仓库。

执行完上述两个命令后,远程分支将被还原 n 次提交。


解释:

  1. 使用 git switch 命令,将分支还原到前 n 次提交的状态。选项 -C 强制创建一个同名的新分支。

    • branch_name 替换为你的分支名称,
    • 在命令结尾处替换 n 为你想要还原的提交次数。

    命令 #1:git switch -C branch_name origin/branch_name~n

    例如:git switch -C feature/dashboard origin/feature/dashboard~1 // 这个命令将会撤销 dashboard 分支中最新的1次提交。

  2. 强制推送本地更改到远程仓库。

    命令 #2:git push --force


提示: 要撤销已提交但未推送的更改,可以使用 git reset HEAD~ 命令。


15

简化自pctroll的答案,类似地基于这篇博客文章

# look up the commit id in git log or on github, e.g. 42480f3, then do
git checkout master
git checkout your_branch
git revert 42480f3
# a text editor will open, close it with ctrl+x (editor dependent)
git push origin your_branch
# or replace origin with your remote

3
可以,谢谢。我想永久删除远程分支历史记录中的一个提交(例如包含pwd),该怎么做? - Smiles
1
我也试过了,但我和Smiles有同样的问题,我不想让任何人在历史记录中看到我的提交/还原..我该如何删除它? - Roberto Rodriguez
也适用于我。 - Sumit

12

可能为时已晚,但对我有帮助的是听起来很酷的“核”选项。基本上使用命令filter-branch,您可以删除文件或在整个git历史记录中更改大量文件的某些内容。

最好的解释可以在这里找到。


11
还不算太晚,对于遇到类似问题的漫游者可能会有所帮助 :) - Arnis Lapsa
这对我有所帮助,但是官方的 Git 文档现在不鼓励使用 filter-branch,而是推荐使用 https://github.com/newren/git-filter-repo。 - TanguyP

5

这是一种干净的方法,可以从远程仓库中删除您的提交记录,而不会丢失您的工作。

快速答案

git reset --soft HEAD~1 # 1 represents only last 1 commit 
git stash # hold your work temporary storage temporarily.
git pull # bring your local in sync with remote
git reset --hard HEAD~1 # hard reset 1 commit behind (deletes local commit)
git push -f # force push to sync local with remote
git stash pop # get back your unpushed work from stash

为什么这个有效的详细解释。

以下是我的情况发生了什么

  1. 我正在处理一个名为 redesign 的分支。

  2. 我不小心提交并推送了一个包含我的机密文件的工作。所以我的机密现在被暴露了(Gitguardian 在我的电子邮件中警告我 xD)。 现在我想从存储库中删除我的机密文件,但是想保留我的工作。

  3. 运行 git reset --soft HEAD~1,这将使您的本地存储库在您的计算机上向后移动 1 个提交(修改数字以向后移动 n 个提交)。

  4. 现在您将看到您提交的文件为 未暂存

  5. 通过运行 git stash 将此工作暂时移动到存储区中,以便在本地保存。

  6. 现在通过运行 git pull 使您的本地与远程同步。

  7. 现在运行 git reset --hard HEAD~1(再次修改数字以删除 n 个提交),以从您的存储库中删除远程提交,并进行强制推送 git push -f

  8. 您将看到来自远程存储库的提交已被删除。

  9. 现在通过运行 git stash pop 从存储区中获取您的工作。

  10. 现在对未推送的工作进行所需的更改。


清晰易懂。谢谢! - Yashash Gaurav
很棒的回答!简单明了,解释得很好。 - Tapa Dipti Sitaula

5
有时候解决这个问题最简单的方法是从你知道代码没问题的地方创建一个新的分支。这样,如果以后需要从它里面挑选其他提交记录,就可以让那个错误的分支历史保持不变。这也确保你没有丢失任何提交历史。
从你本地的错误分支开始:
git log

复制你想要分支所在的提交哈希并退出git log

git checkout theHashYouJustCopied
git checkout -b your_new_awesome_branch

现在你已经拥有了一个你想要的新分支。
如果你还需要保留从原来的分支中没有包含在新分支中的特定提交,那么你可以使用 cherry-pick 命令来选择需要的特定提交:
git checkout the_errant_branch
git log

复制需要拉取到稳定分支的提交哈希值,然后退出git log。
git checkout your_new_awesome_branch
git cherry-pick theHashYouJustCopied

给自己点个赞。


非常容易,谁在乎你有一个死掉的分支?创建一个新的分支,然后就完成了! - Andrew Fox
1
这是一个真正优秀的答案。我很高兴我这样做,而不是挑选提交或操作远程分支。 - Magnilex

1
 git reset --soft commit_id
 git stash save "message"
 git reset --hard commit_id
 git stash apply stash stash@{0}
 git push --force

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