]> git.ipfire.org Git - thirdparty/git.git/blobdiff - send-pack.c
t1503: mark symlink test as REFFILES
[thirdparty/git.git] / send-pack.c
index 5f215b13c7dc1a8a020094236625554db94591b2..5a79e0e711031907bcec04acc18a64c2b9d40adc 100644 (file)
@@ -56,7 +56,9 @@ static void feed_object(const struct object_id *oid, FILE *fh, int negative)
 /*
  * Make a pack stream and spit it out into file descriptor fd
  */
-static int pack_objects(int fd, struct ref *refs, struct oid_array *extra, struct send_pack_args *args)
+static int pack_objects(int fd, struct ref *refs, struct oid_array *advertised,
+                       struct oid_array *negotiated,
+                       struct send_pack_args *args)
 {
        /*
         * 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
         * parameters by writing to the pipe.
         */
        po_in = xfdopen(po.in, "w");
-       for (i = 0; i < extra->nr; i++)
-               feed_object(&extra->oid[i], po_in, 1);
+       for (i = 0; i < advertised->nr; i++)
+               feed_object(&advertised->oid[i], po_in, 1);
+       for (i = 0; i < negotiated->nr; i++)
+               feed_object(&negotiated->oid[i], po_in, 1);
 
        while (refs) {
                if (!is_null_oid(&refs->old_oid))
@@ -409,11 +413,55 @@ static void reject_invalid_nonce(const char *nonce, int len)
        }
 }
 
+static void get_commons_through_negotiation(const char *url,
+                                           const struct ref *remote_refs,
+                                           struct oid_array *commons)
+{
+       struct child_process child = CHILD_PROCESS_INIT;
+       const struct ref *ref;
+       int len = the_hash_algo->hexsz + 1; /* hash + NL */
+
+       child.git_cmd = 1;
+       child.no_stdin = 1;
+       child.out = -1;
+       strvec_pushl(&child.args, "fetch", "--negotiate-only", NULL);
+       for (ref = remote_refs; ref; ref = ref->next)
+               strvec_pushf(&child.args, "--negotiation-tip=%s", oid_to_hex(&ref->new_oid));
+       strvec_push(&child.args, url);
+
+       if (start_command(&child))
+               die(_("send-pack: unable to fork off fetch subprocess"));
+
+       do {
+               char hex_hash[GIT_MAX_HEXSZ + 1];
+               int read_len = read_in_full(child.out, hex_hash, len);
+               struct object_id oid;
+               const char *end;
+
+               if (!read_len)
+                       break;
+               if (read_len != len)
+                       die("invalid length read %d", read_len);
+               if (parse_oid_hex(hex_hash, &oid, &end) || *end != '\n')
+                       die("invalid hash");
+               oid_array_append(commons, &oid);
+       } while (1);
+
+       if (finish_command(&child)) {
+               /*
+                * The information that push negotiation provides is useful but
+                * not mandatory.
+                */
+               warning(_("push negotiation failed; proceeding anyway with push"));
+       }
+}
+
 int send_pack(struct send_pack_args *args,
              int fd[], struct child_process *conn,
              struct ref *remote_refs,
              struct oid_array *extra_have)
 {
+       struct oid_array commons = OID_ARRAY_INIT;
        int in = fd[0];
        int out = fd[1];
        struct strbuf req_buf = STRBUF_INIT;
@@ -426,6 +474,7 @@ int send_pack(struct send_pack_args *args,
        int quiet_supported = 0;
        int agent_supported = 0;
        int advertise_sid = 0;
+       int push_negotiate = 0;
        int use_atomic = 0;
        int atomic_supported = 0;
        int use_push_options = 0;
@@ -437,6 +486,16 @@ int send_pack(struct send_pack_args *args,
        const char *push_cert_nonce = NULL;
        struct packet_reader reader;
 
+       if (!remote_refs) {
+               fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
+                       "Perhaps you should specify a branch.\n");
+               return 0;
+       }
+
+       git_config_get_bool("push.negotiate", &push_negotiate);
+       if (push_negotiate)
+               get_commons_through_negotiation(args->url, remote_refs, &commons);
+
        git_config_get_bool("transfer.advertisesid", &advertise_sid);
 
        /* Does the other end support the reporting? */
@@ -481,11 +540,6 @@ int send_pack(struct send_pack_args *args,
                }
        }
 
-       if (!remote_refs) {
-               fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
-                       "Perhaps you should specify a branch.\n");
-               return 0;
-       }
        if (args->atomic && !atomic_supported)
                die(_("the receiving end does not support --atomic push"));
 
@@ -625,7 +679,7 @@ int send_pack(struct send_pack_args *args,
                           PACKET_READ_DIE_ON_ERR_PACKET);
 
        if (need_pack_data && cmds_sent) {
-               if (pack_objects(out, remote_refs, extra_have, args) < 0) {
+               if (pack_objects(out, remote_refs, extra_have, &commons, args) < 0) {
                        if (args->stateless_rpc)
                                close(out);
                        if (git_connection_is_socket(conn))