【问题标题】:Listing and deleting Git commits that are under no branch (dangling?)列出和删除没有分支的 Git 提交(悬空?)
【发布时间】:2011-04-15 11:31:41
【问题描述】:

我有一个 Git 存储库,其中包含大量不属于特定分支的提交,我可以 git show 它们,但是当我尝试列出包含它们的分支时,它什么也不报告。

我认为这是悬空提交/树问题(由于 -D 分支),所以我修剪了 repo,但之后我仍然看到相同的行为:

$ git fetch origin

$ git fsck --unreachable
$ git fsck

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

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

并且它不能通过任何分支到达

$ git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042

没有输出。

该提交的具体状态是什么?如何列出处于相似状态的所有提交?我怎样才能删除这样的提交?

【问题讨论】:

标签: git branch git-dangling


【解决方案1】:

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

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

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

git gc 删除无法访问的对象(提交、树、blob(文件))。如果对象不属于某个分支的历史记录,则该对象是不可访问的。其实有点复杂:

Stashes 是使用 reflog 实现的(即不是分支或标签)。这意味着它们会受到垃圾回收。

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

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

也可以通过 reflog 访问对象。分支记录了某个项目的历史,而引用日志记录了这些分支的历史。如果您修改、重置等,提交会从分支历史记录中删除,但 git 会保留它们,以防您意识到自己犯了错误。 Reflogs 是一种方便的方式来找出对分支(或 HEAD)执行了哪些破坏性(和其他)操作,从而更容易撤消破坏性操作。

所以我们还必须删除 reflogs 才能真正删除从分支中无法访问的所有内容。我们通过使--all reflogs 过期来做到这一点。 git 再次保留一些 reflogs 来保护用户,所以我们必须再次告诉它不要这样做:--expire-unreachable=now

由于我主要使用 reflog 来从破坏性操作中恢复,因此我通常使用 --expire=now 代替,这会完全破坏 reflog。

【讨论】:

  • 我告诉你使用哪些命令不明显- gc 不应该足够吗?如果您之前从未使用过 git-reflog,您将不会知道。因此,现在您知道必须使用哪些命令,您应该在他们的手册页中查找提到的选项。当然,我可以直接从那里复制这些信息……
  • 好吧,实际上我确切地说了它的作用:“删除所有悬空提交和那些可以从 reflog 中访问的提交”。如果您不知道什么是 reflog:请再次阅读手册。
  • 虽然给出的答案可能是正确的,但@erikb85 正确地指出,没有关于你被告知要做什么的教育。跟进 RTFM 的帮助就更小了。是的,我们都应该阅读所有文档。在某些情况下,进行搜索的人可能没有充分了解文档以了解发生了什么。因此,对这些命令的作用进行一些教育将对以后找到此答案的每个人都有帮助。
  • git reflog expire --expire-unreachable=now --all 放下你所有的藏匿处!
  • 我认为这个答案需要一个明确的警告,最好在顶部。我的编辑建议被拒绝了,因为我想我应该在评论中向作者建议?请接受此编辑stackoverflow.com/review/suggested-edits/26023983 或以您自己的方式添加警告。它会掉落你所有的藏匿处也很重要!
【解决方案2】:

没有输出,没有任何悬空(对吗?)

请注意,从您的 reflog 引用的提交被认为是可访问的。

该提交的具体状态是什么?如何列出所有具有相似状态的提交

通过--no-reflogs 说服git fsck 将它们展示给您。

如何删除这样的提交?

一旦您的 reflog 条目过期,这些对象也将被 git gc 清理。

到期由gc.pruneexpiregc.reflogexpiregc.reflogexpireunreachable 设置控制。参照。 git help config.

默认值都比较合理。

