@@ -56,7 +56,9 @@ static void feed_object(const struct object_id *oid, FILE *fh, int negative)
5656/*
5757 * Make a pack stream and spit it out into file descriptor fd
5858 */
59- static int pack_objects (int fd , struct ref * refs , struct oid_array * extra , struct send_pack_args * args )
59+ static int pack_objects (int fd , struct ref * refs , struct oid_array * advertised ,
60+ struct oid_array * negotiated ,
61+ struct send_pack_args * args )
6062{
6163 /*
6264 * The child becomes pack-objects --revs; we feed
@@ -94,8 +96,10 @@ static int pack_objects(int fd, struct ref *refs, struct oid_array *extra, struc
9496 * parameters by writing to the pipe.
9597 */
9698 po_in = xfdopen (po .in , "w" );
97- for (i = 0 ; i < extra -> nr ; i ++ )
98- feed_object (& extra -> oid [i ], po_in , 1 );
99+ for (i = 0 ; i < advertised -> nr ; i ++ )
100+ feed_object (& advertised -> oid [i ], po_in , 1 );
101+ for (i = 0 ; i < negotiated -> nr ; i ++ )
102+ feed_object (& negotiated -> oid [i ], po_in , 1 );
99103
100104 while (refs ) {
101105 if (!is_null_oid (& refs -> old_oid ))
@@ -409,11 +413,55 @@ static void reject_invalid_nonce(const char *nonce, int len)
409413 }
410414}
411415
416+ static void get_commons_through_negotiation (const char * url ,
417+ const struct ref * remote_refs ,
418+ struct oid_array * commons )
419+ {
420+ struct child_process child = CHILD_PROCESS_INIT ;
421+ const struct ref * ref ;
422+ int len = the_hash_algo -> hexsz + 1 ; /* hash + NL */
423+
424+ child .git_cmd = 1 ;
425+ child .no_stdin = 1 ;
426+ child .out = -1 ;
427+ strvec_pushl (& child .args , "fetch" , "--negotiate-only" , NULL );
428+ for (ref = remote_refs ; ref ; ref = ref -> next )
429+ strvec_pushf (& child .args , "--negotiation-tip=%s" , oid_to_hex (& ref -> new_oid ));
430+ strvec_push (& child .args , url );
431+
432+ if (start_command (& child ))
433+ die (_ ("send-pack: unable to fork off fetch subprocess" ));
434+
435+ do {
436+ char hex_hash [GIT_MAX_HEXSZ + 1 ];
437+ int read_len = read_in_full (child .out , hex_hash , len );
438+ struct object_id oid ;
439+ const char * end ;
440+
441+ if (!read_len )
442+ break ;
443+ if (read_len != len )
444+ die ("invalid length read %d" , read_len );
445+ if (parse_oid_hex (hex_hash , & oid , & end ) || * end != '\n' )
446+ die ("invalid hash" );
447+ oid_array_append (commons , & oid );
448+ } while (1 );
449+
450+ if (finish_command (& child )) {
451+ /*
452+ * The information that push negotiation provides is useful but
453+ * not mandatory.
454+ */
455+ warning (_ ("push negotiation failed; proceeding anyway with push" ));
456+ }
457+ }
458+
412459int send_pack (struct send_pack_args * args ,
413460 int fd [], struct child_process * conn ,
414461 struct ref * remote_refs ,
415462 struct oid_array * extra_have )
416463{
464+ struct oid_array commons = OID_ARRAY_INIT ;
417465 int in = fd [0 ];
418466 int out = fd [1 ];
419467 struct strbuf req_buf = STRBUF_INIT ;
@@ -426,6 +474,7 @@ int send_pack(struct send_pack_args *args,
426474 int quiet_supported = 0 ;
427475 int agent_supported = 0 ;
428476 int advertise_sid = 0 ;
477+ int push_negotiate = 0 ;
429478 int use_atomic = 0 ;
430479 int atomic_supported = 0 ;
431480 int use_push_options = 0 ;
@@ -437,6 +486,10 @@ int send_pack(struct send_pack_args *args,
437486 const char * push_cert_nonce = NULL ;
438487 struct packet_reader reader ;
439488
489+ git_config_get_bool ("push.negotiate" , & push_negotiate );
490+ if (push_negotiate )
491+ get_commons_through_negotiation (args -> url , remote_refs , & commons );
492+
440493 git_config_get_bool ("transfer.advertisesid" , & advertise_sid );
441494
442495 /* Does the other end support the reporting? */
@@ -625,7 +678,7 @@ int send_pack(struct send_pack_args *args,
625678 PACKET_READ_DIE_ON_ERR_PACKET );
626679
627680 if (need_pack_data && cmds_sent ) {
628- if (pack_objects (out , remote_refs , extra_have , args ) < 0 ) {
681+ if (pack_objects (out , remote_refs , extra_have , & commons , args ) < 0 ) {
629682 if (args -> stateless_rpc )
630683 close (out );
631684 if (git_connection_is_socket (conn ))
0 commit comments