Skip to content

Commit 4fa3f00

Browse files
jonathantanmygitster
authored andcommitted
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 <jonathantanmy@google.com> Reviewed-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent d1185aa commit 4fa3f00

File tree

2 files changed

+27
-4
lines changed

2 files changed

+27
-4
lines changed

fetch-pack.c

+9-4
Original file line numberDiff line numberDiff line change
@@ -1143,6 +1143,7 @@ static void add_common(struct strbuf *req_buf, struct oidset *common)
11431143
}
11441144

11451145
static int add_haves(struct fetch_negotiator *negotiator,
1146+
int seen_ack,
11461147
struct strbuf *req_buf,
11471148
int *haves_to_send, int *in_vain)
11481149
{
@@ -1157,7 +1158,7 @@ static int add_haves(struct fetch_negotiator *negotiator,
11571158
}
11581159

11591160
*in_vain += haves_added;
1160-
if (!haves_added || *in_vain >= MAX_IN_VAIN) {
1161+
if (!haves_added || (seen_ack && *in_vain >= MAX_IN_VAIN)) {
11611162
/* Send Done */
11621163
packet_buf_write(req_buf, "done\n");
11631164
ret = 1;
@@ -1173,7 +1174,7 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
11731174
struct fetch_pack_args *args,
11741175
const struct ref *wants, struct oidset *common,
11751176
int *haves_to_send, int *in_vain,
1176-
int sideband_all)
1177+
int sideband_all, int seen_ack)
11771178
{
11781179
int ret = 0;
11791180
struct strbuf req_buf = STRBUF_INIT;
@@ -1230,7 +1231,8 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
12301231
add_common(&req_buf, common);
12311232

12321233
/* Add initial haves */
1233-
ret = add_haves(negotiator, &req_buf, haves_to_send, in_vain);
1234+
ret = add_haves(negotiator, seen_ack, &req_buf,
1235+
haves_to_send, in_vain);
12341236
}
12351237

12361238
/* Send request */
@@ -1464,6 +1466,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
14641466
int haves_to_send = INITIAL_FLUSH;
14651467
struct fetch_negotiator negotiator_alloc;
14661468
struct fetch_negotiator *negotiator;
1469+
int seen_ack = 0;
14671470

14681471
if (args->no_dependents) {
14691472
negotiator = NULL;
@@ -1520,7 +1523,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
15201523
if (send_fetch_request(negotiator, fd[1], args, ref,
15211524
&common,
15221525
&haves_to_send, &in_vain,
1523-
reader.use_sideband))
1526+
reader.use_sideband,
1527+
seen_ack))
15241528
state = FETCH_GET_PACK;
15251529
else
15261530
state = FETCH_PROCESS_ACKS;
@@ -1533,6 +1537,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
15331537
break;
15341538
case COMMON_FOUND:
15351539
in_vain = 0;
1540+
seen_ack = 1;
15361541
/* fallthrough */
15371542
case NO_COMMON_FOUND:
15381543
state = FETCH_SEND_REQUEST;

t/t5500-fetch-pack.sh

+18
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,24 @@ test_expect_success 'clone shallow with packed refs' '
385385
test_cmp count8.expected count8.actual
386386
'
387387

388+
test_expect_success 'in_vain not triggered before first ACK' '
389+
rm -rf myserver myclient trace &&
390+
git init myserver &&
391+
test_commit -C myserver foo &&
392+
git clone "file://$(pwd)/myserver" myclient &&
393+
394+
# MAX_IN_VAIN is 256. Because of batching, the client will send 496
395+
# (16+32+64+128+256) commits, not 256, before giving up. So create 496
396+
# irrelevant commits.
397+
test_commit_bulk -C myclient 496 &&
398+
399+
# The new commit that the client wants to fetch.
400+
test_commit -C myserver bar &&
401+
402+
GIT_TRACE_PACKET="$(pwd)/trace" git -C myclient fetch --progress origin &&
403+
test_i18ngrep "Total 3 " trace
404+
'
405+
388406
test_expect_success 'fetch in shallow repo unreachable shallow objects' '
389407
(
390408
git clone --bare --branch B --single-branch "file://$(pwd)/." no-reflog &&

0 commit comments

Comments
 (0)