Merge branch 'aw/mirror-push' into jk/send-pack
[git/git.git] / builtin-send-pack.c
index 22cb50b..418925e 100644 (file)
@@ -8,7 +8,7 @@
 #include "send-pack.h"
 
 static const char send_pack_usage[] =
-"git-send-pack [--all] [--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 = {
@@ -195,7 +195,8 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref)
                return;
 
        if (!remote_find_tracking(remote, &rs)) {
-               fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
+               if (args.verbose)
+                       fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
                if (is_null_sha1(ref->peer_ref->new_sha1)) {
                        if (delete_ref(rs.dst, NULL))
                                error("Failed to delete");
@@ -206,7 +207,18 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref)
        }
 }
 
-static int do_send_pack(int in, int out, struct remote *remote, int nr_refspec, const char **refspec)
+static const char *prettify_ref(const char *name)
+{
+       return name + (
+               !prefixcmp(name, "refs/heads/") ? 11 :
+               !prefixcmp(name, "refs/tags/") ? 10 :
+               !prefixcmp(name, "refs/remotes/") ? 13 :
+               0);
+}
+
+#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
+
+static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec)
 {
        struct ref *ref;
        int new_refs;
@@ -214,6 +226,13 @@ static int do_send_pack(int in, int out, struct remote *remote, int nr_refspec,
        int ask_for_status_report = 0;
        int allow_deleting_refs = 0;
        int expect_status_report = 0;
+       int shown_dest = 0;
+       int flags = MATCH_REFS_NONE;
+
+       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);
@@ -229,7 +248,7 @@ static int do_send_pack(int in, int out, struct remote *remote, int nr_refspec,
        if (!remote_tail)
                remote_tail = &remote_refs;
        if (match_refs(local_refs, remote_refs, &remote_tail,
-                      nr_refspec, refspec, args.send_all))
+                                              nr_refspec, refspec, flags))
                return -1;
 
        if (!remote_refs) {
@@ -245,21 +264,41 @@ static int do_send_pack(int in, int out, struct remote *remote, int nr_refspec,
        for (ref = remote_refs; ref; ref = ref->next) {
                char old_hex[60], *new_hex;
                int will_delete_ref;
+               const char *pretty_ref;
+               const char *pretty_peer = NULL; /* only used when not deleting */
+               const unsigned char *new_sha1;
 
-               if (!ref->peer_ref)
-                       continue;
+               if (!ref->peer_ref) {
+                       if (!args.send_mirror)
+                               continue;
+                       new_sha1 = null_sha1;
+               }
+               else
+                       new_sha1 = ref->peer_ref->new_sha1;
+
+               if (!shown_dest) {
+                       fprintf(stderr, "To %s\n", dest);
+                       shown_dest = 1;
+               }
 
+               will_delete_ref = is_null_sha1(new_sha1);
+
+               pretty_ref = prettify_ref(ref->name);
+               if (!will_delete_ref)
+                       pretty_peer = prettify_ref(ref->peer_ref->name);
 
-               will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
                if (will_delete_ref && !allow_deleting_refs) {
-                       error("remote does not support deleting refs");
+                       fprintf(stderr, " ! %-*s %s (remote does not support deleting refs)\n",
+                                       SUMMARY_WIDTH, "[rejected]", pretty_ref);
                        ret = -2;
                        continue;
                }
                if (!will_delete_ref &&
-                   !hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
+                   !hashcmp(ref->old_sha1, new_sha1)) {
                        if (args.verbose)
-                               fprintf(stderr, "'%s': up-to-date\n", ref->name);
+                               fprintf(stderr, " = %-*s %s -> %s\n",
+                                       SUMMARY_WIDTH, "[up to date]",
+                                       pretty_peer, pretty_ref);
                        continue;
                }
 
@@ -287,8 +326,7 @@ static int do_send_pack(int in, int out, struct remote *remote, int nr_refspec,
                    !is_null_sha1(ref->old_sha1) &&
                    !ref->force) {
                        if (!has_sha1_file(ref->old_sha1) ||
-                           !ref_newer(ref->peer_ref->new_sha1,
-                                      ref->old_sha1)) {
+                           !ref_newer(new_sha1, ref->old_sha1)) {
                                /* We do not have the remote ref, or
                                 * we know that the remote ref is not
                                 * an ancestor of what we are trying to
@@ -296,17 +334,14 @@ static int do_send_pack(int in, int out, struct remote *remote, int nr_refspec,
                                 * commits at the remote end and likely
                                 * we were not up to date to begin with.
                                 */
-                               error("remote '%s' is not an ancestor of\n"
-                                     " local  '%s'.\n"
-                                     " Maybe you are not up-to-date and "
-                                     "need to pull first?",
-                                     ref->name,
-                                     ref->peer_ref->name);
+                               fprintf(stderr, " ! %-*s %s -> %s (non-fast forward)\n",
+                                               SUMMARY_WIDTH, "[rejected]",
+                                               pretty_peer, pretty_ref);
                                ret = -2;
                                continue;
                        }
                }
-               hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
+               hashcpy(ref->new_sha1, new_sha1);
                if (!will_delete_ref)
                        new_refs++;
                strcpy(old_hex, sha1_to_hex(ref->old_sha1));
@@ -325,14 +360,41 @@ static int do_send_pack(int in, int out, struct remote *remote, int nr_refspec,
                                        old_hex, new_hex, ref->name);
                }
                if (will_delete_ref)
-                       fprintf(stderr, "deleting '%s'\n", ref->name);
+                       fprintf(stderr, " - %-*s %s\n",
+                               SUMMARY_WIDTH, "[deleting]",
+                               pretty_ref);
+               else if (is_null_sha1(ref->old_sha1)) {
+                       const char *msg;
+
+                       if (!prefixcmp(ref->name, "refs/tags/"))
+                               msg = "[new tag]";
+                       else
+                               msg = "[new branch]";
+                       fprintf(stderr, " * %-*s %s -> %s\n",
+                               SUMMARY_WIDTH, msg,
+                               pretty_peer, pretty_ref);
+               }
                else {
-                       fprintf(stderr, "updating '%s'", ref->name);
-                       if (strcmp(ref->name, ref->peer_ref->name))
-                               fprintf(stderr, " using '%s'",
-                                       ref->peer_ref->name);
-                       fprintf(stderr, "\n  from %s\n  to   %s\n",
-                               old_hex, new_hex);
+                       char quickref[83];
+                       char type = ' ';
+                       const char *msg = "";
+                       const char *old_abb;
+                       old_abb = find_unique_abbrev(ref->old_sha1, DEFAULT_ABBREV);
+                       strcpy(quickref, old_abb ? old_abb : old_hex);
+                       if (ref_newer(ref->peer_ref->new_sha1, ref->old_sha1))
+                               strcat(quickref, "..");
+                       else {
+                               strcat(quickref, "...");
+                               type = '+';
+                               msg = " (forced update)";
+                       }
+                       strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
+
+                       fprintf(stderr, " %c %-*s %s -> %s%s\n",
+                               type,
+                               SUMMARY_WIDTH, quickref,
+                               pretty_peer, pretty_ref,
+                               msg);
                }
        }
 
@@ -411,6 +473,10 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                                args.dry_run = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "--mirror")) {
+                               args.send_mirror = 1;
+                               continue;
+                       }
                        if (!strcmp(arg, "--force")) {
                                args.force_update = 1;
                                continue;
@@ -435,7 +501,12 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
        }
        if (!dest)
                usage(send_pack_usage);
-       if (heads && args.send_all)
+       /*
+        * --all and --mirror are incompatible; neither makes sense
+        * with any refspecs.
+        */
+       if ((heads && (args.send_all || args.send_mirror)) ||
+                                       (args.send_all && args.send_mirror))
                usage(send_pack_usage);
 
        if (remote_name) {
@@ -461,7 +532,7 @@ int send_pack(struct send_pack_args *my_args,
        verify_remote_names(nr_heads, heads);
 
        conn = git_connect(fd, dest, args.receivepack, args.verbose ? CONNECT_VERBOSE : 0);
-       ret = do_send_pack(fd[0], fd[1], remote, nr_heads, heads);
+       ret = do_send_pack(fd[0], fd[1], remote, dest, nr_heads, heads);
        close(fd[0]);
        close(fd[1]);
        ret |= finish_connect(conn);