On Mon, Jan 06, 2014 at 12:17:21PM -0800, Junio C Hamano wrote:

> > I am fine with rejecting it with a warning, but we should not then
> > complain that the other side did not send us the object, since we should
> > not be asking for it at all. I also do not see us complaining about the
> > funny ref anywhere.  So there is definitely _a_ bug here. :)
> 
> Oh, no question about that.  I was just pointing somebody who
> already has volunteered to take a look in a direction I recall was
> where the issue was ;-)

Oh, crud, did I volunteer? :)

So I found the problem, but I'm really not sure what to make of it. We
do check the refname format when evaluating the refspecs, in:

    do_fetch
      get_ref_map
        get_fetch_map
          check_refname_format

Before calling it, we check that it starts with "refs/", and then pass
the _whole_ refname into check_refname_format. So the latter sees
"refs/stash". And that's acceptable, as it's not a single-level ref.
Thus we do not get the "funny ref" message.

The code looks like this:

  if (!starts_with((*rmp)->peer_ref->name, "refs/") ||
      check_refname_format((*rmp)->peer_ref->name, 0)) {
        /* print funny ref and ignore */

Then we ask fetch_refs_via_pack to get the actual objects for us. And
it checks our refs again, with this call chain:

  do_fetch
    fetch_refs
      transport_fetch_refs
        fetch_refs_via_pack
          fetch_pack
            do_fetch_pack
              everything_local
                filter_refs
                  check_refname_format

Here, the code looks like this:

  if (!memcmp(ref->name, "refs/", 5) &&
      check_refname_format(ref->name + 5, 0))
    ; /* trash */

At first I thought we are doing the same check (is it syntactically
valid, and is it in "refs/"), but we're not. We are actually checking
the format _only_ of stuff in "refs/", and ignoring the rest. Which
really makes no sense to me.

If it were "memcmp(...) || check_refname_format()", then it would be
roughly the same check. But it would still be wrong, because note that
we pass only the bits under "refs/" to check_refname_format. So it sees
only "stash", and then complains that it is single-level.

So the symptom we are seeing is because we are filtering with two
different rulesets in different places. But I'm really not sure how to
resolve it. The one in filter_refs seems nonsensical to me.

Checking _only_ things under refs/ doesn't make sense. And even if that
was sensible, feeding half a ref to check_refname_format does not work.
In addition to the single-level check, it has other rules that want
to see the whole ref (e.g., the ref "@" is not valid, but "refs/@" is
OK; it cannot distinguish them without seeing the prefix).

So I can see two options:

  1. Make the check in filter_refs look like the one in get_fetch_map.
     That at least makes them the same, which alleviates the symptom.
     But we still are running two checks, and if they ever get out of
     sync, it causes problems.

  2. Abolish the check in filter_refs. I think this makes the most sense
     from the perspective of fetch, because we will already have cleaned up
     the ref list. But we might need to leave the filtering in place for
     people who call fetch-pack as a bare plumbing command.

It's really not clear to me what the check in filter_refs was trying to
do. It dates all the way back to 1baaae5 (Make maximal use of the remote
refs, 2005-10-28), but there is not much explanation. I haven't dug into
the list around that time to see if there's any discussion.

-Peff
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to