Message ID | 20190523061121.GB20871@sigill.intra.peff.net (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [v2] upload-pack: strip namespace from symref data | expand |
Jeff King <peff@peff.net> writes: > Revised patch below. Thanks. I never took the "separate namespaces" thing as a serious feature, so it is understandable that I ignored it completely when I did 7171d8c15f. The explanation and the code both look good. Thanks. > -- >8 -- > Subject: [PATCH] upload-pack: strip namespace from symref data > > 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. > > 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 we tend to do the right thing anyway. Bug > (1) means that we said nothing about HEAD (just refs/namespace/foo/HEAD). > And so the client half of the code, from a45b5f0552 (connect: annotate > refs with their symref information in get_remote_head(), 2013-09-17), > does not annotate HEAD, and we use the fallback in guess_remote_head(), > matching refs by object id. Which is usually right. It only falls down > in ambiguous cases, like the one laid out in the included test. > > 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> > --- > 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; > }
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; }