Merge branch 'sv/doc-stripspace'
[gitweb.git] / remote.c
index ebed40dcb65df55ac81facbf5df59ddc28451872..ae4ecfa1163137f7372fd02a51dc8a93319d218f 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -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;
@@ -136,26 +137,51 @@ static void add_url_alias(struct remote *remote, const char *url)
        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;
 }
 
@@ -170,7 +196,6 @@ static struct branch *make_branch(const char *name, int len)
 {
        struct branch *ret;
        int i;
-       char *refname;
 
        for (i = 0; i < branches_nr; i++) {
                if (len ? (!strncmp(name, branches[i]->name, len) &&
@@ -186,10 +211,7 @@ static struct branch *make_branch(const char *name, int 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;
 }
@@ -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) {
@@ -523,7 +544,7 @@ static void free_refspecs(struct refspec *refspec, int nr_refspec)
 static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
 {
        int i;
-       struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec);
+       struct refspec *rs = xcalloc(nr_refspec, sizeof(*rs));
 
        for (i = 0; i < nr_refspec; i++) {
                size_t llen;
@@ -722,13 +743,16 @@ struct remote *pushremote_get(const char *name)
 
 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)
@@ -838,21 +862,14 @@ static int match_name_with_pattern(const char *key, const char *name,
        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;
 }
@@ -1062,11 +1079,13 @@ int count_refspec_match(const char *pattern,
                }
        }
        if (!matched) {
-               *matched_ref = matched_weak;
+               if (matched_ref)
+                       *matched_ref = matched_weak;
                return weak_match;
        }
        else {
-               *matched_ref = matched;
+               if (matched_ref)
+                       *matched_ref = matched;
                return match;
        }
 }
@@ -1086,18 +1105,25 @@ static struct ref *alloc_delete_ref(void)
        return ref;
 }
 
-static struct ref *try_explicit_object_name(const char *name)
+static int try_explicit_object_name(const char *name,
+                                   struct ref **match)
 {
        unsigned char sha1[20];
-       struct ref *ref;
 
-       if (!*name)
-               return alloc_delete_ref();
+       if (!*name) {
+               if (match)
+                       *match = alloc_delete_ref();
+               return 0;
+       }
+
        if (get_sha1(name, sha1))
-               return NULL;
-       ref = alloc_ref(name);
-       hashcpy(ref->new_sha1, sha1);
-       return ref;
+               return -1;
+
+       if (match) {
+               *match = alloc_ref(name);
+               hashcpy((*match)->new_sha1, sha1);
+       }
+       return 0;
 }
 
 static struct ref *make_linked_ref(const char *name, struct ref ***tail)
@@ -1112,7 +1138,8 @@ static char *guess_ref(const char *name, struct ref *peer)
        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;
 
@@ -1127,12 +1154,37 @@ static char *guess_ref(const char *name, struct ref *peer)
        return strbuf_detach(&buf, NULL);
 }
 
+static int match_explicit_lhs(struct ref *src,
+                             struct refspec *rs,
+                             struct ref **match,
+                             int *allocated_match)
+{
+       switch (count_refspec_match(rs->src, src, match)) {
+       case 1:
+               if (allocated_match)
+                       *allocated_match = 0;
+               return 0;
+       case 0:
+               /* The source could be in the get_sha1() format
+                * not a reference name.  :refs/other is a
+                * way to delete 'other' ref at the remote end.
+                */
+               if (try_explicit_object_name(rs->src, match) < 0)
+                       return error("src refspec %s does not match any.", rs->src);
+               if (allocated_match)
+                       *allocated_match = 1;
+               return 0;
+       default:
+               return error("src refspec %s matches more than one.", rs->src);
+       }
+}
+
 static int match_explicit(struct ref *src, struct ref *dst,
                          struct ref ***dst_tail,
                          struct refspec *rs)
 {
        struct ref *matched_src, *matched_dst;
-       int copy_src;
+       int allocated_src;
 
        const char *dst_value = rs->dst;
        char *dst_guess;
@@ -1141,29 +1193,16 @@ static int match_explicit(struct ref *src, struct ref *dst,
                return 0;
 
        matched_src = matched_dst = NULL;
-       switch (count_refspec_match(rs->src, src, &matched_src)) {
-       case 1:
-               copy_src = 1;
-               break;
-       case 0:
-               /* The source could be in the get_sha1() format
-                * not a reference name.  :refs/other is a
-                * way to delete 'other' ref at the remote end.
-                */
-               matched_src = try_explicit_object_name(rs->src);
-               if (!matched_src)
-                       return error("src refspec %s does not match any.", rs->src);
-               copy_src = 0;
-               break;
-       default:
-               return error("src refspec %s matches more than one.", rs->src);
-       }
+       if (match_explicit_lhs(src, rs, &matched_src, &allocated_src) < 0)
+               return -1;
 
        if (!dst_value) {
                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/")))
@@ -1175,7 +1214,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
        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",
@@ -1202,7 +1241,9 @@ static int match_explicit(struct ref *src, struct ref *dst,
                return error("dst ref %s receives from more than one src.",
                      matched_dst->name);
        else {
-               matched_dst->peer_ref = copy_src ? copy_ref(matched_src) : matched_src;
+               matched_dst->peer_ref = allocated_src ?
+                                       matched_src :
+                                       copy_ref(matched_src);
                matched_dst->force = rs->force;
        }
        return 0;
@@ -1315,7 +1356,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
        }
        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) {
@@ -1380,7 +1421,32 @@ static void prepare_ref_index(struct string_list *ref_index, struct ref *ref)
        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);
+}
+
+/*
+ * Given only the set of local refs, sanity-check the set of push
+ * refspecs. We can't catch all errors that match_push_refs would,
+ * but we can catch some errors early before even talking to the
+ * remote side.
+ */
+int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)
+{
+       struct refspec *refspec = parse_push_refspec(nr_refspec, refspec_names);
+       int ret = 0;
+       int i;
+
+       for (i = 0; i < nr_refspec; i++) {
+               struct refspec *rs = refspec + i;
+
+               if (rs->pattern || rs->matching)
+                       continue;
+
+               ret |= match_explicit_lhs(src, rs, NULL, NULL);
+       }
+
+       free_refspec(nr_refspec, refspec);
+       return ret;
 }
 
 /*
@@ -1610,7 +1676,7 @@ static int ignore_symref_update(const char *refname)
        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);
 }
@@ -1852,7 +1918,8 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
 
        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;
@@ -1879,7 +1946,7 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
 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)) {
@@ -1895,8 +1962,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
                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"),
@@ -1942,6 +2008,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
                        strbuf_addf(sb,
                                _("  (use \"git pull\" to merge the remote branch into yours)\n"));
        }
+       free(base);
        return 1;
 }
 
@@ -2068,7 +2135,7 @@ struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fet
        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;