列出并删除不在分支下的 Git 提交(悬空?)

我有一个 Git 存储库,它提交了大量没有特定分支的内容,我可以对它们进行 git show,但是当我试图列出包含它们的分支时,它什么也不会报告。

我认为这是一个悬而未决的提交/树问题(由于 -D 分支的原因) ,所以我删除了回购,但是之后我仍然看到了相同的行为:

$ git fetch origin


$ git fsck --unreachable
$ git fsck

没有输出,没有悬挂(对吗?)。但是提交存在

$ git show 793db7f272ba4bbdd1e32f14410a52a412667042
commit 793db7f272ba4bbdd1e32f14410a52a412667042
Author: ...

它不能通过任何分支到达

$ git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042

没有输出。

那个承诺的确切状态是什么?如何列出处于类似状态的所有提交?我怎么能删除这样的提交呢?

94107 次浏览

git gc --prune=<date>默认删除两周以前的对象。你可以设定一个更近的日期。但是,创建松散对象的 git 命令通常会运行 git gc-auto (如果松散对象的数量超过配置变量 gc.auto 的值,就会删除这些对象)。

确实要删除这些提交吗?Auto 的默认设置将确保松散对象不会占用不合理的内存量,并且将松散对象存储一段时间通常是一个好主意。这样,如果您明天意识到您删除的分支中包含您需要的提交,就可以恢复它。

没有输出,没有悬挂(对吗?)

请注意,从 reflog 引用的提交被认为是可以实现的。

提交的确切状态是什么? 我如何列出所有具有相似状态的提交

通过 --no-reflogs来说服 git fsck向你展示它们。

我怎么能删除这样的提交呢?

一旦 reflog 条目过期,那些对象也将被 git gc清除。

过期受 gc.pruneexpiregc.reflogexpiregc.reflogexpireunreachable设置的调节。参照 git help config

违约都是合理的。

要删除所有悬空的提交(包括那些仍然可以从隐藏和其他重新登录访问的提交) ,请执行以下操作:

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

但要确定这是你想要的。我建议你阅读手册,但要点如下:

git gc删除不可到达的对象(提交、树、 blobs (文件))。如果某个对象不是某个分支历史记录的一部分,则该对象是不可访问的。实际上情况要复杂一些:

使用 reflog 实现存储(即不使用分支或标记)。这意味着它们受到垃圾收集的影响。

git gc做一些其他的事情,但是他们在这里不相关,也不危险。

小于两周的无法访问的对象不会被删除,所以我们使用 --prune=now,意思是“删除之前创建的无法访问的对象”。

也可以通过 reflog 访问对象。分支记录某个项目的历史,而 relog 记录这些分支的历史。如果您修改,重置等提交从分支历史中删除,但 git 保留它们,以防您意识到您犯了一个错误。重新记录是查明在分支(或 HEAD)上执行了哪些破坏性(和其他)操作的一种方便方法,这使得撤消破坏性操作变得更加容易。

因此,我们还必须删除 relog,以实际删除分支中无法访问的所有内容。我们通过过期 --all重写来实现这一点。同样,git 保留了一些 relog 来保护用户,所以我们必须再次告诉它不要这样做: --expire-unreachable=now

因为我主要使用 reflog 来从破坏性操作中恢复,所以我通常使用 --expire=now,它可以完全消除 reflog。

git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042

可能只是需要

git branch -a --contains 793db7f272ba4bbdd1e32f14410a52a412667042

也从远程报告分支

我有一个类似的问题。我运行 git branch --contains <commit>,它没有返回任何输出,就像问题中一样。

但即使在逃跑之后

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

我的提交仍然可以使用 git show <commit>访问。这是因为其分离/悬挂的“分支”中的一个提交被标记了。我删除了标签,再次运行上面的命令,然后我就成功了。git show <commit>返回 fatal: bad object <commit>-正是我需要的。希望这能帮助其他和我一样被困在这里的人。

我也遇到了同样的问题,但我还是遵循了这个帖子里的所有建议:

git reflog expire --expire-unreachable=now --all
git gc --prune=now
git fsck --unreachable --no-reflogs   # no output
git branch -a --contains <commit>     # no output
git show <commit>                     # still shows up

如果它不是一个重新测试,而不是一个分支,... ... 它必须是 一个标签

git tag                             # showed several old tags created before the cleanup

我用 git tag -d <tagname>删除了标签并重新进行了清理,旧的提交已经不复存在。

更新: 如果它不是一个标签,那么它也可能是一个隐藏的标签

我无意中碰到了同样的情况,发现我的存储包含对不可达提交的引用,因此假定的不可达提交可以从存储中访问。

这就是我所做的,让它变得遥不可及。

git stash clear
git reflog expire --expire-unreachable=now --all
git fsck --unreachable
git gc --prune=now

如果这个藏匿点真的是一个“不存在”的藏匿点,而不是一个标签,
git fsck --full
可能会有帮助。当没有其他解决方案时,它对我起作用了。

(清除破损的存货比这个线程更准确地描述了我的问题)

我知道这不是一个好的解决方案,但我做了一个 filter-branch,结果是重复的,无法访问的提交,不属于任何分支,但不能自动删除它们,我尝试了每一个单一的解决方案张贴在这里,绝对没有工作。因此,我转到一个远程存储库(github) ,删除我的本地存储库,然后再次提取,删除所有这些无法访问的提交

TL: DR;

提交可以通过以下方式引用:

  • 树枝
  • 一个标签
  • 藏起来了

使用这个命令查找它的确切内容:

git for-each-ref --contains <SHA>

说来话长

我做了一个 filter-repo的回购清理一些巨大的文件。

很明显,最后的结果是一些悬而未决的提交。我尝试了所有的答案,但没有一个是有效的。

git branch --contains <SHA>           # nothing!
git fsck --unreachable --no-reflogs   # nothing!

然后我发现并删除了一个标签感谢 这个答案

承诺还是不断出现!

所以我最终运行了这个命令:

git for-each-ref --contains <SHA>

它又回来了

1b9bf9c63209a4728b2d3dc7946da836dc331bbd commit refs/stash

找到了!

我运行 git stash-它(奇怪地)是空的。如果我的存储中没有任何东西,那么存储引用如何能够提交?

然而,运行 git stash drop几次删除了一些上帝遗弃的“摇摆”存储基于过时的分支和 终于我能够 gc提交,我的回购立即得到15 MB 小。