]> git.ipfire.org Git - thirdparty/git.git/blobdiff - remote.c
url rewriting: take longest and first match
[thirdparty/git.git] / remote.c
index bec2ba1adbed02af54572be924dcfbb47bf9c423..1f836967588456b03c1390157ca04aad7b8e3eac 100644 (file)
--- a/remote.c
+++ b/remote.c
 #include "remote.h"
 #include "refs.h"
 
+struct counted_string {
+       size_t len;
+       const char *s;
+};
+struct rewrite {
+       const char *base;
+       size_t baselen;
+       struct counted_string *instead_of;
+       int instead_of_nr;
+       int instead_of_alloc;
+};
+
 static struct remote **remotes;
-static int allocated_remotes;
+static int remotes_alloc;
+static int remotes_nr;
 
 static struct branch **branches;
-static int allocated_branches;
+static int branches_alloc;
+static int branches_nr;
 
 static struct branch *current_branch;
 static const char *default_remote_name;
 
+static struct rewrite **rewrite;
+static int rewrite_alloc;
+static int rewrite_nr;
+
 #define BUF_SIZE (2048)
 static char buffer[BUF_SIZE];
 
+static const char *alias_url(const char *url)
+{
+       int i, j;
+       char *ret;
+       struct counted_string *longest;
+       int longest_i;
+
+       longest = NULL;
+       longest_i = -1;
+       for (i = 0; i < rewrite_nr; i++) {
+               if (!rewrite[i])
+                       continue;
+               for (j = 0; j < rewrite[i]->instead_of_nr; j++) {
+                       if (!prefixcmp(url, rewrite[i]->instead_of[j].s) &&
+                           (!longest ||
+                            longest->len < rewrite[i]->instead_of[j].len)) {
+                               longest = &(rewrite[i]->instead_of[j]);
+                               longest_i = i;
+                       }
+               }
+       }
+       if (!longest)
+               return url;
+
+       ret = malloc(rewrite[longest_i]->baselen +
+                    (strlen(url) - longest->len) + 1);
+       strcpy(ret, rewrite[longest_i]->base);
+       strcpy(ret + rewrite[longest_i]->baselen, url + longest->len);
+       return ret;
+}
+
 static void add_push_refspec(struct remote *remote, const char *ref)
 {
-       int nr = remote->push_refspec_nr + 1;
-       remote->push_refspec =
-               xrealloc(remote->push_refspec, nr * sizeof(char *));
-       remote->push_refspec[nr-1] = ref;
-       remote->push_refspec_nr = nr;
+       ALLOC_GROW(remote->push_refspec,
+                  remote->push_refspec_nr + 1,
+                  remote->push_refspec_alloc);
+       remote->push_refspec[remote->push_refspec_nr++] = ref;
 }
 
 static void add_fetch_refspec(struct remote *remote, const char *ref)
 {
-       int nr = remote->fetch_refspec_nr + 1;
-       remote->fetch_refspec =
-               xrealloc(remote->fetch_refspec, nr * sizeof(char *));
-       remote->fetch_refspec[nr-1] = ref;
-       remote->fetch_refspec_nr = nr;
+       ALLOC_GROW(remote->fetch_refspec,
+                  remote->fetch_refspec_nr + 1,
+                  remote->fetch_refspec_alloc);
+       remote->fetch_refspec[remote->fetch_refspec_nr++] = ref;
 }
 
 static void add_url(struct remote *remote, const char *url)
 {
-       int nr = remote->url_nr + 1;
-       remote->url =
-               xrealloc(remote->url, nr * sizeof(char *));
-       remote->url[nr-1] = url;
-       remote->url_nr = nr;
+       ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);
+       remote->url[remote->url_nr++] = url;
+}
+
+static void add_url_alias(struct remote *remote, const char *url)
+{
+       add_url(remote, alias_url(url));
 }
 
 static struct remote *make_remote(const char *name, int len)
 {
-       int i, empty = -1;
+       struct remote *ret;
+       int i;
 
-       for (i = 0; i < allocated_remotes; i++) {
-               if (!remotes[i]) {
-                       if (empty < 0)
-                               empty = i;
-               } else {
-                       if (len ? (!strncmp(name, remotes[i]->name, len) &&
-                                  !remotes[i]->name[len]) :
-                           !strcmp(name, remotes[i]->name))
-                               return remotes[i];
-               }
+       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 (empty < 0) {
-               empty = allocated_remotes;
-               allocated_remotes += allocated_remotes ? allocated_remotes : 1;
-               remotes = xrealloc(remotes,
-                                  sizeof(*remotes) * allocated_remotes);
-               memset(remotes + empty, 0,
-                      (allocated_remotes - empty) * sizeof(*remotes));
-       }
-       remotes[empty] = xcalloc(1, sizeof(struct remote));
+       ret = xcalloc(1, sizeof(struct remote));
+       ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);
+       remotes[remotes_nr++] = ret;
        if (len)
-               remotes[empty]->name = xstrndup(name, len);
+               ret->name = xstrndup(name, len);
        else
-               remotes[empty]->name = xstrdup(name);
-       return remotes[empty];
+               ret->name = xstrdup(name);
+       return ret;
 }
 
 static void add_merge(struct branch *branch, const char *name)
 {
-       int nr = branch->merge_nr + 1;
-       branch->merge_name =
-               xrealloc(branch->merge_name, nr * sizeof(char *));
-       branch->merge_name[nr-1] = name;
-       branch->merge_nr = nr;
+       ALLOC_GROW(branch->merge_name, branch->merge_nr + 1,
+                  branch->merge_alloc);
+       branch->merge_name[branch->merge_nr++] = name;
 }
 
 static struct branch *make_branch(const char *name, int len)
 {
-       int i, empty = -1;
+       struct branch *ret;
+       int i;
        char *refname;
 
-       for (i = 0; i < allocated_branches; i++) {
-               if (!branches[i]) {
-                       if (empty < 0)
-                               empty = i;
-               } else {
-                       if (len ? (!strncmp(name, branches[i]->name, len) &&
-                                  !branches[i]->name[len]) :
-                           !strcmp(name, branches[i]->name))
-                               return branches[i];
-               }
+       for (i = 0; i < branches_nr; i++) {
+               if (len ? (!strncmp(name, branches[i]->name, len) &&
+                          !branches[i]->name[len]) :
+                   !strcmp(name, branches[i]->name))
+                       return branches[i];
        }
 
-       if (empty < 0) {
-               empty = allocated_branches;
-               allocated_branches += allocated_branches ? allocated_branches : 1;
-               branches = xrealloc(branches,
-                                  sizeof(*branches) * allocated_branches);
-               memset(branches + empty, 0,
-                      (allocated_branches - empty) * sizeof(*branches));
-       }
-       branches[empty] = xcalloc(1, sizeof(struct branch));
+       ALLOC_GROW(branches, branches_nr + 1, branches_alloc);
+       ret = xcalloc(1, sizeof(struct branch));
+       branches[branches_nr++] = ret;
        if (len)
-               branches[empty]->name = xstrndup(name, len);
+               ret->name = xstrndup(name, len);
        else
-               branches[empty]->name = xstrdup(name);
+               ret->name = xstrdup(name);
        refname = malloc(strlen(name) + strlen("refs/heads/") + 1);
        strcpy(refname, "refs/heads/");
-       strcpy(refname + strlen("refs/heads/"),
-              branches[empty]->name);
-       branches[empty]->refname = refname;
+       strcpy(refname + strlen("refs/heads/"), ret->name);
+       ret->refname = refname;
+
+       return ret;
+}
+
+static struct rewrite *make_rewrite(const char *base, int len)
+{
+       struct rewrite *ret;
+       int i;
+
+       for (i = 0; i < rewrite_nr; i++) {
+               if (len
+                   ? (len == rewrite[i]->baselen &&
+                      !strncmp(base, rewrite[i]->base, len))
+                   : !strcmp(base, rewrite[i]->base))
+                       return rewrite[i];
+       }
 
-       return branches[empty];
+       ALLOC_GROW(rewrite, rewrite_nr + 1, rewrite_alloc);
+       ret = xcalloc(1, sizeof(struct rewrite));
+       rewrite[rewrite_nr++] = ret;
+       if (len) {
+               ret->base = xstrndup(base, len);
+               ret->baselen = len;
+       }
+       else {
+               ret->base = xstrdup(base);
+               ret->baselen = strlen(base);
+       }
+       return ret;
+}
+
+static void add_instead_of(struct rewrite *rewrite, const char *instead_of)
+{
+       ALLOC_GROW(rewrite->instead_of, rewrite->instead_of_nr + 1, rewrite->instead_of_alloc);
+       rewrite->instead_of[rewrite->instead_of_nr].s = instead_of;
+       rewrite->instead_of[rewrite->instead_of_nr].len = strlen(instead_of);
+       rewrite->instead_of_nr++;
 }
 
 static void read_remotes_file(struct remote *remote)
@@ -154,7 +215,7 @@ static void read_remotes_file(struct remote *remote)
 
                switch (value_list) {
                case 0:
-                       add_url(remote, xstrdup(s));
+                       add_url_alias(remote, xstrdup(s));
                        break;
                case 1:
                        add_push_refspec(remote, xstrdup(s));
@@ -206,7 +267,7 @@ static void read_branches_file(struct remote *remote)
        } else {
                branch = "refs/heads/master";
        }
-       add_url(remote, p);
+       add_url_alias(remote, p);
        add_fetch_refspec(remote, branch);
        remote->fetch_tags = 1; /* always auto-follow */
 }
