summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2024-12-06 09:08:00 +0100
committerJunio C Hamano <gitster@pobox.com>2024-12-07 13:58:59 +0100
commit6c915c3f85f70b6adbe1a6b17dea743e8ff6dee1 (patch)
treed786dece293662e058ed147e9ad838bf37fd7a7d
parentMerge branch 'bf/set-head-symref' into js/set-head-symref-fix (diff)
downloadgit-6c915c3f85f70b6adbe1a6b17dea743e8ff6dee1.tar.xz
git-6c915c3f85f70b6adbe1a6b17dea743e8ff6dee1.zip
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>
-rw-r--r--builtin/fetch.c20
-rwxr-xr-xt/t5510-fetch.sh17
2 files changed, 36 insertions, 1 deletions
diff --git a/builtin/fetch.c b/builtin/fetch.c
index a64de4485f..3eb6f3acc9 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -1643,6 +1643,21 @@ cleanup:
return result;
}
+static int uses_remote_tracking(struct transport *transport, struct refspec *rs)
+{
+ if (!remote_is_configured(transport->remote, 0))
+ return 0;
+
+ if (!rs->nr)
+ rs = &transport->remote->fetch;
+
+ for (int i = 0; i < rs->nr; i++)
+ if (rs->items[i].dst)
+ return 1;
+
+ return 0;
+}
+
static int do_fetch(struct transport *transport,
struct refspec *rs,
const struct fetch_config *config)
@@ -1712,7 +1727,10 @@ static int do_fetch(struct transport *transport,
"refs/tags/");
}
- strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
+ if (uses_remote_tracking(transport, rs)) {
+ must_list_refs = 1;
+ strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
+ }
if (must_list_refs) {
trace2_region_enter("fetch", "remote_refs", the_repository);
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 87698341f5..d7602333ff 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -189,6 +189,23 @@ test_expect_success 'fetch --prune --tags with refspec prunes based on refspec'
git rev-parse sometag
'
+test_expect_success 'fetch --tags gets tags even without a configured remote' '
+ REMOTE="$(pwd)/test_tag_1" &&
+ git init test_tag_1 &&
+ (
+ cd test_tag_1 &&
+ test_commit foo
+ ) &&
+ git init test_tag_2 &&
+ (
+ cd test_tag_2 &&
+ git fetch --tags "file://$REMOTE" &&
+ echo "foo" >expect &&
+ git tag >actual &&
+ test_cmp expect actual
+ )
+'
+
test_expect_success REFFILES 'fetch --prune fails to delete branches' '
cd "$D" &&
git clone . prune-fail &&