ls-files --others --directory: give trailing slash
[gitweb.git] / send-pack.c
index 9f9a6e70b8789f63891c5b6e07822c546af8eec4..cd361934449e9bdbce229febaccb8b638037b2e7 100644 (file)
@@ -8,6 +8,7 @@ static const char send_pack_usage[] =
 "git-send-pack [--all] [--exec=git-receive-pack] <remote> [<head>...]\n"
 "  --all and explicit <head> specification are mutually exclusive.";
 static const char *exec = "git-receive-pack";
+static int verbose = 0;
 static int send_all = 0;
 static int force_update = 0;
 
@@ -126,12 +127,12 @@ static int ref_newer(const unsigned char *new_sha1,
        /* Both new and old must be commit-ish and new is descendant of
         * old.  Otherwise we require --force.
         */
-       o = deref_tag(parse_object(old_sha1));
+       o = deref_tag(parse_object(old_sha1), NULL, 0);
        if (!o || o->type != commit_type)
                return 0;
        old = (struct commit *) o;
 
-       o = deref_tag(parse_object(new_sha1));
+       o = deref_tag(parse_object(new_sha1), NULL, 0);
        if (!o || o->type != commit_type)
                return 0;
        new = (struct commit *) o;
@@ -175,21 +176,65 @@ 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;
        if (match_refs(local_refs, remote_refs, &remote_tail,
                       nr_refspec, refspec, send_all))
                return -1;
+
+       if (!remote_refs) {
+               fprintf(stderr, "No refs in common and none specified; doing nothing.\n");
+               return 0;
+       }
+
        /*
         * Finally, tell the other end!
         */
@@ -199,7 +244,8 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
                if (!ref->peer_ref)
                        continue;
                if (!memcmp(ref->old_sha1, ref->peer_ref->new_sha1, 20)) {
-                       fprintf(stderr, "'%s': up-to-date\n", ref->name);
+                       if (verbose)
+                               fprintf(stderr, "'%s': up-to-date\n", ref->name);
                        continue;
                }
 
@@ -222,35 +268,46 @@ 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));
-                               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;
                        }
                }
                memcpy(ref->new_sha1, ref->peer_ref->new_sha1, 20);
                if (is_zero_sha1(ref->new_sha1)) {
                        error("cannot happen anymore");
+                       ret = -3;
                        continue;
                }
                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);
@@ -261,7 +318,15 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
        if (new_refs)
                pack_objects(out, remote_refs);
        close(out);
-       return 0;
+
+       if (expect_status_report) {
+               if (receive_status(in))
+                       ret = -4;
+       }
+
+       if (!new_refs && ret == 0)
+               fprintf(stderr, "Everything up-to-date\n");
+       return ret;
 }
 
 
@@ -273,6 +338,7 @@ int main(int argc, char **argv)
        int fd[2], ret;
        pid_t pid;
 
+       setup_git_directory();
        argv++;
        for (i = 1; i < argc; i++, argv++) {
                char *arg = *argv;
@@ -290,6 +356,10 @@ int main(int argc, char **argv)
                                force_update = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "--verbose")) {
+                               verbose = 1;
+                               continue;
+                       }
                        usage(send_pack_usage);
                }
                if (!dest) {