Re: [PATCH 5/6] refs: stop resolving ref corresponding to reflogs
- From
Junio C Hamano <gitster@pobox.com>
- Date
- Feb 20, 2024, 00:14 UTC
- Message-ID
- <xmqqjzn0yote.fsf@gitster.g>
- In-Reply-To
- <a7459b9483660d1a44df500aaee85ad38146eb02.1708353264.git.ps@pks.im>
Patrick Steinhardt <ps@pks.im> writes:
Show 5 quoted lines
> Refactor the code to call `check_refname_format()` directly instead of > trying to resolve the ref. This is significantly more efficient given > that we don't have to hit the object database anymore to list reflogs. > And second, it ensures that we end up showing reflogs of broken refs, > which will help to make the reflog more useful.
And the user would notice corrupt ones among those reflogs listed when using "rev-list -g" on the reflog anyway? Which sounds like a sensible thing to do.
Show 5 quoted lines
> Note that this really only impacts the case where the corresponding ref > is corrupt. Reflogs for nonexistent refs would have been returned to the > caller beforehand already as we did not pass `RESOLVE_REF_READING` to > the function, and thus `refs_resolve_ref_unsafe()` would have returned > successfully in that case.
What do "Reflogs for nonexistent refs" really mean? With the files backend, if "git branch -d main" that removed the "main" branch somehow forgot to remove the ".git/logs/refs/heads/main" file, the reflog entries in such a file is for nonexistent ref. Is that what you meant? As a tool to help diagnosing and correcting minor repo breakages, finding such a leftover file that should not exist is a good idea, I would think.
Would we see missing reflog for a ref that exists in the iteration? I guess we shouldn't, as the reflog iterator that recursively enumerates files under "$GIT_DIR/logs/" would not see such a missing reflog by definition.
Show 12 quoted lines
> diff --git a/refs/files-backend.c b/refs/files-backend.c
> index 2b3c99b00d..741148087d 100644
> --- a/refs/files-backend.c
> +++ b/refs/files-backend.c
> @@ -2130,17 +2130,9 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
> while ((ok = dir_iterator_advance(diter)) == ITER_OK) {
> if (!S_ISREG(diter->st.st_mode))
> continue;
> - if (diter->basename[0] == '.')
> + if (check_refname_format(diter->basename,
> + REFNAME_ALLOW_ONELEVEL))
> continue;A tangent.
I've never liked the code arrangement in the check_refname_format() that assumes that each level can be separately checked with exactly the same logic, and the only thing ALLOW_ONELEVEL does is to include pseudorefs and HEAD; this makes such assumption even more ingrained. I am not sure what to think about it, but let's keep reading.
> - if (ends_with(diter->basename, ".lock")) > - continue;
This can safely go, as it is rejected by check_refname_format().
Show 6 quoted lines
> - if (!refs_resolve_ref_unsafe(iter->ref_store,
> - diter->relative_path, 0,
> - NULL, NULL)) {
> - error("bad ref for %s", diter->path.buf);
> - continue;
> - }This is the focus of this step in the series. We did not abort the iteration before, but now we no longer issue any error message.
Show 17 quoted lines
> iter->base.refname = diter->relative_path;
> return ITER_OK;
> diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
> index 889bb1f1ba..efbbf23c72 100644
> --- a/refs/reftable-backend.c
> +++ b/refs/reftable-backend.c
> @@ -1659,11 +1659,9 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator)
> if (iter->last_name && !strcmp(iter->log.refname, iter->last_name))
> continue;
>
> - if (!refs_resolve_ref_unsafe(&iter->refs->base, iter->log.refname,
> - 0, NULL, NULL)) {
> - error(_("bad ref for %s"), iter->log.refname);
> + if (check_refname_format(iter->log.refname,
> + REFNAME_ALLOW_ONELEVEL))
> continue;
> - }This side is much more straight-forward. Looking good.
> > free(iter->last_name); > iter->last_name = xstrdup(iter->log.refname);