列出和删除没有分支的Git提交(悬挂吗?)


146

我有一个Git存储库,里面有很多没有特定分支的提交,我可以git show,但是当我尝试列出包含它们的分支时,它什么也不报告。

我以为这是悬而未决的提交/树问题(由于-D分支),所以我修剪了存储库,但此后我仍然看到相同的行为:

$ git fetch origin

$ git fsck --unreachable
$ git fsck

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

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

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

$ git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042

没有输出。

提交的确切状态是什么?如何列出处于相似状态的所有提交?我该如何删除那些提交?


Answers:


75

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

请注意,您的引用日志中引用的提交被认为是可以到达的。

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

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

我该如何删除那些提交?

一旦您的reflog条目过期,这些对象也将由清除git gc

到期由调控gc.pruneexpiregc.reflogexpiregc.reflogexpireunreachable设置。cf. git help config

默认值都非常合理。


2
所以您基本上是说悬挂的提交的重排会在一段时间后自动删除吗?
MoralCode '16

2
基本上:是的–除了这个问题有点困惑之外。我是说一段时间后会自动删除所有 reflog条目,但是您可以通过配置设置进行更改。而且,由于仅在没有指向提交的内容(包括引用日志条目)时才将其称为悬空,因此“悬空提交的引用”不是问题。它们将是“ 无法实现的提交的重新引用”。
亚里斯多德·帕加尔兹

“它们将是“无法实现的提交的翻新”。但是您说“ refref引用的提交被认为是可以到达的”。那么,“对无法到达的提交的重新引用”怎么可能呢?我很困惑。
LarsH

1
是的,我并不一致。通常,人们不会考虑reflog,而当他们说“无法访问”时,则意味着“来自ref”。甚至以git help glossary这种方式定义……而“可到达”的定义并未以这种方式缩小,因此它们是矛盾的。好笑–所以我说的实际上与…中的混淆是一致的gitglossary,但是,不是使概念令人困惑的只是术语。关键是“悬空的”提交是其他没有指向的提交。如果我说“为否则无法实现的提交致谢”…… 会有所帮助吗?
亚里斯多德·帕加尔兹

这一切都很令人困惑。让我们简单点。在分支上时master,您可以执行git commit并提交000001。然后,您这样做git commit --amend,这使您可以做出承诺000002。不再有指向的标签或分支000001,没有--reflog选项就无法在日志中看到它,但是如果需要,您仍然可以使用git checkout 000001。现在的问题是,是000001一个悬空提交,或无法访问提交,或者两者都不是,还是两者兼而有之?
chharvey

264

要删除所有悬空的提交和从引用日志中可以到达的提交,请执行以下操作:

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

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

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

git gc 还有其他一些事情,但它们在这里并不相关,也不危险。

小于两个星期的无法访问的对象不会被删除,因此我们使用--prune=now这意味着“删除之前创建的无法访问的对象”。

也可以通过reflog到达对象。分支记录某些项目的历史记录,而刷新记录则记录这些分支的历史记录。如果您进行修改,重置等。提交将从分支历史记录中删除,但是git会保留它们,以防万一您意识到自己做错了。使用刷新日志可以方便地找出对分支(或HEAD)执行了哪些破坏性(和其他)操作,从而更容易撤消破坏性操作。

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

由于我主要使用reflog从破坏性操作中恢复,因此我通常改用reflog --expire=now,这将彻底消除reflog。


1
我告诉您要使用的命令不明显-gc是否足够?如果您从未使用过git-reflog,那么您将不知道。因此,既然您知道必须使用什么命令,则应该在他们的手册页中查找提到的选项。当然,我可以只从那里复制该信息...
tarsius

1
好吧,实际上我确切地说出了它的作用:“删除所有悬空的提交和从reflog中可以到达的提交”。如果您不知道什么是更新记录:请再次阅读该手册。
tarsius

7
尽管给出的答案可能是正确的,但@ erikb85指出您没有被告知要做什么是正确的。跟进RTFM甚至没有多大帮助。是的,我们都应该阅读所有文档。在某些情况下,进行搜索的人员可能没有充分阅读文档以了解正在发生的事情。因此,对命令的作用进行一些教育对于以后找到此答案的每个人都是有帮助的。
Lee Saferite

@LeeSaferite希望大家现在都开心:-)
tarsius

12
git reflog expire --expire-unreachable=now --all丢弃所有藏匿处!
Vsevolod Golovanov

22

在遵循了该线程中的所有建议之后,我仍然遇到相同的问题:

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 <tagname>并重新进行了清理,旧的提交不见了。


已经有一个关于标签的答案(stackoverflow.com/a/37335660/450127),而且似乎并没有增加任何新的东西。难道不应该删除此内容以支持先前的答案吗?
伊恩·邓恩

确实,我以某种方式忽略了这个答案。尽管有4个人认为我的回答很有帮助,所以也许那不是没有用吗?我也将所有可能性归纳为一个简洁的答案。
jakub.g 2017年

1
即使重复,此页面也可能会出现在Google搜索结果中,并且可以立即为遇到相同问题的人们提供帮助,而不仅仅是将人们一遍又一遍地重定向到可能具有正确答案的链接。
Alexandre T.

14
git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042

可能只需要

git branch -a --contains 793db7f272ba4bbdd1e32f14410a52a412667042

还可以报告远程站点的分支


谢谢,现在我发现我的remotes / origin / next仍然保留此提交。如何将其删除?git push -d origin next没有帮助。
iRaS


谢谢- git fetch --prune做到了。但在所有答案中,我都缺少对引用此提交的标记的检查。我仍然不知道如何通过提交检查标签(我删除了所有标签)。
iRaS

但是...这是否意味着仅可从远程分支(而没有本地分支)访问的提交被认为是可访问的,因此git fsck --unreachable实际上正在通过网络与远程进行通信以找出可访问的提交?
LarsH '18

1
回答了我自己的问题...是的,仅从远程分支(而没有本地分支)可以访问的提交被认为是可以访问的;但git fsck --unreachable不需要通过网络与远程进行通信即可找出哪些远程分支包含哪些提交。远程分支机构信息存储在本地(例如.git/refs/remotes/origin或中packed-refs)。
LarsH '18

8

我有一个类似的问题。我跑了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>-正是我所需要的。希望这可以帮助像我一样陷入困境的其他人。


您如何删除标签?
bapors

@bapors列出所有标记,找到引用有问题的提交的标记,然后将其删除。stackoverflow.com/questions/5480258/…–
安德鲁·拉尔森

4

我无意中遇到了同样的情况,发现我的存储中包含对无法实现的提交的引用,因此,可以从存储中获取假定的无法实现的提交。

这些就是我所做的,以使其真正无法实现。

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

2

git gc --prune=<date>默认情况下,修剪两个星期之前的对象。您可以设置一个最近的日期。但是,创建松散对象的git命令通常将运行git gc --auto(如果松散对象的数量超过配置变量gc.auto的值,则会修剪松散对象)。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.