config: write to $XDG_CONFIG_HOME/git/config file when appropriate
[gitweb.git] / remote.c
index 4b06a11732cfc66747178d697f56ef39d7a1eb72..6833538829e1d3e5fe4a525a1b44cbebd9f96efb 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -7,6 +7,9 @@
 #include "dir.h"
 #include "tag.h"
 #include "string-list.h"
+#include "mergesort.h"
+
+enum map_direction { FROM_SRC, FROM_DST };
 
 static struct refspec s_tag_refspec = {
        0,
@@ -916,6 +919,27 @@ void free_refs(struct ref *ref)
        }
 }
 
+int ref_compare_name(const void *va, const void *vb)
+{
+       const struct ref *a = va, *b = vb;
+       return strcmp(a->name, b->name);
+}
+
+static void *ref_list_get_next(const void *a)
+{
+       return ((const struct ref *)a)->next;
+}
+
+static void ref_list_set_next(void *a, void *next)
+{
+       ((struct ref *)a)->next = next;
+}
+
+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)
@@ -978,16 +1002,20 @@ static void tail_link_ref(struct ref *ref, struct ref ***tail)
        *tail = &ref->next;
 }
 
+static struct ref *alloc_delete_ref(void)
+{
+       struct ref *ref = alloc_ref("(delete)");
+       hashclr(ref->new_sha1);
+       return ref;
+}
+
 static struct ref *try_explicit_object_name(const char *name)
 {
        unsigned char sha1[20];
        struct ref *ref;
 
-       if (!*name) {
-               ref = alloc_ref("(delete)");
-               hashclr(ref->new_sha1);
-               return ref;
-       }
+       if (!*name)
+               return alloc_delete_ref();
        if (get_sha1(name, sha1))
                return NULL;
        ref = alloc_ref(name);
@@ -1111,7 +1139,7 @@ static int match_explicit_refs(struct ref *src, struct ref *dst,
 }
 
 static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref *ref,
-               int send_mirror, const struct refspec **ret_pat)
+               int send_mirror, int direction, const struct refspec **ret_pat)
 {
        const struct refspec *pat;
        char *name;
@@ -1126,7 +1154,12 @@ static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref
 
                if (rs[i].pattern) {
                        const char *dst_side = rs[i].dst ? rs[i].dst : rs[i].src;
-                       if (match_name_with_pattern(rs[i].src, ref->name, dst_side, &name)) {
+                       int match;
+                       if (direction == FROM_SRC)
+                               match = match_name_with_pattern(rs[i].src, ref->name, dst_side, &name);
+                       else
+                               match = match_name_with_pattern(dst_side, ref->name, rs[i].src, &name);
+                       if (match) {
                                matching_refs = i;
                                break;
                        }
@@ -1173,6 +1206,7 @@ int match_push_refs(struct ref *src, struct ref **dst,
        struct refspec *rs;
        int send_all = flags & MATCH_REFS_ALL;
        int send_mirror = flags & MATCH_REFS_MIRROR;
+       int send_prune = flags & MATCH_REFS_PRUNE;
        int errs;
        static const char *default_refspec[] = { ":", NULL };
        struct ref *ref, **dst_tail = tail_ref(dst);
@@ -1193,7 +1227,7 @@ int match_push_refs(struct ref *src, struct ref **dst,
                if (ref->peer_ref)
                        continue;
 
-               dst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, &pat);
+               dst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_SRC, &pat);
                if (!dst_name)
                        continue;
 
@@ -1220,6 +1254,23 @@ int match_push_refs(struct ref *src, struct ref **dst,
        free_name:
                free(dst_name);
        }
+       if (send_prune) {
+               /* check for missing refs on the remote */
+               for (ref = *dst; ref; ref = ref->next) {
+                       char *src_name;
+
+                       if (ref->peer_ref)
+                               /* We're already sending something to this ref. */
+                               continue;
+
+                       src_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_DST, NULL);
+                       if (src_name) {
+                               if (!find_ref_by_name(src, src_name))
+                                       ref->peer_ref = alloc_delete_ref();
+                               free(src_name);
+                       }
+               }
+       }
        if (errs)
                return -1;
        return 0;