Merge branch 'jc/checkout-local-track-report'
authorJunio C Hamano <gitster@pobox.com>
Wed, 7 Jan 2015 20:40:59 +0000 (12:40 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 7 Jan 2015 20:41:00 +0000 (12:41 -0800)
The report from "git checkout" on a branch that builds on another
local branch by setting its branch.*.merge to branch name (not a
full refname) incorrectly said that the upstream is gone.

* jc/checkout-local-track-report:
checkout: report upstream correctly even with loosely defined branch.*.merge

1  2 
remote.c
diff --combined remote.c
index ae4ecfa1163137f7372fd02a51dc8a93319d218f,ecbe363176c102e26415cc587bc8f0f0c1b4b19b..5b9c6931c1e66adb03f5d505aa8dd57169452eae
+++ b/remote.c
@@@ -42,7 -42,6 +42,7 @@@ struct rewrites 
  static struct remote **remotes;
  static int remotes_alloc;
  static int remotes_nr;
 +static struct hashmap remotes_hash;
  
  static struct branch **branches;
  static int branches_alloc;
@@@ -137,51 -136,26 +137,51 @@@ static void add_url_alias(struct remot
        add_pushurl_alias(remote, url);
  }
  
 +struct remotes_hash_key {
 +      const char *str;
 +      int len;
 +};
 +
 +static int remotes_hash_cmp(const struct remote *a, const struct remote *b, const struct remotes_hash_key *key)
 +{
 +      if (key)
 +              return strncmp(a->name, key->str, key->len) || a->name[key->len];
 +      else
 +              return strcmp(a->name, b->name);
 +}
 +
 +static inline void init_remotes_hash(void)
 +{
 +      if (!remotes_hash.cmpfn)
 +              hashmap_init(&remotes_hash, (hashmap_cmp_fn)remotes_hash_cmp, 0);
 +}
 +
  static struct remote *make_remote(const char *name, int len)
  {
 -      struct remote *ret;
 -      int i;
 +      struct remote *ret, *replaced;
 +      struct remotes_hash_key lookup;
 +      struct hashmap_entry lookup_entry;
  
 -      for (i = 0; i < remotes_nr; i++) {
 -              if (len ? (!strncmp(name, remotes[i]->name, len) &&
 -                         !remotes[i]->name[len]) :
 -                  !strcmp(name, remotes[i]->name))
 -                      return remotes[i];
 -      }
 +      if (!len)
 +              len = strlen(name);
 +
 +      init_remotes_hash();
 +      lookup.str = name;
 +      lookup.len = len;
 +      hashmap_entry_init(&lookup_entry, memhash(name, len));
 +
 +      if ((ret = hashmap_get(&remotes_hash, &lookup_entry, &lookup)) != NULL)
 +              return ret;
  
        ret = xcalloc(1, sizeof(struct remote));
        ret->prune = -1;  /* unspecified */
        ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);
        remotes[remotes_nr++] = ret;
 -      if (len)
 -              ret->name = xstrndup(name, len);
 -      else
 -              ret->name = xstrdup(name);
 +      ret->name = xstrndup(name, len);
 +
 +      hashmap_entry_init(ret, lookup_entry.hash);
 +      replaced = hashmap_put(&remotes_hash, ret);
 +      assert(replaced == NULL);  /* no previous entry overwritten */
        return ret;
  }
  