@@ -220,19 +281,35 @@ static int handle_config(const char *key, const char *value)
        if (!prefixcmp(key, "branch.")) {
                name = key + 7;
                subkey = strrchr(name, '.');
-               branch = make_branch(name, subkey - name);
                if (!subkey)
                        return 0;
-               if (!value)
-                       return 0;
+               branch = make_branch(name, subkey - name);
                if (!strcmp(subkey, ".remote")) {
+                       if (!value)
+                               return config_error_nonbool(key);
                        branch->remote_name = xstrdup(value);
                        if (branch == current_branch)
                                default_remote_name = branch->remote_name;
-               } else if (!strcmp(subkey, ".merge"))
+               } else if (!strcmp(subkey, ".merge")) {
+                       if (!value)
+                               return config_error_nonbool(key);
                        add_merge(branch, xstrdup(value));
+               }
                return 0;
        }
+       if (!prefixcmp(key, "url.")) {
+               struct rewrite *rewrite;
+               name = key + 5;
+               subkey = strrchr(name, '.');
+               if (!subkey)
+                       return 0;
+               rewrite = make_rewrite(name, subkey - name);
+               if (!strcmp(subkey, ".insteadof")) {
+                       if (!value)
+                               return config_error_nonbool(key);
+                       add_instead_of(rewrite, xstrdup(value));
+               }
+       }
        if (prefixcmp(key,  "remote."))
                return 0;
        name = key + 7;
