From 4fa3f00abb55c3334cce71e201a5ff2c70f8561f Mon Sep 17 00:00:00 2001 From: Jonathan Tan Date: Mon, 27 Apr 2020 17:01:09 -0700 Subject: fetch-pack: in protocol v2, in_vain only after ACK When fetching, Git stops negotiation when it has sent at least MAX_IN_VAIN (which is 256) "have" lines without having any of them ACK-ed. But this is supposed to trigger only after the first ACK, as pack-protocol.txt says: However, the 256 limit *only* turns on in the canonical client implementation if we have received at least one "ACK %s continue" during a prior round. This helps to ensure that at least one common ancestor is found before we give up entirely. The code path for protocol v0 observes this, but not protocol v2, resulting in shorter negotiation rounds but significantly larger packfiles. Teach the code path for protocol v2 to check this criterion only after at least one ACK was received. Signed-off-by: Jonathan Tan Reviewed-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- t/t5500-fetch-pack.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 't') diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 6b97923964..95ed08db1b 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -385,6 +385,24 @@ test_expect_success 'clone shallow with packed refs' ' test_cmp count8.expected count8.actual ' +test_expect_success 'in_vain not triggered before first ACK' ' + rm -rf myserver myclient trace && + git init myserver && + test_commit -C myserver foo && + git clone "file://$(pwd)/myserver" myclient && + + # MAX_IN_VAIN is 256. Because of batching, the client will send 496 + # (16+32+64+128+256) commits, not 256, before giving up. So create 496 + # irrelevant commits. + test_commit_bulk -C myclient 496 && + + # The new commit that the client wants to fetch. + test_commit -C myserver bar && + + GIT_TRACE_PACKET="$(pwd)/trace" git -C myclient fetch --progress origin && + test_i18ngrep "Total 3 " trace +' + test_expect_success 'fetch in shallow repo unreachable shallow objects' ' ( git clone --bare --branch B --single-branch "file://$(pwd)/." no-reflog && -- cgit v1.3 From 2f0a093dd640e0dad0b261dae2427f2541b5426c Mon Sep 17 00:00:00 2001 From: Jonathan Tan Date: Mon, 27 Apr 2020 17:01:10 -0700 Subject: fetch-pack: in protocol v2, reset in_vain upon ACK In the function process_acks() in fetch-pack.c, the variable received_ack is meant to track that an ACK was received, but it was never set. This results in negotiation terminating prematurely through the in_vain counter, when the counter should have been reset upon every ACK. Therefore, reset the in_vain counter upon every ACK. Helped-by: Jonathan Nieder Signed-off-by: Jonathan Tan Reviewed-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- fetch-pack.c | 1 + t/t5500-fetch-pack.sh | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) (limited to 't') diff --git a/fetch-pack.c b/fetch-pack.c index 3891f8bb86..10314df25b 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -1307,6 +1307,7 @@ static enum common_found process_acks(struct fetch_negotiator *negotiator, if (skip_prefix(reader->line, "ACK ", &arg)) { struct object_id oid; + received_ack = 1; if (!get_oid_hex(arg, &oid)) { struct commit *commit; oidset_insert(common, &oid); diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 95ed08db1b..fd39aad93f 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -403,6 +403,36 @@ test_expect_success 'in_vain not triggered before first ACK' ' test_i18ngrep "Total 3 " trace ' +test_expect_success 'in_vain resetted upon ACK' ' + rm -rf myserver myclient trace && + git init myserver && + + # Linked list of commits on master. The first is common; the rest are + # not. + test_commit -C myserver first_master_commit && + git clone "file://$(pwd)/myserver" myclient && + test_commit_bulk -C myclient 255 && + + # Another linked list of commits on anotherbranch with no connection to + # master. The first is common; the rest are not. + git -C myserver checkout --orphan anotherbranch && + test_commit -C myserver first_anotherbranch_commit && + git -C myclient fetch origin anotherbranch:refs/heads/anotherbranch && + git -C myclient checkout anotherbranch && + test_commit_bulk -C myclient 255 && + + # The new commit that the client wants to fetch. + git -C myserver checkout master && + test_commit -C myserver to_fetch && + + # The client will send (as "have"s) all 256 commits in anotherbranch + # first. The 256th commit is common between the client and the server, + # and should reset in_vain. This allows negotiation to continue until + # the client reports that first_anotherbranch_commit is common. + GIT_TRACE_PACKET="$(pwd)/trace" git -C myclient fetch --progress origin master && + test_i18ngrep "Total 3 " trace +' + test_expect_success 'fetch in shallow repo unreachable shallow objects' ' ( git clone --bare --branch B --single-branch "file://$(pwd)/." no-reflog && -- cgit v1.3