transport: do not list refs if possible

When all refs to be fetched are exact OIDs, it is possible to perform a fetch without requiring the remote to list refs if protocol v2 is used. Teach Git to do this. This currently has an effect only for lazy fetches done from partial clones. The change necessary to likewise optimize "git fetch <remote> <sha-1>" will be done in a subsequent patch. 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 01775651481ecd9c7288a85cfb7999f7f38ab37c
3 files changed +16 -3
fetch-pack.c
+1 -1
@@ -1598,7 +1598,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
1598 if (nr_sought)
1599 nr_sought = remove_duplicates_in_refs(sought, nr_sought);
1600
1601 - if (!ref) {
1601 + if (version != protocol_v2 && !ref) {
1602 packet_flush(fd[1]);
1603 die(_("no matching remote head"));
1604 }
t/t5702-protocol-v2.sh
+4
@@ -286,6 +286,10 @@ test_expect_success 'dynamically fetch missing object' '
286 grep "version 2" trace
287 '
288
289 +test_expect_success 'when dynamically fetching missing object, do not list refs' '
290 + ! grep "git> command=ls-refs" trace
291 +'
292 +
293 test_expect_success 'partial fetch' '
294 rm -rf client "$(pwd)/trace" &&
295 git init client &&
transport.c
+11 -2
@@ -341,8 +341,17 @@ static int fetch_refs_via_pack(struct transport *transport,
341 args.server_options = transport->server_options;
342 args.negotiation_tips = data->options.negotiation_tips;
343
344 - if (!data->got_remote_heads)
345 - refs_tmp = get_refs_via_connect(transport, 0, NULL);
344 + if (!data->got_remote_heads) {
345 + int i;
346 + int must_list_refs = 0;
347 + for (i = 0; i < nr_heads; i++) {
348 + if (!to_fetch[i]->exact_oid) {
349 + must_list_refs = 1;
350 + break;
351 + }
352 + }
353 + refs_tmp = handshake(transport, 0, NULL, must_list_refs);
354 + }
355
356 switch (data->version) {
357 case protocol_v2: