Merge branch 'db/push-cleanup'
authorJunio C Hamano <gitster@pobox.com>
Thu, 26 Mar 2009 07:28:46 +0000 (00:28 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 26 Mar 2009 07:28:46 +0000 (00:28 -0700)
* db/push-cleanup:
Move push matching and reporting logic into transport.c
Use a common function to get the pretty name of refs

Conflicts:
transport.c

1  2 
builtin-fetch.c
refs.c
transport.c
diff --combined builtin-fetch.c
index 7293146525f525f628499744481796dad8a4a861,f3bdeda05994179dcd564e4a2ae758ca0d2c23a0..3c998ea740adf474ee980fc763bd7156b9ce81af
@@@ -197,11 -197,7 +197,7 @@@ static int update_local_ref(struct ref 
        struct commit *current = NULL, *updated;
        enum object_type type;
        struct branch *current_branch = branch_get(NULL);
-       const char *pretty_ref = ref->name + (
-               !prefixcmp(ref->name, "refs/heads/") ? 11 :
-               !prefixcmp(ref->name, "refs/tags/") ? 10 :
-               !prefixcmp(ref->name, "refs/remotes/") ? 13 :
-               0);
+       const char *pretty_ref = prettify_ref(ref);
  
        *display = 0;
        type = sha1_object_info(ref->new_sha1, NULL);
@@@ -544,8 -540,7 +540,8 @@@ static void check_not_current_branch(st
        for (; ref_map; ref_map = ref_map->next)
                if (ref_map->peer_ref && !strcmp(current_branch->refname,
                                        ref_map->peer_ref->name))
 -                      die("Refusing to fetch into current branch");
 +                      die("Refusing to fetch into current branch %s "
 +                          "of non-bare repository", current_branch->refname);
  }
  
  static int do_fetch(struct transport *transport,
@@@ -637,9 -632,6 +633,9 @@@ int cmd_fetch(int argc, const char **ar
        else
                remote = remote_get(argv[0]);
  
 +      if (!remote)
 +              die("Where do you want to fetch from today?");
 +
        transport = transport_get(remote, remote->url[0]);
        if (verbosity >= 2)
                transport->verbose = 1;
        if (depth)
                set_option(TRANS_OPT_DEPTH, depth);
  
 -      if (!transport->url)
 -              die("Where do you want to fetch from today?");
 -
        if (argc > 1) {
                int j = 0;
                refs = xcalloc(argc + 1, sizeof(const char *));
diff --combined refs.c
index aeef257ee3cadfe751628f4157a4d63d77237090,8f968b51a1fd616c2cef33c338c97b692ec888cb..26b001453bd1566f2ab3554fffeb63ec0437ceaa
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -694,7 -694,6 +694,7 @@@ static inline int bad_ref_char(int ch
  int check_ref_format(const char *ref)
  {
        int ch, level, bad_type;
 +      int ret = CHECK_REF_FORMAT_OK;
        const char *cp = ref;
  
        level = 0;
                        return CHECK_REF_FORMAT_ERROR;
                bad_type = bad_ref_char(ch);
                if (bad_type) {
 -                      return (bad_type == 2 && !*cp)
 -                              ? CHECK_REF_FORMAT_WILDCARD
 -                              : CHECK_REF_FORMAT_ERROR;
 +                      if (bad_type == 2 && (!*cp || *cp == '/') &&
 +                          ret == CHECK_REF_FORMAT_OK)
 +                              ret = CHECK_REF_FORMAT_WILDCARD;
 +                      else
 +                              return CHECK_REF_FORMAT_ERROR;
                }
  
                /* scan the rest of the path component */
                while ((ch = *cp++) != 0) {
                        bad_type = bad_ref_char(ch);
                        if (bad_type) {
 -                              return (bad_type == 2 && !*cp)
 -                                      ? CHECK_REF_FORMAT_WILDCARD
 -                                      : CHECK_REF_FORMAT_ERROR;
 +                              return CHECK_REF_FORMAT_ERROR;
                        }
                        if (ch == '/')
                                break;
                if (!ch) {
                        if (level < 2)
                                return CHECK_REF_FORMAT_ONELEVEL;
 -                      return CHECK_REF_FORMAT_OK;
 +                      return ret;
                }
        }
  }
  
+ const char *prettify_ref(const struct ref *ref)
+ {
+       const char *name = ref->name;
+       return name + (
+               !prefixcmp(name, "refs/heads/") ? 11 :
+               !prefixcmp(name, "refs/tags/") ? 10 :
+               !prefixcmp(name, "refs/remotes/") ? 13 :
+               0);
+ }
  const char *ref_rev_parse_rules[] = {
        "%.*s",
        "refs/%.*s",
@@@ -996,7 -1005,7 +1006,7 @@@ int delete_ref(const char *refname, con
  
        err = unlink(git_path("logs/%s", lock->ref_name));
        if (err && errno != ENOENT)
 -              fprintf(stderr, "warning: unlink(%s) failed: %s",
 +              warning("unlink(%s) failed: %s",
                        git_path("logs/%s", lock->ref_name), strerror(errno));
        invalidate_cached_refs();
        unlock_ref(lock);
@@@ -1438,7 -1447,8 +1448,7 @@@ int read_ref_at(const char *ref, unsign
                                if (get_sha1_hex(rec + 41, sha1))
                                        die("Log %s is corrupt.", logfile);
                                if (hashcmp(logged_sha1, sha1)) {
 -                                      fprintf(stderr,
 -                                              "warning: Log %s has gap after %s.\n",
 +                                      warning("Log %s has gap after %s.",
                                                logfile, show_date(date, tz, DATE_RFC2822));
                                }
                        }
                                if (get_sha1_hex(rec + 41, logged_sha1))
                                        die("Log %s is corrupt.", logfile);
                                if (hashcmp(logged_sha1, sha1)) {
 -                                      fprintf(stderr,
 -                                              "warning: Log %s unexpectedly ended on %s.\n",
 +                                      warning("Log %s unexpectedly ended on %s.",
                                                logfile, show_date(date, tz, DATE_RFC2822));
                                }
                        }
diff --combined transport.c
index 9ae92cd39c3f45cb2a58a24801a790ce6622d22c,73bb9b50bec2e9c42488d0b04453e1d5ee06564b..3dfb03c06ed82102f60045d6889db97cf036651e
@@@ -138,12 -138,7 +138,12 @@@ static void insert_packed_refs(const ch
        }
  }
  
- static struct ref *get_refs_via_rsync(struct transport *transport)
 +static const char *rsync_url(const char *url)
 +{
 +      return prefixcmp(url, "rsync://") ? skip_prefix(url, "rsync:") : url;
 +}
 +
+ static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
  {
        struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
        struct ref dummy, *tail = &dummy;
        const char *args[5];
        int temp_dir_len;
  
+       if (for_push)
+               return NULL;
        /* copy the refs to the temporary directory */
  
        strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX"));
                die ("Could not make temporary directory");
        temp_dir_len = temp_dir.len;
  
 -      strbuf_addstr(&buf, transport->url);
 +      strbuf_addstr(&buf, rsync_url(transport->url));
        strbuf_addstr(&buf, "/refs");
  
        memset(&rsync, 0, sizeof(rsync));
                die ("Could not run rsync to get refs");
  
        strbuf_reset(&buf);
 -      strbuf_addstr(&buf, transport->url);
 +      strbuf_addstr(&buf, rsync_url(transport->url));
        strbuf_addstr(&buf, "/packed-refs");
  
        args[2] = buf.buf;
@@@ -211,7 -209,7 +214,7 @@@ static int fetch_objs_via_rsync(struct 
        const char *args[8];
        int result;
  
 -      strbuf_addstr(&buf, transport->url);
 +      strbuf_addstr(&buf, rsync_url(transport->url));
        strbuf_addstr(&buf, "/objects/");
  
        memset(&rsync, 0, sizeof(rsync));
@@@ -290,7 -288,7 +293,7 @@@ static int rsync_transport_push(struct 
  
        /* first push the objects */
  
 -      strbuf_addstr(&buf, transport->url);
 +      strbuf_addstr(&buf, rsync_url(transport->url));
        strbuf_addch(&buf, '/');
  
        memset(&rsync, 0, sizeof(rsync));
        args[i++] = NULL;
  
        if (run_command(&rsync))
 -              return error("Could not push objects to %s", transport->url);
 +              return error("Could not push objects to %s",
 +                              rsync_url(transport->url));
  
        /* copy the refs to the temporary directory; they could be packed. */
  
        if (!(flags & TRANSPORT_PUSH_FORCE))
                args[i++] = "--ignore-existing";
        args[i++] = temp_dir.buf;
 -      args[i++] = transport->url;
 +      args[i++] = rsync_url(transport->url);
        args[i++] = NULL;
        if (run_command(&rsync))
 -              result = error("Could not push to %s", transport->url);
 +              result = error("Could not push to %s",
 +                              rsync_url(transport->url));
  
        if (remove_dir_recursively(&temp_dir, 0))
                warning ("Could not remove temporary directory %s.",
@@@ -429,7 -425,7 +432,7 @@@ static int curl_transport_push(struct t
        return !!err;
  }
  
- static struct ref *get_refs_via_curl(struct transport *transport)
+ static struct ref *get_refs_via_curl(struct transport *transport, int for_push)
  {
        struct strbuf buffer = STRBUF_INIT;
        char *data, *start, *mid;
  
        struct walker *walker;
  
+       if (for_push)
+               return NULL;
        if (!transport->data)
                transport->data = get_http_walker(transport->url,
                                                transport->remote);
@@@ -532,12 -531,15 +538,15 @@@ struct bundle_transport_data 
        struct bundle_header header;
  };
  
- static struct ref *get_refs_from_bundle(struct transport *transport)
+ static struct ref *get_refs_from_bundle(struct transport *transport, int for_push)
  {
        struct bundle_transport_data *data = transport->data;
        struct ref *result = NULL;
        int i;
  
+       if (for_push)
+               return NULL;
        if (data->fd > 0)
                close(data->fd);
        data->fd = read_bundle_header(transport->url, &data->header);
@@@ -578,6 -580,7 +587,7 @@@ struct git_transport_data 
        int fd[2];
        const char *uploadpack;
        const char *receivepack;
+       struct extra_have_objects extra_have;
  };
  
  static int set_git_option(struct transport *connection,
        return 1;
  }
  
- static int connect_setup(struct transport *transport)
+ static int connect_setup(struct transport *transport, int for_push, int verbose)
  {
        struct git_transport_data *data = transport->data;
-       data->conn = git_connect(data->fd, transport->url, data->uploadpack, 0);
+       data->conn = git_connect(data->fd, transport->url,
+                                for_push ? data->receivepack : data->uploadpack,
+                                verbose ? CONNECT_VERBOSE : 0);
        return 0;
  }
  
- static struct ref *get_refs_via_connect(struct transport *transport)
+ static struct ref *get_refs_via_connect(struct transport *transport, int for_push)
  {
        struct git_transport_data *data = transport->data;
        struct ref *refs;
  
-       connect_setup(transport);
-       get_remote_heads(data->fd[0], &refs, 0, NULL, 0, NULL);
+       connect_setup(transport, for_push, 0);
+       get_remote_heads(data->fd[0], &refs, 0, NULL,
+                        for_push ? REF_NORMAL : 0, &data->extra_have);
  
        return refs;
  }
@@@ -654,7 -660,7 +667,7 @@@ static int fetch_refs_via_pack(struct t
                origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
  
        if (!data->conn) {
-               connect_setup(transport);
+               connect_setup(transport, 0, 0);
                get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0, NULL);
        }
  
        return (refs ? 0 : -1);
  }
  
- static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags)
+ static int refs_pushed(struct ref *ref)
+ {
+       for (; ref; ref = ref->next) {
+               switch(ref->status) {
+               case REF_STATUS_NONE:
+               case REF_STATUS_UPTODATE:
+                       break;
+               default:
+                       return 1;
+               }
+       }
+       return 0;
+ }
+ static void update_tracking_ref(struct remote *remote, struct ref *ref, int verbose)
+ {
+       struct refspec rs;
+       if (ref->status != REF_STATUS_OK && ref->status != REF_STATUS_UPTODATE)
+               return;
+       rs.src = ref->name;
+       rs.dst = NULL;
+       if (!remote_find_tracking(remote, &rs)) {
+               if (verbose)
+                       fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
+               if (ref->deletion) {
+                       delete_ref(rs.dst, NULL, 0);
+               } else
+                       update_ref("update by push", rs.dst,
+                                       ref->new_sha1, NULL, 0, 0);
+               free(rs.dst);
+       }
+ }
+ #define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
+ static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg)
+ {
+       fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary);
+       if (from)
+               fprintf(stderr, "%s -> %s", prettify_ref(from), prettify_ref(to));
+       else
+               fputs(prettify_ref(to), stderr);
+       if (msg) {
+               fputs(" (", stderr);
+               fputs(msg, stderr);
+               fputc(')', stderr);
+       }
+       fputc('\n', stderr);
+ }
+ static const char *status_abbrev(unsigned char sha1[20])
+ {
+       return find_unique_abbrev(sha1, DEFAULT_ABBREV);
+ }
+ static void print_ok_ref_status(struct ref *ref)
+ {
+       if (ref->deletion)
+               print_ref_status('-', "[deleted]", ref, NULL, NULL);
+       else if (is_null_sha1(ref->old_sha1))
+               print_ref_status('*',
+                       (!prefixcmp(ref->name, "refs/tags/") ? "[new tag]" :
+                         "[new branch]"),
+                       ref, ref->peer_ref, NULL);
+       else {
+               char quickref[84];
+               char type;
+               const char *msg;
+               strcpy(quickref, status_abbrev(ref->old_sha1));
+               if (ref->nonfastforward) {
+                       strcat(quickref, "...");
+                       type = '+';
+                       msg = "forced update";
+               } else {
+                       strcat(quickref, "..");
+                       type = ' ';
+                       msg = NULL;
+               }
+               strcat(quickref, status_abbrev(ref->new_sha1));
+               print_ref_status(type, quickref, ref, ref->peer_ref, msg);
+       }
+ }
+ 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, int verbose)
+ {
+       struct ref *ref;
+       int n = 0;
+       if (verbose) {
+               for (ref = refs; ref; ref = ref->next)
+                       if (ref->status == REF_STATUS_UPTODATE)
+                               n += print_one_push_status(ref, dest, n);
+       }
+       for (ref = refs; ref; ref = ref->next)
+               if (ref->status == REF_STATUS_OK)
+                       n += print_one_push_status(ref, dest, n);
+       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);
+       }
+ }
+ static void verify_remote_names(int nr_heads, const char **heads)
+ {
+       int i;
+       for (i = 0; i < nr_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) : local;
+               switch (check_ref_format(remote)) {
+               case 0: /* ok */
+               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",
+                   heads[i]);
+       }
+ }
+ static int git_transport_push(struct transport *transport, struct ref *remote_refs, int flags)
  {
        struct git_transport_data *data = transport->data;
        struct send_pack_args args;
+       int ret;
+       if (!data->conn) {
+               struct ref *tmp_refs;
+               connect_setup(transport, 1, 0);
+               get_remote_heads(data->fd[0], &tmp_refs, 0, NULL, REF_NORMAL,
+                                NULL);
+       }
  
-       args.receivepack = data->receivepack;
-       args.send_all = !!(flags & TRANSPORT_PUSH_ALL);
        args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR);
        args.force_update = !!(flags & TRANSPORT_PUSH_FORCE);
        args.use_thin_pack = data->thin;
        args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE);
        args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
  
-       return send_pack(&args, transport->url, transport->remote, refspec_nr, refspec);
+       ret = send_pack(&args, data->fd, data->conn, remote_refs,
+                       &data->extra_have);
+       close(data->fd[1]);
+       close(data->fd[0]);
+       ret |= finish_connect(data->conn);
+       data->conn = NULL;
+       return ret;
  }
  
  static int disconnect_git(struct transport *transport)
@@@ -730,7 -932,7 +939,7 @@@ struct transport *transport_get(struct 
        ret->remote = remote;
        ret->url = url;
  
 -      if (!prefixcmp(url, "rsync://")) {
 +      if (!prefixcmp(url, "rsync:")) {
                ret->get_refs_list = get_refs_via_rsync;
                ret->fetch = fetch_objs_via_rsync;
                ret->push = rsync_transport_push;
                ret->set_option = set_git_option;
                ret->get_refs_list = get_refs_via_connect;
                ret->fetch = fetch_refs_via_pack;
-               ret->push = git_transport_push;
+               ret->push_refs = git_transport_push;
                ret->disconnect = disconnect_git;
  
                data->thin = 1;
@@@ -787,15 -989,53 +996,53 @@@ int transport_set_option(struct transpo
  int transport_push(struct transport *transport,
                   int refspec_nr, const char **refspec, int flags)
  {
-       if (!transport->push)
-               return 1;
-       return transport->push(transport, refspec_nr, refspec, flags);
+       verify_remote_names(refspec_nr, refspec);
+       if (transport->push)
+               return transport->push(transport, refspec_nr, refspec, flags);
+       if (transport->push_refs) {
+               struct ref *remote_refs =
+                       transport->get_refs_list(transport, 1);
+               struct ref **remote_tail;
+               struct ref *local_refs = get_local_heads();
+               int match_flags = MATCH_REFS_NONE;
+               int verbose = flags & TRANSPORT_PUSH_VERBOSE;
+               int ret;
+               if (flags & TRANSPORT_PUSH_ALL)
+                       match_flags |= MATCH_REFS_ALL;
+               if (flags & TRANSPORT_PUSH_MIRROR)
+                       match_flags |= MATCH_REFS_MIRROR;
+               remote_tail = &remote_refs;
+               while (*remote_tail)
+                       remote_tail = &((*remote_tail)->next);
+               if (match_refs(local_refs, remote_refs, &remote_tail,
+                              refspec_nr, refspec, match_flags)) {
+                       return -1;
+               }
+               ret = transport->push_refs(transport, remote_refs, flags);
+               print_push_status(transport->url, remote_refs, verbose);
+               if (!(flags & TRANSPORT_PUSH_DRY_RUN)) {
+                       struct ref *ref;
+                       for (ref = remote_refs; ref; ref = ref->next)
+                               update_tracking_ref(transport->remote, ref, verbose);
+               }
+               if (!ret && !refs_pushed(remote_refs))
+                       fprintf(stderr, "Everything up-to-date\n");
+               return ret;
+       }
+       return 1;
  }
  
  const struct ref *transport_get_remote_refs(struct transport *transport)
  {
        if (!transport->remote_refs)
-               transport->remote_refs = transport->get_refs_list(transport);
+               transport->remote_refs = transport->get_refs_list(transport, 0);
        return transport->remote_refs;
  }