Merge branch 'fix'
[gitweb.git] / send-pack.c
index a41bbe5ecfdff6fd7cea1bfc3d3415ba57867f2b..990be3f1a338a34025afa4acbe25f4c67ea6ce30 100644 (file)
@@ -3,6 +3,7 @@
 #include "tag.h"
 #include "refs.h"
 #include "pkt-line.h"
+#include "exec_cmd.h"
 
 static const char send_pack_usage[] =
 "git-send-pack [--all] [--exec=git-receive-pack] <remote> [<head>...]\n"
@@ -26,11 +27,11 @@ static int is_zero_sha1(const unsigned char *sha1)
 static void exec_pack_objects(void)
 {
        static char *args[] = {
-               "git-pack-objects",
+               "pack-objects",
                "--stdout",
                NULL
        };
-       execvp("git-pack-objects", args);
+       execv_git_cmd(args);
        die("git-pack-objects exec failed (%s)", strerror(errno));
 }
 
@@ -39,7 +40,7 @@ static void exec_rev_list(struct ref *refs)
        static char *args[1000];
        int i = 0;
 
-       args[i++] = "git-rev-list";     /* 0 */
+       args[i++] = "rev-list"; /* 0 */
        args[i++] = "--objects";        /* 1 */
        while (refs) {
                char *buf = malloc(100);
@@ -58,7 +59,7 @@ static void exec_rev_list(struct ref *refs)
                refs = refs->next;
        }
        args[i] = NULL;
-       execvp("git-rev-list", args);
+       execv_git_cmd(args);
        die("git-rev-list exec failed (%s)", strerror(errno));
 }
 
@@ -176,16 +177,53 @@ static void get_local_heads(void)
        for_each_ref(one_local_ref);
 }
 
+static int receive_status(int in)
+{
+       char line[1000];
+       int ret = 0;
+       int len = packet_read_line(in, line, sizeof(line));
+       if (len < 10 || memcmp(line, "unpack ", 7)) {
+               fprintf(stderr, "did not receive status back\n");
+               return -1;
+       }
+       if (memcmp(line, "unpack ok\n", 10)) {
+               fputs(line, stderr);
+               ret = -1;
+       }
+       while (1) {
+               len = packet_read_line(in, line, sizeof(line));
+               if (!len)
+                       break;
+               if (len < 3 ||
+                   (memcmp(line, "ok", 2) && memcmp(line, "ng", 2))) {
+                       fprintf(stderr, "protocol error: %s\n", line);
+                       ret = -1;
+                       break;
+               }
+               if (!memcmp(line, "ok", 2))
+                       continue;
+               fputs(line, stderr);
+               ret = -1;
+       }
+       return ret;
+}
+
 static int send_pack(int in, int out, int nr_refspec, char **refspec)
 {
        struct ref *ref;
        int new_refs;
        int ret = 0;
+       int ask_for_status_report = 0;
+       int expect_status_report = 0;
 
        /* No funny business with the matcher */
        remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, 1);
        get_local_heads();
 
+       /* Does the other end support the reporting? */
+       if (server_supports("report-status"))
+               ask_for_status_report = 1;
+
        /* match them up */
        if (!remote_tail)
                remote_tail = &remote_refs;
@@ -231,23 +269,21 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
                if (!force_update &&
                    !is_zero_sha1(ref->old_sha1) &&
                    !ref->force) {
-                       if (!has_sha1_file(ref->old_sha1)) {
-                               error("remote '%s' object %s does not "
-                                     "exist on local",
-                                     ref->name, sha1_to_hex(ref->old_sha1));
-                               ret = -2;
-                               continue;
-                       }
-
-                       /* We assume that local is fsck-clean.  Otherwise
-                        * you _could_ have an old tag which points at
-                        * something you do not have, which may or may not
-                        * be a commit.
-                        */
-                       if (!ref_newer(ref->peer_ref->new_sha1,
+                       if (!has_sha1_file(ref->old_sha1) ||
+                           !ref_newer(ref->peer_ref->new_sha1,
                                       ref->old_sha1)) {
-                               error("remote ref '%s' is not a strict "
-                                     "subset of local ref '%s'.", ref->name,
+                               /* We do not have the remote ref, or
+                                * we know that the remote ref is not
+                                * an ancestor of what we are trying to
+                                * push.  Either way this can be losing
+                                * commits at the remote end and likely
+                                * we were not up to date to begin with.
+                                */
+                               error("remote '%s' is not a strict "
+                                     "subset of local ref '%s'. "
+                                     "maybe you are not up-to-date and "
+                                     "need to pull first?",
+                                     ref->name,
                                      ref->peer_ref->name);
                                ret = -2;
                                continue;
@@ -262,7 +298,17 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
                new_refs++;
                strcpy(old_hex, sha1_to_hex(ref->old_sha1));
                new_hex = sha1_to_hex(ref->new_sha1);
-               packet_write(out, "%s %s %s", old_hex, new_hex, ref->name);
+
+               if (ask_for_status_report) {
+                       packet_write(out, "%s %s %s%c%s",
+                                    old_hex, new_hex, ref->name, 0,
+                                    "report-status");
+                       ask_for_status_report = 0;
+                       expect_status_report = 1;
+               }
+               else
+                       packet_write(out, "%s %s %s",
+                                    old_hex, new_hex, ref->name);
                fprintf(stderr, "updating '%s'", ref->name);
                if (strcmp(ref->name, ref->peer_ref->name))
                        fprintf(stderr, " using '%s'", ref->peer_ref->name);
@@ -272,9 +318,15 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
        packet_flush(out);
        if (new_refs)
                pack_objects(out, remote_refs);
-       else
-               fprintf(stderr, "Everything up-to-date\n");
        close(out);
+
+       if (expect_status_report) {
+               if (receive_status(in))
+                       ret = -4;
+       }
+
+       if (!new_refs && ret == 0)
+               fprintf(stderr, "Everything up-to-date\n");
        return ret;
 }