Merge branch 'ft/transport-report-segv'
authorJunio C Hamano <gitster@pobox.com>
Wed, 6 Feb 2013 00:12:32 +0000 (16:12 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 6 Feb 2013 00:12:33 +0000 (16:12 -0800)
A failure to push due to non-ff while on an unborn branch
dereferenced a NULL pointer when showing an error message.

* ft/transport-report-segv:
push: fix segfault when HEAD points nowhere

1  2 
transport.c
diff --combined transport.c
index 384ff9acf1e1c6e192f1f3f75d5fb20c431053ce,af442af329275a693fa713aa5ae9af759f4d4a2a..886ffd8b1e1c9dbb31f3156844790ff98d5ecde3
@@@ -11,7 -11,6 +11,7 @@@
  #include "branch.h"
  #include "url.h"
  #include "submodule.h"
 +#include "string-list.h"
  
  /* rsync support */
  
@@@ -518,7 -517,8 +518,7 @@@ static int fetch_refs_via_pack(struct t
                               int nr_heads, struct ref **to_fetch)
  {
        struct git_transport_data *data = transport->data;
 -      char **heads = xmalloc(nr_heads * sizeof(*heads));
 -      char **origh = xmalloc(nr_heads * sizeof(*origh));
 +      struct string_list sought = STRING_LIST_INIT_DUP;
        const struct ref *refs;
        char *dest = xstrdup(transport->url);
        struct fetch_pack_args args;
        args.depth = data->options.depth;
  
        for (i = 0; i < nr_heads; i++)
 -              origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
 +              string_list_append(&sought, to_fetch[i]->name);
  
        if (!data->got_remote_heads) {
                connect_setup(transport, 0, 0);
  
        refs = fetch_pack(&args, data->fd, data->conn,
                          refs_tmp ? refs_tmp : transport->remote_refs,
 -                        dest, nr_heads, heads, &transport->pack_lockfile);
 +                        dest, &sought, &transport->pack_lockfile);
        close(data->fd[0]);
        close(data->fd[1]);
        if (finish_connect(data->conn))
  
        free_refs(refs_tmp);
  
 -      for (i = 0; i < nr_heads; i++)
 -              free(origh[i]);
 -      free(origh);
 -      free(heads);
 +      string_list_clear(&sought, 0);
        free(dest);
        return (refs ? 0 : -1);
  }
@@@ -659,7 -662,7 +659,7 @@@ static void print_ok_ref_status(struct 
                const char *msg;
  
                strcpy(quickref, status_abbrev(ref->old_sha1));
 -              if (ref->nonfastforward) {
 +              if (ref->forced_update) {
                        strcat(quickref, "...");
                        type = '+';
                        msg = "forced update";
@@@ -695,18 -698,6 +695,18 @@@ static int print_one_push_status(struc
                print_ref_status('!', "[rejected]", ref, ref->peer_ref,
                                                 "non-fast-forward", porcelain);
                break;
 +      case REF_STATUS_REJECT_ALREADY_EXISTS:
 +              print_ref_status('!', "[rejected]", ref, ref->peer_ref,
 +                                               "already exists", porcelain);
 +              break;
 +      case REF_STATUS_REJECT_FETCH_FIRST:
 +              print_ref_status('!', "[rejected]", ref, ref->peer_ref,
 +                                               "fetch first", porcelain);
 +              break;
 +      case REF_STATUS_REJECT_NEEDS_FORCE:
 +              print_ref_status('!', "[rejected]", ref, ref->peer_ref,
 +                                               "needs force", porcelain);
 +              break;
        case REF_STATUS_REMOTE_REJECT:
                print_ref_status('!', "[remote rejected]", ref,
                                                 ref->deletion ? NULL : ref->peer_ref,
  }
  
  void transport_print_push_status(const char *dest, struct ref *refs,
 -                                int verbose, int porcelain, int *nonfastforward)
 +                                int verbose, int porcelain, unsigned int *reject_reasons)
  {
        struct ref *ref;
        int n = 0;
                if (ref->status == REF_STATUS_OK)
                        n += print_one_push_status(ref, dest, n, porcelain);
  
 -      *nonfastforward = 0;
 +      *reject_reasons = 0;
        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, porcelain);
 -              if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD &&
 -                  *nonfastforward != NON_FF_HEAD) {
 +              if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) {
-                       if (!strcmp(head, ref->name))
+                       if (head != NULL && !strcmp(head, ref->name))
 -                              *nonfastforward = NON_FF_HEAD;
 +                              *reject_reasons |= REJECT_NON_FF_HEAD;
                        else
 -                              *nonfastforward = NON_FF_OTHER;
 +                              *reject_reasons |= REJECT_NON_FF_OTHER;
 +              } else if (ref->status == REF_STATUS_REJECT_ALREADY_EXISTS) {
 +                      *reject_reasons |= REJECT_ALREADY_EXISTS;
 +              } else if (ref->status == REF_STATUS_REJECT_FETCH_FIRST) {
 +                      *reject_reasons |= REJECT_FETCH_FIRST;
 +              } else if (ref->status == REF_STATUS_REJECT_NEEDS_FORCE) {
 +                      *reject_reasons |= REJECT_NEEDS_FORCE;
                }
        }
  }
@@@ -1027,86 -1013,11 +1027,86 @@@ void transport_set_verbosity(struct tra
                transport->progress = verbosity >= 0 && isatty(2);
  }
  
 +static void die_with_unpushed_submodules(struct string_list *needs_pushing)
 +{
 +      int i;
 +
 +      fprintf(stderr, "The following submodule paths contain changes that can\n"
 +                      "not be found on any remote:\n");
 +      for (i = 0; i < needs_pushing->nr; i++)
 +              printf("  %s\n", needs_pushing->items[i].string);
 +      fprintf(stderr, "\nPlease try\n\n"
 +                      "       git push --recurse-submodules=on-demand\n\n"
 +                      "or cd to the path and use\n\n"
 +                      "       git push\n\n"
 +                      "to push them to a remote.\n\n");
 +
 +      string_list_clear(needs_pushing, 0);
 +
 +      die("Aborting.");
 +}
 +
 +static int run_pre_push_hook(struct transport *transport,
 +                           struct ref *remote_refs)
 +{
 +      int ret = 0, x;
 +      struct ref *r;
 +      struct child_process proc;
 +      struct strbuf buf;
 +      const char *argv[4];
 +
 +      if (!(argv[0] = find_hook("pre-push")))
 +              return 0;
 +
 +      argv[1] = transport->remote->name;
 +      argv[2] = transport->url;
 +      argv[3] = NULL;
 +
 +      memset(&proc, 0, sizeof(proc));
 +      proc.argv = argv;
 +      proc.in = -1;
 +
 +      if (start_command(&proc)) {
 +              finish_command(&proc);
 +              return -1;
 +      }
 +
 +      strbuf_init(&buf, 256);
 +
 +      for (r = remote_refs; r; r = r->next) {
 +              if (!r->peer_ref) continue;
 +              if (r->status == REF_STATUS_REJECT_NONFASTFORWARD) continue;
 +              if (r->status == REF_STATUS_UPTODATE) continue;
 +
 +              strbuf_reset(&buf);
 +              strbuf_addf( &buf, "%s %s %s %s\n",
 +                       r->peer_ref->name, sha1_to_hex(r->new_sha1),
 +                       r->name, sha1_to_hex(r->old_sha1));
 +
 +              if (write_in_full(proc.in, buf.buf, buf.len) != buf.len) {
 +                      ret = -1;
 +                      break;
 +              }
 +      }
 +
 +      strbuf_release(&buf);
 +
 +      x = close(proc.in);
 +      if (!ret)
 +              ret = x;
 +
 +      x = finish_command(&proc);
 +      if (!ret)
 +              ret = x;
 +
 +      return ret;
 +}
 +
  int transport_push(struct transport *transport,
                   int refspec_nr, const char **refspec, int flags,
 -                 int *nonfastforward)
 +                 unsigned int *reject_reasons)
  {
 -      *nonfastforward = 0;
 +      *reject_reasons = 0;
        transport_verify_remote_names(refspec_nr, refspec);
  
        if (transport->push) {
                        flags & TRANSPORT_PUSH_MIRROR,
                        flags & TRANSPORT_PUSH_FORCE);
  
 -              if ((flags & TRANSPORT_RECURSE_SUBMODULES_CHECK) && !is_bare_repository()) {
 +              if (!(flags & TRANSPORT_PUSH_NO_HOOK))
 +                      if (run_pre_push_hook(transport, remote_refs))
 +                              return -1;
 +
 +              if ((flags & TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND) && !is_bare_repository()) {
                        struct ref *ref = remote_refs;
                        for (; ref; ref = ref->next)
                                if (!is_null_sha1(ref->new_sha1) &&
 -                                  check_submodule_needs_pushing(ref->new_sha1,transport->remote->name))
 -                                      die("There are unpushed submodules, aborting.");
 +                                  !push_unpushed_submodules(ref->new_sha1,
 +                                          transport->remote->name))
 +                                  die ("Failed to push all needed submodules!");
 +              }
 +
 +              if ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND |
 +                            TRANSPORT_RECURSE_SUBMODULES_CHECK)) && !is_bare_repository()) {
 +                      struct ref *ref = remote_refs;
 +                      struct string_list needs_pushing;
 +
 +                      memset(&needs_pushing, 0, sizeof(struct string_list));
 +                      needs_pushing.strdup_strings = 1;
 +                      for (; ref; ref = ref->next)
 +                              if (!is_null_sha1(ref->new_sha1) &&
 +                                  find_unpushed_submodules(ref->new_sha1,
 +                                          transport->remote->name, &needs_pushing))
 +                                      die_with_unpushed_submodules(&needs_pushing);
                }
  
                push_ret = transport->push_refs(transport, remote_refs, flags);
                if (!quiet || err)
                        transport_print_push_status(transport->url, remote_refs,
                                        verbose | porcelain, porcelain,
 -                                      nonfastforward);
 +                                      reject_reasons);
  
                if (flags & TRANSPORT_PUSH_SET_UPSTREAM)
                        set_upstreams(transport, remote_refs, pretend);
@@@ -1271,7 -1163,7 +1271,7 @@@ int transport_disconnect(struct transpo
  }
  
  /*
 - * Strip username (and password) from an url and return
 + * Strip username (and password) from a URL and return
   * it in a newly allocated string.
   */
  char *transport_anonymize_url(const char *url)