refactor userdiff textconv code
[gitweb.git] / builtin-send-pack.c
index 3aab89ca1bb9202232448f16e44e939bff4c820d..910db92b62eb6dd91a4002b2643fef4a76ec8f83 100644 (file)
@@ -8,7 +8,7 @@
 #include "send-pack.h"
 
 static const char send_pack_usage[] =
-"git-send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
+"git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
 "  --all and explicit <ref> specification are mutually exclusive.";
 
 static struct send_pack_args args = {
@@ -18,7 +18,7 @@ static struct send_pack_args args = {
 /*
  * Make a pack stream and spit it out into file descriptor fd
  */
-static int pack_objects(int fd, struct ref *refs)
+static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra)
 {
        /*
         * The child becomes pack-objects --revs; we feed
@@ -34,6 +34,8 @@ static int pack_objects(int fd, struct ref *refs)
                NULL,
        };
        struct child_process po;
+       int i;
+       char buf[42];
 
        if (args.use_thin_pack)
                argv[4] = "--thin";
@@ -43,15 +45,21 @@ static int pack_objects(int fd, struct ref *refs)
        po.out = fd;
        po.git_cmd = 1;
        if (start_command(&po))
-               die("git-pack-objects failed (%s)", strerror(errno));
+               die("git pack-objects failed (%s)", strerror(errno));
 
        /*
         * We feed the pack-objects we just spawned with revision
         * parameters by writing to the pipe.
         */
-       while (refs) {
-               char buf[42];
+       for (i = 0; i < extra->nr; i++) {
+               memcpy(buf + 1, sha1_to_hex(&extra->array[i][0]), 40);
+               buf[0] = '^';
+               buf[41] = '\n';
+               if (!write_or_whine(po.in, buf, 42, "send-pack: send refs"))
+                       break;
+       }
 
+       while (refs) {
                if (!is_null_sha1(refs->old_sha1) &&
                    has_sha1_file(refs->old_sha1)) {
                        memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40);
@@ -71,6 +79,7 @@ static int pack_objects(int fd, struct ref *refs)
                refs = refs->next;
        }
 
+       close(po.in);
        if (finish_command(&po))
                return error("pack-objects died with strange error");
        return 0;
@@ -225,8 +234,7 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref)
                if (args.verbose)
                        fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
                if (ref->deletion) {
-                       if (delete_ref(rs.dst, NULL))
-                               error("Failed to delete");
+                       delete_ref(rs.dst, NULL);
                } else
                        update_ref("update by push", rs.dst,
                                        ref->new_sha1, NULL, 0, 0);
@@ -263,9 +271,7 @@ static void print_ref_status(char flag, const char *summary, struct ref *to, str
 
 static const char *status_abbrev(unsigned char sha1[20])
 {
-       const char *abbrev;
-       abbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV);
-       return abbrev ? abbrev : sha1_to_hex(sha1);
+       return find_unique_abbrev(sha1, DEFAULT_ABBREV);
 }
 
 static void print_ok_ref_status(struct ref *ref)
@@ -298,52 +304,65 @@ static void print_ok_ref_status(struct ref *ref)
        }
 }
 
