Avoid scary errors about tagged trees/blobs during git-fetch
[gitweb.git] / remote.c
index f469fb34e41cc9e1d80c0a71ed928a47bbadd267..bb774d0bcc3f5b3e7a8cdbb29ba43fcaf00f0d4c 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,22 +449,22 @@ 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 struct ref *make_dst(const char *name, struct ref ***dst_tail)
+static struct ref *make_linked_ref(const char *name, struct ref ***tail)
 {
-       struct ref *dst;
+       struct ref *ret;
        size_t len;
 
        len = strlen(name) + 1;
-       dst = xcalloc(1, sizeof(*dst) + len);
-       memcpy(dst->name, name, len);
-       link_dst_tail(dst, dst_tail);
-       return dst;
+       ret = alloc_ref(len);
+       memcpy(ret->name, name, len);
+       tail_link_ref(ret, tail);
+       return ret;
 }
 
 static int match_explicit(struct ref *src, struct ref *dst,
@@ -430,9 +479,6 @@ static int match_explicit(struct ref *src, struct ref *dst,
        if (rs->pattern)
                return errs;
 
-       if (dst_value == NULL)
-               dst_value = rs->src;
-
        matched_src = matched_dst = NULL;
        switch (count_refspec_match(rs->src, src, &matched_src)) {
        case 1:
@@ -445,42 +491,41 @@ static int match_explicit(struct ref *src, struct ref *dst,
                matched_src = try_explicit_object_name(rs->src);
                if (matched_src)
                        break;
-               errs = 1;
                error("src refspec %s does not match any.",
                      rs->src);
                break;
        default:
-               errs = 1;
+               matched_src = NULL;
                error("src refspec %s matches more than one.",
                      rs->src);
                break;
        }
+
+       if (!matched_src)
+               errs = 1;
+
+       if (dst_value == NULL)
+               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_dst(dst_value, dst_tail);
-               else if (!strcmp(rs->src, dst_value) && matched_src)
-                       /* pushing "master:master" when
-                        * remote does not have master yet.
-                        */
-                       matched_dst = make_dst(matched_src->name, dst_tail);
-               else {
-                       errs = 1;
+                       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:
-               errs = 1;
+               matched_dst = NULL;
                error("dst refspec %s matches more than one.",
                      dst_value);
                break;
        }
-       if (errs)
-               return errs;
+       if (errs || matched_dst == NULL)
+               return 1;
        if (matched_dst->peer_ref) {
                errs = 1;
                error("dst ref %s receives from more than one src.",
@@ -549,15 +594,23 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
                        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;
 
                if (pat) {
-                       dst_name = xmalloc(strlen(pat->dst) +
+                       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, pat->dst);
+                       strcpy(dst_name, dst_side);
                        strcat(dst_name, src->name + strlen(pat->src));
                } else
-                       dst_name = strdup(src->name);
+                       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. */
@@ -569,7 +622,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
                        goto free_name;
                if (!dst_peer) {
                        /* Create a new one and link it */
-                       dst_peer = make_dst(dst_name, dst_tail);
+                       dst_peer = make_linked_ref(dst_name, dst_tail);
                        hashcpy(dst_peer->new_sha1, src->new_sha1);
                }
                dst_peer->peer_ref = src;