gc: config option for running --auto in background
[gitweb.git] / remote.c
index 01aa4b4b698184ca5bd7ebdada901679166be07f..a89efab5b766ba9374c3a09656b2d56593884c8e 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -745,35 +745,66 @@ int for_each_remote(each_remote_fn fn, void *priv)
        return result;
 }
 
-void ref_remove_duplicates(struct ref *ref_map)
+static void handle_duplicate(struct ref *ref1, struct ref *ref2)
+{
+       if (strcmp(ref1->name, ref2->name)) {
+               if (ref1->fetch_head_status != FETCH_HEAD_IGNORE &&
+                   ref2->fetch_head_status != FETCH_HEAD_IGNORE) {
+                       die(_("Cannot fetch both %s and %s to %s"),
+                           ref1->name, ref2->name, ref2->peer_ref->name);
+               } else if (ref1->fetch_head_status != FETCH_HEAD_IGNORE &&
+                          ref2->fetch_head_status == FETCH_HEAD_IGNORE) {
+                       warning(_("%s usually tracks %s, not %s"),
+                               ref2->peer_ref->name, ref2->name, ref1->name);
+               } else if (ref1->fetch_head_status == FETCH_HEAD_IGNORE &&
+                          ref2->fetch_head_status == FETCH_HEAD_IGNORE) {
+                       die(_("%s tracks both %s and %s"),
+                           ref2->peer_ref->name, ref1->name, ref2->name);
+               } else {
+                       /*
+                        * This last possibility doesn't occur because
+                        * FETCH_HEAD_IGNORE entries always appear at
+                        * the end of the list.
+                        */
+                       die(_("Internal error"));
+               }
+       }
+       free(ref2->peer_ref);
+       free(ref2);
+}
+
+struct ref *ref_remove_duplicates(struct ref *ref_map)
 {
        struct string_list refs = STRING_LIST_INIT_NODUP;
-       struct string_list_item *item = NULL;
-       struct ref *prev = NULL, *next = NULL;
-       for (; ref_map; prev = ref_map, ref_map = next) {
-               next = ref_map->next;
-               if (!ref_map->peer_ref)
-                       continue;
+       struct ref *retval = NULL;
+       struct ref **p = &retval;
 
-               item = string_list_lookup(&refs, ref_map->peer_ref->name);
-               if (item) {
-                       if (strcmp(((struct ref *)item->util)->name,
-                                  ref_map->name))
-                               die("%s tracks both %s and %s",
-                                   ref_map->peer_ref->name,
-                                   ((struct ref *)item->util)->name,
-                                   ref_map->name);
-                       prev->next = ref_map->next;
-                       free(ref_map->peer_ref);
-                       free(ref_map);
-                       ref_map = prev; /* skip this; we freed it */
-                       continue;
-               }
+       while (ref_map) {
+               struct ref *ref = ref_map;
+
+               ref_map = ref_map->next;
+               ref->next = NULL;
 
-               item = string_list_insert(&refs, ref_map->peer_ref->name);
-               item->util = ref_map;
+               if (!ref->peer_ref) {
+                       *p = ref;
+                       p = &ref->next;
+               } else {
+                       struct string_list_item *item =
+                               string_list_insert(&refs, ref->peer_ref->name);
+
+                       if (item->util) {
+                               /* Entry already existed */
+                               handle_duplicate((struct ref *)item->util, ref);
+                       } else {
+                               *p = ref;
+                               p = &ref->next;
+                               item->util = ref;
+                       }
+               }
        }
+
        string_list_clear(&refs, 0);
+       return retval;
 }
 
 int remote_has_url(struct remote *remote, const char *url)
@@ -821,10 +852,12 @@ static int match_name_with_pattern(const char *key, const char *name,
        return ret;
 }
 
-static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)
+int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)
 {
        int i;
        int find_src = !query->src;
+       const char *needle = find_src ? query->dst : query->src;
+       char **result = find_src ? &query->src : &query->dst;
 
        if (find_src && !query->dst)
                return error("query_refspecs: need either src or dst");
@@ -833,8 +866,6 @@ static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *q
                struct refspec *refspec = &refs[i];
                const char *key = find_src ? refspec->dst : refspec->src;
                const char *value = find_src ? refspec->src : refspec->dst;
-               const char *needle = find_src ? query->dst : query->src;
-               char **result = find_src ? &query->src : &query->dst;
 
                if (!refspec->dst)
                        continue;
@@ -955,9 +986,9 @@ void sort_ref_list(struct ref **l, int (*cmp)(const void *, const void *))
        *l = llist_mergesort(*l, ref_list_get_next, ref_list_set_next, cmp);
 }
 
-static int count_refspec_match(const char *pattern,
-                              struct ref *refs,
-                              struct ref **matched_ref)
+int count_refspec_match(const char *pattern,
+                       struct ref *refs,
+                       struct ref **matched_ref)
 {
        int patlen = strlen(pattern);
        struct ref *matched_weak = NULL;
@@ -1553,6 +1584,13 @@ static int ignore_symref_update(const char *refname)
        return (flag & REF_ISSYMREF);
 }
 
+/*
+ * Create and return a list of (struct ref) consisting of copies of
+ * each remote_ref that matches refspec.  refspec must be a pattern.
+ * Fill in the copies' peer_ref to describe the local tracking refs to
+ * which they map.  Omit any references that would map to an existing
+ * local symbolic ref.
+ */
 static struct ref *get_expanded_map(const struct ref *remote_refs,
                                    const struct refspec *refspec)
 {
@@ -1560,9 +1598,9 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,
        struct ref *ret = NULL;
        struct ref **tail = &ret;
 
-       char *expn_name;
-
        for (ref = remote_refs; ref; ref = ref->next) {
+               char *expn_name = NULL;
+
                if (strchr(ref->name, '^'))
                        continue; /* a dereference item */
                if (match_name_with_pattern(refspec->src, ref->name,
@@ -1571,12 +1609,12 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,
                        struct ref *cpy = copy_ref(ref);
 
                        cpy->peer_ref = alloc_ref(expn_name);
-                       free(expn_name);
                        if (refspec->force)
                                cpy->peer_ref->force = 1;
                        *tail = cpy;
                        tail = &cpy->next;
                }
+               free(expn_name);
        }
 
        return ret;