From 34c2903456046e194b3c3cde23e108a788571ffd Mon Sep 17 00:00:00 2001 From: Jonathan Tan Date: Wed, 6 Jun 2018 13:47:07 -0700 Subject: fetch-pack: split up everything_local() The function everything_local(), despite its name, also (1) marks commits as COMPLETE and COMMON_REF and (2) invokes filter_refs() as important side effects. Extract (1) into its own function (mark_complete_and_common_ref()) and remove (2). The restoring of save_commit_buffer, which was introduced in a1c6d7c1a7 ("fetch-pack: restore save_commit_buffer after use", 2017-12-08), is a concern of the parse_object() call in mark_complete_and_common_ref(), so it has been moved from the end of everything_local() to the end of mark_complete_and_common_ref(). Signed-off-by: Jonathan Tan Signed-off-by: Junio C Hamano diff --git a/fetch-pack.c b/fetch-pack.c index a320ce9..5c87bb8 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -734,12 +734,20 @@ static int add_loose_objects_to_set(const struct object_id *oid, return 0; } -static int everything_local(struct fetch_pack_args *args, - struct ref **refs, - struct ref **sought, int nr_sought) +/* + * Mark recent commits available locally and reachable from a local ref as + * COMPLETE. If args->no_dependents is false, also mark COMPLETE remote refs as + * COMMON_REF (otherwise, we are not planning to participate in negotiation, and + * thus do not need COMMON_REF marks). + * + * The cutoff time for recency is determined by this heuristic: it is the + * earliest commit time of the objects in refs that are commits and that we know + * the commit time of. + */ +static void mark_complete_and_common_ref(struct fetch_pack_args *args, + struct ref **refs) { struct ref *ref; - int retval; int old_save_commit_buffer = save_commit_buffer; timestamp_t cutoff = 0; struct oidset loose_oid_set = OIDSET_INIT; @@ -812,7 +820,18 @@ static int everything_local(struct fetch_pack_args *args, } } - filter_refs(args, refs, sought, nr_sought); + save_commit_buffer = old_save_commit_buffer; +} + +/* + * Returns 1 if every object pointed to by the given remote refs is available + * locally and reachable from a local ref, and 0 otherwise. + */ +static int everything_local(struct fetch_pack_args *args, + struct ref **refs) +{ + struct ref *ref; + int retval; for (retval = 1, ref = *refs; ref ; ref = ref->next) { const struct object_id *remote = &ref->old_oid; @@ -829,8 +848,6 @@ static int everything_local(struct fetch_pack_args *args, ref->name); } - save_commit_buffer = old_save_commit_buffer; - return retval; } @@ -1053,7 +1070,9 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, if (!server_supports("deepen-relative") && args->deepen_relative) die(_("Server does not support --deepen")); - if (everything_local(args, &ref, sought, nr_sought)) { + mark_complete_and_common_ref(args, &ref); + filter_refs(args, &ref, sought, nr_sought); + if (everything_local(args, &ref)) { packet_flush(fd[1]); goto all_done; } @@ -1377,7 +1396,9 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, for_each_cached_alternate(insert_one_alternate_object); /* Filter 'ref' by 'sought' and those that aren't local */ - if (everything_local(args, &ref, sought, nr_sought)) + mark_complete_and_common_ref(args, &ref); + filter_refs(args, &ref, sought, nr_sought); + if (everything_local(args, &ref)) state = FETCH_DONE; else state = FETCH_SEND_REQUEST; -- cgit v0.10.2-6-g49f6 From af008558cc2e16c96ddafc6c7a9453ea3d18957b Mon Sep 17 00:00:00 2001 From: Jonathan Tan Date: Wed, 6 Jun 2018 13:47:08 -0700 Subject: fetch-pack: clear marks before re-marking If tag following is required when using a transport that does not support tag following, fetch_pack() will be invoked twice in the same process, necessitating a clearing of the object flags used by fetch_pack() sometime during the second invocation. This is currently done in find_common(), which means that the invocation of mark_complete_and_common_ref() in do_fetch_pack() is useless. (This cannot be reproduced with Git alone, because all transports that come with Git support tag following.) Therefore, move this clearing from find_common() to its parent function do_fetch_pack(), right before it calls mark_complete_and_common_ref(). This has been occurring since the commit that introduced the clearing of marks, 420e9af498 ("Fix tag following", 2008-03-19). The corresponding code for protocol v2 in do_fetch_pack_v2() does not have this problem, as the clearing of flags is done before any marking (whether by rev_list_insert_ref_oid() or mark_complete_and_common_ref()). Signed-off-by: Jonathan Tan Signed-off-by: Junio C Hamano diff --git a/fetch-pack.c b/fetch-pack.c index 5c87bb8..2812499 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -336,9 +336,6 @@ static int find_common(struct fetch_pack_args *args, if (args->stateless_rpc && multi_ack == 1) die(_("--stateless-rpc requires multi_ack_detailed")); - if (marked) - for_each_ref(clear_marks, NULL); - marked = 1; for_each_ref(rev_list_insert_ref_oid, NULL); for_each_cached_alternate(insert_one_alternate_object); @@ -1070,6 +1067,9 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, if (!server_supports("deepen-relative") && args->deepen_relative) die(_("Server does not support --deepen")); + if (marked) + for_each_ref(clear_marks, NULL); + marked = 1; mark_complete_and_common_ref(args, &ref); filter_refs(args, &ref, sought, nr_sought); if (everything_local(args, &ref)) { -- cgit v0.10.2-6-g49f6 From 21bcf6e429e01961d38bc639b2dc4a6f64b8fdcf Mon Sep 17 00:00:00 2001 From: Jonathan Tan Date: Thu, 14 Jun 2018 15:54:24 -0700 Subject: fetch-pack: directly end negotiation if ACK ready When "ACK %s ready" is received, find_common() clears rev_list in an attempt to stop further "have" lines from being sent [1]. It is much more readable to explicitly break from the loop instead. So explicitly break from the loop, and make the clearing of the rev_list happen unconditionally. [1] The rationale is further described in the originating commit f2cba9299b ("fetch-pack: Finish negotation if remote replies "ACK %s ready"", 2011-03-14). Signed-off-by: Jonathan Tan Signed-off-by: Junio C Hamano diff --git a/fetch-pack.c b/fetch-pack.c index 2812499..60adfc0 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -517,10 +517,8 @@ static int find_common(struct fetch_pack_args *args, mark_common(commit, 0, 1); retval = 0; got_continue = 1; - if (ack == ACK_ready) { - clear_prio_queue(&rev_list); + if (ack == ACK_ready) got_ready = 1; - } break; } } @@ -530,6 +528,8 @@ static int find_common(struct fetch_pack_args *args, print_verbose(args, _("giving up")); break; /* give up */ } + if (got_ready) + break; } } done: @@ -1096,6 +1096,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, die(_("git fetch-pack: fetch failed.")); all_done: + clear_prio_queue(&rev_list); return ref; } @@ -1300,7 +1301,6 @@ static int process_acks(struct packet_reader *reader, struct oidset *common) } if (!strcmp(reader->line, "ready")) { - clear_prio_queue(&rev_list); received_ready = 1; continue; } @@ -1441,6 +1441,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, } } + clear_prio_queue(&rev_list); oidset_clear(&common); return ref; } -- cgit v0.10.2-6-g49f6 From af1c90d13eb325b3fb7b6913757882b663caf1c6 Mon Sep 17 00:00:00 2001 From: Jonathan Tan Date: Thu, 14 Jun 2018 15:54:25 -0700 Subject: fetch-pack: use ref adv. to prune "have" sent In negotiation using protocol v2, fetch-pack sometimes does not make full use of the information obtained in the ref advertisement: specifically, that if the server advertises a commit that the client also has, the client never needs to inform the server that it has the commit's parents, since it can just tell the server that it has the advertised commit and it knows that the server can and will infer the rest. This is because, in do_fetch_pack_v2(), rev_list_insert_ref_oid() is invoked before mark_complete_and_common_ref(). This means that if we have a commit that is both our ref and their ref, it would be enqueued by rev_list_insert_ref_oid() as SEEN, and since it is thus already SEEN, mark_complete_and_common_ref() would not enqueue it. If mark_complete_and_common_ref() were invoked first, as it is in do_fetch_pack() for protocol v0, then mark_complete_and_common_ref() would enqueue it with COMMON_REF | SEEN. The addition of COMMON_REF ensures that its parents are not sent as "have" lines. Change the order in do_fetch_pack_v2() to be consistent with do_fetch_pack(), and to avoid sending unnecessary "have" lines. Signed-off-by: Jonathan Tan Signed-off-by: Junio C Hamano diff --git a/fetch-pack.c b/fetch-pack.c index 60adfc0..806c400 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -1392,9 +1392,6 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, for_each_ref(clear_marks, NULL); marked = 1; - for_each_ref(rev_list_insert_ref_oid, NULL); - for_each_cached_alternate(insert_one_alternate_object); - /* Filter 'ref' by 'sought' and those that aren't local */ mark_complete_and_common_ref(args, &ref); filter_refs(args, &ref, sought, nr_sought); @@ -1402,6 +1399,9 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, state = FETCH_DONE; else state = FETCH_SEND_REQUEST; + + for_each_ref(rev_list_insert_ref_oid, NULL); + for_each_cached_alternate(insert_one_alternate_object); break; case FETCH_SEND_REQUEST: if (send_fetch_request(fd[1], args, ref, &common, diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index d4f4351..e0cdc29 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -755,6 +755,39 @@ test_expect_success 'fetching deepen' ' ) ' +test_expect_success 'use ref advertisement to prune "have" lines sent' ' + rm -rf server client && + git init server && + test_commit -C server both_have_1 && + git -C server tag -d both_have_1 && + test_commit -C server both_have_2 && + + git clone server client && + test_commit -C server server_has && + test_commit -C client client_has && + + # In both protocol v0 and v2, ensure that the parent of both_have_2 is + # not sent as a "have" line. The client should know that the server has + # both_have_2, so it only needs to inform the server that it has + # both_have_2, and the server can infer the rest. + + rm -f trace && + cp -r client clientv0 && + GIT_TRACE_PACKET="$(pwd)/trace" git -C clientv0 \ + fetch origin server_has both_have_2 && + grep "have $(git -C client rev-parse client_has)" trace && + grep "have $(git -C client rev-parse both_have_2)" trace && + ! grep "have $(git -C client rev-parse both_have_2^)" trace && + + rm -f trace && + cp -r client clientv2 && + GIT_TRACE_PACKET="$(pwd)/trace" git -C clientv2 -c protocol.version=2 \ + fetch origin server_has both_have_2 && + grep "have $(git -C client rev-parse client_has)" trace && + grep "have $(git -C client rev-parse both_have_2)" trace && + ! grep "have $(git -C client rev-parse both_have_2^)" trace +' + test_expect_success 'filtering by size' ' rm -rf server client && test_create_repo server && -- cgit v0.10.2-6-g49f6 From d30fe89c37b746ac2f3419ae0e3990a9984fb4cf Mon Sep 17 00:00:00 2001 From: Jonathan Tan Date: Thu, 14 Jun 2018 15:54:26 -0700 Subject: fetch-pack: make negotiation-related vars local Reduce the number of global variables by making the priority queue and the count of non-common commits in it local, passing them as a struct to various functions where necessary. This also helps in the case that fetch_pack() is invoked twice in the same process (when tag following is required when using a transport that does not support tag following), in that different priority queues will now be used in each invocation, instead of reusing the possibly non-empty one. Signed-off-by: Jonathan Tan Signed-off-by: Junio C Hamano diff --git a/fetch-pack.c b/fetch-pack.c index 806c400..86252e1 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -50,8 +50,12 @@ static int marked; */ #define MAX_IN_VAIN 256 -static struct prio_queue rev_list = { compare_commits_by_commit_date }; -static int non_common_revs, multi_ack, use_sideband; +struct negotiation_state { + struct prio_queue rev_list; + int non_common_revs; +}; + +static int multi_ack, use_sideband; /* Allow specifying sha1 if it is a ref tip. */ #define ALLOW_TIP_SHA1 01 /* Allow request of a sha1 if it is reachable from a ref (possibly hidden ref). */ @@ -93,7 +97,9 @@ static void cache_one_alternate(const char *refname, cache->items[cache->nr++] = obj; } -static void for_each_cached_alternate(void (*cb)(struct object *)) +static void for_each_cached_alternate(struct negotiation_state *ns, + void (*cb)(struct negotiation_state *, + struct object *)) { static int initialized; static struct alternate_object_cache cache; @@ -105,10 +111,11 @@ static void for_each_cached_alternate(void (*cb)(struct object *)) } for (i = 0; i < cache.nr; i++) - cb(cache.items[i]); + cb(ns, cache.items[i]); } -static void rev_list_push(struct commit *commit, int mark) +static void rev_list_push(struct negotiation_state *ns, + struct commit *commit, int mark) { if (!(commit->object.flags & mark)) { commit->object.flags |= mark; @@ -116,19 +123,21 @@ static void rev_list_push(struct commit *commit, int mark) if (parse_commit(commit)) return; - prio_queue_put(&rev_list, commit); + prio_queue_put(&ns->rev_list, commit); if (!(commit->object.flags & COMMON)) - non_common_revs++; + ns->non_common_revs++; } } -static int rev_list_insert_ref(const char *refname, const struct object_id *oid) +static int rev_list_insert_ref(struct negotiation_state *ns, + const char *refname, + const struct object_id *oid) { struct object *o = deref_tag(parse_object(oid), refname, 0); if (o && o->type == OBJ_COMMIT) - rev_list_push((struct commit *)o, SEEN); + rev_list_push(ns, (struct commit *)o, SEEN); return 0; } @@ -136,7 +145,7 @@ static int rev_list_insert_ref(const char *refname, const struct object_id *oid) static int rev_list_insert_ref_oid(const char *refname, const struct object_id *oid, int flag, void *cb_data) { - return rev_list_insert_ref(refname, oid); + return rev_list_insert_ref(cb_data, refname, oid); } static int clear_marks(const char *refname, const struct object_id *oid, @@ -156,7 +165,7 @@ static int clear_marks(const char *refname, const struct object_id *oid, when only the server does not yet know that they are common). */ -static void mark_common(struct commit *commit, +static void mark_common(struct negotiation_state *ns, struct commit *commit, int ancestors_only, int dont_parse) { if (commit != NULL && !(commit->object.flags & COMMON)) { @@ -166,12 +175,12 @@ static void mark_common(struct commit *commit, o->flags |= COMMON; if (!(o->flags & SEEN)) - rev_list_push(commit, SEEN); + rev_list_push(ns, commit, SEEN); else { struct commit_list *parents; if (!ancestors_only && !(o->flags & POPPED)) - non_common_revs--; + ns->non_common_revs--; if (!o->parsed && !dont_parse) if (parse_commit(commit)) return; @@ -179,7 +188,8 @@ static void mark_common(struct commit *commit, for (parents = commit->parents; parents; parents = parents->next) - mark_common(parents->item, 0, dont_parse); + mark_common(ns, parents->item, 0, + dont_parse); } } } @@ -188,7 +198,7 @@ static void mark_common(struct commit *commit, Get the next rev to send, ignoring the common. */ -static const struct object_id *get_rev(void) +static const struct object_id *get_rev(struct negotiation_state *ns) { struct commit *commit = NULL; @@ -196,16 +206,16 @@ static const struct object_id *get_rev(void) unsigned int mark; struct commit_list *parents; - if (rev_list.nr == 0 || non_common_revs == 0) + if (ns->rev_list.nr == 0 || ns->non_common_revs == 0) return NULL; - commit = prio_queue_get(&rev_list); + commit = prio_queue_get(&ns->rev_list); parse_commit(commit); parents = commit->parents; commit->object.flags |= POPPED; if (!(commit->object.flags & COMMON)) - non_common_revs--; + ns->non_common_revs--; if (commit->object.flags & COMMON) { /* do not send "have", and ignore ancestors */ @@ -220,9 +230,9 @@ static const struct object_id *get_rev(void) while (parents) { if (!(parents->item->object.flags & SEEN)) - rev_list_push(parents->item, mark); + rev_list_push(ns, parents->item, mark); if (mark & COMMON) - mark_common(parents->item, 1, 0); + mark_common(ns, parents->item, 1, 0); parents = parents->next; } } @@ -296,9 +306,10 @@ static void send_request(struct fetch_pack_args *args, write_or_die(fd, buf->buf, buf->len); } -static void insert_one_alternate_object(struct object *obj) +static void insert_one_alternate_object(struct negotiation_state *ns, + struct object *obj) { - rev_list_insert_ref(NULL, &obj->oid); + rev_list_insert_ref(ns, NULL, &obj->oid); } #define INITIAL_FLUSH 16 @@ -321,7 +332,8 @@ static int next_flush(int stateless_rpc, int count) return count; } -static int find_common(struct fetch_pack_args *args, +static int find_common(struct negotiation_state *ns, + struct fetch_pack_args *args, int fd[2], struct object_id *result_oid, struct ref *refs) { @@ -337,8 +349,8 @@ static int find_common(struct fetch_pack_args *args, if (args->stateless_rpc && multi_ack == 1) die(_("--stateless-rpc requires multi_ack_detailed")); - for_each_ref(rev_list_insert_ref_oid, NULL); - for_each_cached_alternate(insert_one_alternate_object); + for_each_ref(rev_list_insert_ref_oid, ns); + for_each_cached_alternate(ns, insert_one_alternate_object); fetching = 0; for ( ; refs ; refs = refs->next) { @@ -456,7 +468,7 @@ static int find_common(struct fetch_pack_args *args, retval = -1; if (args->no_dependents) goto done; - while ((oid = get_rev())) { + while ((oid = get_rev(ns))) { packet_buf_write(&req_buf, "have %s\n", oid_to_hex(oid)); print_verbose(args, "have %s", oid_to_hex(oid)); in_vain++; @@ -514,7 +526,7 @@ static int find_common(struct fetch_pack_args *args, } else if (!args->stateless_rpc || ack != ACK_common) in_vain = 0; - mark_common(commit, 0, 1); + mark_common(ns, commit, 0, 1); retval = 0; got_continue = 1; if (ack == ACK_ready) @@ -704,7 +716,8 @@ static void filter_refs(struct fetch_pack_args *args, *refs = newlist; } -static void mark_alternate_complete(struct object *obj) +static void mark_alternate_complete(struct negotiation_state *unused, + struct object *obj) { mark_complete(&obj->oid); } @@ -741,7 +754,8 @@ static int add_loose_objects_to_set(const struct object_id *oid, * earliest commit time of the objects in refs that are commits and that we know * the commit time of. */ -static void mark_complete_and_common_ref(struct fetch_pack_args *args, +static void mark_complete_and_common_ref(struct negotiation_state *ns, + struct fetch_pack_args *args, struct ref **refs) { struct ref *ref; @@ -792,7 +806,7 @@ static void mark_complete_and_common_ref(struct fetch_pack_args *args, if (!args->no_dependents) { if (!args->deepen) { for_each_ref(mark_complete_oid, NULL); - for_each_cached_alternate(mark_alternate_complete); + for_each_cached_alternate(NULL, mark_alternate_complete); commit_list_sort_by_date(&complete); if (cutoff) mark_recent_complete_commits(args, cutoff); @@ -810,9 +824,10 @@ static void mark_complete_and_common_ref(struct fetch_pack_args *args, continue; if (!(o->flags & SEEN)) { - rev_list_push((struct commit *)o, COMMON_REF | SEEN); + rev_list_push(ns, (struct commit *)o, + COMMON_REF | SEEN); - mark_common((struct commit *)o, 1, 1); + mark_common(ns, (struct commit *)o, 1, 1); } } } @@ -995,6 +1010,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, struct object_id oid; const char *agent_feature; int agent_len; + struct negotiation_state ns = { { compare_commits_by_commit_date } }; sort_ref_list(&ref, ref_compare_name); QSORT(sought, nr_sought, cmp_ref_by_name); @@ -1070,13 +1086,13 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, if (marked) for_each_ref(clear_marks, NULL); marked = 1; - mark_complete_and_common_ref(args, &ref); + mark_complete_and_common_ref(&ns, args, &ref); filter_refs(args, &ref, sought, nr_sought); if (everything_local(args, &ref)) { packet_flush(fd[1]); goto all_done; } - if (find_common(args, fd, &oid, ref) < 0) + if (find_common(&ns, args, fd, &oid, ref) < 0) if (!args->keep_pack) /* When cloning, it is not unusual to have * no common commit. @@ -1096,7 +1112,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, die(_("git fetch-pack: fetch failed.")); all_done: - clear_prio_queue(&rev_list); + clear_prio_queue(&ns.rev_list); return ref; } @@ -1158,13 +1174,14 @@ static void add_common(struct strbuf *req_buf, struct oidset *common) } } -static int add_haves(struct strbuf *req_buf, int *haves_to_send, int *in_vain) +static int add_haves(struct negotiation_state *ns, struct strbuf *req_buf, + int *haves_to_send, int *in_vain) { int ret = 0; int haves_added = 0; const struct object_id *oid; - while ((oid = get_rev())) { + while ((oid = get_rev(ns))) { packet_buf_write(req_buf, "have %s\n", oid_to_hex(oid)); if (++haves_added >= *haves_to_send) break; @@ -1183,7 +1200,8 @@ static int add_haves(struct strbuf *req_buf, int *haves_to_send, int *in_vain) return ret; } -static int send_fetch_request(int fd_out, const struct fetch_pack_args *args, +static int send_fetch_request(struct negotiation_state *ns, int fd_out, + const struct fetch_pack_args *args, const struct ref *wants, struct oidset *common, int *haves_to_send, int *in_vain) { @@ -1239,7 +1257,7 @@ static int send_fetch_request(int fd_out, const struct fetch_pack_args *args, add_common(&req_buf, common); /* Add initial haves */ - ret = add_haves(&req_buf, haves_to_send, in_vain); + ret = add_haves(ns, &req_buf, haves_to_send, in_vain); } /* Send request */ @@ -1276,7 +1294,9 @@ static int process_section_header(struct packet_reader *reader, return ret; } -static int process_acks(struct packet_reader *reader, struct oidset *common) +static int process_acks(struct negotiation_state *ns, + struct packet_reader *reader, + struct oidset *common) { /* received */ int received_ready = 0; @@ -1295,7 +1315,7 @@ static int process_acks(struct packet_reader *reader, struct oidset *common) struct commit *commit; oidset_insert(common, &oid); commit = lookup_commit(&oid); - mark_common(commit, 0, 1); + mark_common(ns, commit, 0, 1); } continue; } @@ -1373,6 +1393,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, struct packet_reader reader; int in_vain = 0; int haves_to_send = INITIAL_FLUSH; + struct negotiation_state ns = { { compare_commits_by_commit_date } }; packet_reader_init(&reader, fd[0], NULL, 0, PACKET_READ_CHOMP_NEWLINE); @@ -1393,18 +1414,19 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, marked = 1; /* Filter 'ref' by 'sought' and those that aren't local */ - mark_complete_and_common_ref(args, &ref); + mark_complete_and_common_ref(&ns, args, &ref); filter_refs(args, &ref, sought, nr_sought); if (everything_local(args, &ref)) state = FETCH_DONE; else state = FETCH_SEND_REQUEST; - for_each_ref(rev_list_insert_ref_oid, NULL); - for_each_cached_alternate(insert_one_alternate_object); + for_each_ref(rev_list_insert_ref_oid, &ns); + for_each_cached_alternate(&ns, + insert_one_alternate_object); break; case FETCH_SEND_REQUEST: - if (send_fetch_request(fd[1], args, ref, &common, + if (send_fetch_request(&ns, fd[1], args, ref, &common, &haves_to_send, &in_vain)) state = FETCH_GET_PACK; else @@ -1412,7 +1434,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, break; case FETCH_PROCESS_ACKS: /* Process ACKs/NAKs */ - switch (process_acks(&reader, &common)) { + switch (process_acks(&ns, &reader, &common)) { case 2: state = FETCH_GET_PACK; break; @@ -1441,7 +1463,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, } } - clear_prio_queue(&rev_list); + clear_prio_queue(&ns.rev_list); oidset_clear(&common); return ref; } -- cgit v0.10.2-6-g49f6 From d093bc7582fc60b51b42d2edf32b3ce68cd283eb Mon Sep 17 00:00:00 2001 From: Jonathan Tan Date: Thu, 14 Jun 2018 15:54:27 -0700 Subject: fetch-pack: move common check and marking together When receiving 'ACK continue' for a common commit, check if the commit was already known to be common and mark it as such if not up front. This should make future refactoring of how the information about common commits is stored more straightforward. No visible change intended. Signed-off-by: Jonathan Tan Signed-off-by: Junio C Hamano diff --git a/fetch-pack.c b/fetch-pack.c index 86252e1..d076465 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -505,11 +505,14 @@ static int find_common(struct negotiation_state *ns, case ACK_continue: { struct commit *commit = lookup_commit(result_oid); + int was_common; if (!commit) die(_("invalid commit %s"), oid_to_hex(result_oid)); + was_common = commit->object.flags & COMMON; + mark_common(ns, commit, 0, 1); if (args->stateless_rpc && ack == ACK_common - && !(commit->object.flags & COMMON)) { + && !was_common) { /* We need to replay the have for this object * on the next RPC request so the peer knows * it is in common with us. @@ -526,7 +529,6 @@ static int find_common(struct negotiation_state *ns, } else if (!args->stateless_rpc || ack != ACK_common) in_vain = 0; - mark_common(ns, commit, 0, 1); retval = 0; got_continue = 1; if (ack == ACK_ready) -- cgit v0.10.2-6-g49f6 From ec06283844a90c3e9440286401e9ad7d86daa5ae Mon Sep 17 00:00:00 2001 From: Jonathan Tan Date: Thu, 14 Jun 2018 15:54:28 -0700 Subject: fetch-pack: introduce negotiator API Introduce the new files fetch-negotiator.{h,c}, which contains an API behind which the details of negotiation are abstracted. Currently, only one algorithm is available: the existing one. This patch is written to be easily reviewed: static functions are moved verbatim from fetch-pack.c to negotiator/default.c, and it can be seen that the lines replaced by negotiator->X() calls are present in the X() functions respectively. Signed-off-by: Jonathan Tan Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index 1d27f36..96f84d1 100644 --- a/Makefile +++ b/Makefile @@ -859,6 +859,7 @@ LIB_OBJS += ewah/ewah_bitmap.o LIB_OBJS += ewah/ewah_io.o LIB_OBJS += ewah/ewah_rlw.o LIB_OBJS += exec-cmd.o +LIB_OBJS += fetch-negotiator.o LIB_OBJS += fetch-object.o LIB_OBJS += fetch-pack.o LIB_OBJS += fsck.o @@ -891,6 +892,7 @@ LIB_OBJS += merge-blobs.o LIB_OBJS += merge-recursive.o LIB_OBJS += mergesort.o LIB_OBJS += name-hash.o +LIB_OBJS += negotiator/default.o LIB_OBJS += notes.o LIB_OBJS += notes-cache.o LIB_OBJS += notes-merge.o diff --git a/fetch-negotiator.c b/fetch-negotiator.c new file mode 100644 index 0000000..2675d12 --- /dev/null +++ b/fetch-negotiator.c @@ -0,0 +1,8 @@ +#include "git-compat-util.h" +#include "fetch-negotiator.h" +#include "negotiator/default.h" + +void fetch_negotiator_init(struct fetch_negotiator *negotiator) +{ + default_negotiator_init(negotiator); +} diff --git a/fetch-negotiator.h b/fetch-negotiator.h new file mode 100644 index 0000000..b1290aa --- /dev/null +++ b/fetch-negotiator.h @@ -0,0 +1,57 @@ +#ifndef FETCH_NEGOTIATOR +#define FETCH_NEGOTIATOR + +struct commit; + +/* + * An object that supplies the information needed to negotiate the contents of + * the to-be-sent packfile during a fetch. + * + * To set up the negotiator, call fetch_negotiator_init(), then known_common() + * (0 or more times), then add_tip() (0 or more times). + * + * Then, when "have" lines are required, call next(). Call ack() to report what + * the server tells us. + * + * Once negotiation is done, call release(). The negotiator then cannot be used + * (unless reinitialized with fetch_negotiator_init()). + */ +struct fetch_negotiator { + /* + * Before negotiation starts, indicate that the server is known to have + * this commit. + */ + void (*known_common)(struct fetch_negotiator *, struct commit *); + + /* + * Once this function is invoked, known_common() cannot be invoked any + * more. + * + * Indicate that this commit and all its ancestors are to be checked + * for commonality with the server. + */ + void (*add_tip)(struct fetch_negotiator *, struct commit *); + + /* + * Once this function is invoked, known_common() and add_tip() cannot + * be invoked any more. + * + * Return the next commit that the client should send as a "have" line. + */ + const struct object_id *(*next)(struct fetch_negotiator *); + + /* + * Inform the negotiator that the server has the given commit. This + * method must only be called on commits returned by next(). + */ + int (*ack)(struct fetch_negotiator *, struct commit *); + + void (*release)(struct fetch_negotiator *); + + /* internal use */ + void *data; +}; + +void fetch_negotiator_init(struct fetch_negotiator *negotiator); + +#endif diff --git a/fetch-pack.c b/fetch-pack.c index d076465..ba12085 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -15,10 +15,10 @@ #include "connect.h" #include "transport.h" #include "version.h" -#include "prio-queue.h" #include "sha1-array.h" #include "oidset.h" #include "packfile.h" +#include "fetch-negotiator.h" static int transfer_unpack_limit = -1; static int fetch_unpack_limit = -1; @@ -36,13 +36,7 @@ static const char *alternate_shallow_file; /* Remember to update object flag allocation in object.h */ #define COMPLETE (1U << 0) -#define COMMON (1U << 1) -#define COMMON_REF (1U << 2) -#define SEEN (1U << 3) -#define POPPED (1U << 4) -#define ALTERNATE (1U << 5) - -static int marked; +#define ALTERNATE (1U << 1) /* * After sending this many "have"s if we do not get any new ACK , we @@ -50,11 +44,6 @@ static int marked; */ #define MAX_IN_VAIN 256 -struct negotiation_state { - struct prio_queue rev_list; - int non_common_revs; -}; - static int multi_ack, use_sideband; /* Allow specifying sha1 if it is a ref tip. */ #define ALLOW_TIP_SHA1 01 @@ -97,8 +86,8 @@ static void cache_one_alternate(const char *refname, cache->items[cache->nr++] = obj; } -static void for_each_cached_alternate(struct negotiation_state *ns, - void (*cb)(struct negotiation_state *, +static void for_each_cached_alternate(struct fetch_negotiator *negotiator, + void (*cb)(struct fetch_negotiator *, struct object *)) { static int initialized; @@ -111,33 +100,17 @@ static void for_each_cached_alternate(struct negotiation_state *ns, } for (i = 0; i < cache.nr; i++) - cb(ns, cache.items[i]); -} - -static void rev_list_push(struct negotiation_state *ns, - struct commit *commit, int mark) -{ - if (!(commit->object.flags & mark)) { - commit->object.flags |= mark; - - if (parse_commit(commit)) - return; - - prio_queue_put(&ns->rev_list, commit); - - if (!(commit->object.flags & COMMON)) - ns->non_common_revs++; - } + cb(negotiator, cache.items[i]); } -static int rev_list_insert_ref(struct negotiation_state *ns, +static int rev_list_insert_ref(struct fetch_negotiator *negotiator, const char *refname, const struct object_id *oid) { struct object *o = deref_tag(parse_object(oid), refname, 0); if (o && o->type == OBJ_COMMIT) - rev_list_push(ns, (struct commit *)o, SEEN); + negotiator->add_tip(negotiator, (struct commit *)o); return 0; } @@ -148,98 +121,6 @@ static int rev_list_insert_ref_oid(const char *refname, const struct object_id * return rev_list_insert_ref(cb_data, refname, oid); } -static int clear_marks(const char *refname, const struct object_id *oid, - int flag, void *cb_data) -{ - struct object *o = deref_tag(parse_object(oid), refname, 0); - - if (o && o->type == OBJ_COMMIT) - clear_commit_marks((struct commit *)o, - COMMON | COMMON_REF | SEEN | POPPED); - return 0; -} - -/* - This function marks a rev and its ancestors as common. - In some cases, it is desirable to mark only the ancestors (for example - when only the server does not yet know that they are common). -*/ - -static void mark_common(struct negotiation_state *ns, struct commit *commit, - int ancestors_only, int dont_parse) -{ - if (commit != NULL && !(commit->object.flags & COMMON)) { - struct object *o = (struct object *)commit; - - if (!ancestors_only) - o->flags |= COMMON; - - if (!(o->flags & SEEN)) - rev_list_push(ns, commit, SEEN); - else { - struct commit_list *parents; - - if (!ancestors_only && !(o->flags & POPPED)) - ns->non_common_revs--; - if (!o->parsed && !dont_parse) - if (parse_commit(commit)) - return; - - for (parents = commit->parents; - parents; - parents = parents->next) - mark_common(ns, parents->item, 0, - dont_parse); - } - } -} - -/* - Get the next rev to send, ignoring the common. -*/ - -static const struct object_id *get_rev(struct negotiation_state *ns) -{ - struct commit *commit = NULL; - - while (commit == NULL) { - unsigned int mark; - struct commit_list *parents; - - if (ns->rev_list.nr == 0 || ns->non_common_revs == 0) - return NULL; - - commit = prio_queue_get(&ns->rev_list); - parse_commit(commit); - parents = commit->parents; - - commit->object.flags |= POPPED; - if (!(commit->object.flags & COMMON)) - ns->non_common_revs--; - - if (commit->object.flags & COMMON) { - /* do not send "have", and ignore ancestors */ - commit = NULL; - mark = COMMON | SEEN; - } else if (commit->object.flags & COMMON_REF) - /* send "have", and ignore ancestors */ - mark = COMMON | SEEN; - else - /* send "have", also for its ancestors */ - mark = SEEN; - - while (parents) { - if (!(parents->item->object.flags & SEEN)) - rev_list_push(ns, parents->item, mark); - if (mark & COMMON) - mark_common(ns, parents->item, 1, 0); - parents = parents->next; - } - } - - return &commit->object.oid; -} - enum ack_type { NAK = 0, ACK, @@ -306,10 +187,10 @@ static void send_request(struct fetch_pack_args *args, write_or_die(fd, buf->buf, buf->len); } -static void insert_one_alternate_object(struct negotiation_state *ns, +static void insert_one_alternate_object(struct fetch_negotiator *negotiator, struct object *obj) { - rev_list_insert_ref(ns, NULL, &obj->oid); + rev_list_insert_ref(negotiator, NULL, &obj->oid); } #define INITIAL_FLUSH 16 @@ -332,7 +213,7 @@ static int next_flush(int stateless_rpc, int count) return count; } -static int find_common(struct negotiation_state *ns, +static int find_common(struct fetch_negotiator *negotiator, struct fetch_pack_args *args, int fd[2], struct object_id *result_oid, struct ref *refs) @@ -349,8 +230,8 @@ static int find_common(struct negotiation_state *ns, if (args->stateless_rpc && multi_ack == 1) die(_("--stateless-rpc requires multi_ack_detailed")); - for_each_ref(rev_list_insert_ref_oid, ns); - for_each_cached_alternate(ns, insert_one_alternate_object); + for_each_ref(rev_list_insert_ref_oid, negotiator); + for_each_cached_alternate(negotiator, insert_one_alternate_object); fetching = 0; for ( ; refs ; refs = refs->next) { @@ -468,7 +349,7 @@ static int find_common(struct negotiation_state *ns, retval = -1; if (args->no_dependents) goto done; - while ((oid = get_rev(ns))) { + while ((oid = negotiator->next(negotiator))) { packet_buf_write(&req_buf, "have %s\n", oid_to_hex(oid)); print_verbose(args, "have %s", oid_to_hex(oid)); in_vain++; @@ -508,8 +389,7 @@ static int find_common(struct negotiation_state *ns, int was_common; if (!commit) die(_("invalid commit %s"), oid_to_hex(result_oid)); - was_common = commit->object.flags & COMMON; - mark_common(ns, commit, 0, 1); + was_common = negotiator->ack(negotiator, commit); if (args->stateless_rpc && ack == ACK_common && !was_common) { @@ -718,7 +598,7 @@ static void filter_refs(struct fetch_pack_args *args, *refs = newlist; } -static void mark_alternate_complete(struct negotiation_state *unused, +static void mark_alternate_complete(struct fetch_negotiator *unused, struct object *obj) { mark_complete(&obj->oid); @@ -756,7 +636,7 @@ static int add_loose_objects_to_set(const struct object_id *oid, * earliest commit time of the objects in refs that are commits and that we know * the commit time of. */ -static void mark_complete_and_common_ref(struct negotiation_state *ns, +static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator, struct fetch_pack_args *args, struct ref **refs) { @@ -825,12 +705,8 @@ static void mark_complete_and_common_ref(struct negotiation_state *ns, if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE)) continue; - if (!(o->flags & SEEN)) { - rev_list_push(ns, (struct commit *)o, - COMMON_REF | SEEN); - - mark_common(ns, (struct commit *)o, 1, 1); - } + negotiator->known_common(negotiator, + (struct commit *)o); } } @@ -1012,7 +888,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, struct object_id oid; const char *agent_feature; int agent_len; - struct negotiation_state ns = { { compare_commits_by_commit_date } }; + struct fetch_negotiator negotiator; + fetch_negotiator_init(&negotiator); sort_ref_list(&ref, ref_compare_name); QSORT(sought, nr_sought, cmp_ref_by_name); @@ -1085,16 +962,13 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, if (!server_supports("deepen-relative") && args->deepen_relative) die(_("Server does not support --deepen")); - if (marked) - for_each_ref(clear_marks, NULL); - marked = 1; - mark_complete_and_common_ref(&ns, args, &ref); + mark_complete_and_common_ref(&negotiator, args, &ref); filter_refs(args, &ref, sought, nr_sought); if (everything_local(args, &ref)) { packet_flush(fd[1]); goto all_done; } - if (find_common(&ns, args, fd, &oid, ref) < 0) + if (find_common(&negotiator, args, fd, &oid, ref) < 0) if (!args->keep_pack) /* When cloning, it is not unusual to have * no common commit. @@ -1114,7 +988,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, die(_("git fetch-pack: fetch failed.")); all_done: - clear_prio_queue(&ns.rev_list); + negotiator.release(&negotiator); return ref; } @@ -1176,14 +1050,15 @@ static void add_common(struct strbuf *req_buf, struct oidset *common) } } -static int add_haves(struct negotiation_state *ns, struct strbuf *req_buf, +static int add_haves(struct fetch_negotiator *negotiator, + struct strbuf *req_buf, int *haves_to_send, int *in_vain) { int ret = 0; int haves_added = 0; const struct object_id *oid; - while ((oid = get_rev(ns))) { + while ((oid = negotiator->next(negotiator))) { packet_buf_write(req_buf, "have %s\n", oid_to_hex(oid)); if (++haves_added >= *haves_to_send) break; @@ -1202,7 +1077,7 @@ static int add_haves(struct negotiation_state *ns, struct strbuf *req_buf, return ret; } -static int send_fetch_request(struct negotiation_state *ns, int fd_out, +static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out, const struct fetch_pack_args *args, const struct ref *wants, struct oidset *common, int *haves_to_send, int *in_vain) @@ -1259,7 +1134,7 @@ static int send_fetch_request(struct negotiation_state *ns, int fd_out, add_common(&req_buf, common); /* Add initial haves */ - ret = add_haves(ns, &req_buf, haves_to_send, in_vain); + ret = add_haves(negotiator, &req_buf, haves_to_send, in_vain); } /* Send request */ @@ -1296,7 +1171,7 @@ static int process_section_header(struct packet_reader *reader, return ret; } -static int process_acks(struct negotiation_state *ns, +static int process_acks(struct fetch_negotiator *negotiator, struct packet_reader *reader, struct oidset *common) { @@ -1317,7 +1192,7 @@ static int process_acks(struct negotiation_state *ns, struct commit *commit; oidset_insert(common, &oid); commit = lookup_commit(&oid); - mark_common(ns, commit, 0, 1); + negotiator->ack(negotiator, commit); } continue; } @@ -1395,7 +1270,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, struct packet_reader reader; int in_vain = 0; int haves_to_send = INITIAL_FLUSH; - struct negotiation_state ns = { { compare_commits_by_commit_date } }; + struct fetch_negotiator negotiator; + fetch_negotiator_init(&negotiator); packet_reader_init(&reader, fd[0], NULL, 0, PACKET_READ_CHOMP_NEWLINE); @@ -1411,24 +1287,21 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, if (args->depth > 0 || args->deepen_since || args->deepen_not) args->deepen = 1; - if (marked) - for_each_ref(clear_marks, NULL); - marked = 1; - /* Filter 'ref' by 'sought' and those that aren't local */ - mark_complete_and_common_ref(&ns, args, &ref); + mark_complete_and_common_ref(&negotiator, args, &ref); filter_refs(args, &ref, sought, nr_sought); if (everything_local(args, &ref)) state = FETCH_DONE; else state = FETCH_SEND_REQUEST; - for_each_ref(rev_list_insert_ref_oid, &ns); - for_each_cached_alternate(&ns, + for_each_ref(rev_list_insert_ref_oid, &negotiator); + for_each_cached_alternate(&negotiator, insert_one_alternate_object); break; case FETCH_SEND_REQUEST: - if (send_fetch_request(&ns, fd[1], args, ref, &common, + if (send_fetch_request(&negotiator, fd[1], args, ref, + &common, &haves_to_send, &in_vain)) state = FETCH_GET_PACK; else @@ -1436,7 +1309,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, break; case FETCH_PROCESS_ACKS: /* Process ACKs/NAKs */ - switch (process_acks(&ns, &reader, &common)) { + switch (process_acks(&negotiator, &reader, &common)) { case 2: state = FETCH_GET_PACK; break; @@ -1465,7 +1338,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, } } - clear_prio_queue(&ns.rev_list); + negotiator.release(&negotiator); oidset_clear(&common); return ref; } diff --git a/negotiator/default.c b/negotiator/default.c new file mode 100644 index 0000000..382fc77 --- /dev/null +++ b/negotiator/default.c @@ -0,0 +1,176 @@ +#include "cache.h" +#include "default.h" +#include "../commit.h" +#include "../fetch-negotiator.h" +#include "../prio-queue.h" +#include "../refs.h" +#include "../tag.h" + +/* Remember to update object flag allocation in object.h */ +#define COMMON (1U << 2) +#define COMMON_REF (1U << 3) +#define SEEN (1U << 4) +#define POPPED (1U << 5) + +static int marked; + +struct negotiation_state { + struct prio_queue rev_list; + int non_common_revs; +}; + +static void rev_list_push(struct negotiation_state *ns, + struct commit *commit, int mark) +{ + if (!(commit->object.flags & mark)) { + commit->object.flags |= mark; + + if (parse_commit(commit)) + return; + + prio_queue_put(&ns->rev_list, commit); + + if (!(commit->object.flags & COMMON)) + ns->non_common_revs++; + } +} + +static int clear_marks(const char *refname, const struct object_id *oid, + int flag, void *cb_data) +{ + struct object *o = deref_tag(parse_object(oid), refname, 0); + + if (o && o->type == OBJ_COMMIT) + clear_commit_marks((struct commit *)o, + COMMON | COMMON_REF | SEEN | POPPED); + return 0; +} + +/* + * This function marks a rev and its ancestors as common. + * In some cases, it is desirable to mark only the ancestors (for example + * when only the server does not yet know that they are common). + */ +static void mark_common(struct negotiation_state *ns, struct commit *commit, + int ancestors_only, int dont_parse) +{ + if (commit != NULL && !(commit->object.flags & COMMON)) { + struct object *o = (struct object *)commit; + + if (!ancestors_only) + o->flags |= COMMON; + + if (!(o->flags & SEEN)) + rev_list_push(ns, commit, SEEN); + else { + struct commit_list *parents; + + if (!ancestors_only && !(o->flags & POPPED)) + ns->non_common_revs--; + if (!o->parsed && !dont_parse) + if (parse_commit(commit)) + return; + + for (parents = commit->parents; + parents; + parents = parents->next) + mark_common(ns, parents->item, 0, + dont_parse); + } + } +} + +/* + * Get the next rev to send, ignoring the common. + */ +static const struct object_id *get_rev(struct negotiation_state *ns) +{ + struct commit *commit = NULL; + + while (commit == NULL) { + unsigned int mark; + struct commit_list *parents; + + if (ns->rev_list.nr == 0 || ns->non_common_revs == 0) + return NULL; + + commit = prio_queue_get(&ns->rev_list); + parse_commit(commit); + parents = commit->parents; + + commit->object.flags |= POPPED; + if (!(commit->object.flags & COMMON)) + ns->non_common_revs--; + + if (commit->object.flags & COMMON) { + /* do not send "have", and ignore ancestors */ + commit = NULL; + mark = COMMON | SEEN; + } else if (commit->object.flags & COMMON_REF) + /* send "have", and ignore ancestors */ + mark = COMMON | SEEN; + else + /* send "have", also for its ancestors */ + mark = SEEN; + + while (parents) { + if (!(parents->item->object.flags & SEEN)) + rev_list_push(ns, parents->item, mark); + if (mark & COMMON) + mark_common(ns, parents->item, 1, 0); + parents = parents->next; + } + } + + return &commit->object.oid; +} + +static void known_common(struct fetch_negotiator *n, struct commit *c) +{ + if (!(c->object.flags & SEEN)) { + rev_list_push(n->data, c, COMMON_REF | SEEN); + mark_common(n->data, c, 1, 1); + } +} + +static void add_tip(struct fetch_negotiator *n, struct commit *c) +{ + n->known_common = NULL; + rev_list_push(n->data, c, SEEN); +} + +static const struct object_id *next(struct fetch_negotiator *n) +{ + n->known_common = NULL; + n->add_tip = NULL; + return get_rev(n->data); +} + +static int ack(struct fetch_negotiator *n, struct commit *c) +{ + int known_to_be_common = !!(c->object.flags & COMMON); + mark_common(n->data, c, 0, 1); + return known_to_be_common; +} + +static void release(struct fetch_negotiator *n) +{ + clear_prio_queue(&((struct negotiation_state *)n->data)->rev_list); + FREE_AND_NULL(n->data); +} + +void default_negotiator_init(struct fetch_negotiator *negotiator) +{ + struct negotiation_state *ns; + negotiator->known_common = known_common; + negotiator->add_tip = add_tip; + negotiator->next = next; + negotiator->ack = ack; + negotiator->release = release; + negotiator->data = ns = xcalloc(1, sizeof(*ns)); + ns->rev_list.compare = compare_commits_by_commit_date; + + if (marked) + for_each_ref(clear_marks, NULL); + marked = 1; +} diff --git a/negotiator/default.h b/negotiator/default.h new file mode 100644 index 0000000..d23a8f2 --- /dev/null +++ b/negotiator/default.h @@ -0,0 +1,8 @@ +#ifndef NEGOTIATOR_DEFAULT_H +#define NEGOTIATOR_DEFAULT_H + +struct fetch_negotiator; + +void default_negotiator_init(struct fetch_negotiator *negotiator); + +#endif diff --git a/object.h b/object.h index 5c13955..7db4941 100644 --- a/object.h +++ b/object.h @@ -28,7 +28,8 @@ struct object_array { /* * object flag allocation: * revision.h: 0---------10 26 - * fetch-pack.c: 0----5 + * fetch-pack.c: 01 + * negotiator/default.c: 2--5 * walker.c: 0-2 * upload-pack.c: 4 11----------------19 * builtin/blame.c: 12-13 -- cgit v0.10.2-6-g49f6