【讨论】:

  • 所以你基本上是说悬空提交的回流会在一段时间后自动删除?
  • 基本上:是的——除了问题有点混乱。我是说 all reflog 条目会在一段时间后自动删除,但您可以通过配置设置来更改它。而且因为一个提交只有在 nothing 指向它时才被称为dangling——包括 reflog 条目——所以“悬空提交的 reflogs”不是一个东西。它们将是“unreachable 提交的 reflogs”。
  • '它们将是“无法访问的提交的引用日志”。'但是您说“从您的 reflog 中引用的提交被认为是可访问的。”那么“无法访问的提交的 reflogs”怎么会成为一件事呢?我很困惑。
  • 是的,我并不一致。通常人们不会考虑 reflog,当他们说“unreachable”时,它意味着“from a ref”。即使git help glossary 也是这样定义的……而它对“可达”的定义并没有那样缩小,所以它们是矛盾的。有趣——所以我所说的实际上与gitglossary 中的混淆是一致的……不过,混淆的不是概念,而是术语。关键是“悬空”提交是 nothing else 指向的提交。如果我说“reflogs for otherwise unreachable commits”……会有帮助吗?
  • 这一切都非常令人困惑。让我们简单点。在分支master 上时,您执行git commit,并获得提交000001。然后你做git commit --amend,它给你提交000002。不再有指向000001 的标签或分支,如果没有--reflog 选项,您将无法在日志中看到它,但如果您愿意,您仍然可以使用git checkout 000001 来访问它。现在的问题是,000001dangling 提交,还是 unreachable 提交,或者两者都不是,还是两者兼而有之?
【解决方案3】:

我也遇到了同样的问题,但在遵循了这个帖子中的所有建议之后:

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

如果它不是一个 reflog 也不是一个分支,...它必须是 一个标签

git tag                             # showed several old tags created before the cleanup

我删除了带有git tag -d &lt;tagname&gt; 的标签并重新进行了清理,旧的提交消失了。

【讨论】:

  • 已经有一个关于标签的答案 (stackoverflow.com/a/37335660/450127),看起来这并没有增加任何新内容。不应该将其删除以支持较早的答案吗?
  • 确实,不知何故我忽略了这个答案。虽然有 4 个人认为我的回答很有帮助,但也许它不是那么无用?我还将所有可能性归为一个简洁的答案。
  • 即使重复,此页面也可能出现在 Google 搜索结果中,它可以立即帮助遇到相同问题的人,而不是一遍又一遍地将人们重定向到可能的链接有正确的答案。
  • 就我而言,所有解​​决方案都是部分的。缺少的部分正是标签。
【解决方案4】:
git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042

可能只是需要

git branch -a --contains 793db7f272ba4bbdd1e32f14410a52a412667042

还可以从远程报告分支

【讨论】:

  • 谢谢,现在我找到了我的 remotes/origin/next 仍然持有这个提交。如何删除它? git push -d origin next 没有帮助。
  • 谢谢 - git fetch --prune 成功了。但在所有答案中,我都缺少对引用此提交的标签的检查。我仍然不知道如何通过提交检查标签(我删除了所有)。
  • 但是...这是否意味着只能从 remote 分支(而不是本地分支)访问的提交被认为是可访问的,因此git fsck --unreachable 实际上是通过使用远程网络来找出哪些提交是可访问的?
  • 回答了我自己的问题...是的,只能从远程分支(并且没有本地分支)可以访问的提交被认为是可以访问的;但是git fsck --unreachable 不需要通过网络与远程通信来找出哪些远程分支包含哪些提交。远程分支信息存储在本地,例如.git/refs/remotes/origin(或packed-refs)。
【解决方案5】:

我遇到了类似的问题。我跑了git branch --contains &lt;commit&gt;,它没有返回任何输出,就像问题中一样。

但即使在运行之后

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

我的提交仍然可以使用git show &lt;commit&gt; 访问。这是因为它的分离/悬空“分支”中的一个提交被标记了。我删除了标签,再次运行上述命令,我很成功。 git show &lt;commit&gt; 返回fatal: bad object &lt;commit&gt; - 正是我需要的。希望这可以帮助像我一样陷入困境的其他人。

【讨论】:

【解决方案6】:

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

这些是我为了让它真正无法访问而做的。

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

【讨论】:

    【解决方案7】:

    git gc --prune=&lt;date&gt; 默认修剪两周前的对象。您可以设置一个更新的日期。但是,创建松散对象的 git 命令通常会运行 git gc --auto (如果松散对象的数量超过配置变量 gc.auto 的值,它会修剪松散对象)。

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

    【讨论】:

      【解决方案8】:

      如果存储确实是“不存在”而不是标签的存储,
      git fsck --full
      可能会有所帮助。当没有其他解决方案时,它对我有用。

      Git: Remove broken stash 比这个帖子更准确地描述了我的问题)

      【讨论】:

        猜你喜欢
        • 2022-10-15
        • 1970-01-01
        • 1970-01-01
        • 2014-03-23
        • 2017-01-24
        • 1970-01-01
        • 2020-07-29
        • 1970-01-01
        • 2014-09-29
        相关资源
        最近更新 更多