Subject: Re: git fetch --prune fails with "fatal: bad object"

From: Junio C Hamano <[email-masked]>
Date: Sat, 01 Jun 2024 08:53:43 -0700

<[email-masked]> writes:

>> - Why did the repository got into this state in the first place?
>>   It seems that it would be much better solution to prevent refs
>>   from having garbage values in them or to prevent objects that are
>>   necessary from going away than any "prune invalid refs" feature.
>
> I agree. However, there are some configurations where disk write
> caches are enabled and require a sync or some other flush
> operation to force a complete write to disk. In such situations,
> corruptions are always possible despite the best efforts by the
> application.

The question was posed to see where the current "best efforts" are
still inadequate.

>> - "fetch" still feels a wrong place to have the feature, if it is
>>   about fixing a local repository corruption.  You should be able
>>   to recover from such a broken ref even if you are only working
>>   locally without fetching from anybody.
>
> I think fsck would be a better place for this.
>
>>If you can somehow _enumerate_ such broken refs, you could drive update-ref to
>>remove them.

Interesting.  "git fsck" certainly can be used to help you find out
about them.  In a throw-away repository, after manually crafting
some "broken refs" (because update-ref will refuse to create a ref
pointing at a missing object):

    $ git for-each-ref
    9e830ad6c4f43159cef50cb1c2205f513c79bc8b commit refs/heads/master
    $ echo 9e830ad6c4f43159cef50cb1c2205f513c79bc8a >.git/refs/heads/broken-missing
    $ git rev-parse master: >.git/refs/heads/broken-tree
    $ git rev-parse "master:foo /baz" >.git/refs/heads/broken-blob

running "git fsck" does tell you about them, ...

    $ git fsck
    Checking object directories: 100% (256/256), done.
    error: refs/heads/broken-blob: not a commit
    error: refs/heads/broken-missing: invalid sha1 pointer 9e830ad6c4f43159cef50cb1c2205f513c79bc8a
    error: refs/heads/broken-tree: not a commit

... and using the information, you can

    $ for r in refs/heads/broken-{blob,missing,tree}
      do git update-ref -d "$r"
      done

to unbreak the repository.