To remove all dangling commits (including those still reachable from stashes and other reflogs) do this:
git reflog expire --expire-unreachable=now --all
git gc --prune=now
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.
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 output, nothing dangling (right?)
Note that commits referred to from your reflog are considered reachable.
What exactly is the state of that commit? How can I list all commits with similar state
Pass --no-reflogs
to convince git fsck
to show them to you.
How can I delete commits like those?
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
, and gc.reflogexpireunreachable
settings. Cf. git help config
.
The defaults are all quite reasonable.
I had the same issue, still after following all the advice in this thread:
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
If it's not a reflog and not a branch, ...it must be a tag!
git tag # showed several old tags created before the cleanup
I removed the tags with git tag -d <tagname>
and redid the cleanup, and the old commits were gone.
git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042
probably just needs to be
git branch -a --contains 793db7f272ba4bbdd1e32f14410a52a412667042
to also report on branches from remotes
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With