Merge branch 'maint' of git://linux-nfs.org/~bfields/git into maint
[gitweb.git] / remote.c
index d904616cdb18769487df5bca331988fccc2e2c2d..9a88917aab32f32235d0ebaa1ffd0e2f2773a927 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -279,6 +279,25 @@ struct remote *remote_get(const char *name)
        return ret;
 }
 
+int for_each_remote(each_remote_fn fn, void *priv)
+{
+       int i, result = 0;
+       read_config();
+       for (i = 0; i < allocated_remotes && !result; i++) {
+               struct remote *r = remotes[i];
+               if (!r)
+                       continue;
+               if (!r->fetch)
+                       r->fetch = parse_ref_spec(r->fetch_refspec_nr,
+                                       r->fetch_refspec);
+               if (!r->push)
+                       r->push = parse_ref_spec(r->push_refspec_nr,
+                                       r->push_refspec);
+               result = fn(r, priv);
+       }
+       return result;
+}
+
 int remote_has_uri(struct remote *remote, const char *uri)
 {
        int i;
@@ -291,35 +310,65 @@ int remote_has_uri(struct remote *remote, const char *uri)
 
 int remote_find_tracking(struct remote *remote, struct refspec *refspec)
 {
+       int find_src = refspec->src == NULL;
+       char *needle, **result;
        int i;
+
+       if (find_src) {
+               if (refspec->dst == NULL)
+                       return error("find_tracking: need either src or dst");
+               needle = refspec->dst;
+               result = &refspec->src;
+       } else {
+               needle = refspec->src;
+               result = &refspec->dst;
+       }
+
        for (i = 0; i < remote->fetch_refspec_nr; i++) {
                struct refspec *fetch = &remote->fetch[i];
+               const char *key = find_src ? fetch->dst : fetch->src;
+               const char *value = find_src ? fetch->src : fetch->dst;
                if (!fetch->dst)
                        continue;
                if (fetch->pattern) {
-                       if (!prefixcmp(refspec->src, fetch->src)) {
-                               refspec->dst =
-                                       xmalloc(strlen(fetch->dst) +
-                                               strlen(refspec->src) -
-                                               strlen(fetch->src) + 1);
-                               strcpy(refspec->dst, fetch->dst);
-                               strcpy(refspec->dst + strlen(fetch->dst),
-                                      refspec->src + strlen(fetch->src));
-                               refspec->force = fetch->force;
-                               return 0;
-                       }
-               } else {
-                       if (!strcmp(refspec->src, fetch->src)) {
-                               refspec->dst = xstrdup(fetch->dst);
+                       if (!prefixcmp(needle, key)) {
+                               *result = xmalloc(strlen(value) +
+                                                 strlen(needle) -
+                                                 strlen(key) + 1);
+                               strcpy(*result, value);
+                               strcpy(*result + strlen(value),
+                                      needle + strlen(key));
                                refspec->force = fetch->force;
                                return 0;
                        }
+               } else if (!strcmp(needle, key)) {
+                       *result = xstrdup(value);
+                       refspec->force = fetch->force;
+                       return 0;
                }
        }
-       refspec->dst = NULL;
        return -1;
 }
 
+struct ref *alloc_ref(unsigned namelen)
+{
+       struct ref *ret = xmalloc(sizeof(struct ref) + namelen);
+       memset(ret, 0, sizeof(struct ref) + namelen);
+       return ret;
+}
+
+void free_refs(struct ref *ref)
+{
+       struct ref *next;
+       while (ref) {
+               next = ref->next;
+               if (ref->peer_ref)
+                       free(ref->peer_ref);
+               free(ref);
+               ref = next;
+       }
+}
+
 static int count_refspec_match(const char *pattern,
                               struct ref *refs,
                               struct ref **matched_ref)
@@ -333,7 +382,6 @@ static int count_refspec_match(const char *pattern,
        for (weak_match = match = 0; refs; refs = refs->next) {
                char *name = refs->name;
                int namelen = strlen(name);
-               int weak_match;
 
                if (namelen < patlen ||
                    memcmp(name + namelen - patlen, pattern, patlen))
@@ -378,11 +426,12 @@ static int count_refspec_match(const char *pattern,
        }
 }
 
-static void link_dst_tail(struct ref *ref, struct ref ***tail)
+static void tail_link_ref(struct ref *ref, struct ref ***tail)
 {
        **tail = ref;
+       while (ref->next)
+               ref = ref->next;
        *tail = &ref->next;
-       **tail = NULL;
 }
 
 static struct ref *try_explicit_object_name(const char *name)
@@ -392,7 +441,7 @@ static struct ref *try_explicit_object_name(const char *name)
        int len;
 
        if (!*name) {
-               ref = xcalloc(1, sizeof(*ref) + 20);
+               ref = alloc_ref(20);
                strcpy(ref->name, "(delete)");
                hashclr(ref->new_sha1);
                return ref;
@@ -400,96 +449,105 @@ static struct ref *try_explicit_object_name(const char *name)
        if (get_sha1(name, sha1))
                return NULL;
        len = strlen(name) + 1;
-       ref = xcalloc(1, sizeof(*ref) + len);
+       ref = alloc_ref(len);
        memcpy(ref->name, name, len);
        hashcpy(ref->new_sha1, sha1);
        return ref;
 }
 
-static int match_explicit_refs(struct ref *src, struct ref *dst,
-                              struct ref ***dst_tail, struct refspec *rs,
-                              int rs_nr)
+static struct ref *make_linked_ref(const char *name, struct ref ***tail)
 {
-       int i, errs;
-       for (i = errs = 0; i < rs_nr; i++) {
-               struct ref *matched_src, *matched_dst;
+       struct ref *ret;
+       size_t len;
 
-               const char *dst_value = rs[i].dst;
+       len = strlen(name) + 1;
+       ret = alloc_ref(len);
+       memcpy(ret->name, name, len);
+       tail_link_ref(ret, tail);
+       return ret;
+}
 
-               if (rs[i].pattern)
-                       continue;
+static int match_explicit(struct ref *src, struct ref *dst,
+                         struct ref ***dst_tail,
+                         struct refspec *rs,
+                         int errs)
+{
+       struct ref *matched_src, *matched_dst;
 
-               if (dst_value == NULL)
-                       dst_value = rs[i].src;
+       const char *dst_value = rs->dst;
 
-               matched_src = matched_dst = NULL;
-               switch (count_refspec_match(rs[i].src, src, &matched_src)) {
-               case 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[i].src);
-                       if (matched_src)
-                               break;
-                       errs = 1;
-                       error("src refspec %s does not match any.",
-                             rs[i].src);
-                       break;
-               default:
-                       errs = 1;
-                       error("src refspec %s matches more than one.",
-                             rs[i].src);
-                       break;
-               }
-               switch (count_refspec_match(dst_value, dst, &matched_dst)) {
-               case 1:
-                       break;
-               case 0:
-                       if (!memcmp(dst_value, "refs/", 5)) {
-                               int len = strlen(dst_value) + 1;
-                               matched_dst = xcalloc(1, sizeof(*dst) + len);
-                               memcpy(matched_dst->name, dst_value, len);
-                               link_dst_tail(matched_dst, dst_tail);
-                       }
-                       else if (!strcmp(rs[i].src, dst_value) &&
-                                matched_src) {
-                               /* pushing "master:master" when
-                                * remote does not have master yet.
-                                */
-                               int len = strlen(matched_src->name) + 1;
-                               matched_dst = xcalloc(1, sizeof(*dst) + len);
-                               memcpy(matched_dst->name, matched_src->name,
-                                      len);
-                               link_dst_tail(matched_dst, dst_tail);
-                       }
-                       else {
-                               errs = 1;
-                               error("dst refspec %s does not match any "
-                                     "existing ref on the remote and does "
-                                     "not start with refs/.", dst_value);
-                       }
-                       break;
-               default:
-                       errs = 1;
-                       error("dst refspec %s matches more than one.",
-                             dst_value);
+       if (rs->pattern)
+               return errs;
+
+       matched_src = matched_dst = NULL;
+       switch (count_refspec_match(rs->src, src, &matched_src)) {
+       case 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)
                        break;
-               }
-               if (errs)
-                       continue;
-               if (matched_dst->peer_ref) {
-                       errs = 1;
-                       error("dst ref %s receives from more than one src.",
-                             matched_dst->name);
-               }
-               else {
-                       matched_dst->peer_ref = matched_src;
-                       matched_dst->force = rs[i].force;
-               }
+               error("src refspec %s does not match any.",
+                     rs->src);
+               break;
+       default:
+               matched_src = NULL;
+               error("src refspec %s matches more than one.",
+                     rs->src);
+               break;
+       }
+
+       if (!matched_src)
+               errs = 1;
+
+       if (!dst_value) {
+               if (!matched_src)
+                       return errs;
+               dst_value = matched_src->name;
+       }
+
+       switch (count_refspec_match(dst_value, dst, &matched_dst)) {
+       case 1:
+               break;
+       case 0:
+               if (!memcmp(dst_value, "refs/", 5))
+                       matched_dst = make_linked_ref(dst_value, dst_tail);
+               else
+                       error("dst refspec %s does not match any "
+                             "existing ref on the remote and does "
+                             "not start with refs/.", dst_value);
+               break;
+       default:
+               matched_dst = NULL;
+               error("dst refspec %s matches more than one.",
+                     dst_value);
+               break;
+       }
+       if (errs || matched_dst == NULL)
+               return 1;
+       if (matched_dst->peer_ref) {
+               errs = 1;
+               error("dst ref %s receives from more than one src.",
+                     matched_dst->name);
+       }
+       else {
+               matched_dst->peer_ref = matched_src;
+               matched_dst->force = rs->force;
        }
+       return errs;
+}
+
+static int match_explicit_refs(struct ref *src, struct ref *dst,
+                              struct ref ***dst_tail, struct refspec *rs,
+                              int rs_nr)
+{
+       int i, errs;
+       for (i = errs = 0; i < rs_nr; i++)
+               errs |= match_explicit(src, dst, dst_tail, &rs[i], errs);
        return -errs;
 }
 
@@ -501,18 +559,23 @@ static struct ref *find_ref_by_name(struct ref *list, const char *name)
        return NULL;
 }
 
-static int check_pattern_match(struct refspec *rs, int rs_nr, struct ref *src)
+static const struct refspec *check_pattern_match(const struct refspec *rs,
+                                                int rs_nr,
+                                                const struct ref *src)
 {
        int i;
-       if (!rs_nr)
-               return 1;
        for (i = 0; i < rs_nr; i++) {
                if (rs[i].pattern && !prefixcmp(src->name, rs[i].src))
-                       return 1;
+                       return rs + i;
        }
-       return 0;
+       return NULL;
 }
 
+/*
+ * Note. This is used only by "push"; refspec matching rules for
+ * push and fetch are subtly different, so do not try to reuse it
+ * without thinking.
+ */
 int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
               int nr_refspec, char **refspec, int all)
 {
@@ -525,29 +588,51 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
        /* pick the remainder */
        for ( ; src; src = src->next) {
                struct ref *dst_peer;
+               const struct refspec *pat = NULL;
+               char *dst_name;
                if (src->peer_ref)
                        continue;
-               if (!check_pattern_match(rs, nr_refspec, src))
+               if (nr_refspec) {
+                       pat = check_pattern_match(rs, nr_refspec, src);
+                       if (!pat)
+                               continue;
+               }
+               else if (prefixcmp(src->name, "refs/heads/"))
+                       /*
+                        * "matching refs"; traditionally we pushed everything
+                        * including refs outside refs/heads/ hierarchy, but
+                        * that does not make much sense these days.
+                        */
                        continue;
 
-               dst_peer = find_ref_by_name(dst, src->name);
+               if (pat) {
+                       const char *dst_side = pat->dst ? pat->dst : pat->src;
+                       dst_name = xmalloc(strlen(dst_side) +
+                                          strlen(src->name) -
+                                          strlen(pat->src) + 2);
+                       strcpy(dst_name, dst_side);
+                       strcat(dst_name, src->name + strlen(pat->src));
+               } else
+                       dst_name = xstrdup(src->name);
+               dst_peer = find_ref_by_name(dst, dst_name);
                if (dst_peer && dst_peer->peer_ref)
                        /* We're already sending something to this ref. */
-                       continue;
+                       goto free_name;
                if (!dst_peer && !nr_refspec && !all)
                        /* Remote doesn't have it, and we have no
                         * explicit pattern, and we don't have
                         * --all. */
-                       continue;
+                       goto free_name;
                if (!dst_peer) {
                        /* Create a new one and link it */
-                       int len = strlen(src->name) + 1;
-                       dst_peer = xcalloc(1, sizeof(*dst_peer) + len);
-                       memcpy(dst_peer->name, src->name, len);
+                       dst_peer = make_linked_ref(dst_name, dst_tail);
                        hashcpy(dst_peer->new_sha1, src->new_sha1);
-                       link_dst_tail(dst_peer, dst_tail);
                }
                dst_peer->peer_ref = src;
+               if (pat)
+                       dst_peer->force = pat->force;
+       free_name:
+               free(dst_name);
        }
        return 0;
 }