@@@ -196,6 -170,7 +196,6 @@@ static struct branch *make_branch(cons
  {
        struct branch *ret;
        int i;
 -      char *refname;
  
        for (i = 0; i < branches_nr; i++) {
                if (len ? (!strncmp(name, branches[i]->name, len) &&
                ret->name = xstrndup(name, len);
        else
                ret->name = xstrdup(name);
 -      refname = xmalloc(strlen(name) + strlen("refs/heads/") + 1);
 -      strcpy(refname, "refs/heads/");
 -      strcpy(refname + strlen("refs/heads/"), ret->name);
 -      ret->refname = refname;
 +      ret->refname = xstrfmt("refs/heads/%s", ret->name);
  
        return ret;
  }
@@@ -508,10 -486,11 +508,10 @@@ static void read_config(void
                return;
        default_remote_name = "origin";
        current_branch = NULL;
 -      head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag);
 +      head_ref = resolve_ref_unsafe("HEAD", 0, sha1, &flag);
        if (head_ref && (flag & REF_ISSYMREF) &&
 -          starts_with(head_ref, "refs/heads/")) {
 -              current_branch =
 -                      make_branch(head_ref + strlen("refs/heads/"), 0);
 +          skip_prefix(head_ref, "refs/heads/", &head_ref)) {
 +              current_branch = make_branch(head_ref, 0);
        }
        git_config(handle_config, NULL);
        if (branch_pushremote_name) {
@@@ -743,16 -722,13 +743,16 @@@ struct remote *pushremote_get(const cha
  
  int remote_is_configured(const char *name)
  {
 -      int i;
 +      struct remotes_hash_key lookup;
 +      struct hashmap_entry lookup_entry;
        read_config();
  
 -      for (i = 0; i < remotes_nr; i++)
 -              if (!strcmp(name, remotes[i]->name))
 -                      return 1;
 -      return 0;
 +      init_remotes_hash();
 +      lookup.str = name;
 +      lookup.len = strlen(name);
 +      hashmap_entry_init(&lookup_entry, memhash(name, lookup.len));
 +
 +      return hashmap_get(&remotes_hash, &lookup_entry, &lookup) != NULL;
  }
  
  int for_each_remote(each_remote_fn fn, void *priv)
@@@ -862,14 -838,21 +862,14 @@@ static int match_name_with_pattern(cons
        ret = !strncmp(name, key, klen) && namelen >= klen + ksuffixlen &&
                !memcmp(name + namelen - ksuffixlen, kstar + 1, ksuffixlen);
        if (ret && value) {
 +              struct strbuf sb = STRBUF_INIT;
                const char *vstar = strchr(value, '*');
 -              size_t vlen;
 -              size_t vsuffixlen;
                if (!vstar)
                        die("Value '%s' of pattern has no '*'", value);
 -              vlen = vstar - value;
 -              vsuffixlen = strlen(vstar + 1);
 -              *result = xmalloc(vlen + vsuffixlen +
 -                                strlen(name) -
 -                                klen - ksuffixlen + 1);
 -              strncpy(*result, value, vlen);
 -              strncpy(*result + vlen,
 -                      name + klen, namelen - klen - ksuffixlen);
 -              strcpy(*result + vlen + namelen - klen - ksuffixlen,
 -                     vstar + 1);
 +              strbuf_add(&sb, value, vstar - value);
 +              strbuf_add(&sb, name + klen, namelen - klen - ksuffixlen);
 +              strbuf_addstr(&sb, vstar + 1);
 +              *result = strbuf_detach(&sb, NULL);
        }
        return ret;
  }
@@@ -1138,8 -1121,7 +1138,8 @@@ static char *guess_ref(const char *name
        struct strbuf buf = STRBUF_INIT;
        unsigned char sha1[20];
  
 -      const char *r = resolve_ref_unsafe(peer->name, sha1, 1, NULL);
 +      const char *r = resolve_ref_unsafe(peer->name, RESOLVE_REF_READING,
 +                                         sha1, NULL);
        if (!r)
                return NULL;
  
@@@ -1200,9 -1182,7 +1200,9 @@@ static int match_explicit(struct ref *s
                unsigned char sha1[20];
                int flag;
  
 -              dst_value = resolve_ref_unsafe(matched_src->name, sha1, 1, &flag);
 +              dst_value = resolve_ref_unsafe(matched_src->name,
 +                                             RESOLVE_REF_READING,
 +                                             sha1, &flag);
                if (!dst_value ||
                    ((flag & REF_ISSYMREF) &&
                     !starts_with(dst_value, "refs/heads/")))
        case 1:
                break;
        case 0:
 -              if (!memcmp(dst_value, "refs/", 5))
 +              if (starts_with(dst_value, "refs/"))
                        matched_dst = make_linked_ref(dst_value, dst_tail);
                else if (is_null_sha1(matched_src->new_sha1))
                        error("unable to delete '%s': remote ref does not exist",
@@@ -1356,7 -1336,7 +1356,7 @@@ static void add_missing_tags(struct re
        }
        clear_commit_marks_many(sent_tips.nr, sent_tips.tip, TMP_MARK);
  
 -      sort_string_list(&dst_tag);
 +      string_list_sort(&dst_tag);
  
        /* Collect tags they do not have. */
        for (ref = src; ref; ref = ref->next) {
@@@ -1421,7 -1401,7 +1421,7 @@@ static void prepare_ref_index(struct st
        for ( ; ref; ref = ref->next)
                string_list_append_nodup(ref_index, ref->name)->util = ref;
  
 -      sort_string_list(ref_index);
 +      string_list_sort(ref_index);
  }
  
  /*
@@@ -1631,6 -1611,27 +1631,27 @@@ void set_ref_status_for_push(struct re
        }
  }
  
+ static void set_merge(struct branch *ret)
+ {
+       char *ref;
+       unsigned char sha1[20];
+       int i;
+       ret->merge = xcalloc(ret->merge_nr, sizeof(*ret->merge));
+       for (i = 0; i < ret->merge_nr; i++) {
+               ret->merge[i] = xcalloc(1, sizeof(**ret->merge));
+               ret->merge[i]->src = xstrdup(ret->merge_name[i]);
+               if (!remote_find_tracking(ret->remote, ret->merge[i]) ||
+                   strcmp(ret->remote_name, "."))
+                       continue;
+               if (dwim_ref(ret->merge_name[i], strlen(ret->merge_name[i]),
+                            sha1, &ref) == 1)
+                       ret->merge[i]->dst = ref;
+               else
+                       ret->merge[i]->dst = xstrdup(ret->merge_name[i]);
+       }
+ }
  struct branch *branch_get(const char *name)
  {
        struct branch *ret;
                ret = make_branch(name, 0);
        if (ret && ret->remote_name) {
                ret->remote = remote_get(ret->remote_name);
-               if (ret->merge_nr) {
-                       int i;
-                       ret->merge = xcalloc(ret->merge_nr, sizeof(*ret->merge));
-                       for (i = 0; i < ret->merge_nr; i++) {
-                               ret->merge[i] = xcalloc(1, sizeof(**ret->merge));
-                               ret->merge[i]->src = xstrdup(ret->merge_name[i]);
-                               if (remote_find_tracking(ret->remote, ret->merge[i])
-                                   && !strcmp(ret->remote_name, "."))
-                                       ret->merge[i]->dst = xstrdup(ret->merge_name[i]);
-                       }
-               }
+               if (ret->merge_nr)
+                       set_merge(ret);
        }
        return ret;
  }
@@@ -1676,7 -1668,7 +1688,7 @@@ static int ignore_symref_update(const c
        unsigned char sha1[20];
        int flag;
  
 -      if (!resolve_ref_unsafe(refname, sha1, 0, &flag))
 +      if (!resolve_ref_unsafe(refname, 0, sha1, &flag))
                return 0; /* non-existing refs are OK */
        return (flag & REF_ISSYMREF);
  }
@@@ -1918,8 -1910,7 +1930,8 @@@ int stat_tracking_info(struct branch *b
  
        init_revisions(&revs, NULL);
        setup_revisions(rev_argc, rev_argv, &revs, NULL);
 -      prepare_revision_walk(&revs);
 +      if (prepare_revision_walk(&revs))
 +              die("revision walk setup failed");
  
        /* ... and count the commits on each side. */
        *num_ours = 0;
  int format_tracking_info(struct branch *branch, struct strbuf *sb)
  {
        int ours, theirs;
 -      const char *base;
 +      char *base;
        int upstream_is_gone = 0;
  
        switch (stat_tracking_info(branch, &ours, &theirs)) {
                break;
        }
  
 -      base = branch->merge[0]->dst;
 -      base = shorten_unambiguous_ref(base, 0);
 +      base = shorten_unambiguous_ref(branch->merge[0]->dst, 0);
        if (upstream_is_gone) {
                strbuf_addf(sb,
                        _("Your branch is based on '%s', but the upstream is gone.\n"),
                        strbuf_addf(sb,
                                _("  (use \"git pull\" to merge the remote branch into yours)\n"));
        }
 +      free(base);
        return 1;
  }
  
@@@ -2135,7 -2126,7 +2147,7 @@@ struct ref *get_stale_heads(struct refs
        info.ref_count = ref_count;
        for (ref = fetch_map; ref; ref = ref->next)
                string_list_append(&ref_names, ref->name);
 -      sort_string_list(&ref_names);
 +      string_list_sort(&ref_names);
        for_each_ref(get_stale_heads_cb, &info);
        string_list_clear(&ref_names, 0);
        return stale_refs;