如何撤销最近的提交并将其从历史记录中删除?

78

我进行了一次提交,但又撤消了它

git revert HEAD^

只需使用 git log 命令

➜  git:(master) git log
commit 45a0b1371e4705c4f875141232d7a97351f0ed8b
Author: Daniel Palacio <danpal@gmail.com>
Date:   Tue Jan 17 16:32:15 2012 -0800

    Production explanation

但是如果我执行 git log --all,它仍然会显示出来。我需要将其从历史记录中删除,因为其中包含敏感信息。

git log --all
commit 5d44355080500ee6518f157c084f519da47b9391
Author: Daniel Palacio
Date:   Tue Jan 17 16:40:48 2012 -0800

    This commit has to be reset

commit 45a0b1371e4705c4f875141232d7a97351f0ed8b
Author: Daniel Palacio 
Date:   Tue Jan 17 16:32:15 2012 -0800

    Production explanation

我该如何从历史记录中删除提交记录5d44355080500ee6518f157c084f519da47b9391呢?


@AdrianCornish:这是一个不错的开始,但它并没有解决“敏感信息”的问题。 - Lily Ballard
@AdrianCornish:是的,但也要假设他不介意敏感信息留在他的本地副本。他应该不介意,但我不能代表他发言。 - Lily Ballard
为什么硬重置会在数据库清理后保留数据? - Adrian Cornish
@AdrianCornish:这不会影响程序,但是在提交无法访问后,它需要大约1.5个月才能从磁盘中删除(假设进行了定期垃圾回收)。 - Lily Ballard
@AdrianCornish:使用默认设置,git gc将在1个月后从reflogs中过期不可达的提交。此外,一旦从任何 ref(包括reflogs)中无法访问,git gc将把对象解压缩为松散的文件。再次使用默认设置,git gc将删除任何不可访问的松散对象,一旦它们至少有2周的历史记录。因此,如果您定期进行git gc,一旦提交已经不可达了1个月+2周(例如1.5个月),可以合理地期望它已被删除。 - Lily Ballard
显示剩余4条评论
5个回答

78

首先,git revert不是这里正确的命令。它会创建一个新的提交来撤销旧的提交,这不是你所要求的。其次,看起来你想要恢复HEAD而不是HEAD^

如果你还没有把这个提交推送到其他地方,你可以使用git reset --hard HEAD^来放弃最新的提交(这也会放弃任何未提交的更改,所以请确保你没有需要保存的更改)。假设你可以接受敏感信息在你的副本中存在而不在其他人的副本中,你就完成了。你可以继续工作,后续的git push不会推送你不好的提交。

如果这不是一个安全的假设(虽然如果不是,我很想知道原因),那么你需要过期你的引用日志并强制进行垃圾回收以立即收集所有未完成的对象。你可以使用以下命令实现:

git reflog expire --expire=now --expire-unreachable=now --all
git gc --prune=now

尽管如此,只有在确实需要这样做时才应该这样做。


如果你已经推送了你的提交,那么你基本上就没有办法了。你可以强制推送来远程还原它(但只有远程端允许),但你无法从远程端的数据库中删除提交本身,所以任何有权限访问该存储库的人都可以找到它,只要他们知道该如何查找。


3
当然,如果你可以访问远程文件系统,你可以像处理本地文件一样清理它。 - Cascabel
你不知道如何做到相同的事情,只删除历史记录中间的提交记录吗? - Denis Denisov
2
@Denji 假设你还没有将它推送到任何地方,你可以使用 git rebase 从你的历史记录中删除该提交。有很多关于如何做这件事的文档,包括在手册本身中。特别留意 --onto 标志。请注意,如果你有比要删除的提交更近的合并提交,那么事情就会变得更加复杂。 - Lily Ballard
这对我没有用,但是下面的命令使用~而不是^可以。我在Windows上;这是区别吗? - Topher Fangio
@TopherFangio HEAD~HEAD^ 应该是相同的。后者是首选形式(前者通常用于向后多步,例如 HEAD~3)。如果 HEAD^ 对您不起作用,也许您的 shell 解释 ^ 的方式有些奇怪?我不熟悉 Windows,所以不知道那里可能会有什么怪癖。尝试转义一下? - Lily Ballard
显示剩余3条评论

50

如果您不关心提交(commit)的话,只需执行以下操作:

git reset --hard HEAD~

撤销提交需要执行以下操作:

如果你想让更改在工作目录中生效,请执行以下操作:

git reset HEAD~
根据你使用 git revert 所做的操作不同,你可能需要更改上面的命令。Revert 会创建一个新的提交来还原你想要还原的提交。因此将存在两个提交。你可能需要使用 HEAD~2 来删除它们。
请注意,通常情况下,revert 是撤销更改的更安全的方式。但在这里,由于你想要删除敏感数据,reset 是最好的方法。

26

这里有一个不错的解决方案:这里。 要删除最后(顶部)的提交,可以执行以下操作:

git push [remote] +[bad_commit]^:[branch]

其中 [bad_commit] 是 [branch] 当前指向的提交,如果 [branch] 本地检出,则也可以执行以下操作

git reset HEAD^ --hard
git push [remote] -f

哇,谢谢,那很顺利。 - ihatetoregister
这真是救了我! - mati.o

9
如果您还未推送提交,只需执行以下操作:
git reset --hard HEAD~2
(HEAD~2用于删除原始提交和"revert"提交)。
这将将当前分支重置为要删除的提交之前的历史记录点。如果该提交不在任何其他分支中,则不会推送到您的origin。

那并没有帮助到历史部分。提交仍然在历史记录中。 - daniel
2
不是真的,从这个点开始,该提交将不再是您当前分支的一部分,并且最终将被git gc删除。如果您现在想要实际删除提交,可以使用git gc立即修剪它,或者重新克隆存储库;当克隆存储库时,只会克隆某些分支可达的提交。 - Bruno Oliveira

7

这里有一个简单的可行解决方案,可以从远程仓库中删除最后一次提交:

  1. 克隆仓库并找到最后一个“好”的提交(....c407)
$ git clone git@host:PROJ/myrepo.git 
$ cd myrepo 
$ git log --pretty=oneline

234987fed789de97232ababababcef3234958723 bad_commit
e4a2ec4a80ef63e1e2e694051d9eb3951b14c407 v4.3
2f116449144dbe46e7260d5dac2304803751b5c2 v4.2
检出最后一个好的提交到一个新的临时分支。
$ git checkout e4a2ec4a80ef63e1e2e694051d9eb3951b14c407
$ git checkout -b temp_branch
  • 替换远程分支(通过删除并推送临时���支)
  • git push origin --delete dev_branch
    git push origin temp_branch:dev_branch
    

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