+static int print_one_push_status(struct ref *ref, const char *dest, int count)
+{
+       if (!count)
+               fprintf(stderr, "To %s\n", dest);
+
+       switch(ref->status) {
+       case REF_STATUS_NONE:
+               print_ref_status('X', "[no match]", ref, NULL, NULL);
+               break;
+       case REF_STATUS_REJECT_NODELETE:
+               print_ref_status('!', "[rejected]", ref, NULL,
+                               "remote does not support deleting refs");
+               break;
+       case REF_STATUS_UPTODATE:
+               print_ref_status('=', "[up to date]", ref,
+                               ref->peer_ref, NULL);
+               break;
+       case REF_STATUS_REJECT_NONFASTFORWARD:
+               print_ref_status('!', "[rejected]", ref, ref->peer_ref,
+                               "non-fast forward");
+               break;
+       case REF_STATUS_REMOTE_REJECT:
+               print_ref_status('!', "[remote rejected]", ref,
+                               ref->deletion ? NULL : ref->peer_ref,
+                               ref->remote_status);
+               break;
+       case REF_STATUS_EXPECTING_REPORT:
+               print_ref_status('!', "[remote failure]", ref,
+                               ref->deletion ? NULL : ref->peer_ref,
+                               "remote failed to report status");
+               break;
+       case REF_STATUS_OK:
+               print_ok_ref_status(ref);
+               break;
+       }
+
+       return 1;
+}
+
 static void print_push_status(const char *dest, struct ref *refs)
 {
        struct ref *ref;
-       int shown_dest = 0;
+       int n = 0;
 
-       for (ref = refs; ref; ref = ref->next) {
-               if (!ref->status)
-                       continue;
-               if (ref->status == REF_STATUS_UPTODATE && !args.verbose)
-                       continue;
+       if (args.verbose) {
+               for (ref = refs; ref; ref = ref->next)
+                       if (ref->status == REF_STATUS_UPTODATE)
+                               n += print_one_push_status(ref, dest, n);
+       }
 
-               if (!shown_dest) {
-                       fprintf(stderr, "To %s\n", dest);
-                       shown_dest = 1;
-               }
+       for (ref = refs; ref; ref = ref->next)
+               if (ref->status == REF_STATUS_OK)
+                       n += print_one_push_status(ref, dest, n);
 
-               switch(ref->status) {
-               case REF_STATUS_NONE:
-                       print_ref_status('X', "[no match]", ref, NULL, NULL);
-                       break;
-               case REF_STATUS_REJECT_NODELETE:
-                       print_ref_status('!', "[rejected]", ref, NULL,
-                                       "remote does not support deleting refs");
-                       break;
-               case REF_STATUS_UPTODATE:
-                       print_ref_status('=', "[up to date]", ref,
-                                       ref->peer_ref, NULL);
-                       break;
-               case REF_STATUS_REJECT_NONFASTFORWARD:
-                       print_ref_status('!', "[rejected]", ref, ref->peer_ref,
-                                       "non-fast forward");
-                       break;
-               case REF_STATUS_REMOTE_REJECT:
-                       print_ref_status('!', "[remote rejected]", ref,
-                                       ref->deletion ? NULL : ref->peer_ref,
-                                       ref->remote_status);
-                       break;
-               case REF_STATUS_EXPECTING_REPORT:
-                       print_ref_status('!', "[remote failure]", ref,
-                                       ref->deletion ? NULL : ref->peer_ref,
-                                       "remote failed to report status");
-                       break;
-               case REF_STATUS_OK:
-                       print_ok_ref_status(ref);
-                       break;
-               }
+       for (ref = refs; ref; ref = ref->next) {
+               if (ref->status != REF_STATUS_NONE &&
+                   ref->status != REF_STATUS_UPTODATE &&
+                   ref->status != REF_STATUS_OK)
+                       n += print_one_push_status(ref, dest, n);
        }
 }
 
@@ -370,14 +389,17 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
        int expect_status_report = 0;
        int flags = MATCH_REFS_NONE;
        int ret;
+       struct extra_have_objects extra_have;
 
+       memset(&extra_have, 0, sizeof(extra_have));
        if (args.send_all)
                flags |= MATCH_REFS_ALL;
        if (args.send_mirror)
                flags |= MATCH_REFS_MIRROR;
 
        /* No funny business with the matcher */
-       remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL);
+       remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL,
+                                      &extra_have);
        get_local_heads();
 
        /* Does the other end support the reporting? */
@@ -390,12 +412,15 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
        if (!remote_tail)
                remote_tail = &remote_refs;
        if (match_refs(local_refs, remote_refs, &remote_tail,
-                                              nr_refspec, refspec, flags))
+                      nr_refspec, refspec, flags)) {
+               close(out);
                return -1;
+       }
 
        if (!remote_refs) {
                fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
                        "Perhaps you should specify a branch such as 'master'.\n");
+               close(out);
                return 0;
        }
 
@@ -482,12 +507,11 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
 
        packet_flush(out);
        if (new_refs && !args.dry_run) {
-               if (pack_objects(out, remote_refs) < 0) {
-                       close(out);
+               if (pack_objects(out, remote_refs, &extra_have) < 0)
                        return -1;
-               }
        }
-       close(out);
+       else
+               close(out);
 
        if (expect_status_report)
                ret = receive_status(in, remote_refs);
@@ -523,15 +547,25 @@ static void verify_remote_names(int nr_heads, const char **heads)
        int i;
 
        for (i = 0; i < nr_heads; i++) {
-               const char *remote = strchr(heads[i], ':');
+               const char *local = heads[i];
+               const char *remote = strrchr(heads[i], ':');
+
+               if (*local == '+')
+                       local++;
+
+               /* A matching refspec is okay.  */
+               if (remote == local && remote[1] == '\0')
+                       continue;
 
-               remote = remote ? (remote + 1) : heads[i];
+               remote = remote ? (remote + 1) : local;
                switch (check_ref_format(remote)) {
                case 0: /* ok */
-               case -2: /* ok but a single level -- that is fine for
-                         * a match pattern.
-                         */
-               case -3: /* ok but ends with a pattern-match character */
+               case CHECK_REF_FORMAT_ONELEVEL:
+                       /* ok but a single level -- that is fine for
+                        * a match pattern.
+                        */
+               case CHECK_REF_FORMAT_WILDCARD:
+                       /* ok but ends with a pattern-match character */
                        continue;
                }
                die("remote part of refspec is not a valid name in %s",
@@ -633,7 +667,7 @@ int send_pack(struct send_pack_args *my_args,
        conn = git_connect(fd, dest, args.receivepack, args.verbose ? CONNECT_VERBOSE : 0);
        ret = do_send_pack(fd[0], fd[1], remote, dest, nr_heads, heads);
        close(fd[0]);
-       close(fd[1]);
+       /* do_send_pack always closes fd[1] */
        ret |= finish_connect(conn);
        return !!ret;
 }