fetch: do not ask for HEAD unnecessarily

In 3f763ddf28 (fetch: set remote/HEAD if it does not exist, 2024-11-22), git-fetch learned to opportunistically set $REMOTE/HEAD when fetching by always asking for remote HEAD, in the hope that it will help setting refs/remotes/<name>/HEAD if missing. But it is not needed to always ask for remote HEAD. When we are fetching from a remote, for which we have remote-tracking branches, we do need to know about HEAD. But if we are doing one-shot fetch, e.g., $ git fetch --tags https://github.com/git/git we do not even know what sub-hierarchy of refs/remotes/<remote>/ we need to adjust the remote HEAD for. There is no need to ask for HEAD in such a case. Incidentally, because the unconditional request to list "HEAD" affected the number of ref-prefixes requested in the ls-remote request, this affected how the requests for tags are added to the same ls-remote request, breaking "git fetch --tags $URL" performed against a URL that is not configured as a remote. Reported-by: Josh Steadmon <steadmon@google.com> [jc: tests are also borrowed from Josh's patch] Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Dec 6, 2024 at 17:08 UTC 6c915c3f85f70b6adbe1a6b17dea743e8ff6dee1
2 files changed +36 -1
builtin/fetch.c
+19 -1
@@ -1643,6 +1643,21 @@ cleanup:
1643 return result;
1644 }
1645
1646 +static int uses_remote_tracking(struct transport *transport, struct refspec *rs)
1647 +{
1648 + if (!remote_is_configured(transport->remote, 0))
1649 + return 0;
1650 +
1651 + if (!rs->nr)
1652 + rs = &transport->remote->fetch;
1653 +
1654 + for (int i = 0; i < rs->nr; i++)
1655 + if (rs->items[i].dst)
1656 + return 1;
1657 +
1658 + return 0;
1659 +}
1660 +
1661 static int do_fetch(struct transport *transport,
1662 struct refspec *rs,
1663 const struct fetch_config *config)
@@ -1712,7 +1727,10 @@ static int do_fetch(struct transport *transport,
1727 "refs/tags/");
1728 }
1729
1715 - strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
1730 + if (uses_remote_tracking(transport, rs)) {
1731 + must_list_refs = 1;
1732 + strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
1733 + }
1734
1735 if (must_list_refs) {
1736 trace2_region_enter("fetch", "remote_refs", the_repository);
t/t5510-fetch.sh
+17
@@ -189,6 +189,23 @@ test_expect_success 'fetch --prune --tags with refspec prunes based on refspec'
189 git rev-parse sometag
190 '
191
192 +test_expect_success 'fetch --tags gets tags even without a configured remote' '
193 + REMOTE="$(pwd)/test_tag_1" &&
194 + git init test_tag_1 &&
195 + (
196 + cd test_tag_1 &&
197 + test_commit foo
198 + ) &&
199 + git init test_tag_2 &&
200 + (
201 + cd test_tag_2 &&
202 + git fetch --tags "file://$REMOTE" &&
203 + echo "foo" >expect &&
204 + git tag >actual &&
205 + test_cmp expect actual
206 + )
207 +'
208 +
209 test_expect_success REFFILES 'fetch --prune fails to delete branches' '
210 cd "$D" &&
211 git clone . prune-fail &&