Code

Smart fetch over HTTP: client side
[git.git] / builtin-fetch-pack.c
index 615f54974e0b19354d88a40ecba53f586944a96b..8ed4a6feaac2868523e6516e02865132dedbc9f5 100644 (file)
@@ -165,6 +165,24 @@ enum ack_type {
        ACK_ready
 };
 
+static void consume_shallow_list(int fd)
+{
+       if (args.stateless_rpc && args.depth > 0) {
+               /* If we sent a depth we will get back "duplicate"
+                * shallow and unshallow commands every time there
+                * is a block of have lines exchanged.
+                */
+               char line[1000];
+               while (packet_read_line(fd, line, sizeof(line))) {
+                       if (!prefixcmp(line, "shallow "))
+                               continue;
+                       if (!prefixcmp(line, "unshallow "))
+                               continue;
+                       die("git fetch-pack: expected shallow list");
+               }
+       }
+}
+
 static enum ack_type get_ack(int fd, unsigned char *result_sha1)
 {
        static char line[1000];
@@ -190,6 +208,15 @@ static enum ack_type get_ack(int fd, unsigned char *result_sha1)
        die("git fetch_pack: expected ACK/NAK, got '%s'", line);
 }
 
+static void send_request(int fd, struct strbuf *buf)
+{
+       if (args.stateless_rpc) {
+               send_sideband(fd, -1, buf->buf, buf->len, LARGE_PACKET_MAX);
+               packet_flush(fd);
+       } else
+               safe_write(fd, buf->buf, buf->len);
+}
+
 static int find_common(int fd[2], unsigned char *result_sha1,
                       struct ref *refs)
 {
@@ -199,7 +226,10 @@ static int find_common(int fd[2], unsigned char *result_sha1,
        unsigned in_vain = 0;
        int got_continue = 0;
        struct strbuf req_buf = STRBUF_INIT;
+       size_t state_len = 0;
 
+       if (args.stateless_rpc && multi_ack == 1)
+               die("--stateless-rpc requires multi_ack_detailed");
        if (marked)
                for_each_ref(clear_marks, NULL);
        marked = 1;
@@ -256,13 +286,13 @@ static int find_common(int fd[2], unsigned char *result_sha1,
        if (args.depth > 0)
                packet_buf_write(&req_buf, "deepen %d", args.depth);
        packet_buf_flush(&req_buf);
-
-       safe_write(fd[1], req_buf.buf, req_buf.len);
+       state_len = req_buf.len;
 
        if (args.depth > 0) {
                char line[1024];
                unsigned char sha1[20];
 
+               send_request(fd[1], &req_buf);
                while (packet_read_line(fd[0], line, sizeof(line))) {
                        if (!prefixcmp(line, "shallow ")) {
                                if (get_sha1_hex(line + 8, sha1))
@@ -284,28 +314,40 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                        }
                        die("expected shallow/unshallow, got %s", line);
                }
+       } else if (!args.stateless_rpc)
+               send_request(fd[1], &req_buf);
+
+       if (!args.stateless_rpc) {
+               /* If we aren't using the stateless-rpc interface
+                * we don't need to retain the headers.
+                */
+               strbuf_setlen(&req_buf, 0);
+               state_len = 0;
        }
 
        flushes = 0;
        retval = -1;
        while ((sha1 = get_rev())) {
-               packet_write(fd[1], "have %s\n", sha1_to_hex(sha1));
+               packet_buf_write(&req_buf, "have %s\n", sha1_to_hex(sha1));
                if (args.verbose)
                        fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
                in_vain++;
                if (!(31 & ++count)) {
                        int ack;
 
-                       packet_flush(fd[1]);
+                       packet_buf_flush(&req_buf);
+                       send_request(fd[1], &req_buf);
+                       strbuf_setlen(&req_buf, state_len);
                        flushes++;
 
                        /*
                         * We keep one window "ahead" of the other side, and
                         * will wait for an ACK only on the next one
                         */
-                       if (count == 32)
+                       if (!args.stateless_rpc && count == 32)
                                continue;
 
+                       consume_shallow_list(fd[0]);
                        do {
                                ack = get_ack(fd[0], result_sha1);
                                if (args.verbose && ack)
@@ -322,6 +364,17 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                                case ACK_continue: {
                                        struct commit *commit =
                                                lookup_commit(result_sha1);
+                                       if (args.stateless_rpc
+                                        && ack == ACK_common
+                                        && !(commit->object.flags & 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.
+                                                */
+                                               const char *hex = sha1_to_hex(result_sha1);
+                                               packet_buf_write(&req_buf, "have %s\n", hex);
+                                               state_len = req_buf.len;
+                                       }
                                        mark_common(commit, 0, 1);
                                        retval = 0;
                                        in_vain = 0;
@@ -339,7 +392,8 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                }
        }
 done:
-       packet_write(fd[1], "done\n");
+       packet_buf_write(&req_buf, "done\n");
+       send_request(fd[1], &req_buf);
        if (args.verbose)
                fprintf(stderr, "done\n");
        if (retval != 0) {
@@ -348,6 +402,7 @@ done:
        }
        strbuf_release(&req_buf);
 
+       consume_shallow_list(fd[0]);
        while (flushes || multi_ack) {
                int ack = get_ack(fd[0], result_sha1);
                if (ack) {
@@ -672,6 +727,8 @@ static struct ref *do_fetch_pack(int fd[2],
                         */
                        warning("no common commits");
 
+       if (args.stateless_rpc)
+               packet_flush(fd[1]);
        if (get_pack(fd, pack_lockfile))
                die("git fetch-pack: fetch failed.");
 
@@ -742,6 +799,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        struct ref *ref = NULL;
        char *dest = NULL, **heads;
        int fd[2];
+       char *pack_lockfile = NULL;
+       char **pack_lockfile_ptr = NULL;
        struct child_process *conn;
 
        nr_heads = 0;
@@ -791,6 +850,15 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                                args.no_progress = 1;
                                continue;
                        }
+                       if (!strcmp("--stateless-rpc", arg)) {
+                               args.stateless_rpc = 1;
+                               continue;
+                       }
+                       if (!strcmp("--lock-pack", arg)) {
+                               args.lock_pack = 1;
+                               pack_lockfile_ptr = &pack_lockfile;
+                               continue;
+                       }
                        usage(fetch_pack_usage);
                }
                dest = (char *)arg;
@@ -801,19 +869,27 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        if (!dest)
                usage(fetch_pack_usage);
 
-       conn = git_connect(fd, (char *)dest, args.uploadpack,
-                          args.verbose ? CONNECT_VERBOSE : 0);
-       if (conn) {
-               get_remote_heads(fd[0], &ref, 0, NULL, 0, NULL);
-
-               ref = fetch_pack(&args, fd, conn, ref, dest, nr_heads, heads, NULL);
-               close(fd[0]);
-               close(fd[1]);
-               if (finish_connect(conn))
-                       ref = NULL;
+       if (args.stateless_rpc) {
+               conn = NULL;
+               fd[0] = 0;
+               fd[1] = 1;
        } else {
-               ref = NULL;
+               conn = git_connect(fd, (char *)dest, args.uploadpack,
+                                  args.verbose ? CONNECT_VERBOSE : 0);
+       }
+
+       get_remote_heads(fd[0], &ref, 0, NULL, 0, NULL);
+
+       ref = fetch_pack(&args, fd, conn, ref, dest,
+               nr_heads, heads, pack_lockfile_ptr);
+       if (pack_lockfile) {
+               printf("lock %s\n", pack_lockfile);
+               fflush(stdout);
        }
+       close(fd[0]);
+       close(fd[1]);
+       if (finish_connect(conn))
+               ref = NULL;
        ret = !ref;
 
        if (!ret && nr_heads) {