实际删除 git 提交,使其从日志中消失

Actually delete a git commit in such a way that it disappears from the log

我找不到从日志中删除目标提交的方法。关于删除提交的文档要么只是使用 reset 重置指针(它不会删除任何东西,它只会重置指针),要么是 rebase,它会在删除的提交处创建某种新结构。目标提交的哈希值仍然出现。可能有与

相关的旧文档
git rebase -i fork1~1 fork1~3

因为在旧消息中,它声明在编辑器中删除一行,即与目标提交对应的行。当我进入交互式编辑器时,其中一个选项是 "drop",它表示删除提交。但是,我没有成功删除任何提交,因为它们从日志中消失了。我插入单词 drop,它在日志输出中创建了某种类似叉子的东西。但我仍然可以看到我试图删除的提交的哈希值。简而言之,我想确定一个要删除的散列,让它不再出现在日志中。

所以,这里的想法是删除 1bc7112:

*   4518859 (refs/stash) WIP on fork3: 8c438a9 Added first.bsh back in
|\
| * 1bc7112 index on fork3: 8c438a9 Added first.bsh back in
|/
* 8c438a9 Added first.bsh back in
* eef828f (HEAD -> fork4) Removed using git rm
* c21cda0 Third change.

重置为旧提交: git reset --hard <old commit>

当创建 git rebase -i HEAD~X 时,其中 X 是要变基的提交数,您可以在交互模式下变基。您将看到(例如)以下选项:

pick 0a3e7b9 other commit
pick 7cfa14c commit to remove

# Rebase 04ec4b7..7cfa14c onto 04ec4b7 (2 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

键入 i 以进行编辑,接下来您可以在要删除的提交之前添加 ddrop,例如:

d 7cfa14c commit to remove 

pick 在所有其他提交之前,然后键入 esc 以退出编辑模式并键入 :wq 以保存并退出。

现在将在本地删除提交,下次使用 git push --force origin <remote_branch> 将更新推送到远程分支,请注意 --force 选项,因为 rebase 是必需的。

这是预期的行为。

Git 基本上是一个简单的有向图,其中分支是提交的自动高级名称(aka refs),因此是某些图子树中的头节点。重置是更改哪个提交分支名称的一种方法。

同样,当执行变基时,导致不同哈希计算的更改会导致不同的提交。 Parent hashes are also part of the calculation, which is why dropping a commit affects all descendants. The branch is then updated to point to the new commit tree’s head, while the original commits themselves are not actually removed from the set of all commits and can still be found via the reflog.

重置或变基后这些过时的(包括删除的)commits are “orphans” if and only if they cannot be referenced by any refs+。提交仍然可以通过提交哈希引用,因为它们仍然存在于磁盘上;但是,如果这些提交没有直接引用,并且它们不是任何引用的祖先,则它们不会出现在日志命令中。

+Git refs (aka references) 包括 ~branches~、~tags~ 和 ~stash 条目~.

如果提交不是“孤立的”,因此在正常日志中可见,则有一个分支或标签需要更新。这还包括远程跟踪分支和隐藏条目。更新 and/or 删除这些相关的引用。如果防止这些提交被“孤立”的分支已经被推送到主线上......好吧,希望情况并非如此。


从磁盘中删除提交,它成为孤儿之后可以通过 prune command 来完成,它可以删除所有孤儿。 Git 通常会定期进行 GC 清理,因此通常不需要这样做。

如果已推送提交,则必须将此过程传播到 每个 现在有更改的存储库。如果这涉及强制推送主线分支,则可能 "very annoying" 对其他开发人员 and/or 不可行,具体取决于有多少其他提交是后代。这应该只作为最后的手段来完成,因为提交(以及其中的任何数据)已经泄露并且可能依赖于其他远程提交。

既然您已经发布了日志(作为图表),原因就很清楚了:您要删除的提交根本不是历史记录的一部分。它由藏匿处保存。删除它的方法是删除 stash 本身。

该图揭示了这个藏匿处到底是一只多么奇怪的鸭子。它实际上是一个合并提交;它的第一个 parent 是您创建存储时的 HEAD(因为关于该 HEAD 的工作树存在),其第二个 parent 是当时的整个索引,而 parent那一秒的 parent 是 HEAD。文档是这样画的:

       .----W
      /    /
-----H----I

点是存储,H是HEAD,I是记录的索引,W是记录的工作树。

因此,只要存储存在,H 提交(在您存储时是 head 的那个)就不会消失。

与此同时,您抱怨的提交甚至不是真正的提交!它是您收藏时索引的冻结快照。它是藏品不可或缺的一部分。