列出和删除不在分支下的 Git 提交(悬空?)
我有一个 Git 存储库,其中包含大量不在特定分支下的提交,我可以 git show 它们,但是当我尝试列出包含它们的分支时,它不会报告任何内容。
我认为这是悬空提交/树问题(由于 -D 分支),所以我修剪了存储库,但之后我仍然看到相同的行为:
$ git fetch origin
$ git fsck --unreachable
$ git fsck
没有输出,没有任何悬空(对吗?)。但是提交存在
$ git show 793db7f272ba4bbdd1e32f14410a52a412667042
commit 793db7f272ba4bbdd1e32f14410a52a412667042
Author: ...
,并且无法通过任何分支访问它,因为
$ git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042
没有输出。
该提交的具体状态是什么?如何列出处于相似状态的所有提交?我怎样才能删除这样的提交?
I've got a Git repository with plenty of commits that are under no particular branch, I can git show
them, but when I try to list branches that contain them, it reports back nothing.
I thought this is the dangling commits/tree issue (as a result of -D branch), so I pruned the repo, but I still see the same behavior after that:
$ git fetch origin
$ git fsck --unreachable
$ git fsck
No output, nothing dangling (right?). But the commit exists
$ git show 793db7f272ba4bbdd1e32f14410a52a412667042
commit 793db7f272ba4bbdd1e32f14410a52a412667042
Author: ...
and it is not reachable through any branch as
$ git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042
gives no output.
What exactly is the state of that commit? How can I list all commits in a similar state? How can I delete commits like those?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
要删除所有悬空提交(包括仍然可以从存储和其他引用日志访问的提交),请执行以下操作:
但请确保这就是您想要的。我建议您阅读手册页,但要点如下:
git gc
删除无法访问的对象(提交、树、blob(文件))。如果一个对象不属于某个分支的历史记录,则该对象是不可访问的。实际上它有点复杂:存储是使用引用日志实现的(即不是分支或标签)。这意味着它们会受到垃圾收集的影响。或者,更明确地说:运行上述命令将删除所有存储。
git gc 还做了一些其他的事情,但它们与这里无关,也不危险。
两周内无法访问的对象不会被删除,因此我们使用
--prune=now
,这意味着“删除之前创建的无法访问的对象”。还可以通过引用日志访问对象。分支记录某些项目的历史记录,而引用日志则记录这些分支的历史记录。如果您修改、重置等,提交将从分支历史记录中删除,但 git 会保留它们,以防您意识到犯了错误。引用日志是一种查找在分支(或 HEAD)上执行了哪些破坏性(和其他)操作的便捷方法,从而更容易撤消破坏性操作。
因此,我们还必须删除引用日志,以实际删除分支无法访问的所有内容。我们通过使
--all
引用日志过期来实现此目的。 git 再次保留一些引用日志来保护用户,因此我们必须再次告诉它不要这样做:--expire-unreachable=now
。由于我主要使用引用日志从破坏性操作中恢复,因此我通常使用
--expire=now
来代替,这会完全清除引用日志。To remove all dangling commits (including those still reachable from stashes and other reflogs) do this:
But be certain that this is what you want. I recommend you read the man pages but here is the gist:
git gc
removes unreachable objects (commits, trees, blobs (files)). An object is unreachable if it isn't part of the history of some branch. Actually it is a bit more complicated:Stashes are implemented using the reflog (i.e not not branches or tags). That means that they are subject to garbage collection. Or, to make it more explicit: running the above commands will delete all your stashes.
git gc
does some other things but they are not relevant here and not dangerous.Unreachable objects that are younger than two weeks are not removed so we use
--prune=now
which means "remove unreachable objects that were created before now".Objects can also be reached through the reflog. While branches record the history of some project, reflogs record the history of these branches. If you amend, reset etc. commits are removed from the branch history but git keeps them around in case you realize that you made a mistake. Reflogs are a convenient way to find out what destructive (and other) operations were performed on a branch (or HEAD), making it easier to undo a destructive operation.
So we also have to remove the reflogs to actually remove everything not reachable from a branch. We do so by expiring
--all
reflogs. Again git keeps a bit of the reflogs to protect users so we again have to tell it not to do so:--expire-unreachable=now
.Since I mainly use the reflog to recover from destructive operations I usually use
--expire=now
instead, which zaps the reflogs completely.请注意,引用日志中引用的提交被认为是可达的。
--no-reflogs
说服git fsck
向您显示它们。一旦您的引用日志条目过期,这些对象也将被 git gc 清理。
过期由
gc.pruneexpire
、gc.reflogexpire
和gc.reflogexpireunreachable
设置控制。比照。git 帮助配置
。默认值都是相当合理的。
Note that commits referred to from your reflog are considered reachable.
Pass
--no-reflogs
to convincegit fsck
to show them to you.Once your reflog entries are expired, those objects will then also be cleaned up by
git gc
.Expiry is regulated by the
gc.pruneexpire
,gc.reflogexpire
, andgc.reflogexpireunreachable
settings. Cf.git help config
.The defaults are all quite reasonable.
我遇到了同样的问题,仍然遵循了该线程中的所有建议:
如果它不是引用日志也不是分支,...它必须是标签!
我使用 git tag -d删除了标签并重新进行了清理,旧的提交消失了。
更新:如果事实证明它不是标签,它也可能是一个藏匿处! 检查此答案和此答案
I had the same issue, still after following all the advice in this thread:
If it's not a reflog and not a branch, ...it must be a tag!
I removed the tags with
git tag -d <tagname>
and redid the cleanup, and the old commits were gone.Update: If it turns out it's not a tag, it might also be a stash! Check this answer and this answer
可能只需要
报告远程分支
probably just needs to be
to also report on branches from remotes
我不小心遇到了同样的情况,发现我的存储包含对无法访问的提交的引用,因此可以从存储中访问假定的无法访问的提交。
我所做的就是让它真正变得遥不可及。
I accidentally hit the same situation and found my stashes contain reference to the unreachable commit, and thus the presumed unreachable commit was reachable from stashes.
These were what I did to make it truly unreachable.
我有类似的问题。我运行了 git Branch --contains,它没有返回任何输出,就像问题中一样。
但即使在运行之后,
仍然可以使用 git show访问我的提交。这是因为其分离/悬空的“分支”中的提交之一被标记了。我删除了标签,再次运行上述命令,然后我就成功了。
git show
返回了fatal: bad object
- 正是我所需要的。希望这可以帮助像我一样陷入困境的其他人。I had a similar issue. I ran
git branch --contains <commit>
, and it returned no output just like in the question.But even after running
my commit was still accessible using
git show <commit>
. This was because one of the commits in its detached/dangled "branch" was tagged. I removed the tag, ran the above commands again, and I was golden.git show <commit>
returnedfatal: bad object <commit>
- exactly what I needed. Hopefully this helps someone else that was as stuck as I was.TL:博士;
提交可以通过以下方式引用:
使用此命令查找它的内容正是:
长版本
我对存储库做了一个
filter-repo
来清理一些大文件。显然最终导致了一些悬而未决的提交。我尝试了所有答案,但没有任何效果。
然后,由于 这个答案,我找到并删除了一个标签,
但提交仍然显示出来!
所以我最终运行了这个命令:
它返回了
Bingo!
我运行了 git stash - 它(奇怪地)是空的。如果我的存储中没有任何内容,存储如何引用提交?
然而,运行
git stash drop
几次已经删除了一些基于过时分支的被上帝遗弃的“悬挂”存储,终于我能够gc
提交,我的仓库立即变小了 15MB。更长的版本
(来自另一个用户)我使用了上面的提示,但它返回了
到底是什么替换?
显示您可以列出删除和编辑这些“替换对象”。诡异的。运行 git Replace -d 2acd8f 会删除提交!
TL:DR;
A commit can be referenced by:
Use this command to find what it is exactly:
Long version
I did a
filter-repo
of the repo to clean up some huge files.Obviously ended up with some dangling commits. I tried all the answers, and nothing was working.
Then I found and deleted a tag thanks to this answer
Still the commit kept shwoing up!
So I finally ran this command:
And it returned
Bingo!
I ran
git stash
- and it was (oddly) empty. How can a stash reference a commit if I have nothing in my stash?However running
git stash drop
several times has deleted some god forsaken "dangling" stashes based on obsolete branches and finally I was able togc
that commit, and my repo instantly got 15MB smaller.Longer version
(From another user) I used the tips above but it returned
what the heck is replace?
shows you can list delete and edit these 'replacement objects'. Weird. Running
git replace -d 2acd8f
removes the commit!git gc --prune=
默认删除两周前的对象。您可以设置一个更近的日期。但是,创建松散对象的 git 命令通常会运行 git gc --auto (如果松散对象的数量超过配置变量 gc.auto 的值,则会修剪松散对象)。您确定要删除这些提交吗? gc.auto 的默认设置将确保松散对象不会占用不合理的内存量,并且将松散对象存储一段时间通常是一个好主意。这样,如果您明天意识到已删除的分支包含您需要的提交,则可以恢复它。
git gc --prune=<date>
defaults to prune objects older than two weeks ago. You could set a more recent date. But, git commands that create loose objects generally will run git gc --auto (which prunes loose objects if their number exceeds the value of configuration variable gc.auto).Are you sure that you want to delete these commits? gc.auto's default setting will ensure that the loose objects do not take up an unreasonable amount of memory, and storing loose objects for some amount of time is generally a good idea. That way, if you realize tomorrow that your deleted branch contained a commit you needed, you can recover it.
如果存储确实是“不存在”的存储而不是标签,
git fsck --full
可能有帮助。当没有其他解决方案时,它对我有用。
(Git:删除损坏的存储比这个线程更准确地描述了我的问题)
If the stash is truly a stash that "doesn't exist" and not a tag,
git fsck --full
may help. It worked for me when no other solution did.
(Git: Remove broken stash describes my problem more accurately than this thread)
我知道这不是一个好的解决方案,但我做了一个
filter-branch
并最终得到了重复的、无法访问的提交,这些提交不属于任何分支,但无法自动删除它们,我尝试了每一个解决方案发布在这里,绝对没有任何效果。因此,我推送到远程存储库(github),删除了本地存储库,然后再次拉取并删除了所有那些无法访问的提交I know this is not a good solution but I did a
filter-branch
and ended up with duplicate, unreachable commits which didn't belong to any branch but couldn't remove them automatically, I tried every single solution posted here, absolutely nothing worked. So I pushed to a remote repository (github), deleted my local repository and then pulled again and got rid of all those unreachable commits首先你应该考虑是否真的需要
积极删除/修剪所有无法访问的提交。
您可能收到此消息:
然后< code>git prune 似乎没有解决问题,所以你正在寻找
旋钮可以更积极地删除内容。
修剪最近提交的问题是你可以在最坏的情况下
案例(我不知道这有多大可能)损坏的东西< /a> 如果你也
与遥控器交互。
因此,如果“松散的对象”是您的直接问题,而不是缺少磁盘
空间,或者太多的物体——那么你可以启用这个实验
功能(从 Git 2.40.1 开始):
现在 git gc 将会打包无法访问的对象,这应该会消除该对象
警告。
总的来说,使用实验性功能似乎更安全,如果这意味着
您没有理由修剪最近无法访问的对象。
First you should consider whether you really need to
aggressively delete/prune all unreachable commits.
You might have gotten this message:
And then
git prune
didn’t seem to solve the problem, so you are lookingfor knobs to delete things more aggressively.
The problem with pruning recent commits is that you can in the worst
case (I don’t know how likely this is) corrupt things if you also
interact with a remote.
So if “loose objects” is your direct problem—rather than lack of disk
space, or just too many objects—then you can enable this experimental
feature (as of Git 2.40.1):
Now
git gc
will pack unreachable objects, which should get rid of thatwarning.
It seems on balance safer to use an experimental feature if it means
that you won’t have a reason to prune very recent unreachable objects.
我正在寻找这个问题的解决方案并使用一些我的私人仓库。然后我经历了有趣的行为。
我有镜像克隆的存储库(
clone --mirror
),并在其中运行其他答案中提到的命令。根本没有孤立的提交。然后我将该镜像存储库 (push --mirror
) 推送到 GitHub(到新的空白存储库,以免弄乱原始存储库),实际上没有这些提交。更多详细信息和示例此处,我使用 OP 的存储库进行演示。
I was looking for solution to this question and playing with some my private repo. Then I experienced interesting behavior.
I have mirror cloned repo (
clone --mirror
) and inside of it run commands mentioned in other answers here. There were no orphaned commits at all. Then I pushed that mirror repo (push --mirror
) to GitHub (to new blank repo to not mess with the original one) and indeed there were no these commits.More details and example here where I used OP's repo for demo.