{"thread":{"id":"66385","subject":"[PATCH] builtin/fetch-pack: indicate when we have an exact oid","startedAt":"2026-09-24T13:50:39Z","lastAt":"2026-09-25T04:40:30Z","messageCount":3,"participants":["Nathan Froyd via GitGitGadget","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"553181","messageId":"pull.2420.git.git.1790257834680.gitgitgadget@gmail.com","threadId":"66385","inReplyTo":null,"subject":"[PATCH] builtin/fetch-pack: indicate when we have an exact oid","fromName":"Nathan Froyd via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-09-24T13:50:34Z","receivedAt":"2026-09-24T13:50:39Z","isPatch":true,"body":"From: Nathan Froyd <froydnj@gmail.com>\n\nThe `git fetch` path, when parsing OIDs, properly sets `exact_oid` on\nthe relevant refs; the equivalent path for `git fetch-pack` does not.\nThis oversight results in an invocation of `git fetch-pack $OID`\nsending `want-ref $OID`, which results in errors like:\n\n  fatal: unknown ref $OID\n  fatal: remote error: unknown ref $OID\n\nMake the two paths equivalent by setting `exact_oid` properly.\n\nSigned-off-by: Nathan Froyd <froydnj@gmail.com>\n---\n    builtin/fetch-pack: indicate when we have an exact oid\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2420%2Ffroydnj%2Ffroydnj-fetch-pack-exact-oid-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2420/froydnj/froydnj-fetch-pack-exact-oid-v1\nPull-Request: https://github.com/git/git/pull/2420\n\n builtin/fetch-pack.c               |  4 +-\n t/t5703-upload-pack-ref-in-want.sh | 68 ++++++++++++++++++++++++++++++\n 2 files changed, 71 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c\nindex 86754296fa..bef8a3dfc5 100644\n--- a/builtin/fetch-pack.c\n+++ b/builtin/fetch-pack.c\n@@ -23,13 +23,14 @@ static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,\n \tstruct ref *ref;\n \tstruct object_id oid;\n \tconst char *p;\n+\tint exact_oid = 0;\n \n \tif (!parse_oid_hex(name, &oid, &p)) {\n \t\tif (*p == ' ') {\n \t\t\t/* <oid> <ref>, find refname */\n \t\t\tname = p + 1;\n \t\t} else if (*p == '\\0') {\n-\t\t\t; /* <oid>, leave oid as name */\n+\t\t\texact_oid = 1; /* <oid>, leave oid as name */\n \t\t} else {\n \t\t\t/* <ref>, clear cruft from oid */\n \t\t\toidclr(&oid, the_repository->hash_algo);\n@@ -41,6 +42,7 @@ static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,\n \n \tref = alloc_ref(name);\n \toidcpy(&ref->old_oid, &oid);\n+\tref->exact_oid = exact_oid;\n \t(*nr)++;\n \tALLOC_GROW(*sought, *nr, *alloc);\n \t(*sought)[*nr - 1] = ref;\ndiff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh\nindex 330d049b2c..c02e1fc79c 100755\n--- a/t/t5703-upload-pack-ref-in-want.sh\n+++ b/t/t5703-upload-pack-ref-in-want.sh\n@@ -242,6 +242,74 @@ test_expect_success 'fetching with exact OID' '\n \ttest_grep \"want $oid\" log\n '\n \n+test_expect_success 'fetch-pack with ref and exact OID from arguments' '\n+\ttest_when_finished \"rm -f log\" &&\n+\n+\trm -rf local &&\n+\tcp -r \"$LOCAL_PRISTINE\" local &&\n+\toid=$(git -C \"$REPO\" rev-parse d) &&\n+\tmain_oid=$(git -C \"$REPO\" rev-parse main) &&\n+\tGIT_TRACE_PACKET=\"$(pwd)/log\" git -C local fetch-pack \\\n+\t\t\"$REPO\" refs/heads/main \"$oid\" >actual &&\n+\tcat >expected <<-EOF &&\n+\t$oid $oid\n+\t$main_oid refs/heads/main\n+\tEOF\n+\tsort expected >expected.sorted &&\n+\tsort actual >actual.sorted &&\n+\ttest_cmp expected.sorted actual.sorted &&\n+\tgit -C local cat-file -e \"$oid\" &&\n+\tgit -C local cat-file -e \"$main_oid\" &&\n+\ttest_grep \"want $oid\" log &&\n+\ttest_grep \"want-ref refs/heads/main\" log\n+'\n+\n+test_expect_success 'fetch-pack with ref and exact OID from stdin' '\n+\ttest_when_finished \"rm -f log\" &&\n+\n+\trm -rf local &&\n+\tcp -r \"$LOCAL_PRISTINE\" local &&\n+\toid=$(git -C \"$REPO\" rev-parse d) &&\n+\tmain_oid=$(git -C \"$REPO\" rev-parse main) &&\n+\tcat >input <<-EOF &&\n+\trefs/heads/main\n+\t$oid\n+\tEOF\n+\tGIT_TRACE_PACKET=\"$(pwd)/log\" git -C local fetch-pack \\\n+\t\t--stdin \"$REPO\" <input >actual &&\n+\tcat >expected <<-EOF &&\n+\t$oid $oid\n+\t$main_oid refs/heads/main\n+\tEOF\n+\tsort expected >expected.sorted &&\n+\tsort actual >actual.sorted &&\n+\ttest_cmp expected.sorted actual.sorted &&\n+\tgit -C local cat-file -e \"$oid\" &&\n+\tgit -C local cat-file -e \"$main_oid\" &&\n+\ttest_grep \"want $oid\" log &&\n+\ttest_grep \"want-ref refs/heads/main\" log\n+'\n+\n+test_expect_success 'fetch-pack with OID and ref name resolves the ref' '\n+\ttest_when_finished \"rm -f log\" &&\n+\n+\trm -rf local &&\n+\tcp -r \"$LOCAL_PRISTINE\" local &&\n+\toid=$(git -C \"$REPO\" rev-parse d) &&\n+\tmain_oid=$(git -C \"$REPO\" rev-parse main) &&\n+\tcat >input <<-EOF &&\n+\t$oid refs/heads/main\n+\tEOF\n+\tGIT_TRACE_PACKET=\"$(pwd)/log\" git -C local fetch-pack \\\n+\t\t--stdin \"$REPO\" <input >actual &&\n+\tcat >expected <<-EOF &&\n+\t$main_oid refs/heads/main\n+\tEOF\n+\ttest_cmp expected actual &&\n+\tgit -C local cat-file -e \"$main_oid\" &&\n+\ttest_grep \"want-ref refs/heads/main\" log\n+'\n+\n test_expect_success 'fetching multiple refs' '\n \ttest_when_finished \"rm -f log\" &&\n \n\nbase-commit: 3bc0341126508f78f5869cbfc0005e987efdf0c7\n-- \ngitgitgadget\n"},{"id":"553259","messageId":"xmqqtsnexflv.fsf@gitster.g","threadId":"66385","inReplyTo":"pull.2420.git.git.1790257834680.gitgitgadget@gmail.com","subject":"Re: [PATCH] builtin/fetch-pack: indicate when we have an exact oid","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-09-25T01:51:56Z","receivedAt":"2026-09-25T01:51:58Z","isPatch":true,"body":"\"Nathan Froyd via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Nathan Froyd <froydnj@gmail.com>\n>\n> The `git fetch` path, when parsing OIDs, properly sets `exact_oid` on\n> the relevant refs; the equivalent path for `git fetch-pack` does not.\n> This oversight results in an invocation of `git fetch-pack $OID`\n> sending `want-ref $OID`, which results in errors like:\n>\n>   fatal: unknown ref $OID\n>   fatal: remote error: unknown ref $OID\n>\n> Make the two paths equivalent by setting `exact_oid` properly.\n>\n> Signed-off-by: Nathan Froyd <froydnj@gmail.com>\n> ---\n>     builtin/fetch-pack: indicate when we have an exact oid\n>\n> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2420%2Ffroydnj%2Ffroydnj-fetch-pack-exact-oid-v1\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2420/froydnj/froydnj-fetch-pack-exact-oid-v1\n> Pull-Request: https://github.com/git/git/pull/2420\n>\n>  builtin/fetch-pack.c               |  4 +-\n>  t/t5703-upload-pack-ref-in-want.sh | 68 ++++++++++++++++++++++++++++++\n>  2 files changed, 71 insertions(+), 1 deletion(-)\n>\n> diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c\n> index 86754296fa..bef8a3dfc5 100644\n> --- a/builtin/fetch-pack.c\n> +++ b/builtin/fetch-pack.c\n> @@ -23,13 +23,14 @@ static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,\n>  \tstruct ref *ref;\n>  \tstruct object_id oid;\n>  \tconst char *p;\n> +\tint exact_oid = 0;\n>  \n>  \tif (!parse_oid_hex(name, &oid, &p)) {\n>  \t\tif (*p == ' ') {\n>  \t\t\t/* <oid> <ref>, find refname */\n>  \t\t\tname = p + 1;\n>  \t\t} else if (*p == '\\0') {\n> -\t\t\t; /* <oid>, leave oid as name */\n> +\t\t\texact_oid = 1; /* <oid>, leave oid as name */\n>  \t\t} else {\n>  \t\t\t/* <ref>, clear cruft from oid */\n>  \t\t\toidclr(&oid, the_repository->hash_algo);\n\nUnlike \"git fetch\" that is a higher level wrapper, in \"git fetch-pack\",\na heuristic dwim like this is unwelcome.  In \n\n    $ git fetch-pack <repository> <ref>...\n\nthese <ref> arguments are meant to be passed exactly as given.\n\nI am not sure if there days there still is a reason to run\n\"fetch-pack\" directly instead of \"git fetch\", as the manual of \"git\nfetch-pack\" itself suggets.  But if there is, then shouldn't we give\nit an explicit way to say \"this is not a ref but is an object name\"\nin a more unambiguous way.  Otherwise, we lose the ability to send\n\"want-ref 3ce1e94f7d5607971b5de6762bbdb57e86fc5707\", even if we\nwanted to, which is something \"git fetch\" does not let you do, and\nin turn may be a valid reason why somebody want to use \"fetch-pack\"\nover \"fetch\" in the first place.\n\n"},{"id":"553262","messageId":"xmqqbj9mx7t2.fsf@gitster.g","threadId":"66385","inReplyTo":"xmqqtsnexflv.fsf@gitster.g","subject":"Re: [PATCH] builtin/fetch-pack: indicate when we have an exact oid","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-09-25T04:40:25Z","receivedAt":"2026-09-25T04:40:30Z","isPatch":true,"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Unlike \"git fetch\" that is a higher level wrapper, in \"git fetch-pack\",\n> a heuristic dwim like this is unwelcome.  In \n>\n>     $ git fetch-pack <repository> <ref>...\n>\n> these <ref> arguments are meant to be passed exactly as given.\n\nNeed a bit of clarification.  I should have said\n\n    ... passed to the other side on \"want-ref\" packet as given on\n    the command line.\n"}]}