url rewriting: take longest and first match
authorJunio C Hamano <gitster@pobox.com>
Mon, 25 Feb 2008 06:25:04 +0000 (22:25 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 25 Feb 2008 06:34:13 +0000 (22:34 -0800)
Earlier we had a cop-out in the documentation to make the
behaviour "undefined" if configuration had more than one
insteadOf that would match the target URL, like this:

    [url "git://git.or.cz/"]
insteadOf = "git.or.cz:"       ; (1)
insteadOf = "repo.or.cz:"      ; (2)
    [url "/local/mirror/"]
insteadOf = "git.or.cz:myrepo" ; (3)
insteadOf = "repo.or.cz:"      ; (4)

It would be most natural to take the longest and first match, i.e.

 - rewrite "git.or.cz:frotz" to "git://git.or.cz/frotz" by using
   (1),

 - rewrite "git.or.cz:myrepo/xyzzy" to "/local/mirror/xyzzy" by favoring
   (3) over (1), and

 - rewrite "repo.or.cz:frotz" to "git://git.or.cz/frotz" by
   favoring (2) over (4).

Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/config.txt
remote.c

index 298138917a3eb5a0f75ff11a09d286efef3d45b5..57b9b998639d509e0d092fa860ba1eaeee688c76 100644 (file)
@@ -894,9 +894,8 @@ url.<base>.insteadOf::
        methods, this feature allows people to specify any of the
        equivalent URLs and have git automatically rewrite the URL to
        the best alternative for the particular user, even for a
-       never-before-seen repository on the site. The effect of
-       having multiple `insteadOf` values from different
-       `<base>` match to an URL is undefined.
+       never-before-seen repository on the site.  When more than one
+       insteadOf strings match a given URL, the longest match is used.
 
 user.email::
        Your email address to be recorded in any newly created commits.
index 0012954b6f795d75d442f4c58f29dc194888d022..1f836967588456b03c1390157ca04aad7b8e3eac 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -2,9 +2,14 @@
 #include "remote.h"
 #include "refs.h"
 
+struct counted_string {
+       size_t len;
+       const char *s;
+};
 struct rewrite {
        const char *base;
-       const char **instead_of;
+       size_t baselen;
+       struct counted_string *instead_of;
        int instead_of_nr;
        int instead_of_alloc;
 };
@@ -30,21 +35,32 @@ 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])) {
-                               char *ret = malloc(strlen(rewrite[i]->base) -
-                                                  strlen(rewrite[i]->instead_of[j]) +
-                                                  strlen(url) + 1);
-                               strcpy(ret, rewrite[i]->base);
-                               strcat(ret, url + strlen(rewrite[i]->instead_of[j]));
-                               return ret;
+                       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;
                        }
                }
        }
-       return url;
+       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)
@@ -137,27 +153,33 @@ static struct rewrite *make_rewrite(const char *base, int len)
        int i;
 
        for (i = 0; i < rewrite_nr; i++) {
-               if (len ? (!strncmp(base, rewrite[i]->base, len) &&
-                          !rewrite[i]->base[len]) :
-                   !strcmp(base, rewrite[i]->base))
+               if (len
+                   ? (len == rewrite[i]->baselen &&
+                      !strncmp(base, rewrite[i]->base, len))
+                   : !strcmp(base, rewrite[i]->base))
                        return rewrite[i];
        }
 
        ALLOC_GROW(rewrite, rewrite_nr + 1, rewrite_alloc);
        ret = xcalloc(1, sizeof(struct rewrite));
        rewrite[rewrite_nr++] = ret;
-       if (len)
+       if (len) {
                ret->base = xstrndup(base, len);
-       else
+               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++] = instead_of;
+       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)