negotiator: add have_sent() interface

In a future change, we will introduce a capability to choose specific commit OIDs as 'have's in fetch negotiation, with the ability to have the negotiator choose more 'have's to increase coverage beyond that required core set. The negotiator works to avoid emitting 'have's that can reach each other, but that logic is hidden beneath the negotiator's iterator function pointer ('next'). We need a way to communicate to the negotiator that we have picked a 'have' so it could incorporate that into its logic. Add a have_sent() method to the fetch_negotiator interface. This is the signal that allows the negotiator to track the commit as already shown and can perform the proper bookkeeping to avoid emitting those objects or anything they can reach. For our non-trivial negotiators, it is sufficient to mark these commits as common, so the implementation is quite simple. This logic will be exercised in the next change. Reviewed-by: Matthew John Cheetham <mjcheetham@outlook.com> Signed-off-by: Derrick Stolee <stolee@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Derrick Stolee committed May 19, 2026 at 16:24 UTC 22b2f3d2a319af32e9f3add0b3cc7732cbf4733b
4 files changed +32
fetch-negotiator.h
+9
@@ -47,6 +47,15 @@ struct fetch_negotiator {
47 */
48 int (*ack)(struct fetch_negotiator *, struct commit *);
49
50 + /*
51 + * Inform the negotiator that this commit has already been sent as
52 + * a "have" line outside of the negotiator's control. The negotiator
53 + * should avoid outputting it from next() and may use it to optimize
54 + * further negotiation (e.g., by treating it and its ancestors as
55 + * common).
56 + */
57 + void (*have_sent)(struct fetch_negotiator *, struct commit *);
58 +
59 void (*release)(struct fetch_negotiator *);
60
61 /* internal use */
negotiator/default.c
+8
@@ -175,6 +175,13 @@ static int ack(struct fetch_negotiator *n, struct commit *c)
175 return known_to_be_common;
176 }
177
178 +static void have_sent(struct fetch_negotiator *n, struct commit *c)
179 +{
180 + if (repo_parse_commit(the_repository, c))
181 + return;
182 + mark_common(n->data, c, 0, 0);
183 +}
184 +
185 static void release(struct fetch_negotiator *n)
186 {
187 clear_prio_queue(&((struct negotiation_state *)n->data)->rev_list);
@@ -188,6 +195,7 @@ void default_negotiator_init(struct fetch_negotiator *negotiator)
195 negotiator->add_tip = add_tip;
196 negotiator->next = next;
197 negotiator->ack = ack;
198 + negotiator->have_sent = have_sent;
199 negotiator->release = release;
200 negotiator->data = CALLOC_ARRAY(ns, 1);
201 ns->rev_list.compare = compare_commits_by_commit_date;
negotiator/noop.c
+7
@@ -29,6 +29,12 @@ static int ack(struct fetch_negotiator *n UNUSED, struct commit *c UNUSED)
29 return 0;
30 }
31
32 +static void have_sent(struct fetch_negotiator *n UNUSED,
33 + struct commit *c UNUSED)
34 +{
35 + /* nothing to do */
36 +}
37 +
38 static void release(struct fetch_negotiator *n UNUSED)
39 {
40 /* nothing to release */
@@ -40,6 +46,7 @@ void noop_negotiator_init(struct fetch_negotiator *negotiator)
46 negotiator->add_tip = add_tip;
47 negotiator->next = next;
48 negotiator->ack = ack;
49 + negotiator->have_sent = have_sent;
50 negotiator->release = release;
51 negotiator->data = NULL;
52 }
negotiator/skipping.c
+8
@@ -243,6 +243,13 @@ static int ack(struct fetch_negotiator *n, struct commit *c)
243 return known_to_be_common;
244 }
245
246 +static void have_sent(struct fetch_negotiator *n, struct commit *c)
247 +{
248 + if (repo_parse_commit(the_repository, c))
249 + return;
250 + mark_common(n->data, c);
251 +}
252 +
253 static void release(struct fetch_negotiator *n)
254 {
255 struct data *data = n->data;
@@ -259,6 +266,7 @@ void skipping_negotiator_init(struct fetch_negotiator *negotiator)
266 negotiator->add_tip = add_tip;
267 negotiator->next = next;
268 negotiator->ack = ack;
269 + negotiator->have_sent = have_sent;
270 negotiator->release = release;
271 negotiator->data = CALLOC_ARRAY(data, 1);
272 data->rev_list.compare = compare;