fetch: do not list refs if fetching only hashes
If only hash literals are given on a "git fetch" command-line, tag following is not requested, and the fetch is done using protocol v2, a list of refs is not required from the remote. Therefore, optimize by invoking transport_get_remote_refs() only if we need the refs. Signed-off-by: Jonathan Tan <jonathantanmy@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Jonathan Tan committed
Sep 27, 2018 at 12:24 UTC
e70a3030e747312327a4f3619247bf8a986aa577
3 files changed
+54
-6
builtin/fetch.c
+26
-6
@@ -1175,6 +1175,7 @@ static int do_fetch(struct transport *transport,
1175
int retcode = 0;
1176
const struct ref *remote_refs;
1177
struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
1178
+ int must_list_refs = 1;
1179
1180
if (tags == TAGS_DEFAULT) {
1181
if (transport->remote->fetch_tags == 2)
@@ -1190,17 +1191,36 @@ static int do_fetch(struct transport *transport,
1191
goto cleanup;
1192
}
1193
1193
- if (rs->nr)
1194
+ if (rs->nr) {
1195
+ int i;
1196
+
1197
refspec_ref_prefixes(rs, &ref_prefixes);
1195
- else if (transport->remote && transport->remote->fetch.nr)
1198
+
1199
+ /*
1200
+ * We can avoid listing refs if all of them are exact
1201
+ * OIDs
1202
+ */
1203
+ must_list_refs = 0;
1204
+ for (i = 0; i < rs->nr; i++) {
1205
+ if (!rs->items[i].exact_sha1) {
1206
+ must_list_refs = 1;
1207
+ break;
1208
+ }
1209
+ }
1210
+ } else if (transport->remote && transport->remote->fetch.nr)
1211
refspec_ref_prefixes(&transport->remote->fetch, &ref_prefixes);
1212
1198
- if (ref_prefixes.argc &&
1199
- (tags == TAGS_SET || (tags == TAGS_DEFAULT))) {
1200
- argv_array_push(&ref_prefixes, "refs/tags/");
1213
+ if (tags == TAGS_SET || tags == TAGS_DEFAULT) {
1214
+ must_list_refs = 1;
1215
+ if (ref_prefixes.argc)
1216
+ argv_array_push(&ref_prefixes, "refs/tags/");
1217
}
1218
1203
- remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
1219
+ if (must_list_refs)
1220
+ remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
1221
+ else
1222
+ remote_refs = NULL;
1223
+
1224
argv_array_clear(&ref_prefixes);
1225
1226
ref_map = get_ref_map(transport->remote, remote_refs, rs,
t/t5551-http-fetch-smart.sh
+15
@@ -381,6 +381,21 @@ test_expect_success 'using fetch command in remote-curl updates refs' '
381
test_cmp expect actual
382
'
383
384
+test_expect_success 'fetch by SHA-1 without tag following' '
385
+ SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server" &&
386
+ rm -rf "$SERVER" client &&
387
+
388
+ git init "$SERVER" &&
389
+ test_commit -C "$SERVER" foo &&
390
+
391
+ git clone $HTTPD_URL/smart/server client &&
392
+
393
+ test_commit -C "$SERVER" bar &&
394
+ git -C "$SERVER" rev-parse bar >bar_hash &&
395
+ git -C client -c protocol.version=0 fetch \
396
+ --no-tags origin $(cat bar_hash)
397
+'
398
+
399
test_expect_success 'GIT_REDACT_COOKIES redacts cookies' '
400
rm -rf clone &&
401
echo "Set-Cookie: Foo=1" >cookies &&
t/t5702-protocol-v2.sh
+13
@@ -79,6 +79,19 @@ test_expect_success 'fetch with git:// using protocol v2' '
79
grep "fetch< version 2" log
80
'
81
82
+test_expect_success 'fetch by hash without tag following with protocol v2 does not list refs' '
83
+ test_when_finished "rm -f log" &&
84
+
85
+ test_commit -C "$daemon_parent" two_a &&
86
+ git -C "$daemon_parent" rev-parse two_a >two_a_hash &&
87
+
88
+ GIT_TRACE_PACKET="$(pwd)/log" git -C daemon_child -c protocol.version=2 \
89
+ fetch --no-tags origin $(cat two_a_hash) &&
90
+
91
+ grep "fetch< version 2" log &&
92
+ ! grep "fetch> command=ls-refs" log
93
+'
94
+
95
test_expect_success 'pull with git:// using protocol v2' '
96
test_when_finished "rm -f log" &&
97