Re: [PATCH] upload-pack: strip namespace from symref data
- From
Ævar Arnfjörð Bjarmason <avarab@gmail.com>
- Date
- May 22, 2019, 10:33 UTC
- Message-ID
- <875zq2vkvf.fsf@evledraar.gmail.com>
- In-Reply-To
- <20190522043146.GA2563@sigill.intra.peff.net>
On Wed, May 22 2019, Jeff King wrote:
> Since 7171d8c15f (upload-pack: send symbolic ref information as > capability, 2013-09-17), we've sent cloning and fetching clients special > information about which branch HEAD is pointing to, so that they don't > have to guess based on matching up commit ids.
I'd add a 2nd paragraph here:
The client will then use the corresponding code added in a45b5f0552
("connect: annotate refs with their symref information in
get_remote_head()", 2013-09-17) to find the intended symref. See
9907d1359c ("Merge branch 'jc/upload-pack-send-symref'", 2013-10-30)
for the full set of changes related to this....because, see later...
Show 22 quoted lines
> However, this feature has never worked properly with the GIT_NAMESPACE > feature. Because upload-pack uses head_ref_namespaced(find_symref), we > do find and report on refs/namespaces/foo/HEAD instead of the actual > HEAD of the repo. This makes sense, since the branch pointed to by the > top-level HEAD may not be advertised at all. But we do two things wrong: > > 1. We report the full name refs/namespaces/foo/HEAD, instead of just > HEAD. Meaning no client is going to bother doing anything with that > symref, since we're not otherwise advertising it. > > 2. We report the symref destination using its full name (e.g., > refs/namespaces/foo/refs/heads/master). That's similarly useless to > the client, who only saw "refs/heads/master" in the advertisement. > > We should be stripping the namespace prefix off of both places (which > this patch fixes). > > Likely nobody noticed because bug (1) means that from the client's > perspective, we did not report on HEAD at all. And thus it uses the > pre-7171d8c15f fallback code to guess the correct HEAD, which is usually > right. It only falls down in ambiguous cases (like the one laid out in > the included test).
...because here you're talking about "the client's perspective" and "it uses the pre-7171d8c15f [...] code", but this should say "the pre-a45b5f0552 code", i.e. mention the commit that changed the *client* logic.
Well, the client also "uses" the server-side code indirectly, but I think it's easier to follow along if we note what both sides are (as I dug up when reviewing this...).
Show 25 quoted lines
> This also means that we don't have to worry about breaking anybody who > was putting pre-stripped names into their namespace symrefs when we fix > bug (2). Because of bug (1), nobody would have been using the symref we > advertised in the first place (not to mention that those symrefs would > have appeared broken for any non-namespaced access). > > Note that we have separate fixes here for the v0 and v2 protocols. The > symref advertisement moved in v2 to be a part of the ls-refs command. > This actually gets part (1) right, since the symref annotation > piggy-backs on the existing ref advertisement, which is properly > stripped. But it still needs a fix for part (2). The included tests > cover both protocols. > > Reported-by: Bryan Turner <bturner@atlassian.com> > Signed-off-by: Jeff King <peff@peff.net> > --- > This is the same as my earlier fix, but with the v2 bit added, and of > course tests and a commit message. Thanks (as usual) for a helpful bug > report. > > I don't know if we have a general philosophy for testing v0 versus v2. > Without specifying the protocol at all, we'd catch the former on a > regular run and the latter under a GIT_TEST_PROTOCOL_VERSION=2 run. So > we _could_ just rely on that, but since I had to do two separate fixes, > it made sense to me to include explicit tests.
Yeah, we shouldn't rely on the GIT_TEST_* stuff for coverage, it should just be used to find things that fall between the cracks.
Show 70 quoted lines
> ls-refs.c | 3 ++-
> t/t5509-fetch-push-namespaces.sh | 28 ++++++++++++++++++++++++++++
> upload-pack.c | 4 ++--
> 3 files changed, 32 insertions(+), 3 deletions(-)
>
> diff --git a/ls-refs.c b/ls-refs.c
> index 0a7dbc6442..818aef70a0 100644
> --- a/ls-refs.c
> +++ b/ls-refs.c
> @@ -57,7 +57,8 @@ static int send_ref(const char *refname, const struct object_id *oid,
> if (!symref_target)
> die("'%s' is a symref but it is not?", refname);
>
> - strbuf_addf(&refline, " symref-target:%s", symref_target);
> + strbuf_addf(&refline, " symref-target:%s",
> + strip_namespace(symref_target));
> }
>
> if (data->peel) {
> diff --git a/t/t5509-fetch-push-namespaces.sh b/t/t5509-fetch-push-namespaces.sh
> index c88df78c0b..75cbfcc392 100755
> --- a/t/t5509-fetch-push-namespaces.sh
> +++ b/t/t5509-fetch-push-namespaces.sh
> @@ -124,4 +124,32 @@ test_expect_success 'try to update a hidden full ref' '
> test_must_fail git -C original push pushee-namespaced master
> '
>
> +test_expect_success 'set up ambiguous HEAD' '
> + git init ambiguous &&
> + (
> + cd ambiguous &&
> + git commit --allow-empty -m foo &&
> + git update-ref refs/namespaces/ns/refs/heads/one HEAD &&
> + git update-ref refs/namespaces/ns/refs/heads/two HEAD &&
> + git symbolic-ref refs/namespaces/ns/HEAD \
> + refs/namespaces/ns/refs/heads/two
> + )
> +'
> +
> +test_expect_success 'clone chooses correct HEAD (v0)' '
> + GIT_NAMESPACE=ns git -c protocol.version=0 \
> + clone ambiguous ambiguous-v0 &&
> + echo refs/heads/two >expect &&
> + git -C ambiguous-v0 symbolic-ref HEAD >actual &&
> + test_cmp expect actual
> +'
> +
> +test_expect_success 'clone chooses correct HEAD (v2)' '
> + GIT_NAMESPACE=ns git -c protocol.version=2 \
> + clone ambiguous ambiguous-v2 &&
> + echo refs/heads/two >expect &&
> + git -C ambiguous-v2 symbolic-ref HEAD >actual &&
> + test_cmp expect actual
> +'
> +
> test_done
> diff --git a/upload-pack.c b/upload-pack.c
> index 24298913c0..4d2129e7fc 100644
> --- a/upload-pack.c
> +++ b/upload-pack.c
> @@ -1037,8 +1037,8 @@ static int find_symref(const char *refname, const struct object_id *oid,
> symref_target = resolve_ref_unsafe(refname, 0, NULL, &flag);
> if (!symref_target || (flag & REF_ISSYMREF) == 0)
> die("'%s' is a symref but it is not?", refname);
> - item = string_list_append(cb_data, refname);
> - item->util = xstrdup(symref_target);
> + item = string_list_append(cb_data, strip_namespace(refname));
> + item->util = xstrdup(strip_namespace(symref_target));
> return 0;
> }