@@ -278,10 +355,24 @@ static int handle_config(const char *key, const char *value)
        } else if (!strcmp(subkey, ".tagopt")) {
                if (!strcmp(value, "--no-tags"))
                        remote->fetch_tags = -1;
+       } else if (!strcmp(subkey, ".proxy")) {
+               remote->http_proxy = xstrdup(value);
        }
        return 0;
 }
 
+static void alias_all_urls(void)
+{
+       int i, j;
+       for (i = 0; i < remotes_nr; i++) {
+               if (!remotes[i])
+                       continue;
+               for (j = 0; j < remotes[i]->url_nr; j++) {
+                       remotes[i]->url[j] = alias_url(remotes[i]->url[j]);
+               }
+       }
+}
+
 static void read_config(void)
 {
        unsigned char sha1[20];
@@ -298,6 +389,7 @@ static void read_config(void)
                        make_branch(head_ref + strlen("refs/heads/"), 0);
        }
        git_config(handle_config);
+       alias_all_urls();
 }
 
 struct refspec *parse_ref_spec(int nr_refspec, const char **refspec)
@@ -338,6 +430,16 @@ struct refspec *parse_ref_spec(int nr_refspec, const char **refspec)
        return rs;
 }
 
+static int valid_remote_nick(const char *name)
+{
+       if (!name[0] || /* not empty */
+           (name[0] == '.' && /* not "." */
+            (!name[1] || /* not ".." */
+             (name[1] == '.' && !name[2]))))
+               return 0;
+       return !strchr(name, '/'); /* no slash */
+}
+
 struct remote *remote_get(const char *name)
 {
        struct remote *ret;
@@ -346,14 +448,14 @@ struct remote *remote_get(const char *name)
        if (!name)
                name = default_remote_name;
        ret = make_remote(name, 0);
-       if (name[0] != '/') {
+       if (valid_remote_nick(name)) {
                if (!ret->url)
                        read_remotes_file(ret);
                if (!ret->url)
                        read_branches_file(ret);
        }
        if (!ret->url)
-               add_url(ret, name);
+               add_url_alias(ret, name);
        if (!ret->url)
                return NULL;
        ret->fetch = parse_ref_spec(ret->fetch_refspec_nr, ret->fetch_refspec);
@@ -365,7 +467,7 @@ int for_each_remote(each_remote_fn fn, void *priv)
 {
        int i, result = 0;
        read_config();
-       for (i = 0; i < allocated_remotes && !result; i++) {
+       for (i = 0; i < remotes_nr && !result; i++) {
                struct remote *r = remotes[i];
                if (!r)
                        continue;
@@ -417,25 +519,6 @@ int remote_has_url(struct remote *remote, const char *url)
        return 0;
 }
 
-/*
- * Returns true if, under the matching rules for fetching, name is the
- * same as the given full name.
- */
-static int ref_matches_abbrev(const char *name, const char *full)
-{
-       if (!prefixcmp(name, "refs/") || !strcmp(name, "HEAD"))
-               return !strcmp(name, full);
-       if (prefixcmp(full, "refs/"))
-               return 0;
-       if (!prefixcmp(name, "heads/") ||
-           !prefixcmp(name, "tags/") ||
-           !prefixcmp(name, "remotes/"))
-               return !strcmp(name, full + 5);
-       if (prefixcmp(full + 5, "heads/"))
-               return 0;
-       return !strcmp(full + 11, name);
-}
-
 int remote_find_tracking(struct remote *remote, struct refspec *refspec)
 {
        int find_src = refspec->src == NULL;
@@ -485,7 +568,7 @@ struct ref *alloc_ref(unsigned namelen)
        return ret;
 }
 
-static struct ref *copy_ref(struct ref *ref)
+static struct ref *copy_ref(const struct ref *ref)
 {
        struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1);
        memcpy(ret, ref, sizeof(struct ref) + strlen(ref->name) + 1);
@@ -493,6 +576,18 @@ static struct ref *copy_ref(struct ref *ref)
        return ret;
 }
 
+struct ref *copy_ref_list(const struct ref *ref)
+{
+       struct ref *ret = NULL;
+       struct ref **tail = &ret;
+       while (ref) {
+               *tail = copy_ref(ref);
+               ref = ref->next;
+               tail = &((*tail)->next);
+       }
+       return ret;
+}
+
 void free_refs(struct ref *ref)
 {
        struct ref *next;
@@ -519,10 +614,7 @@ static int count_refspec_match(const char *pattern,
                char *name = refs->name;
                int namelen = strlen(name);
 
-               if (namelen < patlen ||
-                   memcmp(name + namelen - patlen, pattern, patlen))
-                       continue;
-               if (namelen != patlen && name[namelen - patlen - 1] != '/')
+               if (!refname_match(pattern, name, ref_rev_parse_rules))
                        continue;
 
                /* A match is "weak" if it is with refs outside
@@ -684,14 +776,6 @@ static int match_explicit_refs(struct ref *src, struct ref *dst,
        return -errs;
 }
 
-static struct ref *find_ref_by_name(struct ref *list, const char *name)
-{
-       for ( ; list; list = list->next)
-               if (!strcmp(list->name, name))
-                       return list;
-       return NULL;
-}
-
 static const struct refspec *check_pattern_match(const struct refspec *rs,
                                                 int rs_nr,
                                                 const struct ref *src)
@@ -710,10 +794,12 @@ static const struct refspec *check_pattern_match(const struct refspec *rs,
  * without thinking.
  */
 int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
-              int nr_refspec, char **refspec, int all)
+              int nr_refspec, const char **refspec, int flags)
 {
        struct refspec *rs =
                parse_ref_spec(nr_refspec, (const char **) refspec);
+       int send_all = flags & MATCH_REFS_ALL;
+       int send_mirror = flags & MATCH_REFS_MIRROR;
 
        if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
                return -1;
@@ -730,7 +816,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
                        if (!pat)
                                continue;
                }
-               else if (prefixcmp(src->name, "refs/heads/"))
+               else if (!send_mirror && prefixcmp(src->name, "refs/heads/"))
                        /*
                         * "matching refs"; traditionally we pushed everything
                         * including refs outside refs/heads/ hierarchy, but
@@ -751,10 +837,13 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
                if (dst_peer && dst_peer->peer_ref)
                        /* We're already sending something to this ref. */
                        goto free_name;
-               if (!dst_peer && !nr_refspec && !all)
-                       /* Remote doesn't have it, and we have no
+
+               if (!dst_peer && !nr_refspec && !(send_all || send_mirror))
+                       /*
+                        * Remote doesn't have it, and we have no
                         * explicit pattern, and we don't have
-                        * --all. */
+                        * --all nor --mirror.
+                        */
                        goto free_name;
                if (!dst_peer) {
                        /* Create a new one and link it */
@@ -807,13 +896,13 @@ int branch_merge_matches(struct branch *branch,
 {
        if (!branch || i < 0 || i >= branch->merge_nr)
                return 0;
-       return ref_matches_abbrev(branch->merge[i]->src, refname);
+       return refname_match(branch->merge[i]->src, refname, ref_fetch_rules);
 }
 
-static struct ref *get_expanded_map(struct ref *remote_refs,
+static struct ref *get_expanded_map(const struct ref *remote_refs,
                                    const struct refspec *refspec)
 {
-       struct ref *ref;
+       const struct ref *ref;
        struct ref *ret = NULL;
        struct ref **tail = &ret;
 
@@ -824,7 +913,7 @@ static struct ref *get_expanded_map(struct ref *remote_refs,
                if (strchr(ref->name, '^'))
                        continue; /* a dereference item */
                if (!prefixcmp(ref->name, refspec->src)) {
-                       char *match;
+                       const char *match;
                        struct ref *cpy = copy_ref(ref);
                        match = ref->name + remote_prefix_len;
 
@@ -842,19 +931,19 @@ static struct ref *get_expanded_map(struct ref *remote_refs,
        return ret;
 }
 
-static struct ref *find_ref_by_name_abbrev(struct ref *refs, const char *name)
+static const struct ref *find_ref_by_name_abbrev(const struct ref *refs, const char *name)
 {
-       struct ref *ref;
+       const struct ref *ref;
        for (ref = refs; ref; ref = ref->next) {
-               if (ref_matches_abbrev(name, ref->name))
+               if (refname_match(name, ref->name, ref_fetch_rules))
                        return ref;
        }
        return NULL;
 }
 
-struct ref *get_remote_ref(struct ref *remote_refs, const char *name)
+struct ref *get_remote_ref(const struct ref *remote_refs, const char *name)
 {
-       struct ref *ref = find_ref_by_name_abbrev(remote_refs, name);
+       const struct ref *ref = find_ref_by_name_abbrev(remote_refs, name);
 
        if (!ref)
                return NULL;
@@ -887,7 +976,7 @@ static struct ref *get_local_ref(const char *name)
        return ret;
 }
 
-int get_fetch_map(struct ref *remote_refs,
+int get_fetch_map(const struct ref *remote_refs,
                  const struct refspec *refspec,
                  struct ref ***tail,
                  int missing_ok)