{"thread":{"id":"56182","subject":"[PATCH] upload-pack.c: treat want-ref relative to namespace","startedAt":"2021-07-30T13:59:26Z","lastAt":"2021-08-16T12:40:45Z","messageCount":35,"participants":["Kim Altintop","Junio C Hamano","Jonathan Tan","Eric Sunshine","Jonathan Nieder","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"431570","messageId":"20210730135845.633234-1-kim@eagain.st","threadId":"56182","inReplyTo":null,"subject":"[PATCH] upload-pack.c: treat want-ref relative to namespace","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-07-30T13:59:16Z","receivedAt":"2021-07-30T13:59:26Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"When 'upload-pack' runs within the context of a git namespace, treat any\n'want-ref' lines the client sends as relative to that namespace.\n\nAlso check if the wanted ref is hidden via 'hideRefs', and respond with\nan error otherwise. It was previously possible to request any ref, but\nnote that this is still the case unless 'hideRefs' is in effect.\n\nSigned-off-by: Kim Altintop <kim@eagain.st>\n---\n\nPlease excuse my newbie ness.\n\n t/t5703-upload-pack-ref-in-want.sh | 77 ++++++++++++++++++++++++++++++\n upload-pack.c                      | 15 +++---\n 2 files changed, 86 insertions(+), 6 deletions(-)\n\ndiff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh\nindex e9e471621d..9fb16848bc 100755\n--- a/t/t5703-upload-pack-ref-in-want.sh\n+++ b/t/t5703-upload-pack-ref-in-want.sh\n@@ -298,6 +298,83 @@ test_expect_success 'fetching with wildcard that matches multiple refs' '\n \tgrep \"want-ref refs/heads/o/bar\" log\n '\n\n+REPO=\"$(pwd)/repo-ns\"\n+\n+test_expect_success 'setup namespaced repo' '\n+\t(\n+\t\tgit init -b main \"$REPO\" &&\n+\t\tcd \"$REPO\" &&\n+\t\ttest_commit a &&\n+\t\ttest_commit b &&\n+\t\tgit checkout a &&\n+\t\ttest_commit c &&\n+\t\tgit checkout a &&\n+\t\ttest_commit d &&\n+\t\tgit update-ref refs/heads/ns-no b &&\n+\t\tgit update-ref refs/namespaces/ns/refs/heads/ns-yes c &&\n+\t\tgit update-ref refs/namespaces/ns/refs/heads/hidden d\n+\t) &&\n+    git -C \"$REPO\" config uploadpack.allowRefInWant true &&\n+    git -C \"$REPO\" config transfer.hideRefs refs/heads/hidden\n+'\n+\n+test_expect_success 'want-ref with namespaces' '\n+\toid=$(git -C \"$REPO\" rev-parse c) &&\n+\tcat >expected_refs <<-EOF &&\n+\t$oid refs/heads/ns-yes\n+\tEOF\n+\t>expected_commits &&\n+\n+\toid=$(git -C \"$REPO\" rev-parse c) &&\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_command fetch)\n+\t0001\n+\tno-progress\n+\twant-ref refs/heads/ns-yes\n+\thave $oid\n+\tdone\n+\t0000\n+\tEOF\n+\n+\tGIT_NAMESPACE=ns && export GIT_NAMESPACE &&\n+\ttest-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tcheck_output\n+'\n+\n+test_expect_success 'want-ref outside namespace' '\n+\toid=$(git -C \"$REPO\" rev-parse c) &&\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_command fetch)\n+\t0001\n+\tno-progress\n+\twant-ref refs/heads/ns-no\n+\thave $oid\n+\tdone\n+\t0000\n+\tEOF\n+\n+\tGIT_NAMESPACE=ns && export GIT_NAMESPACE &&\n+\ttest_must_fail test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tgrep \"unknown ref\" out\n+'\n+\n+test_expect_success 'hideRefs with namespaces' '\n+\toid=$(git -C \"$REPO\" rev-parse c) &&\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_command fetch)\n+\t0001\n+\tno-progress\n+\twant-ref refs/heads/hidden\n+\thave $oid\n+\tdone\n+\t0000\n+\tEOF\n+\n+\tGIT_NAMESPACE=ns && export GIT_NAMESPACE &&\n+\ttest_must_fail test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tgrep \"unknown ref\" out\n+'\n+\n . \"$TEST_DIRECTORY\"/lib-httpd.sh\n start_httpd\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 297b76fcb4..008ac75125 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -1417,21 +1417,24 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,\n \t\t\t  struct string_list *wanted_refs,\n \t\t\t  struct object_array *want_obj)\n {\n-\tconst char *arg;\n+\tconst char *refname_nons;\n \tif (skip_prefix(line, \"want-ref \", &arg)) {\n \t\tstruct object_id oid;\n \t\tstruct string_list_item *item;\n \t\tstruct object *o;\n+\t\tstruct strbuf refname = STRBUF_INIT;\n\n-\t\tif (read_ref(arg, &oid)) {\n-\t\t\tpacket_writer_error(writer, \"unknown ref %s\", arg);\n-\t\t\tdie(\"unknown ref %s\", arg);\n+\t\tstrbuf_addf(&refname, \"%s%s\", get_git_namespace(), refname_nons);\n+\t\tif (ref_is_hidden(refname_nons, refname.buf) ||\n+\t\t    read_ref(refname.buf, &oid)) {\n+\t\t\tpacket_writer_error(writer, \"unknown ref %s\", refname_nons);\n+\t\t\tdie(\"unknown ref %s\", refname.buf);\n \t\t}\n\n-\t\titem = string_list_append(wanted_refs, arg);\n+\t\titem = string_list_append(wanted_refs, refname_nons);\n \t\titem->util = oiddup(&oid);\n\n-\t\to = parse_object_or_die(&oid, arg);\n+\t\to = parse_object_or_die(&oid, refname);\n \t\tif (!(o->flags & WANTED)) {\n \t\t\to->flags |= WANTED;\n \t\t\tadd_object_array(o, NULL, want_obj);\n--\n2.32.0\n\n\n"},{"id":"431571","messageId":"CD6J50083UWZ.QYE3HJQ3D3B4@schmidt","threadId":"56182","inReplyTo":"20210730135845.633234-1-kim@eagain.st","subject":"Re: [PATCH] upload-pack.c: treat want-ref relative to namespace","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-07-30T14:04:26Z","receivedAt":"2021-07-30T14:04:52Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"> Please excuse my newbie ness.\n\nOops I'm sorry. `send-email` remembered this from a dry run.\n\nJonathan: I took the test code from your original patch introducing ref-in-want,\nbut modified it substantially. Let me know if it is conventional to credit you\nanyway, and by which trailer.\n\n"},{"id":"431597","messageId":"xmqqbl6j1vgh.fsf@gitster.g","threadId":"56182","inReplyTo":"20210730135845.633234-1-kim@eagain.st","subject":"Re: [PATCH] upload-pack.c: treat want-ref relative to namespace","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-07-30T18:57:02Z","receivedAt":"2021-07-30T18:57:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Kim Altintop <kim@eagain.st> writes:\n\n> When 'upload-pack' runs within the context of a git namespace, treat any\n> 'want-ref' lines the client sends as relative to that namespace.\n>\n> Also check if the wanted ref is hidden via 'hideRefs', and respond with\n> an error otherwise. It was previously possible to request any ref, but\n> note that this is still the case unless 'hideRefs' is in effect.\n>\n> Signed-off-by: Kim Altintop <kim@eagain.st>\n> ---\n\nNicely described.  I have a question on the last sentence, though.\nDo you mean that any ref can be requested when a namespace is in\nuse, as long as 'hideRefs' is not in effect?  What does \"any ref\"\nexactly mean---even thouse outside the given namespace (and if so\nhow?)  I wonder if the last sentence is making the description more\nconfusing without adding any clarity.  In other words, would this\nwork as a replacement for the second paragraph, or does it say\nsomething different from what you wanted to say?\n\n    Requests for any ref, even those that are marked to be hidden\n    via the 'transfer.hideRefs' configuration, were allowed but it\n    is problematic for such and such reasons.  Respond with an error\n    if a requested ref is to be hidden.\n\nI couldn't tell why you thought it was problematic, so left \"for\nsuch and such reasons\" to be filled in, but there still may be an\nissue.\n\nHow does the error response look like?  We shouldn't be saying \"you\nrequested for the hidden/x branch, but you are not allowed to do so,\nas that is hidden\".  To hide something, we should pretend that the\nthing does not exist, so that we can hide even the fact that we are\nhiding it.\n\nTo help future readers of \"git log\" who find this change from you,\nwe should clarify the \"respond with an error\" part of your proposed\nlog message (e.g. \"pretend that the wanted ref does not exist when\nit is hidden via the 'transfer.hiderefs' configuration\" or something\nelse).\n\n> +test_expect_success 'setup namespaced repo' '\n> +\t(\n> +\t\tgit init -b main \"$REPO\" &&\n> +\t\tcd \"$REPO\" &&\n> +\t\ttest_commit a &&\n> +\t\ttest_commit b &&\n> +\t\tgit checkout a &&\n> +\t\ttest_commit c &&\n> +\t\tgit checkout a &&\n> +\t\ttest_commit d &&\n> +\t\tgit update-ref refs/heads/ns-no b &&\n> +\t\tgit update-ref refs/namespaces/ns/refs/heads/ns-yes c &&\n> +\t\tgit update-ref refs/namespaces/ns/refs/heads/hidden d\n> +\t) &&\n> +    git -C \"$REPO\" config uploadpack.allowRefInWant true &&\n> +    git -C \"$REPO\" config transfer.hideRefs refs/heads/hidden\n> +'\n\nI wonder why the last two are outside the subshell?  IOW, you could\nhave configured the newly created repository while you were still in\nthere.\n\n> +test_expect_success 'want-ref with namespaces' '\n> +\toid=$(git -C \"$REPO\" rev-parse c) &&\n> +\tcat >expected_refs <<-EOF &&\n> +\t$oid refs/heads/ns-yes\n> +\tEOF\n> +\t>expected_commits &&\n> +\n> +\toid=$(git -C \"$REPO\" rev-parse c) &&\n> +\ttest-tool pkt-line pack >in <<-EOF &&\n> +\t$(write_command fetch)\n> +\t0001\n> +\tno-progress\n> +\twant-ref refs/heads/ns-yes\n> +\thave $oid\n> +\tdone\n> +\t0000\n> +\tEOF\n> +\n> +\tGIT_NAMESPACE=ns && export GIT_NAMESPACE &&\n> +\ttest-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n> +\tcheck_output\n> +'\n\nUnless you mean to make all subsequent tests to be done inside the\n'ns' namespace, and even when you do, you do not want to do this\nin order to keep each test as independent as possible (iow, make\nsome of them skippable without affecting the later tests).  Run the\nfinal test in a subshell, e.g.\n\n\toid=$(git -C \"$REPO\" rev-parse c) &&\n\ttest-tool pkt-line pack >in <<-EOF &&\n\t...\n\tEOF\n\n\t(\n        \texport GIT_NAMESPACE=ns &&\n\t\ttest-tool ... >out <in\n\t) &&\n\tcheck_output\n\nor if the command you want to run with a custom environment variable\nis a single external executable like this case, do\n\n\toid=$(git -C \"$REPO\" rev-parse c) &&\n\ttest-tool pkt-line pack >in <<-EOF &&\n\t...\n\tEOF\n\tGIT_NAMESPACE=ns test-tool ... >out <in &&\n\tcheck_output\n\nThat way, the environment will be kept clean without GIT_NAMESPACE\noutside the invocation of test-tool.\n\nNote that you cannot use this technique directly with test_must_fail\nwhich is *not* an external executable but is a shell function.\n\n\ttest_must_fail env GIT_NAMESPACE=ns test-tool ...\n\nwould be the way to write a step that must fail.\n\n> diff --git a/upload-pack.c b/upload-pack.c\n> index 297b76fcb4..008ac75125 100644\n> --- a/upload-pack.c\n> +++ b/upload-pack.c\n> @@ -1417,21 +1417,24 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,\n>  \t\t\t  struct string_list *wanted_refs,\n>  \t\t\t  struct object_array *want_obj)\n>  {\n> -\tconst char *arg;\n> +\tconst char *refname_nons;\n>  \tif (skip_prefix(line, \"want-ref \", &arg)) {\n\nDon't you receive the result in refname_nons here, as arg is no\nlonger there?\n\n>  \t\tstruct object_id oid;\n>  \t\tstruct string_list_item *item;\n>  \t\tstruct object *o;\n> +\t\tstruct strbuf refname = STRBUF_INIT;\n>\n> -\t\tif (read_ref(arg, &oid)) {\n> -\t\t\tpacket_writer_error(writer, \"unknown ref %s\", arg);\n> -\t\t\tdie(\"unknown ref %s\", arg);\n> +\t\tstrbuf_addf(&refname, \"%s%s\", get_git_namespace(), refname_nons);\n> +\t\tif (ref_is_hidden(refname_nons, refname.buf) ||\n> +\t\t    read_ref(refname.buf, &oid)) {\n> +\t\t\tpacket_writer_error(writer, \"unknown ref %s\", refname_nons);\n> +\t\t\tdie(\"unknown ref %s\", refname.buf);\n>  \t\t}\n\nOK.  Assuming that it makes sense for the hideRefs mechanism to kick\nin here (which I would prefer to hear from others who've worked with\nthis code, say Jonathan Tan?), the updated code makes sense.\n\nThanks.\n\n\n> -\t\titem = string_list_append(wanted_refs, arg);\n> +\t\titem = string_list_append(wanted_refs, refname_nons);\n>  \t\titem->util = oiddup(&oid);\n>\n> -\t\to = parse_object_or_die(&oid, arg);\n> +\t\to = parse_object_or_die(&oid, refname);\n>  \t\tif (!(o->flags & WANTED)) {\n>  \t\t\to->flags |= WANTED;\n>  \t\t\tadd_object_array(o, NULL, want_obj);\n> --\n> 2.32.0\n"},{"id":"431603","messageId":"CD6S5MUMEIW3.3ULMDKKSQ92UW@schmidt","threadId":"56182","inReplyTo":"xmqqbl6j1vgh.fsf@gitster.g","subject":"Re: [PATCH] upload-pack.c: treat want-ref relative to namespace","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-07-30T21:08:20Z","receivedAt":"2021-07-30T21:08:29Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"On Fri Jul 30, 2021 at 8:57 PM CEST, Junio C Hamano wrote:\n>\n> Kim Altintop <kim@eagain.st> writes:\n[..]\n> > Also check if the wanted ref is hidden via 'hideRefs', and respond with\n> > an error otherwise. It was previously possible to request any ref, but\n> > note that this is still the case unless 'hideRefs' is in effect.\n[..]\n> Nicely described. I have a question on the last sentence, though.\n> Do you mean that any ref can be requested when a namespace is in\n> use, as long as 'hideRefs' is not in effect? What does \"any ref\"\n> exactly mean---even thouse outside the given namespace (and if so\n> how?)\n\nThank you. It seems like I got confused for a moment when writing the commit\nmessage. It's not possible to get a ref outside the namespace anymore. I removed\nthat sentence.\n\n> > +test_expect_success 'setup namespaced repo' '\n> > +\t(\n> > +\t\tgit init -b main \"$REPO\" &&\n> > +\t\tcd \"$REPO\" &&\n> > +\t\ttest_commit a &&\n> > +\t\ttest_commit b &&\n> > +\t\tgit checkout a &&\n> > +\t\ttest_commit c &&\n> > +\t\tgit checkout a &&\n> > +\t\ttest_commit d &&\n> > +\t\tgit update-ref refs/heads/ns-no b &&\n> > +\t\tgit update-ref refs/namespaces/ns/refs/heads/ns-yes c &&\n> > +\t\tgit update-ref refs/namespaces/ns/refs/heads/hidden d\n> > +\t) &&\n> > +    git -C \"$REPO\" config uploadpack.allowRefInWant true &&\n> > +    git -C \"$REPO\" config transfer.hideRefs refs/heads/hidden\n> > +'\n>\n> I wonder why the last two are outside the subshell? IOW, you could\n> have configured the newly created repository while you were still in\n> there.\n\nTo be honest, I don't know. I did that because the other repo setups in that\nfile follow the same pattern, I suppose that qualifies as \"cargo culting\". Happy\nto remove the subshell, unless others point out that there is some specific\nreason for it.\n\n> Unless you mean to make all subsequent tests to be done inside the\n> 'ns' namespace, and even when you do, you do not want to do this\n> in order to keep each test as independent as possible (iow, make\n> some of them skippable without affecting the later tests). Run the\n> final test in a subshell, e.g.\n>\n> oid=$(git -C \"$REPO\" rev-parse c) &&\n> test-tool pkt-line pack >in <<-EOF &&\n> ...\n> EOF\n>\n> (\n> export GIT_NAMESPACE=ns &&\n> test-tool ... >out <in\n> ) &&\n> check_output\n>\n> or if the command you want to run with a custom environment variable\n> is a single external executable like this case, do\n>\n> oid=$(git -C \"$REPO\" rev-parse c) &&\n> test-tool pkt-line pack >in <<-EOF &&\n> ...\n> EOF\n> GIT_NAMESPACE=ns test-tool ... >out <in &&\n> check_output\n>\n> That way, the environment will be kept clean without GIT_NAMESPACE\n> outside the invocation of test-tool.\n>\n> Note that you cannot use this technique directly with test_must_fail\n> which is *not* an external executable but is a shell function.\n>\n> test_must_fail env GIT_NAMESPACE=ns test-tool ...\n>\n> would be the way to write a step that must fail.\n\n\nAh thanks! I had tried\n\n...\nGIT_NAMESPACE=ns test-tool ... >out <in\n\n\nbut the linter complained about this without giving a hint as to what the fix\nwould be. It turns out that \"env\" works, ie.\n\n...\nenv GIT_NAMESPACE=ns test-tool ...\ntest_must_fail env GIT_NAMESPACE=ns test-tool ...\n\n\n>\n> > diff --git a/upload-pack.c b/upload-pack.c\n> > index 297b76fcb4..008ac75125 100644\n> > --- a/upload-pack.c\n> > +++ b/upload-pack.c\n> > @@ -1417,21 +1417,24 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,\n> >  \t\t\t  struct string_list *wanted_refs,\n> >  \t\t\t  struct object_array *want_obj)\n> >  {\n> > -\tconst char *arg;\n> > +\tconst char *refname_nons;\n> >  \tif (skip_prefix(line, \"want-ref \", &arg)) {\n>\n> Don't you receive the result in refname_nons here, as arg is no\n> longer there?\n\nOuch. Will fix.\n\n>\n> >  \t\tstruct object_id oid;\n> >  \t\tstruct string_list_item *item;\n> >  \t\tstruct object *o;\n> > +\t\tstruct strbuf refname = STRBUF_INIT;\n> >\n> > -\t\tif (read_ref(arg, &oid)) {\n> > -\t\t\tpacket_writer_error(writer, \"unknown ref %s\", arg);\n> > -\t\t\tdie(\"unknown ref %s\", arg);\n> > +\t\tstrbuf_addf(&refname, \"%s%s\", get_git_namespace(), refname_nons);\n> > +\t\tif (ref_is_hidden(refname_nons, refname.buf) ||\n> > +\t\t    read_ref(refname.buf, &oid)) {\n> > +\t\t\tpacket_writer_error(writer, \"unknown ref %s\", refname_nons);\n> > +\t\t\tdie(\"unknown ref %s\", refname.buf);\n> >  \t\t}\n>\n> OK. Assuming that it makes sense for the hideRefs mechanism to kick\n> in here (which I would prefer to hear from others who've worked with\n> this code, say Jonathan Tan?), the updated code makes sense.\n\nI have also updated the code for the v2 to use refname_nons for any die() calls,\nas I realised that this may be transmitted to the client via sideband (is that\ncorrect?).\n\n"},{"id":"431640","messageId":"20210731203415.618641-1-kim@eagain.st","threadId":"56182","inReplyTo":"20210730135845.633234-1-kim@eagain.st","subject":"[PATCH v2] upload-pack.c: treat want-ref relative to namespace","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-07-31T20:36:59Z","receivedAt":"2021-07-31T20:37:10Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"When 'upload-pack' runs within the context of a git namespace, treat any\n'want-ref' lines the client sends as relative to that namespace.\n\nAlso check if the wanted ref is hidden via 'hideRefs'. If it is hidden,\nrespond with an error as if the ref didn't exist.\n\nSigned-off-by: Kim Altintop <kim@eagain.st>\n---\n\nChanges from v1:\n\n  * Amend commit message\n  * upload-pack.c: fix variable renaming (how could this even work?)\n  * upload-pack.c: hide namespace in all output, including die()\n  * t5703: don't use subshell in repo setup\n  * t5703: use \"env\" keyword to correctly scope GIT_NAMESPACE\n\n\n t/t5703-upload-pack-ref-in-want.sh | 72 ++++++++++++++++++++++++++++++\n upload-pack.c                      | 17 ++++---\n 2 files changed, 82 insertions(+), 7 deletions(-)\n\ndiff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh\nindex e9e471621d..96df3073d1 100755\n--- a/t/t5703-upload-pack-ref-in-want.sh\n+++ b/t/t5703-upload-pack-ref-in-want.sh\n@@ -298,6 +298,78 @@ test_expect_success 'fetching with wildcard that matches multiple refs' '\n \tgrep \"want-ref refs/heads/o/bar\" log\n '\n\n+REPO=\"$(pwd)/repo-ns\"\n+\n+test_expect_success 'setup namespaced repo' '\n+\tgit init -b main \"$REPO\" &&\n+\tcd \"$REPO\" &&\n+\ttest_commit a &&\n+\ttest_commit b &&\n+\tgit checkout a &&\n+\ttest_commit c &&\n+\tgit checkout a &&\n+\ttest_commit d &&\n+\tgit update-ref refs/heads/ns-no b &&\n+\tgit update-ref refs/namespaces/ns/refs/heads/ns-yes c &&\n+\tgit update-ref refs/namespaces/ns/refs/heads/hidden d &&\n+\tgit -C \"$REPO\" config uploadpack.allowRefInWant true &&\n+\tgit -C \"$REPO\" config transfer.hideRefs refs/heads/hidden\n+'\n+\n+test_expect_success 'want-ref with namespaces' '\n+\toid=$(git -C \"$REPO\" rev-parse c) &&\n+\tcat >expected_refs <<-EOF &&\n+\t$oid refs/heads/ns-yes\n+\tEOF\n+\t>expected_commits &&\n+\n+\toid=$(git -C \"$REPO\" rev-parse c) &&\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_command fetch)\n+\t0001\n+\tno-progress\n+\twant-ref refs/heads/ns-yes\n+\thave $oid\n+\tdone\n+\t0000\n+\tEOF\n+\n+\tenv GIT_NAMESPACE=ns test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tcheck_output\n+'\n+\n+test_expect_success 'want-ref outside namespace' '\n+\toid=$(git -C \"$REPO\" rev-parse c) &&\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_command fetch)\n+\t0001\n+\tno-progress\n+\twant-ref refs/heads/ns-no\n+\thave $oid\n+\tdone\n+\t0000\n+\tEOF\n+\n+\ttest_must_fail env GIT_NAMESPACE=ns test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tgrep \"unknown ref\" out\n+'\n+\n+test_expect_success 'hideRefs with namespaces' '\n+\toid=$(git -C \"$REPO\" rev-parse c) &&\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_command fetch)\n+\t0001\n+\tno-progress\n+\twant-ref refs/heads/hidden\n+\thave $oid\n+\tdone\n+\t0000\n+\tEOF\n+\n+\ttest_must_fail env GIT_NAMESPACE=ns test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tgrep \"unknown ref\" out\n+'\n+\n . \"$TEST_DIRECTORY\"/lib-httpd.sh\n start_httpd\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 297b76fcb4..c897802f1c 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -1417,21 +1417,24 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,\n \t\t\t  struct string_list *wanted_refs,\n \t\t\t  struct object_array *want_obj)\n {\n-\tconst char *arg;\n-\tif (skip_prefix(line, \"want-ref \", &arg)) {\n+\tconst char *refname_nons;\n+\tif (skip_prefix(line, \"want-ref \", &refname_nons)) {\n \t\tstruct object_id oid;\n \t\tstruct string_list_item *item;\n \t\tstruct object *o;\n+\t\tstruct strbuf refname = STRBUF_INIT;\n\n-\t\tif (read_ref(arg, &oid)) {\n-\t\t\tpacket_writer_error(writer, \"unknown ref %s\", arg);\n-\t\t\tdie(\"unknown ref %s\", arg);\n+\t\tstrbuf_addf(&refname, \"%s%s\", get_git_namespace(), refname_nons);\n+\t\tif (ref_is_hidden(refname_nons, refname.buf) ||\n+\t\t    read_ref(refname.buf, &oid)) {\n+\t\t\tpacket_writer_error(writer, \"unknown ref %s\", refname_nons);\n+\t\t\tdie(\"unknown ref %s\", refname_nons);\n \t\t}\n\n-\t\titem = string_list_append(wanted_refs, arg);\n+\t\titem = string_list_append(wanted_refs, refname_nons);\n \t\titem->util = oiddup(&oid);\n\n-\t\to = parse_object_or_die(&oid, arg);\n+\t\to = parse_object_or_die(&oid, refname_nons);\n \t\tif (!(o->flags & WANTED)) {\n \t\t\to->flags |= WANTED;\n \t\t\tadd_object_array(o, NULL, want_obj);\n--\n2.32.0\n\n\n"},{"id":"431759","messageId":"20210802210644.3432544-1-jonathantanmy@google.com","threadId":"56182","inReplyTo":"20210731203415.618641-1-kim@eagain.st","subject":"Re: [PATCH v2] upload-pack.c: treat want-ref relative to namespace","fromName":"Jonathan Tan","fromEmail":"jonathantanmy@google.com","sentAt":"2021-08-02T21:06:44Z","receivedAt":"2021-08-02T21:06:52Z","isPatch":true,"sender":{"key":"jonathantanmy@fastmail.com","avatar":null},"body":"> diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh\n> index e9e471621d..96df3073d1 100755\n> --- a/t/t5703-upload-pack-ref-in-want.sh\n> +++ b/t/t5703-upload-pack-ref-in-want.sh\n> @@ -298,6 +298,78 @@ test_expect_success 'fetching with wildcard that matches multiple refs' '\n>  \tgrep \"want-ref refs/heads/o/bar\" log\n>  '\n> \n> +REPO=\"$(pwd)/repo-ns\"\n> +\n> +test_expect_success 'setup namespaced repo' '\n> +\tgit init -b main \"$REPO\" &&\n> +\tcd \"$REPO\" &&\n> +\ttest_commit a &&\n> +\ttest_commit b &&\n> +\tgit checkout a &&\n> +\ttest_commit c &&\n> +\tgit checkout a &&\n> +\ttest_commit d &&\n> +\tgit update-ref refs/heads/ns-no b &&\n> +\tgit update-ref refs/namespaces/ns/refs/heads/ns-yes c &&\n> +\tgit update-ref refs/namespaces/ns/refs/heads/hidden d &&\n> +\tgit -C \"$REPO\" config uploadpack.allowRefInWant true &&\n> +\tgit -C \"$REPO\" config transfer.hideRefs refs/heads/hidden\n> +'\n\nIf you're not using a subshell to set up the repo, you should add '-C\n\"$REPO\"' to all the \"git\" commands (like you do in the last 2 lines)\ninstead of \"cd\"-ing halfway through the test. The helper function\ntest_commit also has that facility ('test_commit -C \"$REPO\" a', for\nexample).\n\n> +test_expect_success 'want-ref with namespaces' '\n> +\toid=$(git -C \"$REPO\" rev-parse c) &&\n> +\tcat >expected_refs <<-EOF &&\n> +\t$oid refs/heads/ns-yes\n> +\tEOF\n> +\t>expected_commits &&\n> +\n> +\toid=$(git -C \"$REPO\" rev-parse c) &&\n> +\ttest-tool pkt-line pack >in <<-EOF &&\n> +\t$(write_command fetch)\n> +\t0001\n> +\tno-progress\n> +\twant-ref refs/heads/ns-yes\n> +\thave $oid\n\nDo we need this \"have\" line? I think we can just omit it, since what the\nclient has is irrelevant to the test. (Same for the other tests.)\n\n> +\tdone\n> +\t0000\n> +\tEOF\n> +\n> +\tenv GIT_NAMESPACE=ns test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n> +\tcheck_output\n> +'\n> +\n> +test_expect_success 'want-ref outside namespace' '\n> +\toid=$(git -C \"$REPO\" rev-parse c) &&\n> +\ttest-tool pkt-line pack >in <<-EOF &&\n> +\t$(write_command fetch)\n> +\t0001\n> +\tno-progress\n> +\twant-ref refs/heads/ns-no\n> +\thave $oid\n> +\tdone\n> +\t0000\n> +\tEOF\n> +\n> +\ttest_must_fail env GIT_NAMESPACE=ns test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n> +\tgrep \"unknown ref\" out\n> +'\n\nFor the failure tests, it's safer to write them in pairs - one that\nsucceeds and one that fails. Here, a typo in \"ns-no\" (e.g. if I wrote\n\"ns-noo\" instead) would cause the exact same result, but if we were to\nwrite a pair of tests, we wouldn't have this problem.\n\nTo do this, you can bundle the same code into a function and call them\nfrom both tests. E.g.:\n\n  setup_want_ns_no () {\n    (common code)\n  }\n\n  test_expect_success 'want-ref without namespace works...' '\n    setup_want_ref_outside_namespace &&\n    test-tool ... &&\n    check_output\n  '\n\n  test_expect_success '...but, with namespace, does not work' '\n    setup_want_ref_outside_namespace &&\n    test_must_fail env GIT_NAMESPACE=ns ... &&\n    grep \"unknown ref\" out\n  '\n\nThe first test_expect_success does seem redundant, but I can't think of\na better way to ensure that the helper function (setup_want_ns_no in\nthis case) is written correctly.\n\n> +test_expect_success 'hideRefs with namespaces' '\n> +\toid=$(git -C \"$REPO\" rev-parse c) &&\n> +\ttest-tool pkt-line pack >in <<-EOF &&\n> +\t$(write_command fetch)\n> +\t0001\n> +\tno-progress\n> +\twant-ref refs/heads/hidden\n> +\thave $oid\n> +\tdone\n> +\t0000\n> +\tEOF\n> +\n> +\ttest_must_fail env GIT_NAMESPACE=ns test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n> +\tgrep \"unknown ref\" out\n> +'\n> +\n\nSame for this.\n\n> diff --git a/upload-pack.c b/upload-pack.c\n> index 297b76fcb4..c897802f1c 100644\n> --- a/upload-pack.c\n> +++ b/upload-pack.c\n> @@ -1417,21 +1417,24 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,\n>  \t\t\t  struct string_list *wanted_refs,\n>  \t\t\t  struct object_array *want_obj)\n>  {\n> -\tconst char *arg;\n> -\tif (skip_prefix(line, \"want-ref \", &arg)) {\n> +\tconst char *refname_nons;\n> +\tif (skip_prefix(line, \"want-ref \", &refname_nons)) {\n>  \t\tstruct object_id oid;\n>  \t\tstruct string_list_item *item;\n>  \t\tstruct object *o;\n> +\t\tstruct strbuf refname = STRBUF_INIT;\n\n\"refname\" needs to be released somewhere.\n\n> \n> -\t\tif (read_ref(arg, &oid)) {\n> -\t\t\tpacket_writer_error(writer, \"unknown ref %s\", arg);\n> -\t\t\tdie(\"unknown ref %s\", arg);\n> +\t\tstrbuf_addf(&refname, \"%s%s\", get_git_namespace(), refname_nons);\n> +\t\tif (ref_is_hidden(refname_nons, refname.buf) ||\n> +\t\t    read_ref(refname.buf, &oid)) {\n> +\t\t\tpacket_writer_error(writer, \"unknown ref %s\", refname_nons);\n> +\t\t\tdie(\"unknown ref %s\", refname_nons);\n>  \t\t}\n> \n> -\t\titem = string_list_append(wanted_refs, arg);\n> +\t\titem = string_list_append(wanted_refs, refname_nons);\n>  \t\titem->util = oiddup(&oid);\n> \n> -\t\to = parse_object_or_die(&oid, arg);\n> +\t\to = parse_object_or_die(&oid, refname_nons);\n>  \t\tif (!(o->flags & WANTED)) {\n>  \t\t\to->flags |= WANTED;\n>  \t\t\tadd_object_array(o, NULL, want_obj);\n\nBesides my comments about the tests, I think that this patch looks good.\nJunio had a relevant question [1]:\n\n> OK.  Assuming that it makes sense for the hideRefs mechanism to kick\n> in here (which I would prefer to hear from others who've worked with\n> this code, say Jonathan Tan?), the updated code makes sense.\n\nI think it makes sense here. I checked my old code that Kim linked [2],\nand in that patch I put it somewhere else (specifically, the part that\nprints out the \"wanted-ref\" lines), but it makes sense that it is\ndifferent. In my version, \"want-ref\" supports globs and is allowed to\nmatch 0 refs, but in Brandon's final version, \"want-ref\" does not\nsupport globs and must match the ref, so checking it upon parse (as is\ndone in this commit) is the most reasonable.\n\nQuestions from Kim:\n\n> Jonathan: I took the test code from your original patch introducing ref-in-want,\n> but modified it substantially. Let me know if it is conventional to credit you\n> anyway, and by which trailer.\n\nI don't think there's a convention, but you can use the \"Helped-by:\"\ntrailer if you want.\n\n> I have also updated the code for the v2 to use refname_nons for any die() calls,\n> as I realised that this may be transmitted to the client via sideband (is that\n> correct?).\n\nI believe that only packet_writer_error() output is sent via sideband,\nbut it still makes sense for the server-side output to print out the ref\nas read from the client verbatim.\n\n[1] https://lore.kernel.org/git/xmqqbl6j1vgh.fsf@gitster.g/\n[2] https://lore.kernel.org/git/d0d42b3bb4cf755f122591e191354c53848f197d.1485381677.git.jonathantanmy@google.com/\n"},{"id":"431976","messageId":"CDB0M5IP0WNJ.3I09ZETKF0VXS@schmidt","threadId":"56182","inReplyTo":"20210802210644.3432544-1-jonathantanmy@google.com","subject":"Re: [PATCH v2] upload-pack.c: treat want-ref relative to namespace","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-08-04T20:36:48Z","receivedAt":"2021-08-04T20:37:30Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"On Mon Aug 2, 2021 at 11:06 PM CEST, Jonathan Tan wrote:\n> > +test_expect_success 'setup namespaced repo' '\n> > +\tgit init -b main \"$REPO\" &&\n> > +\tcd \"$REPO\" &&\n> > +\ttest_commit a &&\n> > +\ttest_commit b &&\n> > +\tgit checkout a &&\n> > +\ttest_commit c &&\n> > +\tgit checkout a &&\n> > +\ttest_commit d &&\n> > +\tgit update-ref refs/heads/ns-no b &&\n> > +\tgit update-ref refs/namespaces/ns/refs/heads/ns-yes c &&\n> > +\tgit update-ref refs/namespaces/ns/refs/heads/hidden d &&\n> > +\tgit -C \"$REPO\" config uploadpack.allowRefInWant true &&\n> > +\tgit -C \"$REPO\" config transfer.hideRefs refs/heads/hidden\n> > +'\n>\n> If you're not using a subshell to set up the repo, you should add '-C\n> \"$REPO\"' to all the \"git\" commands (like you do in the last 2 lines)\n> instead of \"cd\"-ing halfway through the test. The helper function\n> test_commit also has that facility ('test_commit -C \"$REPO\" a', for\n> example).\n\nAh, that answers the question raised by Junio in the first review. I'll revert\nto using a subshell, as that seems clearer and is used throughout the file.\n\n> > +test_expect_success 'want-ref with namespaces' '\n> > +\toid=$(git -C \"$REPO\" rev-parse c) &&\n> > +\tcat >expected_refs <<-EOF &&\n> > +\t$oid refs/heads/ns-yes\n> > +\tEOF\n> > +\t>expected_commits &&\n> > +\n> > +\toid=$(git -C \"$REPO\" rev-parse c) &&\n> > +\ttest-tool pkt-line pack >in <<-EOF &&\n> > +\t$(write_command fetch)\n> > +\t0001\n> > +\tno-progress\n> > +\twant-ref refs/heads/ns-yes\n> > +\thave $oid\n>\n> Do we need this \"have\" line? I think we can just omit it, since what the\n> client has is irrelevant to the test. (Same for the other tests.)\n\nHm no. I think I misread how this works.\n\n> > +test_expect_success 'want-ref outside namespace' '\n> > +\toid=$(git -C \"$REPO\" rev-parse c) &&\n> > +\ttest-tool pkt-line pack >in <<-EOF &&\n> > +\t$(write_command fetch)\n> > +\t0001\n> > +\tno-progress\n> > +\twant-ref refs/heads/ns-no\n> > +\thave $oid\n> > +\tdone\n> > +\t0000\n> > +\tEOF\n> > +\n> > +\ttest_must_fail env GIT_NAMESPACE=ns test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n> > +\tgrep \"unknown ref\" out\n> > +'\n>\n> For the failure tests, it's safer to write them in pairs - one that\n> succeeds and one that fails. Here, a typo in \"ns-no\" (e.g. if I wrote\n> \"ns-noo\" instead) would cause the exact same result, but if we were to\n> write a pair of tests, we wouldn't have this problem.\n\nThat's a good suggestion. However, I'm having some difficulties finding just the\nright amount of common code to extract due to\n\na. having to pass the namespace via env instead of --namespace (and the\n   different position for the success / test_must_fail cases), and\nb. having to rely on _persistent_ config for hideRefs, as opposed to being able\n   to pass it via -c to upload-pack (ie. I'm worried about tests breaking if\n   they get reordered)\n\nSo I'm not exactly happy with what I came up with for v3, but I'd also be\nreluctant to add --namespace / -c support to test-tool as part of this patch.\nLet me know what you think.\n\n\n> > diff --git a/upload-pack.c b/upload-pack.c\n> > index 297b76fcb4..c897802f1c 100644\n> > --- a/upload-pack.c\n> > +++ b/upload-pack.c\n> > @@ -1417,21 +1417,24 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,\n> >  \t\t\t  struct string_list *wanted_refs,\n> >  \t\t\t  struct object_array *want_obj)\n> >  {\n> > -\tconst char *arg;\n> > -\tif (skip_prefix(line, \"want-ref \", &arg)) {\n> > +\tconst char *refname_nons;\n> > +\tif (skip_prefix(line, \"want-ref \", &refname_nons)) {\n> >  \t\tstruct object_id oid;\n> >  \t\tstruct string_list_item *item;\n> >  \t\tstruct object *o;\n> > +\t\tstruct strbuf refname = STRBUF_INIT;\n>\n> \"refname\" needs to be released somewhere.\n\nYeap :/\n\n"},{"id":"431978","messageId":"20210804203829.661565-1-kim@eagain.st","threadId":"56182","inReplyTo":"20210731203415.618641-1-kim@eagain.st","subject":"[PATCH v3] upload-pack.c: treat want-ref relative to namespace","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-08-04T20:42:42Z","receivedAt":"2021-08-04T20:43:03Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"When 'upload-pack' runs within the context of a git namespace, treat any\n'want-ref' lines the client sends as relative to that namespace.\n\nAlso check if the wanted ref is hidden via 'hideRefs'. If it is hidden,\nrespond with an error as if the ref didn't exist.\n\nHelped-by: Jonathan Tan <jonathantanmy@google.com>\nSigned-off-by: Kim Altintop <kim@eagain.st>\n---\n\nChanges from v2:\n\n    * upload-pack.c: release strbuf\n    * t5730: revert to scoping to $REPO via subshell in setup\n    * t5730: add \"cross-check\" tests as per review comments from Jonathan\n\n t/t5703-upload-pack-ref-in-want.sh | 128 +++++++++++++++++++++++++++++\n upload-pack.c                      |  18 ++--\n 2 files changed, 139 insertions(+), 7 deletions(-)\n\ndiff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh\nindex e9e471621d..4a828042bb 100755\n--- a/t/t5703-upload-pack-ref-in-want.sh\n+++ b/t/t5703-upload-pack-ref-in-want.sh\n@@ -298,6 +298,134 @@ test_expect_success 'fetching with wildcard that matches multiple refs' '\n \tgrep \"want-ref refs/heads/o/bar\" log\n '\n\n+REPO=\"$(pwd)/repo-ns\"\n+\n+write_fetch_want_ref() {\n+\tlocal wanted_ref=\"$1\"\n+\n+\twrite_command fetch\n+\techo \"0001\"\n+\techo \"no-progress\"\n+\techo \"want-ref $1\"\n+\techo \"done\"\n+\techo \"0000\"\n+}\n+\n+test_expect_success 'setup namespaced repo' '\n+\t(\n+\t\tgit init -b main \"$REPO\" &&\n+\t\tcd \"$REPO\" &&\n+\t\ttest_commit a &&\n+\t\ttest_commit b &&\n+\t\tgit checkout a &&\n+\t\ttest_commit c &&\n+\t\tgit checkout a &&\n+\t\ttest_commit d &&\n+\t\tgit update-ref refs/heads/ns-no b &&\n+\t\tgit update-ref refs/namespaces/ns/refs/heads/ns-yes c &&\n+\t\tgit update-ref refs/namespaces/ns/refs/heads/hidden d\n+\t) &&\n+\tgit -C \"$REPO\" config uploadpack.allowRefInWant true\n+'\n+\n+test_expect_success 'with namespace: want-ref is considered relative to namespace' '\n+\twanted_ref=refs/heads/ns-yes &&\n+\n+\toid=$(git -C \"$REPO\" rev-parse $wanted_ref) &&\n+\tcat >expected_refs <<-EOF &&\n+\t$oid $wanted_ref\n+\tEOF\n+\tgit -C \"$REPO\" rev-parse a $wanted_ref >expected_commits &&\n+\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_fetch_want_ref $wanted_ref)\n+\tEOF\n+\n+\tenv GIT_NAMESPACE=ns test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tcheck_output\n+'\n+\n+test_expect_success 'with namespace: want-ref outside namespace is unknown' '\n+\twanted_ref=refs/heads/ns-no &&\n+\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_fetch_want_ref $wanted_ref)\n+\tEOF\n+\n+\ttest_must_fail env GIT_NAMESPACE=ns \\\n+\t\ttest-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tgrep \"unknown ref\" out\n+'\n+\n+# Cross-check refs/heads/ns-no indeed exists\n+test_expect_success 'without namespace: want-ref outside namespace succeeds' '\n+\twanted_ref=refs/heads/ns-no &&\n+\n+\toid=$(git -C \"$REPO\" rev-parse $wanted_ref) &&\n+\tcat >expected_refs <<-EOF &&\n+\t$oid $wanted_ref\n+\tEOF\n+\tgit -C \"$REPO\" rev-parse a $wanted_ref >expected_commits &&\n+\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_fetch_want_ref $wanted_ref)\n+\tEOF\n+\n+\ttest-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tcheck_output\n+'\n+\n+test_expect_success 'with namespace: hideRefs is matched, relative to namespace' '\n+\twanted_ref=refs/heads/hidden &&\n+\tgit -C \"$REPO\" config transfer.hideRefs $wanted_ref &&\n+\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_fetch_want_ref $wanted_ref)\n+\tEOF\n+\n+\ttest_must_fail env GIT_NAMESPACE=ns \\\n+\t\ttest-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tgrep \"unknown ref\" out\n+'\n+\n+# Cross-check refs/heads/hidden indeed exists\n+test_expect_success 'with namespace: want-ref succeeds if hideRefs is removed' '\n+\twanted_ref=refs/heads/hidden &&\n+\tgit -C \"$REPO\" config --unset transfer.hideRefs $wanted_ref &&\n+\n+\toid=$(git -C \"$REPO\" rev-parse $wanted_ref) &&\n+\tcat >expected_refs <<-EOF &&\n+\t$oid $wanted_ref\n+\tEOF\n+\tgit -C \"$REPO\" rev-parse a $wanted_ref >expected_commits &&\n+\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_fetch_want_ref $wanted_ref)\n+\tEOF\n+\n+\tenv GIT_NAMESPACE=ns test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tcheck_output\n+'\n+\n+test_expect_success 'without namespace: relative hideRefs does not match' '\n+\twanted_ref=refs/namespaces/ns/refs/heads/hidden &&\n+\tgit -C \"$REPO\" config transfer.hideRefs refs/heads/hidden &&\n+\n+\toid=$(git -C \"$REPO\" rev-parse $wanted_ref) &&\n+\tcat >expected_refs <<-EOF &&\n+\t$oid $wanted_ref\n+\tEOF\n+\tgit -C \"$REPO\" rev-parse a $wanted_ref >expected_commits &&\n+\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_fetch_want_ref $wanted_ref)\n+\tEOF\n+\n+\tenv GIT_NAMESPACE=ns test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tcheck_output\n+'\n+\n+\n . \"$TEST_DIRECTORY\"/lib-httpd.sh\n start_httpd\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 297b76fcb4..6ce07231d3 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -1417,21 +1417,25 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,\n \t\t\t  struct string_list *wanted_refs,\n \t\t\t  struct object_array *want_obj)\n {\n-\tconst char *arg;\n-\tif (skip_prefix(line, \"want-ref \", &arg)) {\n+\tconst char *refname_nons;\n+\tif (skip_prefix(line, \"want-ref \", &refname_nons)) {\n \t\tstruct object_id oid;\n \t\tstruct string_list_item *item;\n \t\tstruct object *o;\n+\t\tstruct strbuf refname = STRBUF_INIT;\n\n-\t\tif (read_ref(arg, &oid)) {\n-\t\t\tpacket_writer_error(writer, \"unknown ref %s\", arg);\n-\t\t\tdie(\"unknown ref %s\", arg);\n+\t\tstrbuf_addf(&refname, \"%s%s\", get_git_namespace(), refname_nons);\n+\t\tif (ref_is_hidden(refname_nons, refname.buf) ||\n+\t\t    read_ref(refname.buf, &oid)) {\n+\t\t\tpacket_writer_error(writer, \"unknown ref %s\", refname_nons);\n+\t\t\tdie(\"unknown ref %s\", refname_nons);\n \t\t}\n+\t\tstrbuf_release(&refname);\n\n-\t\titem = string_list_append(wanted_refs, arg);\n+\t\titem = string_list_append(wanted_refs, refname_nons);\n \t\titem->util = oiddup(&oid);\n\n-\t\to = parse_object_or_die(&oid, arg);\n+\t\to = parse_object_or_die(&oid, refname_nons);\n \t\tif (!(o->flags & WANTED)) {\n \t\t\to->flags |= WANTED;\n \t\t\tadd_object_array(o, NULL, want_obj);\n--\n2.32.0\n\n\n"},{"id":"431981","messageId":"20210804205951.668140-1-kim@eagain.st","threadId":"56182","inReplyTo":"20210804203829.661565-1-kim@eagain.st","subject":"[PATCH v4] upload-pack.c: treat want-ref relative to namespace","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-08-04T21:00:37Z","receivedAt":"2021-08-04T21:01:25Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"When 'upload-pack' runs within the context of a git namespace, treat any\n'want-ref' lines the client sends as relative to that namespace.\n\nAlso check if the wanted ref is hidden via 'hideRefs'. If it is hidden,\nrespond with an error as if the ref didn't exist.\n\nHelped-by: Jonathan Tan <jonathantanmy@google.com>\nSigned-off-by: Kim Altintop <kim@eagain.st>\n---\n\nChanges from v3:\n\n    * t5703: make tests actually pass :)\n\n\n t/t5703-upload-pack-ref-in-want.sh | 140 +++++++++++++++++++++++++++++\n upload-pack.c                      |  18 ++--\n 2 files changed, 151 insertions(+), 7 deletions(-)\n\ndiff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh\nindex e9e471621d..4da86d771b 100755\n--- a/t/t5703-upload-pack-ref-in-want.sh\n+++ b/t/t5703-upload-pack-ref-in-want.sh\n@@ -298,6 +298,146 @@ test_expect_success 'fetching with wildcard that matches multiple refs' '\n \tgrep \"want-ref refs/heads/o/bar\" log\n '\n\n+REPO=\"$(pwd)/repo-ns\"\n+\n+write_fetch_want_ref() {\n+\tlocal wanted_ref=\"$1\"\n+\n+\twrite_command fetch\n+\techo \"0001\"\n+\techo \"no-progress\"\n+\techo \"want-ref $1\"\n+\techo \"done\"\n+\techo \"0000\"\n+}\n+\n+test_expect_success 'setup namespaced repo' '\n+\t(\n+\t\tgit init -b main \"$REPO\" &&\n+\t\tcd \"$REPO\" &&\n+\t\ttest_commit a &&\n+\t\ttest_commit b &&\n+\t\tgit checkout a &&\n+\t\ttest_commit c &&\n+\t\tgit checkout a &&\n+\t\ttest_commit d &&\n+\t\tgit update-ref refs/heads/ns-no b &&\n+\t\tgit update-ref refs/namespaces/ns/refs/heads/ns-yes c &&\n+\t\tgit update-ref refs/namespaces/ns/refs/heads/hidden d\n+\t) &&\n+\tgit -C \"$REPO\" config uploadpack.allowRefInWant true\n+'\n+\n+test_expect_success 'with namespace: want-ref is considered relative to namespace' '\n+\twanted_ref=refs/heads/ns-yes &&\n+\n+\toid=$(git -C \"$REPO\" rev-parse \"refs/namespaces/ns/$wanted_ref\") &&\n+\tcat >expected_refs <<-EOF &&\n+\t$oid $wanted_ref\n+\tEOF\n+\tcat >expected_commits <<-EOF &&\n+\t$oid\n+\t$(git -C \"$REPO\" rev-parse a)\n+\tEOF\n+\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_fetch_want_ref $wanted_ref)\n+\tEOF\n+\n+\tenv GIT_NAMESPACE=ns test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tcheck_output\n+'\n+\n+test_expect_success 'with namespace: want-ref outside namespace is unknown' '\n+\twanted_ref=refs/heads/ns-no &&\n+\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_fetch_want_ref $wanted_ref)\n+\tEOF\n+\n+\ttest_must_fail env GIT_NAMESPACE=ns \\\n+\t\ttest-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tgrep \"unknown ref\" out\n+'\n+\n+# Cross-check refs/heads/ns-no indeed exists\n+test_expect_success 'without namespace: want-ref outside namespace succeeds' '\n+\twanted_ref=refs/heads/ns-no &&\n+\n+\toid=$(git -C \"$REPO\" rev-parse $wanted_ref) &&\n+\tcat >expected_refs <<-EOF &&\n+\t$oid $wanted_ref\n+\tEOF\n+\tcat >expected_commits <<-EOF &&\n+\t$oid\n+\t$(git -C \"$REPO\" rev-parse a)\n+\tEOF\n+\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_fetch_want_ref $wanted_ref)\n+\tEOF\n+\n+\ttest-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tcheck_output\n+'\n+\n+test_expect_success 'with namespace: hideRefs is matched, relative to namespace' '\n+\twanted_ref=refs/heads/hidden &&\n+\tgit -C \"$REPO\" config transfer.hideRefs $wanted_ref &&\n+\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_fetch_want_ref $wanted_ref)\n+\tEOF\n+\n+\ttest_must_fail env GIT_NAMESPACE=ns \\\n+\t\ttest-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tgrep \"unknown ref\" out\n+'\n+\n+# Cross-check refs/heads/hidden indeed exists\n+test_expect_success 'with namespace: want-ref succeeds if hideRefs is removed' '\n+\twanted_ref=refs/heads/hidden &&\n+\tgit -C \"$REPO\" config --unset transfer.hideRefs $wanted_ref &&\n+\n+\toid=$(git -C \"$REPO\" rev-parse \"refs/namespaces/ns/$wanted_ref\") &&\n+\tcat >expected_refs <<-EOF &&\n+\t$oid $wanted_ref\n+\tEOF\n+\tcat >expected_commits <<-EOF &&\n+\t$oid\n+\t$(git -C \"$REPO\" rev-parse a)\n+\tEOF\n+\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_fetch_want_ref $wanted_ref)\n+\tEOF\n+\n+\tenv GIT_NAMESPACE=ns test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tcheck_output\n+'\n+\n+test_expect_success 'without namespace: relative hideRefs does not match' '\n+\twanted_ref=refs/namespaces/ns/refs/heads/hidden &&\n+\tgit -C \"$REPO\" config transfer.hideRefs refs/heads/hidden &&\n+\n+\toid=$(git -C \"$REPO\" rev-parse $wanted_ref) &&\n+\tcat >expected_refs <<-EOF &&\n+\t$oid $wanted_ref\n+\tEOF\n+\tcat >expected_commits <<-EOF &&\n+\t$oid\n+\t$(git -C \"$REPO\" rev-parse a)\n+\tEOF\n+\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_fetch_want_ref $wanted_ref)\n+\tEOF\n+\n+\ttest-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tcheck_output\n+'\n+\n+\n . \"$TEST_DIRECTORY\"/lib-httpd.sh\n start_httpd\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 297b76fcb4..6ce07231d3 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -1417,21 +1417,25 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,\n \t\t\t  struct string_list *wanted_refs,\n \t\t\t  struct object_array *want_obj)\n {\n-\tconst char *arg;\n-\tif (skip_prefix(line, \"want-ref \", &arg)) {\n+\tconst char *refname_nons;\n+\tif (skip_prefix(line, \"want-ref \", &refname_nons)) {\n \t\tstruct object_id oid;\n \t\tstruct string_list_item *item;\n \t\tstruct object *o;\n+\t\tstruct strbuf refname = STRBUF_INIT;\n\n-\t\tif (read_ref(arg, &oid)) {\n-\t\t\tpacket_writer_error(writer, \"unknown ref %s\", arg);\n-\t\t\tdie(\"unknown ref %s\", arg);\n+\t\tstrbuf_addf(&refname, \"%s%s\", get_git_namespace(), refname_nons);\n+\t\tif (ref_is_hidden(refname_nons, refname.buf) ||\n+\t\t    read_ref(refname.buf, &oid)) {\n+\t\t\tpacket_writer_error(writer, \"unknown ref %s\", refname_nons);\n+\t\t\tdie(\"unknown ref %s\", refname_nons);\n \t\t}\n+\t\tstrbuf_release(&refname);\n\n-\t\titem = string_list_append(wanted_refs, arg);\n+\t\titem = string_list_append(wanted_refs, refname_nons);\n \t\titem->util = oiddup(&oid);\n\n-\t\to = parse_object_or_die(&oid, arg);\n+\t\to = parse_object_or_die(&oid, refname_nons);\n \t\tif (!(o->flags & WANTED)) {\n \t\t\to->flags |= WANTED;\n \t\t\tadd_object_array(o, NULL, want_obj);\n--\n2.32.0\n\n\n"},{"id":"431983","messageId":"xmqq35rooqr5.fsf@gitster.g","threadId":"56182","inReplyTo":"20210804203829.661565-1-kim@eagain.st","subject":"Re: [PATCH v3] upload-pack.c: treat want-ref relative to namespace","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-08-04T21:15:58Z","receivedAt":"2021-08-04T21:16:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Kim Altintop <kim@eagain.st> writes:\n\n> diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh\n> index e9e471621d..4a828042bb 100755\n> --- a/t/t5703-upload-pack-ref-in-want.sh\n> +++ b/t/t5703-upload-pack-ref-in-want.sh\n> @@ -298,6 +298,134 @@ test_expect_success 'fetching with wildcard that matches multiple refs' '\n>  \tgrep \"want-ref refs/heads/o/bar\" log\n>  '\n>\n> +REPO=\"$(pwd)/repo-ns\"\n> +\n> +write_fetch_want_ref() {\n\nStyle; missing SP before ().\n\n> +\tlocal wanted_ref=\"$1\"\n> +\n> +\twrite_command fetch\n> +\techo \"0001\"\n> +\techo \"no-progress\"\n> +\techo \"want-ref $1\"\n> +\techo \"done\"\n> +\techo \"0000\"\n> +}\n\nAll other helper shell functions in this file are defined near the\ntop.  If we compare them with this, we notice that this does not\ncheck for any errors.  We should string these steps together with\n\"&&\".\n\nAlso, having this in the middle looks a bit out of place.  If this\nhelper is useful only in the tests that are being added by this\npatch, that may be OK, but we may want to have a preliminary\nclean-up step before the main patch that adds this helper function\nnear the top (perhaps after \"write_command\" is defined), and use it\nin existing tests.  It seems that 'invalid want-ref line', 'basic\nwant-ref', and 'multiple want-ref lines' tests among others may\nbenefit from a slightly expanded variant, something along the lines\nof ...\n\n    write_fetch_command () {\n\twrite_command fetch &&\n\techo \"0001\" &&\n\techo \"no-progress\" || return\n        # want-ref ...\n\t# --\n\t# want ...\n\t# --\n\t# have ...\n\t# --\n        while :\n\tdo\n\t\tcase $# in 0) break ;; esac &&\n\t\tcase \"$1\" in --) shift; break ;; esac &&\n\t\techo \"want-ref $1\" &&\n\t\tshift || return\n\tdone &&\n        while :\n\tdo\n\t\tcase $# in 0) break ;; esac &&\n\t\tcase \"$1\" in --) shift; break ;; esac &&\n\t\techo \"want $1\" &&\n\t\tshift || return\n\tdone &&\n        while :\n\tdo\n\t\tcase $# in 0) break ;; esac &&\n\t\tcase \"$1\" in --) shift; break ;; esac &&\n\t\techo \"have $1\" &&\n\t\tshift || return\n\tdone &&\n\techo \"done\" &&\n\techo \"0000\"\n    }\n\nand with something like that, an existing test like this one:\n\ntest_expect_success 'mix want and want-ref' '\n\toid=$(git rev-parse f) &&\n\tcat >expected_refs <<-EOF &&\n\t$oid refs/heads/main\n\tEOF\n\tgit rev-parse e f >expected_commits &&\n\n\ttest-tool pkt-line pack >in <<-EOF &&\n\t$(write_command fetch)\n\t0001\n\tno-progress\n\twant-ref refs/heads/main\n\twant $(git rev-parse e)\n\thave $(git rev-parse a)\n\tdone\n\t0000\n\tEOF\n\n\ttest-tool serve-v2 --stateless-rpc >out <in &&\n\tcheck_output\n'\n\nmay become\n\ntest_expect_success 'mix want and want-ref' '\n\toid=$(git rev-parse f) &&\n\tcat >expected_refs <<-EOF &&\n\t$oid refs/heads/main\n\tEOF\n\tgit rev-parse e f >expected_commits &&\n\n\ttest-tool pkt-line pack >in <<-EOF &&\n\t$(write_fetch_command\n\t\trefs/heads/main\n\t\t--\n\t\t$(git rev-parse e)\n\t\t--\n\t\t$(git rev-parse a)\n\t)\n\tEOF\n\n\ttest-tool serve-v2 --stateless-rpc >out <in &&\n\tcheck_output\n'\n\nAnd after preparing a reusable helper like that, we can add\nnamespace tests using the same helper in the main patch (so it would\nbecome an at least 2-patch series).\n\n> +\n> +test_expect_success 'setup namespaced repo' '\n> +\t(\n> +\t\tgit init -b main \"$REPO\" &&\n> +\t\tcd \"$REPO\" &&\n> +\t\ttest_commit a &&\n> +\t\ttest_commit b &&\n> +\t\tgit checkout a &&\n> +\t\ttest_commit c &&\n> +\t\tgit checkout a &&\n> +\t\ttest_commit d &&\n> +\t\tgit update-ref refs/heads/ns-no b &&\n> +\t\tgit update-ref refs/namespaces/ns/refs/heads/ns-yes c &&\n> +\t\tgit update-ref refs/namespaces/ns/refs/heads/hidden d\n> +\t) &&\n> +\tgit -C \"$REPO\" config uploadpack.allowRefInWant true\n\nI do not see a reason why the last step wants to be done outside the\nsubshell, which we would be using anyway.  Does it clarify something?\n\n> +test_expect_success 'with namespace: want-ref is considered relative to namespace' '\n> +\twanted_ref=refs/heads/ns-yes &&\n> +\n> +\toid=$(git -C \"$REPO\" rev-parse $wanted_ref) &&\n> +\tcat >expected_refs <<-EOF &&\n> +\t$oid $wanted_ref\n> +\tEOF\n> +\tgit -C \"$REPO\" rev-parse a $wanted_ref >expected_commits &&\n> +\n> +\ttest-tool pkt-line pack >in <<-EOF &&\n> +\t$(write_fetch_want_ref $wanted_ref)\n> +\tEOF\n> +\n> +\tenv GIT_NAMESPACE=ns test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n\nI am not sure why we want \"env\" in front (it does not hurt, but it\nshould be unnecessary, as test-tool is a plain-vanilla binary\nexecutable, not a shell function).  Is this a workaround for a buggy\ntest linter or something?\n\n> +\tcheck_output\n> +'\n> +\n> +test_expect_success 'with namespace: want-ref outside namespace is unknown' '\n> +\twanted_ref=refs/heads/ns-no &&\n> +\n> +\ttest-tool pkt-line pack >in <<-EOF &&\n> +\t$(write_fetch_want_ref $wanted_ref)\n> +\tEOF\n> +\n> +\ttest_must_fail env GIT_NAMESPACE=ns \\\n> +\t\ttest-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n\nNote that this one does need \"env\" here.\n\n> diff --git a/upload-pack.c b/upload-pack.c\n> index 297b76fcb4..6ce07231d3 100644\n> --- a/upload-pack.c\n> +++ b/upload-pack.c\n> @@ -1417,21 +1417,25 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,\n>  \t\t\t  struct string_list *wanted_refs,\n>  \t\t\t  struct object_array *want_obj)\n>  {\n> -\tconst char *arg;\n> -\tif (skip_prefix(line, \"want-ref \", &arg)) {\n> +\tconst char *refname_nons;\n> +\tif (skip_prefix(line, \"want-ref \", &refname_nons)) {\n>  \t\tstruct object_id oid;\n>  \t\tstruct string_list_item *item;\n>  \t\tstruct object *o;\n> +\t\tstruct strbuf refname = STRBUF_INIT;\n>\n> -\t\tif (read_ref(arg, &oid)) {\n> -\t\t\tpacket_writer_error(writer, \"unknown ref %s\", arg);\n> -\t\t\tdie(\"unknown ref %s\", arg);\n> +\t\tstrbuf_addf(&refname, \"%s%s\", get_git_namespace(), refname_nons);\n> +\t\tif (ref_is_hidden(refname_nons, refname.buf) ||\n> +\t\t    read_ref(refname.buf, &oid)) {\n> +\t\t\tpacket_writer_error(writer, \"unknown ref %s\", refname_nons);\n> +\t\t\tdie(\"unknown ref %s\", refname_nons);\n\nLooks good.\n\n>  \t\t}\n> +\t\tstrbuf_release(&refname);\n>\n> -\t\titem = string_list_append(wanted_refs, arg);\n> +\t\titem = string_list_append(wanted_refs, refname_nons);\n>  \t\titem->util = oiddup(&oid);\n>\n> -\t\to = parse_object_or_die(&oid, arg);\n> +\t\to = parse_object_or_die(&oid, refname_nons);\n>  \t\tif (!(o->flags & WANTED)) {\n>  \t\t\to->flags |= WANTED;\n>  \t\t\tadd_object_array(o, NULL, want_obj);\n> --\n> 2.32.0\n"},{"id":"431993","messageId":"CDB2GX6ICZMC.1FDRFIKZU9GU6@schmidt","threadId":"56182","inReplyTo":"xmqq35rooqr5.fsf@gitster.g","subject":"Re: [PATCH v3] upload-pack.c: treat want-ref relative to namespace","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-08-04T22:04:05Z","receivedAt":"2021-08-04T22:04:53Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"On Wed Aug 4, 2021 at 11:15 PM CEST, Junio C Hamano wrote:\n>\n> Kim Altintop <kim@eagain.st> writes:\n>\n> > diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh\n> > index e9e471621d..4a828042bb 100755\n> > --- a/t/t5703-upload-pack-ref-in-want.sh\n> > +++ b/t/t5703-upload-pack-ref-in-want.sh\n> > @@ -298,6 +298,134 @@ test_expect_success 'fetching with wildcard that matches multiple refs' '\n> >  \tgrep \"want-ref refs/heads/o/bar\" log\n> >  '\n> >\n> > +REPO=\"$(pwd)/repo-ns\"\n> > +\n> > +write_fetch_want_ref() {\n>\n> Style; missing SP before ().\n>\n> > +\tlocal wanted_ref=\"$1\"\n> > +\n> > +\twrite_command fetch\n> > +\techo \"0001\"\n> > +\techo \"no-progress\"\n> > +\techo \"want-ref $1\"\n> > +\techo \"done\"\n> > +\techo \"0000\"\n> > +}\n>\n> All other helper shell functions in this file are defined near the\n> top. If we compare them with this, we notice that this does not\n> check for any errors. We should string these steps together with\n> \"&&\".\n>\n> Also, having this in the middle looks a bit out of place. If this\n> helper is useful only in the tests that are being added by this\n> patch, that may be OK, but we may want to have a preliminary\n> clean-up step before the main patch that adds this helper function\n> near the top (perhaps after \"write_command\" is defined), and use it\n> in existing tests. It seems that 'invalid want-ref line', 'basic\n> want-ref', and 'multiple want-ref lines' tests among others may\n> benefit from a slightly expanded variant, something along the lines\n> of ...\n>\n> write_fetch_command () {\n> write_command fetch &&\n> echo \"0001\" &&\n> echo \"no-progress\" || return\n> # want-ref ...\n> # --\n> # want ...\n> # --\n> # have ...\n> # --\n> while :\n> do\n> case $# in 0) break ;; esac &&\n> case \"$1\" in --) shift; break ;; esac &&\n> echo \"want-ref $1\" &&\n> shift || return\n> done &&\n> while :\n> do\n> case $# in 0) break ;; esac &&\n> case \"$1\" in --) shift; break ;; esac &&\n> echo \"want $1\" &&\n> shift || return\n> done &&\n> while :\n> do\n> case $# in 0) break ;; esac &&\n> case \"$1\" in --) shift; break ;; esac &&\n> echo \"have $1\" &&\n> shift || return\n> done &&\n> echo \"done\" &&\n> echo \"0000\"\n> }\n>\n> and with something like that, an existing test like this one:\n>\n> test_expect_success 'mix want and want-ref' '\n> oid=$(git rev-parse f) &&\n> cat >expected_refs <<-EOF &&\n> $oid refs/heads/main\n> EOF\n> git rev-parse e f >expected_commits &&\n>\n> test-tool pkt-line pack >in <<-EOF &&\n> $(write_command fetch)\n> 0001\n> no-progress\n> want-ref refs/heads/main\n> want $(git rev-parse e)\n> have $(git rev-parse a)\n> done\n> 0000\n> EOF\n>\n> test-tool serve-v2 --stateless-rpc >out <in &&\n> check_output\n> '\n>\n> may become\n>\n> test_expect_success 'mix want and want-ref' '\n> oid=$(git rev-parse f) &&\n> cat >expected_refs <<-EOF &&\n> $oid refs/heads/main\n> EOF\n> git rev-parse e f >expected_commits &&\n>\n> test-tool pkt-line pack >in <<-EOF &&\n> $(write_fetch_command\n> refs/heads/main\n> --\n> $(git rev-parse e)\n> --\n> $(git rev-parse a)\n> )\n> EOF\n>\n> test-tool serve-v2 --stateless-rpc >out <in &&\n> check_output\n> '\n>\n> And after preparing a reusable helper like that, we can add\n> namespace tests using the same helper in the main patch (so it would\n> become an at least 2-patch series).\n\n\nOk got it. I did not want to come forward with a grand refactoring of that test\nin my very first patch, but since reviewers seem to think it's in order I'll\ngive it a spin.\n\n\n> > +\n> > +test_expect_success 'setup namespaced repo' '\n> > +\t(\n> > +\t\tgit init -b main \"$REPO\" &&\n> > +\t\tcd \"$REPO\" &&\n> > +\t\ttest_commit a &&\n> > +\t\ttest_commit b &&\n> > +\t\tgit checkout a &&\n> > +\t\ttest_commit c &&\n> > +\t\tgit checkout a &&\n> > +\t\ttest_commit d &&\n> > +\t\tgit update-ref refs/heads/ns-no b &&\n> > +\t\tgit update-ref refs/namespaces/ns/refs/heads/ns-yes c &&\n> > +\t\tgit update-ref refs/namespaces/ns/refs/heads/hidden d\n> > +\t) &&\n> > +\tgit -C \"$REPO\" config uploadpack.allowRefInWant true\n>\n> I do not see a reason why the last step wants to be done outside the\n> subshell, which we would be using anyway. Does it clarify something?\n\nI agree, but was sticking to the style that was already there. I'll then revise\nthe other setup steps as part of the refactor.\n\n\n> > +test_expect_success 'with namespace: want-ref is considered relative to namespace' '\n> > +\twanted_ref=refs/heads/ns-yes &&\n> > +\n> > +\toid=$(git -C \"$REPO\" rev-parse $wanted_ref) &&\n> > +\tcat >expected_refs <<-EOF &&\n> > +\t$oid $wanted_ref\n> > +\tEOF\n> > +\tgit -C \"$REPO\" rev-parse a $wanted_ref >expected_commits &&\n> > +\n> > +\ttest-tool pkt-line pack >in <<-EOF &&\n> > +\t$(write_fetch_want_ref $wanted_ref)\n> > +\tEOF\n> > +\n> > +\tenv GIT_NAMESPACE=ns test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n>\n> I am not sure why we want \"env\" in front (it does not hurt, but it\n> should be unnecessary, as test-tool is a plain-vanilla binary\n> executable, not a shell function). Is this a workaround for a buggy\n> test linter or something?\n\nThe linter did indeed ask me to write `GIT_NAMESPACE=ns && export GIT_NAMESPACE\n&& test-tool ...` in v1 of the patch, but now it doesn't... nevermind, I must\nhave held something the wrong way.\n\n"},{"id":"431997","messageId":"CAPig+cTVOC_MndYTQqCY5PpCtXWSRm17FULf7eW26giVoCN-6Q@mail.gmail.com","threadId":"56182","inReplyTo":"CDB2GX6ICZMC.1FDRFIKZU9GU6@schmidt","subject":"Re: [PATCH v3] upload-pack.c: treat want-ref relative to namespace","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-08-04T22:17:09Z","receivedAt":"2021-08-04T22:17:31Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Aug 4, 2021 at 6:04 PM Kim Altintop <kim@eagain.st> wrote:\n> On Wed Aug 4, 2021 at 11:15 PM CEST, Junio C Hamano wrote:\n> > Kim Altintop <kim@eagain.st> writes:\n> > > +   env GIT_NAMESPACE=ns test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n> >\n> > I am not sure why we want \"env\" in front (it does not hurt, but it\n> > should be unnecessary, as test-tool is a plain-vanilla binary\n> > executable, not a shell function). Is this a workaround for a buggy\n> > test linter or something?\n>\n> The linter did indeed ask me to write `GIT_NAMESPACE=ns && export GIT_NAMESPACE\n> && test-tool ...` in v1 of the patch, but now it doesn't... nevermind, I must\n> have held something the wrong way.\n\nThe linter will complain about unportable:\n\n    export FOO=bar\n\nand ask you to write it as:\n\n    FOO=bar && export FOO\n\nwhich is probably what you encountered.\n\nIt will also complain about\n\n    FOO=bar some-command\n\nwhich should be rewritten as:\n\n    env FOO=bar some-command\n\nif, and only if, `some-command` is a shell function. If not a shell\nfunction, then:\n\n    FOO=bar some-command\n\nis just fine.\n"},{"id":"431998","messageId":"xmqqczqsn9ck.fsf@gitster.g","threadId":"56182","inReplyTo":"CDB2GX6ICZMC.1FDRFIKZU9GU6@schmidt","subject":"Re: [PATCH v3] upload-pack.c: treat want-ref relative to namespace","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-08-04T22:17:15Z","receivedAt":"2021-08-04T22:17:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Kim Altintop <kim@eagain.st> writes:\n\n>> > +\t) &&\n>> > +\tgit -C \"$REPO\" config uploadpack.allowRefInWant true\n>>\n>> I do not see a reason why the last step wants to be done outside the\n>> subshell, which we would be using anyway. Does it clarify something?\n>\n> I agree, but was sticking to the style that was already there. I'll then revise\n> the other setup steps as part of the refactor.\n\nTouching existing tests only for this, unlike the creation of a\nhelper out of existing test to improve reuse, does not improve\nanything.  I was curious if there was a good reason to create in a\nsubshell and configure in a separate step, and \"others do the same\"\nis good enough to satisfy the curiousity.\n\nThanks.\n"},{"id":"432000","messageId":"xmqq8s1gn92e.fsf@gitster.g","threadId":"56182","inReplyTo":"CDB2GX6ICZMC.1FDRFIKZU9GU6@schmidt","subject":"Re: [PATCH v3] upload-pack.c: treat want-ref relative to namespace","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-08-04T22:23:21Z","receivedAt":"2021-08-04T22:23:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Kim Altintop <kim@eagain.st> writes:\n\n> ...\n>> test-tool pkt-line pack >in <<-EOF &&\n>> $(write_fetch_command\n>> refs/heads/main\n>> --\n>> $(git rev-parse e)\n>> --\n>> $(git rev-parse a)\n>> )\n\nSorry, I missed all the necessary backslashes at the end of these lines.\n"},{"id":"432316","messageId":"20210809175530.75326-1-kim@eagain.st","threadId":"56182","inReplyTo":"20210804205951.668140-1-kim@eagain.st","subject":"[PATCH 0/3] upload-pack: treat want-ref relative to namespace","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-08-09T17:56:19Z","receivedAt":"2021-08-09T17:56:41Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"Fifth reroll of [0].\n\nThe patch has been split into three commits, where the first is a small\nrefactoring of the test file, the second is the original patch + tests, and the\nlast one attempts to express the `transfer.hideRefs` behaviour more accurately\nin the docs.\n\nThanks for your help and patience!\n\nThis version of the patch is also available at [1].\n\n[0]: https://lore.kernel.org/git/20210804205951.668140-1-kim@eagain.st/\n[1]: https://github.com/kim/git/tree/ka/namespaced-want-ref-v5\n\nKim Altintop (3):\n  t5730: introduce fetch command helper\n  upload-pack.c: treat want-ref relative to namespace\n  docs: clarify the interaction of transfer.hideRefs and namespaces\n\n Documentation/config/transfer.txt  |  17 ++-\n t/t5703-upload-pack-ref-in-want.sh | 236 +++++++++++++++++++++++++----\n upload-pack.c                      |  18 ++-\n 3 files changed, 224 insertions(+), 47 deletions(-)\n\n--\n2.32.0\n\n\n"},{"id":"432317","messageId":"20210809175530.75326-2-kim@eagain.st","threadId":"56182","inReplyTo":"20210809175530.75326-1-kim@eagain.st","subject":"[PATCH 1/3] t5730: introduce fetch command helper","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-08-09T17:56:45Z","receivedAt":"2021-08-09T17:57:32Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"Assembling a \"raw\" fetch command to be fed directly to \"test-tool serve-v2\"\nis extracted into a test helper.\n\nSuggested-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Kim Altintop <kim@eagain.st>\n---\n t/t5703-upload-pack-ref-in-want.sh | 107 ++++++++++++++++++++---------\n 1 file changed, 74 insertions(+), 33 deletions(-)\n\ndiff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh\nindex e9e471621d..cd4744b016 100755\n--- a/t/t5703-upload-pack-ref-in-want.sh\n+++ b/t/t5703-upload-pack-ref-in-want.sh\n@@ -40,6 +40,54 @@ write_command () {\n \tfi\n }\n\n+# Write a complete fetch command to stdout, suitable for use with `test-tool\n+# pkt-line`. \"want-ref\", \"want\", and \"have\" values can be given in this order,\n+# with sections separated by \"--\".\n+#\n+# Examples:\n+#\n+# write_fetch_command refs/heads/main\n+#\n+# write_fetch_command \\\n+#\trefs/heads/main \\\n+#\t-- \\\n+#\t-- \\\n+#\t$(git rev-parse x)\n+#\n+# write_fetch_command \\\n+#\t--\n+#\t$(git rev-parse a) \\\n+#\t--\n+#\t$(git rev-parse b)\n+write_fetch_command () {\n+\twrite_command fetch &&\n+\techo \"0001\" &&\n+\techo \"no-progress\" || return\n+    while :\n+\tdo\n+\t\tcase $# in 0) break ;; esac &&\n+\t\tcase \"$1\" in --) shift; break ;; esac &&\n+\t\techo \"want-ref $1\" &&\n+\t\tshift || return\n+\tdone &&\n+    while :\n+\tdo\n+\t\tcase $# in 0) break ;; esac &&\n+\t\tcase \"$1\" in --) shift; break ;; esac &&\n+\t\techo \"want $1\" &&\n+\t\tshift || return\n+\tdone &&\n+    while :\n+\tdo\n+\t\tcase $# in 0) break ;; esac &&\n+\t\tcase \"$1\" in --) shift; break ;; esac &&\n+\t\techo \"have $1\" &&\n+\t\tshift || return\n+\tdone &&\n+\techo \"done\" &&\n+\techo \"0000\"\n+}\n+\n # c(o/foo) d(o/bar)\n #        \\ /\n #         b   e(baz)  f(main)\n@@ -97,15 +145,13 @@ test_expect_success 'basic want-ref' '\n \tEOF\n \tgit rev-parse f >expected_commits &&\n\n-\toid=$(git rev-parse a) &&\n \ttest-tool pkt-line pack >in <<-EOF &&\n-\t$(write_command fetch)\n-\t0001\n-\tno-progress\n-\twant-ref refs/heads/main\n-\thave $oid\n-\tdone\n-\t0000\n+\t$(write_fetch_command \\\n+\t\trefs/heads/main \\\n+\t\t-- \\\n+\t\t-- \\\n+\t\t$(git rev-parse a) \\\n+\t)\n \tEOF\n\n \ttest-tool serve-v2 --stateless-rpc >out <in &&\n@@ -121,16 +167,14 @@ test_expect_success 'multiple want-ref lines' '\n \tEOF\n \tgit rev-parse c d >expected_commits &&\n\n-\toid=$(git rev-parse b) &&\n \ttest-tool pkt-line pack >in <<-EOF &&\n-\t$(write_command fetch)\n-\t0001\n-\tno-progress\n-\twant-ref refs/heads/o/foo\n-\twant-ref refs/heads/o/bar\n-\thave $oid\n-\tdone\n-\t0000\n+\t$(write_fetch_command \\\n+\t\trefs/heads/o/foo \\\n+\t\trefs/heads/o/bar \\\n+\t\t-- \\\n+\t\t-- \\\n+\t\t$(git rev-parse b) \\\n+\t)\n \tEOF\n\n \ttest-tool serve-v2 --stateless-rpc >out <in &&\n@@ -145,14 +189,13 @@ test_expect_success 'mix want and want-ref' '\n \tgit rev-parse e f >expected_commits &&\n\n \ttest-tool pkt-line pack >in <<-EOF &&\n-\t$(write_command fetch)\n-\t0001\n-\tno-progress\n-\twant-ref refs/heads/main\n-\twant $(git rev-parse e)\n-\thave $(git rev-parse a)\n-\tdone\n-\t0000\n+\t$(write_fetch_command \\\n+\t\trefs/heads/main \\\n+\t\t-- \\\n+\t\t$(git rev-parse e) \\\n+\t\t-- \\\n+\t\t$(git rev-parse a) \\\n+\t)\n \tEOF\n\n \ttest-tool serve-v2 --stateless-rpc >out <in &&\n@@ -166,15 +209,13 @@ test_expect_success 'want-ref with ref we already have commit for' '\n \tEOF\n \t>expected_commits &&\n\n-\toid=$(git rev-parse c) &&\n \ttest-tool pkt-line pack >in <<-EOF &&\n-\t$(write_command fetch)\n-\t0001\n-\tno-progress\n-\twant-ref refs/heads/o/foo\n-\thave $oid\n-\tdone\n-\t0000\n+\t$(write_fetch_command \\\n+\t\trefs/heads/o/foo \\\n+\t\t-- \\\n+\t\t-- \\\n+\t\t$(git rev-parse c) \\\n+\t)\n \tEOF\n\n \ttest-tool serve-v2 --stateless-rpc >out <in &&\n--\n2.32.0\n\n\n"},{"id":"432318","messageId":"20210809175530.75326-3-kim@eagain.st","threadId":"56182","inReplyTo":"20210809175530.75326-1-kim@eagain.st","subject":"[PATCH 2/3] upload-pack.c: treat want-ref relative to namespace","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-08-09T17:57:09Z","receivedAt":"2021-08-09T17:59:52Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"When 'upload-pack' runs within the context of a git namespace, treat any\n'want-ref' lines the client sends as relative to that namespace.\n\nAlso check if the wanted ref is hidden via 'hideRefs'. If it is hidden,\nrespond with an error as if the ref didn't exist.\n\nHelped-by: Jonathan Tan <jonathantanmy@google.com>\nSigned-off-by: Kim Altintop <kim@eagain.st>\n---\n t/t5703-upload-pack-ref-in-want.sh | 129 +++++++++++++++++++++++++++++\n upload-pack.c                      |  18 ++--\n 2 files changed, 140 insertions(+), 7 deletions(-)\n\ndiff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh\nindex cd4744b016..3e1e0b8fe1 100755\n--- a/t/t5703-upload-pack-ref-in-want.sh\n+++ b/t/t5703-upload-pack-ref-in-want.sh\n@@ -339,6 +339,135 @@ test_expect_success 'fetching with wildcard that matches multiple refs' '\n \tgrep \"want-ref refs/heads/o/bar\" log\n '\n\n+REPO=\"$(pwd)/repo-ns\"\n+\n+test_expect_success 'setup namespaced repo' '\n+\t(\n+\t\tgit init -b main \"$REPO\" &&\n+\t\tcd \"$REPO\" &&\n+\t\ttest_commit a &&\n+\t\ttest_commit b &&\n+\t\tgit checkout a &&\n+\t\ttest_commit c &&\n+\t\tgit checkout a &&\n+\t\ttest_commit d &&\n+\t\tgit update-ref refs/heads/ns-no b &&\n+\t\tgit update-ref refs/namespaces/ns/refs/heads/ns-yes c &&\n+\t\tgit update-ref refs/namespaces/ns/refs/heads/hidden d\n+\t) &&\n+\tgit -C \"$REPO\" config uploadpack.allowRefInWant true\n+'\n+\n+test_expect_success 'with namespace: want-ref is considered relative to namespace' '\n+\twanted_ref=refs/heads/ns-yes &&\n+\n+\toid=$(git -C \"$REPO\" rev-parse \"refs/namespaces/ns/$wanted_ref\") &&\n+\tcat >expected_refs <<-EOF &&\n+\t$oid $wanted_ref\n+\tEOF\n+\tcat >expected_commits <<-EOF &&\n+\t$oid\n+\t$(git -C \"$REPO\" rev-parse a)\n+\tEOF\n+\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_fetch_command $wanted_ref)\n+\tEOF\n+\n+\tGIT_NAMESPACE=ns test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tcheck_output\n+'\n+\n+test_expect_success 'with namespace: want-ref outside namespace is unknown' '\n+\twanted_ref=refs/heads/ns-no &&\n+\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_fetch_command $wanted_ref)\n+\tEOF\n+\n+\ttest_must_fail env GIT_NAMESPACE=ns \\\n+\t\ttest-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tgrep \"unknown ref\" out\n+'\n+\n+# Cross-check refs/heads/ns-no indeed exists\n+test_expect_success 'without namespace: want-ref outside namespace succeeds' '\n+\twanted_ref=refs/heads/ns-no &&\n+\n+\toid=$(git -C \"$REPO\" rev-parse $wanted_ref) &&\n+\tcat >expected_refs <<-EOF &&\n+\t$oid $wanted_ref\n+\tEOF\n+\tcat >expected_commits <<-EOF &&\n+\t$oid\n+\t$(git -C \"$REPO\" rev-parse a)\n+\tEOF\n+\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_fetch_command $wanted_ref)\n+\tEOF\n+\n+\ttest-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tcheck_output\n+'\n+\n+test_expect_success 'with namespace: hideRefs is matched, relative to namespace' '\n+\twanted_ref=refs/heads/hidden &&\n+\tgit -C \"$REPO\" config transfer.hideRefs $wanted_ref &&\n+\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_fetch_command $wanted_ref)\n+\tEOF\n+\n+\ttest_must_fail env GIT_NAMESPACE=ns \\\n+\t\ttest-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tgrep \"unknown ref\" out\n+'\n+\n+# Cross-check refs/heads/hidden indeed exists\n+test_expect_success 'with namespace: want-ref succeeds if hideRefs is removed' '\n+\twanted_ref=refs/heads/hidden &&\n+\tgit -C \"$REPO\" config --unset transfer.hideRefs $wanted_ref &&\n+\n+\toid=$(git -C \"$REPO\" rev-parse \"refs/namespaces/ns/$wanted_ref\") &&\n+\tcat >expected_refs <<-EOF &&\n+\t$oid $wanted_ref\n+\tEOF\n+\tcat >expected_commits <<-EOF &&\n+\t$oid\n+\t$(git -C \"$REPO\" rev-parse a)\n+\tEOF\n+\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_fetch_command $wanted_ref)\n+\tEOF\n+\n+\tGIT_NAMESPACE=ns test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tcheck_output\n+'\n+\n+test_expect_success 'without namespace: relative hideRefs does not match' '\n+\twanted_ref=refs/namespaces/ns/refs/heads/hidden &&\n+\tgit -C \"$REPO\" config transfer.hideRefs refs/heads/hidden &&\n+\n+\toid=$(git -C \"$REPO\" rev-parse $wanted_ref) &&\n+\tcat >expected_refs <<-EOF &&\n+\t$oid $wanted_ref\n+\tEOF\n+\tcat >expected_commits <<-EOF &&\n+\t$oid\n+\t$(git -C \"$REPO\" rev-parse a)\n+\tEOF\n+\n+\ttest-tool pkt-line pack >in <<-EOF &&\n+\t$(write_fetch_command $wanted_ref)\n+\tEOF\n+\n+\ttest-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tcheck_output\n+'\n+\n+\n . \"$TEST_DIRECTORY\"/lib-httpd.sh\n start_httpd\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 297b76fcb4..6ce07231d3 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -1417,21 +1417,25 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,\n \t\t\t  struct string_list *wanted_refs,\n \t\t\t  struct object_array *want_obj)\n {\n-\tconst char *arg;\n-\tif (skip_prefix(line, \"want-ref \", &arg)) {\n+\tconst char *refname_nons;\n+\tif (skip_prefix(line, \"want-ref \", &refname_nons)) {\n \t\tstruct object_id oid;\n \t\tstruct string_list_item *item;\n \t\tstruct object *o;\n+\t\tstruct strbuf refname = STRBUF_INIT;\n\n-\t\tif (read_ref(arg, &oid)) {\n-\t\t\tpacket_writer_error(writer, \"unknown ref %s\", arg);\n-\t\t\tdie(\"unknown ref %s\", arg);\n+\t\tstrbuf_addf(&refname, \"%s%s\", get_git_namespace(), refname_nons);\n+\t\tif (ref_is_hidden(refname_nons, refname.buf) ||\n+\t\t    read_ref(refname.buf, &oid)) {\n+\t\t\tpacket_writer_error(writer, \"unknown ref %s\", refname_nons);\n+\t\t\tdie(\"unknown ref %s\", refname_nons);\n \t\t}\n+\t\tstrbuf_release(&refname);\n\n-\t\titem = string_list_append(wanted_refs, arg);\n+\t\titem = string_list_append(wanted_refs, refname_nons);\n \t\titem->util = oiddup(&oid);\n\n-\t\to = parse_object_or_die(&oid, arg);\n+\t\to = parse_object_or_die(&oid, refname_nons);\n \t\tif (!(o->flags & WANTED)) {\n \t\t\to->flags |= WANTED;\n \t\t\tadd_object_array(o, NULL, want_obj);\n--\n2.32.0\n\n\n"},{"id":"432319","messageId":"20210809175530.75326-4-kim@eagain.st","threadId":"56182","inReplyTo":"20210809175530.75326-1-kim@eagain.st","subject":"[PATCH 3/3] docs: clarify the interaction of transfer.hideRefs and namespaces","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-08-09T17:57:24Z","receivedAt":"2021-08-09T17:59:54Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"Expand the section about namespaces in the documentation of\n`transfer.hideRefs` to point out the subtle differences between\n`upload-pack` and `receive-pack`.\n\n9bedd82017 (upload-pack.c: treat want-ref relative to namespace,\n2021-07-30) taught `upload-pack` to reject `want-ref`s for hidden refs,\nwhich is now documented.\n\nSigned-off-by: Kim Altintop <kim@eagain.st>\n---\n Documentation/config/transfer.txt | 17 ++++++++++-------\n 1 file changed, 10 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/config/transfer.txt b/Documentation/config/transfer.txt\nindex 505126a780..09ebb399ce 100644\n--- a/Documentation/config/transfer.txt\n+++ b/Documentation/config/transfer.txt\n@@ -52,13 +52,16 @@ If you have multiple hideRefs values, later entries override earlier ones\n (and entries in more-specific config files override less-specific ones).\n +\n If a namespace is in use, the namespace prefix is stripped from each\n-reference before it is matched against `transfer.hiderefs` patterns.\n-For example, if `refs/heads/master` is specified in `transfer.hideRefs` and\n-the current namespace is `foo`, then `refs/namespaces/foo/refs/heads/master`\n-is omitted from the advertisements but `refs/heads/master` and\n-`refs/namespaces/bar/refs/heads/master` are still advertised as so-called\n-\"have\" lines. In order to match refs before stripping, add a `^` in front of\n-the ref name. If you combine `!` and `^`, `!` must be specified first.\n+reference before it is matched against `transfer.hiderefs` patterns. For\n+example, if `refs/heads/master` is specified in `transfer.hideRefs` and the\n+current namespace is `foo`, then `refs/namespaces/foo/refs/heads/master` is\n+omitted from the advertisements. If `uploadpack.allowRefInWant` is set,\n+`upload-pack` will treat `want-ref refs/heads/master` in a protocol v2\n+`fetch` command as if `refs/heads/master` was unknown. Note, however, that\n+`receive-pack` will still advertise the object id `refs/heads/master` is\n+pointing to, but will conceil the name of the ref. In order to match refs\n+before stripping, add a `^` in front of the ref name. If you combine `!` and\n+`^`, `!` must be specified first.\n +\n Even if you hide refs, a client may still be able to steal the target\n objects via the techniques described in the \"SECURITY\" section of the\n--\n2.32.0\n\n\n"},{"id":"432328","messageId":"xmqq8s1afmyh.fsf@gitster.g","threadId":"56182","inReplyTo":"20210809175530.75326-2-kim@eagain.st","subject":"Re: [PATCH 1/3] t5730: introduce fetch command helper","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-08-09T19:16:22Z","receivedAt":"2021-08-09T19:16:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Kim Altintop <kim@eagain.st> writes:\n\n> Assembling a \"raw\" fetch command to be fed directly to \"test-tool serve-v2\"\n> is extracted into a test helper.\n>\n> Suggested-by: Junio C Hamano <gitster@pobox.com>\n> Signed-off-by: Kim Altintop <kim@eagain.st>\n> ---\n>  t/t5703-upload-pack-ref-in-want.sh | 107 ++++++++++++++++++++---------\n>  1 file changed, 74 insertions(+), 33 deletions(-)\n>\n> diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh\n> index e9e471621d..cd4744b016 100755\n> --- a/t/t5703-upload-pack-ref-in-want.sh\n> +++ b/t/t5703-upload-pack-ref-in-want.sh\n> @@ -40,6 +40,54 @@ write_command () {\n>  \tfi\n>  }\n>\n> +# Write a complete fetch command to stdout, suitable for use with `test-tool\n> +# pkt-line`. \"want-ref\", \"want\", and \"have\" values can be given in this order,\n> +# with sections separated by \"--\".\n> +#\n> +# Examples:\n> +#\n> +# write_fetch_command refs/heads/main\n> +#\n> +# write_fetch_command \\\n> +#\trefs/heads/main \\\n> +#\t-- \\\n> +#\t-- \\\n> +#\t$(git rev-parse x)\n> +#\n> +# write_fetch_command \\\n> +#\t--\n> +#\t$(git rev-parse a) \\\n> +#\t--\n> +#\t$(git rev-parse b)\n\nHave a blank line here (or a line with \"#\" and nothing else) and it\nwould become easier to read.\n\n> +write_fetch_command () {\n> +\twrite_command fetch &&\n> +\techo \"0001\" &&\n> +\techo \"no-progress\" || return\n\nThe \"while :\" in this helper function are indented with 4 spaces,\nnot a single tab.\n\n> +    while :\n> +\tdo\n> +\t\tcase $# in 0) break ;; esac &&\n> +\t\tcase \"$1\" in --) shift; break ;; esac &&\n> +\t\techo \"want-ref $1\" &&\n> +\t\tshift || return\n> +\tdone &&\n> +    while :\n> +\tdo\n> +\t\tcase $# in 0) break ;; esac &&\n> +\t\tcase \"$1\" in --) shift; break ;; esac &&\n> +\t\techo \"want $1\" &&\n> +\t\tshift || return\n> +\tdone &&\n> +    while :\n> +\tdo\n> +\t\tcase $# in 0) break ;; esac &&\n> +\t\tcase \"$1\" in --) shift; break ;; esac &&\n> +\t\techo \"have $1\" &&\n> +\t\tshift || return\n> +\tdone &&\n> +\techo \"done\" &&\n> +\techo \"0000\"\n> +}\n\nThe error checking of the helper function seems to be reasonable,\nbut ...\n\n>  # c(o/foo) d(o/bar)\n>  #        \\ /\n>  #         b   e(baz)  f(main)\n> @@ -97,15 +145,13 @@ test_expect_success 'basic want-ref' '\n>  \tEOF\n>  \tgit rev-parse f >expected_commits &&\n>\n>  \ttest-tool pkt-line pack >in <<-EOF &&\n> +\t$(write_fetch_command \\\n> +\t\trefs/heads/main \\\n> +\t\t-- \\\n> +\t\t-- \\\n> +\t\t$(git rev-parse a) \\\n> +\t)\n>  \tEOF\n>  \ttest-tool serve-v2 --stateless-rpc >out <in &&\n\n... the code that uses the helper needs rewriting to make use of\nit.  A failure in $(write_fetch_command) here will not cause the\ncaller to stop here.  If we had any \"git\" command used in the\nhelper, I would recommand restructuring the caller to do something\nlike:\n\n    write_fetch_command >pkt-src \\\n\trefs/heads/main \\\n\t-- \\\n\t-- \\\n\t$(git rev-parse a) &&\n    test-tool pkt-line pack <pkt-src >in &&\n    test-tool serve-v2 --stateless-rpc >out <in &&\n\nbut it probably may not be necessary (we only use \"echo\" there, and\nit probably is not worth worrying about 'echo' failing).\n\nThe call to $(git rev-parse a) might fail when rev-parse gets\nbroken, and I think the rewritten version would catch such a\nbreakage while the one inside $(write_fetch_command) in the here-doc\nwould not be.\n\n"},{"id":"432329","messageId":"YRGEwFauT5QjMSNO@google.com","threadId":"56182","inReplyTo":"20210809175530.75326-2-kim@eagain.st","subject":"Re: [PATCH 1/3] t5730: introduce fetch command helper","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2021-08-09T19:40:48Z","receivedAt":"2021-08-09T19:40:56Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi,\n\nKim Altintop wrote:\n\n> Assembling a \"raw\" fetch command to be fed directly to \"test-tool serve-v2\"\n> is extracted into a test helper.\n>\n> Suggested-by: Junio C Hamano <gitster@pobox.com>\n> Signed-off-by: Kim Altintop <kim@eagain.st>\n> ---\n>  t/t5703-upload-pack-ref-in-want.sh | 107 ++++++++++++++++++++---------\n>  1 file changed, 74 insertions(+), 33 deletions(-)\n\nThanks!  Interesting.\n\n> diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh\n> index e9e471621d..cd4744b016 100755\n> --- a/t/t5703-upload-pack-ref-in-want.sh\n> +++ b/t/t5703-upload-pack-ref-in-want.sh\n> @@ -40,6 +40,54 @@ write_command () {\n>  \tfi\n>  }\n> \n> +# Write a complete fetch command to stdout, suitable for use with `test-tool\n> +# pkt-line`. \"want-ref\", \"want\", and \"have\" values can be given in this order,\n> +# with sections separated by \"--\".\n> +#\n> +# Examples:\n> +#\n> +# write_fetch_command refs/heads/main\n> +#\n> +# write_fetch_command \\\n> +#\trefs/heads/main \\\n> +#\t-- \\\n> +#\t-- \\\n> +#\t$(git rev-parse x)\n> +#\n> +# write_fetch_command \\\n> +#\t--\n> +#\t$(git rev-parse a) \\\n> +#\t--\n> +#\t$(git rev-parse b)\n> +write_fetch_command () {\n\nHm, for comparison let me see what this looks like without the helper:\nafter some prior step\n\n\tobject_format=$(test_oid algo) &&\t# probably just once in a setup step\n\tx=$(git rev-parse x) &&\n\nwe can write\n\n\tcat <<-EOF &&\n\t\tcommand=fetch\n\t\tobject-format=$object_format\n\t\t0001\n\t\tno-progress\n\t\twant-ref refs/heads/main\n\t\thave $x\n\t\tdone\n\t\t0000\n\tEOF\n\nI find that a little _easier_ to read than a write_fetch_command call,\nbecause I don't have to chase the definition and x is labeled as a\n'have'.\n\nIs there some additional motivation for this helper?\n\n> +\twrite_command fetch &&\n> +\techo \"0001\" &&\n> +\techo \"no-progress\" || return\n> +    while :\n\nWhitespace seems off.  Junio covers this in his review so I'll ignore\nother instances.\n\n[...]\n> @@ -97,15 +145,13 @@ test_expect_success 'basic want-ref' '\n>  \tEOF\n>  \tgit rev-parse f >expected_commits &&\n> \n> -\toid=$(git rev-parse a) &&\n>  \ttest-tool pkt-line pack >in <<-EOF &&\n> -\t$(write_command fetch)\n> -\t0001\n> -\tno-progress\n> -\twant-ref refs/heads/main\n> -\thave $oid\n> -\tdone\n> -\t0000\n> +\t$(write_fetch_command \\\n> +\t\trefs/heads/main \\\n> +\t\t-- \\\n> +\t\t-- \\\n> +\t\t$(git rev-parse a) \\\n> +\t)\n>  \tEOF\n> \n>  \ttest-tool serve-v2 --stateless-rpc >out <in &&\n> @@ -121,16 +167,14 @@ test_expect_success 'multiple want-ref lines' '\n>  \tEOF\n>  \tgit rev-parse c d >expected_commits &&\n> \n> -\toid=$(git rev-parse b) &&\n>  \ttest-tool pkt-line pack >in <<-EOF &&\n> -\t$(write_command fetch)\n> -\t0001\n> -\tno-progress\n> -\twant-ref refs/heads/o/foo\n> -\twant-ref refs/heads/o/bar\n> -\thave $oid\n> -\tdone\n> -\t0000\n> +\t$(write_fetch_command \\\n> +\t\trefs/heads/o/foo \\\n> +\t\trefs/heads/o/bar \\\n> +\t\t-- \\\n> +\t\t-- \\\n> +\t\t$(git rev-parse b) \\\n> +\t)\n>  \tEOF\n\nHere the entirety of the input to \"test-tool pkt-line pack\" is the\nentirety of the output from write_fetch_command, which would suggest\neither\n\n a. making write_fetch_command pipe its output to \"test-tool pkt-line\n    pack\", or\n\n b. using a pipe instead of a command substitution, like\n    \"write_fetch_command ... | test-tool pkt-line pack >in\"\n\n(although as mentioned above, I think it's simpler to inline the\nwrite_fetch_command and even the write_command as well).\n\nThanks and hope that helps,\nJonathan\n"},{"id":"432337","messageId":"CDFAMOMQ0NMC.19EDQ0WOL1JZE@schmidt","threadId":"56182","inReplyTo":"xmqq8s1afmyh.fsf@gitster.g","subject":"Re: [PATCH 1/3] t5730: introduce fetch command helper","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-08-09T21:18:24Z","receivedAt":"2021-08-09T21:19:05Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"On Mon Aug 9, 2021 at 9:16 PM CEST, Junio C Hamano wrote:\n> >\n> > +# Write a complete fetch command to stdout, suitable for use with `test-tool\n> > +# pkt-line`. \"want-ref\", \"want\", and \"have\" values can be given in this order,\n> > +# with sections separated by \"--\".\n> > +#\n> > +# Examples:\n> > +#\n> > +# write_fetch_command refs/heads/main\n> > +#\n> > +# write_fetch_command \\\n> > +#\trefs/heads/main \\\n> > +#\t-- \\\n> > +#\t-- \\\n> > +#\t$(git rev-parse x)\n> > +#\n> > +# write_fetch_command \\\n> > +#\t--\n> > +#\t$(git rev-parse a) \\\n> > +#\t--\n> > +#\t$(git rev-parse b)\n>\n> Have a blank line here (or a line with \"#\" and nothing else) and it\n> would become easier to read.\n\nNoted.\n\n>\n> > +write_fetch_command () {\n> > +\twrite_command fetch &&\n> > +\techo \"0001\" &&\n> > +\techo \"no-progress\" || return\n>\n> The \"while :\" in this helper function are indented with 4 spaces,\n> not a single tab.\n\nNoted, sorry.\n\nI can't seem to be able teach `git diff --check` to exit non-zero on whitespace\nerrors.\n\n> >  # c(o/foo) d(o/bar)\n> >  #        \\ /\n> >  #         b   e(baz)  f(main)\n> > @@ -97,15 +145,13 @@ test_expect_success 'basic want-ref' '\n> >  \tEOF\n> >  \tgit rev-parse f >expected_commits &&\n> >\n> >  \ttest-tool pkt-line pack >in <<-EOF &&\n> > +\t$(write_fetch_command \\\n> > +\t\trefs/heads/main \\\n> > +\t\t-- \\\n> > +\t\t-- \\\n> > +\t\t$(git rev-parse a) \\\n> > +\t)\n> >  \tEOF\n> >  \ttest-tool serve-v2 --stateless-rpc >out <in &&\n>\n> ... the code that uses the helper needs rewriting to make use of\n> it. A failure in $(write_fetch_command) here will not cause the\n> caller to stop here. If we had any \"git\" command used in the\n> helper, I would recommand restructuring the caller to do something\n> like:\n>\n> write_fetch_command >pkt-src \\\n> \trefs/heads/main \\\n> \t-- \\\n> \t-- \\\n> \t$(git rev-parse a) &&\n> test-tool pkt-line pack <pkt-src >in &&\n> test-tool serve-v2 --stateless-rpc >out <in &&\n>\n> but it probably may not be necessary (we only use \"echo\" there, and\n> it probably is not worth worrying about 'echo' failing).\n>\n> The call to $(git rev-parse a) might fail when rev-parse gets\n> broken, and I think the rewritten version would catch such a\n> breakage while the one inside $(write_fetch_command) in the here-doc\n> would not be.\n\nHeh, this is turning into an exercise to forget everything about bash :/\n\nI didn't know that an error in a nested command substitution would not cause the\nouter one to fail. As we can't use a pipe either (as Jonathan Nieder suggests),\nI think a redirection like this is indeed necessary. The only other option I\ncould think of would be assigning rev-parse to a variable, which arguably is\nless readable:\n\n    have=$(git rew-parse a) &&\n    test-tool pkt-line pack >in <<-EOF &&\n    $(write_fetch_command \\\n\t    refs/heads/main \\\n\t    -- \\\n\t    -- \\\n\t    $have)\n    EOF\n\n\nThanks\n\n"},{"id":"432338","messageId":"xmqqtujye1l5.fsf@gitster.g","threadId":"56182","inReplyTo":"YRGEwFauT5QjMSNO@google.com","subject":"Re: [PATCH 1/3] t5730: introduce fetch command helper","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-08-09T21:43:18Z","receivedAt":"2021-08-09T21:43:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jonathan Nieder <jrnieder@gmail.com> writes:\n\n> Here the entirety of the input to \"test-tool pkt-line pack\" is the\n> entirety of the output from write_fetch_command, which would suggest\n> either\n>\n>  a. making write_fetch_command pipe its output to \"test-tool pkt-line\n>     pack\", or\n>\n>  b. using a pipe instead of a command substitution, like\n>     \"write_fetch_command ... | test-tool pkt-line pack >in\"\n>\n> (although as mentioned above, I think it's simpler to inline the\n> write_fetch_command and even the write_command as well).\n\nYeah, write_command was not saving all that much to begin with.  I\nwas hoping that there were a lot more commonality that we can save\nsome typing.\n\n\n"},{"id":"432339","messageId":"CDFBG4S23B1M.141X9FIPP7YHS@schmidt","threadId":"56182","inReplyTo":"YRGEwFauT5QjMSNO@google.com","subject":"Re: [PATCH 1/3] t5730: introduce fetch command helper","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-08-09T21:56:49Z","receivedAt":"2021-08-09T21:57:29Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"Thanks for chiming in!\n\nOn Mon Aug 9, 2021 at 9:40 PM CEST, Jonathan Nieder wrote:\n> Hm, for comparison let me see what this looks like without the helper:\n> after some prior step\n>\n>\t object_format=$(test_oid algo) && # probably just once in a setup step\n>\t x=$(git rev-parse x) &&\n>\n> we can write\n>\n>\t cat <<-EOF &&\n>\t command=fetch\n>\t object-format=$object_format\n>\t 0001\n>\t no-progress\n>\t want-ref refs/heads/main\n>\t have $x\n>\t done\n>\t 0000\n>\t EOF\n>\n> I find that a little _easier_ to read than a write_fetch_command call,\n> because I don't have to chase the definition and x is labeled as a\n> 'have'.\n>\n> Is there some additional motivation for this helper?\n\nIt was suggested in earlier review rounds. I think it does improve readability\nas quite some lines need to be repeated everywhere a fetch command is assembled.\nI agree though that not having some sort of \"named arguments\" is a bit\ndetrimental.\n\n>\n> >  \ttest-tool serve-v2 --stateless-rpc >out <in &&\n> > @@ -121,16 +167,14 @@ test_expect_success 'multiple want-ref lines' '\n> >  \tEOF\n> >  \tgit rev-parse c d >expected_commits &&\n> >\n> > -\toid=$(git rev-parse b) &&\n> >  \ttest-tool pkt-line pack >in <<-EOF &&\n> > -\t$(write_command fetch)\n> > -\t0001\n> > -\tno-progress\n> > -\twant-ref refs/heads/o/foo\n> > -\twant-ref refs/heads/o/bar\n> > -\thave $oid\n> > -\tdone\n> > -\t0000\n> > +\t$(write_fetch_command \\\n> > +\t\trefs/heads/o/foo \\\n> > +\t\trefs/heads/o/bar \\\n> > +\t\t-- \\\n> > +\t\t-- \\\n> > +\t\t$(git rev-parse b) \\\n> > +\t)\n> >  \tEOF\n>\n> Here the entirety of the input to \"test-tool pkt-line pack\" is the\n> entirety of the output from write_fetch_command, which would suggest\n> either\n>\n> a. making write_fetch_command pipe its output to \"test-tool pkt-line\n> pack\", or\n>\n> b. using a pipe instead of a command substitution, like\n> \"write_fetch_command ... | test-tool pkt-line pack >in\"\n>\n> (although as mentioned above, I think it's simpler to inline the\n> write_fetch_command and even the write_command as well).\n\nYes, although I believe a pipe cannot be used as we don't have bash's `set -o\npipefail` (ie. the exit status will always be the status of the last command in\nthe pipe, even if an earlier one failed).\n\nPerhaps an alternative would be:\n\n\twrite_fetch_command () {\n\t\twrite_command fetch &&\n\t\techo \"0001\" &&\n\t\techo \"no-progress\" &&\n\t\tcat /dev/stdin &&\n\t\techo \"done\" &&\n\t\techo \"0000\"\n \t}\n\n\nWhich would then be called like so:\n\n\twrite_fetch_command >pkt_cmd <<-EOF &&\n\twant-ref refs/heads/main\n\thave $(git rev-parse a)\n\tEOF\n\ttest-tool pkt-line pack <pkt_cmd >in &&\n\ttest-tool serve-v2 --stateless-rpc >out <in &&\n\n\nI'm not sure how portable that is, though. Maybe using `while read -r` instead\nof `cat /dev/stdin`?\n\n"},{"id":"432340","messageId":"xmqqpmume0oa.fsf@gitster.g","threadId":"56182","inReplyTo":"CDFBG4S23B1M.141X9FIPP7YHS@schmidt","subject":"Re: [PATCH 1/3] t5730: introduce fetch command helper","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-08-09T22:03:01Z","receivedAt":"2021-08-09T22:03:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Kim Altintop <kim@eagain.st> writes:\n\n> Perhaps an alternative would be:\n>\n> \twrite_fetch_command () {\n> \t\twrite_command fetch &&\n> \t\techo \"0001\" &&\n> \t\techo \"no-progress\" &&\n> \t\tcat /dev/stdin &&\n> \t\techo \"done\" &&\n> \t\techo \"0000\"\n>  \t}\n>\n>\n> Which would then be called like so:\n>\n> \twrite_fetch_command >pkt_cmd <<-EOF &&\n> \twant-ref refs/heads/main\n> \thave $(git rev-parse a)\n> \tEOF\n> \ttest-tool pkt-line pack <pkt_cmd >in &&\n> \ttest-tool serve-v2 --stateless-rpc >out <in &&\n>\n>\n> I'm not sure how portable that is, though. Maybe using `while read -r` instead\n> of `cat /dev/stdin`?\n\nIf you drop /dev/stdin, the result would be emimently portable.\n\"cat\" without any argument reads from the standard input stream\nand copies it to the standard output stream.\n"},{"id":"432347","messageId":"YRGz1bFdBP8X/U29@google.com","threadId":"56182","inReplyTo":"xmqqpmume0oa.fsf@gitster.g","subject":"Re: [PATCH 1/3] t5730: introduce fetch command helper","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2021-08-09T23:01:41Z","receivedAt":"2021-08-09T23:01:45Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Junio C Hamano wrote:\n> Kim Altintop <kim@eagain.st> writes:\n\n>> Perhaps an alternative would be:\n>>\n>> \twrite_fetch_command () {\n>> \t\twrite_command fetch &&\n>> \t\techo \"0001\" &&\n>> \t\techo \"no-progress\" &&\n>> \t\tcat /dev/stdin &&\n>> \t\techo \"done\" &&\n>> \t\techo \"0000\"\n>>  \t}\n>>\n>>\n>> Which would then be called like so:\n>>\n>> \twrite_fetch_command >pkt_cmd <<-EOF &&\n>> \twant-ref refs/heads/main\n>> \thave $(git rev-parse a)\n>> \tEOF\n>> \ttest-tool pkt-line pack <pkt_cmd >in &&\n>> \ttest-tool serve-v2 --stateless-rpc >out <in &&\n>>\n>>\n>> I'm not sure how portable that is, though. Maybe using `while read -r` instead\n>> of `cat /dev/stdin`?\n>\n> If you drop /dev/stdin, the result would be emimently portable.\n> \"cat\" without any argument reads from the standard input stream\n> and copies it to the standard output stream.\n\nYep, with that tweak (using \"cat\" instead of \"cat /dev/stdin\") this\nlooks like a pleasant enough helper.\n\nJonathan\n"},{"id":"432360","messageId":"20210810114433.GC41632@schmidt.localdomain","threadId":"56182","inReplyTo":"YRGz1bFdBP8X/U29@google.com","subject":"Re: [PATCH 1/3] t5730: introduce fetch command helper","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-08-10T09:44:36Z","receivedAt":"2021-08-10T09:44:47Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"Jonathan Nieder <jrnieder@gmail.com> wrote:\n>\n> Junio C Hamano wrote:\n> > Kim Altintop <kim@eagain.st> writes:\n>\n> >> Perhaps an alternative would be:\n> >>\n> >> \twrite_fetch_command () {\n> >> \t\twrite_command fetch &&\n> >> \t\techo \"0001\" &&\n> >> \t\techo \"no-progress\" &&\n> >> \t\tcat /dev/stdin &&\n> >> \t\techo \"done\" &&\n> >> \t\techo \"0000\"\n> >>  \t}\n> >>\n> >>\n> >> Which would then be called like so:\n> >>\n> >> \twrite_fetch_command >pkt_cmd <<-EOF &&\n> >> \twant-ref refs/heads/main\n> >> \thave $(git rev-parse a)\n> >> \tEOF\n> >> \ttest-tool pkt-line pack <pkt_cmd >in &&\n> >> \ttest-tool serve-v2 --stateless-rpc >out <in &&\n> >>\n> >>\n> >> I'm not sure how portable that is, though. Maybe using `while read -r` instead\n> >> of `cat /dev/stdin`?\n> >\n> > If you drop /dev/stdin, the result would be emimently portable.\n> > \"cat\" without any argument reads from the standard input stream\n> > and copies it to the standard output stream.\n>\n> Yep, with that tweak (using \"cat\" instead of \"cat /dev/stdin\") this\n> looks like a pleasant enough helper.\n\nGreat, I'll reroll like this then. Thanks!\n\n"},{"id":"432361","messageId":"20210810114941.GE41632@schmidt.localdomain","threadId":"56182","inReplyTo":"20210809175530.75326-4-kim@eagain.st","subject":"Re: [PATCH 3/3] docs: clarify the interaction of transfer.hideRefs and namespaces","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-08-10T09:49:43Z","receivedAt":"2021-08-10T09:49:55Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"> diff --git a/Documentation/config/transfer.txt b/Documentation/config/transfer.txt\n> index 505126a780..09ebb399ce 100644\n> --- a/Documentation/config/transfer.txt\n> +++ b/Documentation/config/transfer.txt\n> @@ -52,13 +52,16 @@ If you have multiple hideRefs values, later entries override earlier ones\n>  (and entries in more-specific config files override less-specific ones).\n>  +\n>  If a namespace is in use, the namespace prefix is stripped from each\n> -reference before it is matched against `transfer.hiderefs` patterns.\n> -For example, if `refs/heads/master` is specified in `transfer.hideRefs` and\n> -the current namespace is `foo`, then `refs/namespaces/foo/refs/heads/master`\n> -is omitted from the advertisements but `refs/heads/master` and\n> -`refs/namespaces/bar/refs/heads/master` are still advertised as so-called\n> -\"have\" lines. In order to match refs before stripping, add a `^` in front of\n> -the ref name. If you combine `!` and `^`, `!` must be specified first.\n> +reference before it is matched against `transfer.hiderefs` patterns. For\n> +example, if `refs/heads/master` is specified in `transfer.hideRefs` and the\n> +current namespace is `foo`, then `refs/namespaces/foo/refs/heads/master` is\n> +omitted from the advertisements. If `uploadpack.allowRefInWant` is set,\n> +`upload-pack` will treat `want-ref refs/heads/master` in a protocol v2\n> +`fetch` command as if `refs/heads/master` was unknown. Note, however, that\n> +`receive-pack` will still advertise the object id `refs/heads/master` is\n> +pointing to, but will conceil the name of the ref. In order to match refs\n> +before stripping, add a `^` in front of the ref name. If you combine `!` and\n> +`^`, `!` must be specified first.\n>  +\n>  Even if you hide refs, a client may still be able to steal the target\n>  objects via the techniques described in the \"SECURITY\" section of the\n\nI'd appreciate some feedback on this one before rerolling. Having looked at the\ncode many times recently, it makes sense to me, but that could be different for\nsomeone with less fresh memory. Thanks!\n\n"},{"id":"432623","messageId":"20210813062237.10403-1-kim@eagain.st","threadId":"56182","inReplyTo":"20210809175530.75326-1-kim@eagain.st","subject":"[PATCH v6 0/3] upload-pack: treat want-ref relative to namespace","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-08-13T06:23:30Z","receivedAt":"2021-08-13T06:23:42Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"Round six:\n\n* simplifies the test helper in 1/3 as per review discussion\n* rephrases the doc change in 3/3 to make it read less dense\n* 2/3 only updates the tests to use the revised helper\n\nCC'ing Johannes Schindelin as suggested by git-contacts.\n\n\nPublished-As: https://github.com/kim/git/tree/ka/namespaced-want-ref-v6\n\nKim Altintop (3):\n  t5730: introduce fetch command helper\n  upload-pack.c: treat want-ref relative to namespace\n  docs: clarify the interaction of transfer.hideRefs and namespaces\n\n Documentation/config/transfer.txt  |  14 +-\n t/t5703-upload-pack-ref-in-want.sh | 208 ++++++++++++++++++++++++-----\n upload-pack.c                      |  18 ++-\n 3 files changed, 192 insertions(+), 48 deletions(-)\n\n--\n2.32.0\n\n\n"},{"id":"432624","messageId":"20210813062237.10403-2-kim@eagain.st","threadId":"56182","inReplyTo":"20210809175530.75326-1-kim@eagain.st","subject":"[PATCH v6 1/3] t5730: introduce fetch command helper","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-08-13T06:23:40Z","receivedAt":"2021-08-13T06:23:53Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"Assembling a \"raw\" fetch command to be fed directly to \"test-tool serve-v2\"\nis extracted into a test helper.\n\nSuggested-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Kim Altintop <kim@eagain.st>\n---\n t/t5703-upload-pack-ref-in-want.sh | 73 +++++++++++++++---------------\n 1 file changed, 37 insertions(+), 36 deletions(-)\n\ndiff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh\nindex e9e471621d..3dad21ff45 100755\n--- a/t/t5703-upload-pack-ref-in-want.sh\n+++ b/t/t5703-upload-pack-ref-in-want.sh\n@@ -40,6 +40,30 @@ write_command () {\n \tfi\n }\n\n+# Write a complete fetch command to stdout, suitable for use with `test-tool\n+# pkt-line`. \"want-ref\", \"want\", and \"have\" lines are read from stdin.\n+#\n+# Examples:\n+#\n+# write_fetch_command <<-EOF\n+# want-ref refs/heads/main\n+# have $(git rev-parse a)\n+# EOF\n+#\n+# write_fetch_command <<-EOF\n+# want $(git rev-parse b)\n+# have $(git rev-parse a)\n+# EOF\n+#\n+write_fetch_command () {\n+\twrite_command fetch &&\n+\techo \"0001\" &&\n+\techo \"no-progress\" &&\n+\tcat &&\n+\techo \"done\" &&\n+\techo \"0000\"\n+}\n+\n # c(o/foo) d(o/bar)\n #        \\ /\n #         b   e(baz)  f(main)\n@@ -77,15 +101,11 @@ test_expect_success 'config controls ref-in-want advertisement' '\n '\n\n test_expect_success 'invalid want-ref line' '\n-\ttest-tool pkt-line pack >in <<-EOF &&\n-\t$(write_command fetch)\n-\t0001\n-\tno-progress\n+\twrite_fetch_command >pkt <<-EOF &&\n \twant-ref refs/heads/non-existent\n-\tdone\n-\t0000\n \tEOF\n\n+\ttest-tool pkt-line pack <pkt >in &&\n \ttest_must_fail test-tool serve-v2 --stateless-rpc 2>out <in &&\n \tgrep \"unknown ref\" out\n '\n@@ -97,16 +117,11 @@ test_expect_success 'basic want-ref' '\n \tEOF\n \tgit rev-parse f >expected_commits &&\n\n-\toid=$(git rev-parse a) &&\n-\ttest-tool pkt-line pack >in <<-EOF &&\n-\t$(write_command fetch)\n-\t0001\n-\tno-progress\n+\twrite_fetch_command >pkt <<-EOF &&\n \twant-ref refs/heads/main\n-\thave $oid\n-\tdone\n-\t0000\n+\thave $(git rev-parse a)\n \tEOF\n+\ttest-tool pkt-line pack <pkt >in &&\n\n \ttest-tool serve-v2 --stateless-rpc >out <in &&\n \tcheck_output\n@@ -121,17 +136,12 @@ test_expect_success 'multiple want-ref lines' '\n \tEOF\n \tgit rev-parse c d >expected_commits &&\n\n-\toid=$(git rev-parse b) &&\n-\ttest-tool pkt-line pack >in <<-EOF &&\n-\t$(write_command fetch)\n-\t0001\n-\tno-progress\n+\twrite_fetch_command >pkt <<-EOF &&\n \twant-ref refs/heads/o/foo\n \twant-ref refs/heads/o/bar\n-\thave $oid\n-\tdone\n-\t0000\n+\thave $(git rev-parse b)\n \tEOF\n+\ttest-tool pkt-line pack <pkt >in &&\n\n \ttest-tool serve-v2 --stateless-rpc >out <in &&\n \tcheck_output\n@@ -144,16 +154,12 @@ test_expect_success 'mix want and want-ref' '\n \tEOF\n \tgit rev-parse e f >expected_commits &&\n\n-\ttest-tool pkt-line pack >in <<-EOF &&\n-\t$(write_command fetch)\n-\t0001\n-\tno-progress\n+\twrite_fetch_command >pkt <<-EOF &&\n \twant-ref refs/heads/main\n \twant $(git rev-parse e)\n \thave $(git rev-parse a)\n-\tdone\n-\t0000\n \tEOF\n+\ttest-tool pkt-line pack <pkt >in &&\n\n \ttest-tool serve-v2 --stateless-rpc >out <in &&\n \tcheck_output\n@@ -166,16 +172,11 @@ test_expect_success 'want-ref with ref we already have commit for' '\n \tEOF\n \t>expected_commits &&\n\n-\toid=$(git rev-parse c) &&\n-\ttest-tool pkt-line pack >in <<-EOF &&\n-\t$(write_command fetch)\n-\t0001\n-\tno-progress\n+\twrite_fetch_command >pkt <<-EOF &&\n \twant-ref refs/heads/o/foo\n-\thave $oid\n-\tdone\n-\t0000\n+\thave $(git rev-parse c)\n \tEOF\n+\ttest-tool pkt-line pack <pkt >in &&\n\n \ttest-tool serve-v2 --stateless-rpc >out <in &&\n \tcheck_output\n--\n2.32.0\n\n\n"},{"id":"432626","messageId":"20210813062237.10403-3-kim@eagain.st","threadId":"56182","inReplyTo":"20210809175530.75326-1-kim@eagain.st","subject":"[PATCH v6 2/3] upload-pack.c: treat want-ref relative to namespace","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-08-13T06:23:50Z","receivedAt":"2021-08-13T06:24:00Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"When 'upload-pack' runs within the context of a git namespace, treat any\n'want-ref' lines the client sends as relative to that namespace.\n\nAlso check if the wanted ref is hidden via 'hideRefs'. If it is hidden,\nrespond with an error as if the ref didn't exist.\n\nHelped-by: Jonathan Tan <jonathantanmy@google.com>\nSigned-off-by: Kim Altintop <kim@eagain.st>\n---\n t/t5703-upload-pack-ref-in-want.sh | 135 +++++++++++++++++++++++++++++\n upload-pack.c                      |  18 ++--\n 2 files changed, 146 insertions(+), 7 deletions(-)\n\ndiff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh\nindex 3dad21ff45..220098523a 100755\n--- a/t/t5703-upload-pack-ref-in-want.sh\n+++ b/t/t5703-upload-pack-ref-in-want.sh\n@@ -299,6 +299,141 @@ test_expect_success 'fetching with wildcard that matches multiple refs' '\n \tgrep \"want-ref refs/heads/o/bar\" log\n '\n\n+REPO=\"$(pwd)/repo-ns\"\n+\n+test_expect_success 'setup namespaced repo' '\n+\t(\n+\t\tgit init -b main \"$REPO\" &&\n+\t\tcd \"$REPO\" &&\n+\t\ttest_commit a &&\n+\t\ttest_commit b &&\n+\t\tgit checkout a &&\n+\t\ttest_commit c &&\n+\t\tgit checkout a &&\n+\t\ttest_commit d &&\n+\t\tgit update-ref refs/heads/ns-no b &&\n+\t\tgit update-ref refs/namespaces/ns/refs/heads/ns-yes c &&\n+\t\tgit update-ref refs/namespaces/ns/refs/heads/hidden d\n+\t) &&\n+\tgit -C \"$REPO\" config uploadpack.allowRefInWant true\n+'\n+\n+test_expect_success 'with namespace: want-ref is considered relative to namespace' '\n+\twanted_ref=refs/heads/ns-yes &&\n+\n+\toid=$(git -C \"$REPO\" rev-parse \"refs/namespaces/ns/$wanted_ref\") &&\n+\tcat >expected_refs <<-EOF &&\n+\t$oid $wanted_ref\n+\tEOF\n+\tcat >expected_commits <<-EOF &&\n+\t$oid\n+\t$(git -C \"$REPO\" rev-parse a)\n+\tEOF\n+\n+\twrite_fetch_command >pkt <<-EOF &&\n+\twant-ref $wanted_ref\n+\tEOF\n+\ttest-tool pkt-line pack <pkt >in &&\n+\n+\tGIT_NAMESPACE=ns test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tcheck_output\n+'\n+\n+test_expect_success 'with namespace: want-ref outside namespace is unknown' '\n+\twanted_ref=refs/heads/ns-no &&\n+\n+\twrite_fetch_command >pkt <<-EOF &&\n+\twant-ref $wanted_ref\n+\tEOF\n+\ttest-tool pkt-line pack <pkt >in &&\n+\n+\ttest_must_fail env GIT_NAMESPACE=ns \\\n+\t\ttest-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tgrep \"unknown ref\" out\n+'\n+\n+# Cross-check refs/heads/ns-no indeed exists\n+test_expect_success 'without namespace: want-ref outside namespace succeeds' '\n+\twanted_ref=refs/heads/ns-no &&\n+\n+\toid=$(git -C \"$REPO\" rev-parse $wanted_ref) &&\n+\tcat >expected_refs <<-EOF &&\n+\t$oid $wanted_ref\n+\tEOF\n+\tcat >expected_commits <<-EOF &&\n+\t$oid\n+\t$(git -C \"$REPO\" rev-parse a)\n+\tEOF\n+\n+\twrite_fetch_command >pkt <<-EOF &&\n+\twant-ref $wanted_ref\n+\tEOF\n+\ttest-tool pkt-line pack <pkt >in &&\n+\n+\ttest-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tcheck_output\n+'\n+\n+test_expect_success 'with namespace: hideRefs is matched, relative to namespace' '\n+\twanted_ref=refs/heads/hidden &&\n+\tgit -C \"$REPO\" config transfer.hideRefs $wanted_ref &&\n+\n+\twrite_fetch_command >pkt <<-EOF &&\n+\twant-ref $wanted_ref\n+\tEOF\n+\ttest-tool pkt-line pack <pkt >in &&\n+\n+\ttest_must_fail env GIT_NAMESPACE=ns \\\n+\t\ttest-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tgrep \"unknown ref\" out\n+'\n+\n+# Cross-check refs/heads/hidden indeed exists\n+test_expect_success 'with namespace: want-ref succeeds if hideRefs is removed' '\n+\twanted_ref=refs/heads/hidden &&\n+\tgit -C \"$REPO\" config --unset transfer.hideRefs $wanted_ref &&\n+\n+\toid=$(git -C \"$REPO\" rev-parse \"refs/namespaces/ns/$wanted_ref\") &&\n+\tcat >expected_refs <<-EOF &&\n+\t$oid $wanted_ref\n+\tEOF\n+\tcat >expected_commits <<-EOF &&\n+\t$oid\n+\t$(git -C \"$REPO\" rev-parse a)\n+\tEOF\n+\n+\twrite_fetch_command >pkt <<-EOF &&\n+\twant-ref $wanted_ref\n+\tEOF\n+\ttest-tool pkt-line pack <pkt >in &&\n+\n+\tGIT_NAMESPACE=ns test-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tcheck_output\n+'\n+\n+test_expect_success 'without namespace: relative hideRefs does not match' '\n+\twanted_ref=refs/namespaces/ns/refs/heads/hidden &&\n+\tgit -C \"$REPO\" config transfer.hideRefs refs/heads/hidden &&\n+\n+\toid=$(git -C \"$REPO\" rev-parse $wanted_ref) &&\n+\tcat >expected_refs <<-EOF &&\n+\t$oid $wanted_ref\n+\tEOF\n+\tcat >expected_commits <<-EOF &&\n+\t$oid\n+\t$(git -C \"$REPO\" rev-parse a)\n+\tEOF\n+\n+\twrite_fetch_command >pkt <<-EOF &&\n+\twant-ref $wanted_ref\n+\tEOF\n+\ttest-tool pkt-line pack <pkt >in &&\n+\n+\ttest-tool -C \"$REPO\" serve-v2 --stateless-rpc >out <in &&\n+\tcheck_output\n+'\n+\n+\n . \"$TEST_DIRECTORY\"/lib-httpd.sh\n start_httpd\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 297b76fcb4..6ce07231d3 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -1417,21 +1417,25 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,\n \t\t\t  struct string_list *wanted_refs,\n \t\t\t  struct object_array *want_obj)\n {\n-\tconst char *arg;\n-\tif (skip_prefix(line, \"want-ref \", &arg)) {\n+\tconst char *refname_nons;\n+\tif (skip_prefix(line, \"want-ref \", &refname_nons)) {\n \t\tstruct object_id oid;\n \t\tstruct string_list_item *item;\n \t\tstruct object *o;\n+\t\tstruct strbuf refname = STRBUF_INIT;\n\n-\t\tif (read_ref(arg, &oid)) {\n-\t\t\tpacket_writer_error(writer, \"unknown ref %s\", arg);\n-\t\t\tdie(\"unknown ref %s\", arg);\n+\t\tstrbuf_addf(&refname, \"%s%s\", get_git_namespace(), refname_nons);\n+\t\tif (ref_is_hidden(refname_nons, refname.buf) ||\n+\t\t    read_ref(refname.buf, &oid)) {\n+\t\t\tpacket_writer_error(writer, \"unknown ref %s\", refname_nons);\n+\t\t\tdie(\"unknown ref %s\", refname_nons);\n \t\t}\n+\t\tstrbuf_release(&refname);\n\n-\t\titem = string_list_append(wanted_refs, arg);\n+\t\titem = string_list_append(wanted_refs, refname_nons);\n \t\titem->util = oiddup(&oid);\n\n-\t\to = parse_object_or_die(&oid, arg);\n+\t\to = parse_object_or_die(&oid, refname_nons);\n \t\tif (!(o->flags & WANTED)) {\n \t\t\to->flags |= WANTED;\n \t\t\tadd_object_array(o, NULL, want_obj);\n--\n2.32.0\n\n\n"},{"id":"432625","messageId":"20210813062237.10403-4-kim@eagain.st","threadId":"56182","inReplyTo":"20210809175530.75326-1-kim@eagain.st","subject":"[PATCH v6 3/3] docs: clarify the interaction of transfer.hideRefs and namespaces","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-08-13T06:23:54Z","receivedAt":"2021-08-13T06:24:01Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"Expand the section about namespaces in the documentation of\n`transfer.hideRefs` to point out the subtle differences between\n`upload-pack` and `receive-pack`.\n\nffcfb68176 (upload-pack.c: treat want-ref relative to namespace,\n2021-07-30) taught `upload-pack` to reject `want-ref`s for hidden refs,\nwhich is now mentioned. It is clarified that at no point the name of a\nhidden ref is revealed, but the object id it points to may.\n\nSigned-off-by: Kim Altintop <kim@eagain.st>\n---\n Documentation/config/transfer.txt | 14 +++++++++-----\n 1 file changed, 9 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/config/transfer.txt b/Documentation/config/transfer.txt\nindex 505126a780..b49429eb4d 100644\n--- a/Documentation/config/transfer.txt\n+++ b/Documentation/config/transfer.txt\n@@ -52,13 +52,17 @@ If you have multiple hideRefs values, later entries override earlier ones\n (and entries in more-specific config files override less-specific ones).\n +\n If a namespace is in use, the namespace prefix is stripped from each\n-reference before it is matched against `transfer.hiderefs` patterns.\n+reference before it is matched against `transfer.hiderefs` patterns. In\n+order to match refs before stripping, add a `^` in front of the ref name. If\n+you combine `!` and `^`, `!` must be specified first.\n++\n For example, if `refs/heads/master` is specified in `transfer.hideRefs` and\n the current namespace is `foo`, then `refs/namespaces/foo/refs/heads/master`\n-is omitted from the advertisements but `refs/heads/master` and\n-`refs/namespaces/bar/refs/heads/master` are still advertised as so-called\n-\"have\" lines. In order to match refs before stripping, add a `^` in front of\n-the ref name. If you combine `!` and `^`, `!` must be specified first.\n+is omitted from the advertisements. If `uploadpack.allowRefInWant` is set,\n+`upload-pack` will treat `want-ref refs/heads/master` in a protocol v2\n+`fetch` command as if `refs/namespaces/foo/refs/heads/master` did not exist.\n+`receive-pack`, on the other hand, will still advertise the object id the\n+ref is pointing to without mentioning its name (a so-called \".have\" line).\n +\n Even if you hide refs, a client may still be able to steal the target\n objects via the techniques described in the \"SECURITY\" section of the\n--\n2.32.0\n\n\n"},{"id":"432748","messageId":"nycvar.QRO.7.76.6.2108142344570.59@tvgsbejvaqbjf.bet","threadId":"56182","inReplyTo":"20210813062237.10403-1-kim@eagain.st","subject":"Re: [PATCH v6 0/3] upload-pack: treat want-ref relative to namespace","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-08-14T21:46:49Z","receivedAt":"2021-08-14T21:56:03Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Kim,\n\nOn Fri, 13 Aug 2021, Kim Altintop wrote:\n\n> CC'ing Johannes Schindelin as suggested by git-contacts.\n\n`git-contacts` wouldn't know that there are better experts on the\nnamespace matter.\n\nMy only comment is that I would find the diff to `upload-pack.c` much\neasier to parse if the `arg` variable hadn't been renamed.\n\nCiao,\nJohannes\n"},{"id":"432768","messageId":"xmqqeeausi6g.fsf@gitster.g","threadId":"56182","inReplyTo":"nycvar.QRO.7.76.6.2108142344570.59@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v6 0/3] upload-pack: treat want-ref relative to namespace","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-08-15T17:59:19Z","receivedAt":"2021-08-15T18:01:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> My only comment is that I would find the diff to `upload-pack.c` much\n> easier to parse if the `arg` variable hadn't been renamed.\n\nIf we only look at the patch text, perhaps.\n\nWhat is left in the resulting file is what people will be reading in\nthe coming years, and because instead of just one string (whose name\ndid not matter and a short-and-sweet arg was a perfect name for it),\nwe now have to deal with two strings (one is the full and real name,\nand the other is the name without namespace prefix) we need to be\nclear which is which, the rename is an essential part of the patch\nto keep the result easier to understand.\n\nWhen I apply a patch to my tree to review it (e-mailed patch review\nis merely an efficient way to transmit the change---I do not expect\nthat I can always review all non-trivial patches without stepping\nout of my MUA).  My review, after noticing typos, style glitches and\ndesign issues, of code and its correctness will begin after that.\n\n\"git show\" can be driven with different options and in some cases\nuse of certaion options helps seeing what is going on quite well.\n\"--diff-algorithm\" shifts which preimage lines match which postimage\nlines and sometimes helps reduce the clutter greatly. \"--word-diff\"\nis a great tool to see where in reflowed documentation patch actual\nchanges are. \"--color-moved\" lets reviewer skip pure code movement,\nvarious options to \"ignore space\" sometimes helps declutter the\npatch. etc.\n\nI wonder if we already have an option (or if there isn't, if we can\ndesign such a new option cleanly), that would help in this case.\nEssentially, what you want is an \"I know what used to be arg is\nrenamed to refname_nons in this patch; take advantage of that\nknowledge and show a diff that is less cluttered\" option.\n\nThanks for a comment, anyway.\n"},{"id":"432769","messageId":"20210815213453.GB10013@schmidt.localdomain","threadId":"56182","inReplyTo":"nycvar.QRO.7.76.6.2108142344570.59@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v6 0/3] upload-pack: treat want-ref relative to namespace","fromName":"Kim Altintop","fromEmail":"kim@eagain.st","sentAt":"2021-08-15T19:35:04Z","receivedAt":"2021-08-15T19:35:19Z","isPatch":true,"sender":{"key":"kim@eagain.st","avatar":"https://avatars.githubusercontent.com/u/6163?v=4"},"body":"Hi Johannes,\n\nthanks for your response. This is my very first patch to git.git, and a lot of\nit is learning about the conventions and expectations towards contributors, as\nwell as getting familiar with the tooling while trying to avoid silly mistakes.\nSo please bear with me.\n\nOn Sat, 14 Aug 2021 Johannes Schindelin wrote:\n\n> > CC'ing Johannes Schindelin as suggested by git-contacts.\n>\n> `git-contacts` wouldn't know that there are better experts on the\n> namespace matter.\n\nI can see now that this could come across weird. I should've written:\n\n\"CC'ing Johannes Schindelin, who started to turn up in `git-contacts` output,\nalthough I couldn't quite infer why. I haven't received any feedback about the\ndocumentation change yet, and didn't have much success trying to find reviewers\nby inspecting the history (parts of the file where moved). I am assuming that\n`git-contacts` is better than me at this, and Johannes' name shows up because of\ntouching the documentation. Johannes: feel free to ignore if this assumption is\nwrong.\"\n\nWith this said, if you have any suggestions about finding reviewers for specific\nparts of a patch, or who are the experts on a more cross-cutting topic, I would\nappreciate if you'd share them!\n\n> My only comment is that I would find the diff to `upload-pack.c` much\n> easier to parse if the `arg` variable hadn't been renamed.\n\nCan you explain why? Just because the diff would be smaller? I can see that in a\nlarger patch it might have been preferable to put the rename into a separate\ncommit, but in a hunk-sized change it seemed fine. It is also that this\nparticular naming (\"refname_nons\") is used in other places in upload-pack.c, so\nit seemed obvious that, if I introduce namespace handling where it was\npreviously missing, the terminology (if you will) should be the same.\n\nFrom you comment, it seems like the proposer of a patch should assume that the\nreviewers only look at the diff as sent in the email, and not any context.\nJunio's response suggests something else, but I guess it's fair that if someone\nfeels like they got CC'ed by mistake, they're not going to spend too much time.\n\nSo my question from above stands: are there better ways to find the right people\nto CC, especially for newbies?\n\nThanks,\nKim\n\n"},{"id":"432795","messageId":"nycvar.QRO.7.76.6.2108161431500.55@tvgsbejvaqbjf.bet","threadId":"56182","inReplyTo":"20210815213453.GB10013@schmidt.localdomain","subject":"Re: [PATCH v6 0/3] upload-pack: treat want-ref relative to namespace","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-08-16T12:39:48Z","receivedAt":"2021-08-16T12:40:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Kim,\n\nOn Sun, 15 Aug 2021, Kim Altintop wrote:\n\n> On Sat, 14 Aug 2021 Johannes Schindelin wrote:\n>\n> > My only comment is that I would find the diff to `upload-pack.c` much\n> > easier to parse if the `arg` variable hadn't been renamed.\n>\n> Can you explain why?\n\nYes. I prefer patches to be really obvious. That way, it is really easy to\nspot bugs.\n\nIn this instance, the same patch that introduces a conditional block\n_also_ renames an involved variable.\n\nTo satisfy myself that the patch does what is intended, I therefore have\nto virtually split the patch into the rename part and the\nadded-conditional-block part.\n\nIt would be easier for me if the modifications were presented as two\nseparate patches. And I could imagine that I am not alone in this: you\nyourself might also have an easier time looking at the commits in six\nmonths from now if those concerns are separated into their own commit.\n\n> Just because the diff would be smaller? I can see that in a larger patch\n> it might have been preferable to put the rename into a separate commit,\n> but in a hunk-sized change it seemed fine. It is also that this\n> particular naming (\"refname_nons\") is used in other places in\n> upload-pack.c, so it seemed obvious that, if I introduce namespace\n> handling where it was previously missing, the terminology (if you will)\n> should be the same.\n>\n> From you comment, it seems like the proposer of a patch should assume\n> that the reviewers only look at the diff as sent in the email, and not\n> any context. Junio's response suggests something else, but I guess it's\n> fair that if someone feels like they got CC'ed by mistake, they're not\n> going to spend too much time.\n\nIn this instance, I indeed did not spend more time than on reviewing the\npatch, simply because I am (currently, at least) not all that familiar\nwith the `upload-pack.c` machinery. I probably touched it in the past, but\nfor the moment, all I can comment on is the shape of the patch series,\nwhich is what I did.\n\n> So my question from above stands: are there better ways to find the\n> right people to CC, especially for newbies?\n\nWhen I look for reviewers in projects other than the ones where I know the\nusual reviewers' special areas of interest, I like to pick a function at\nthe center of my contribution, then look at `git log -L\n:<function>:<file>` and try to figure out who was the last person to\nimplement non-stylistic changes on that function. This has worked\nrelatively well for me, in the past. Maybe it can help you here, too?\n\nCiao,\nDscho\n"}]}