url rewriting: take longest and first match

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>
This commit is contained in:
Junio C Hamano 2008-02-24 22:25:04 -08:00
parent 55029ae4da
commit 844112cac0
2 changed files with 40 additions and 19 deletions

View File

@ -894,9 +894,8 @@ url.<base>.insteadOf::
methods, this feature allows people to specify any of the methods, this feature allows people to specify any of the
equivalent URLs and have git automatically rewrite the URL to equivalent URLs and have git automatically rewrite the URL to
the best alternative for the particular user, even for a the best alternative for the particular user, even for a
never-before-seen repository on the site. The effect of never-before-seen repository on the site. When more than one
having multiple `insteadOf` values from different insteadOf strings match a given URL, the longest match is used.
`<base>` match to an URL is undefined.
user.email:: user.email::
Your email address to be recorded in any newly created commits. Your email address to be recorded in any newly created commits.

View File

@ -2,9 +2,14 @@
#include "remote.h" #include "remote.h"
#include "refs.h" #include "refs.h"
struct counted_string {
size_t len;
const char *s;
};
struct rewrite { struct rewrite {
const char *base; const char *base;
const char **instead_of; size_t baselen;
struct counted_string *instead_of;
int instead_of_nr; int instead_of_nr;
int instead_of_alloc; int instead_of_alloc;
}; };
@ -30,21 +35,32 @@ static char buffer[BUF_SIZE];
static const char *alias_url(const char *url) static const char *alias_url(const char *url)
{ {
int i, j; int i, j;
char *ret;
struct counted_string *longest;
int longest_i;
longest = NULL;
longest_i = -1;
for (i = 0; i < rewrite_nr; i++) { for (i = 0; i < rewrite_nr; i++) {
if (!rewrite[i]) if (!rewrite[i])
continue; continue;
for (j = 0; j < rewrite[i]->instead_of_nr; j++) { for (j = 0; j < rewrite[i]->instead_of_nr; j++) {
if (!prefixcmp(url, rewrite[i]->instead_of[j])) { if (!prefixcmp(url, rewrite[i]->instead_of[j].s) &&
char *ret = malloc(strlen(rewrite[i]->base) - (!longest ||
strlen(rewrite[i]->instead_of[j]) + longest->len < rewrite[i]->instead_of[j].len)) {
strlen(url) + 1); longest = &(rewrite[i]->instead_of[j]);
strcpy(ret, rewrite[i]->base); longest_i = i;
strcat(ret, url + strlen(rewrite[i]->instead_of[j]));
return ret;
} }
} }
} }
if (!longest)
return url; 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) 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; int i;
for (i = 0; i < rewrite_nr; i++) { for (i = 0; i < rewrite_nr; i++) {
if (len ? (!strncmp(base, rewrite[i]->base, len) && if (len
!rewrite[i]->base[len]) : ? (len == rewrite[i]->baselen &&
!strcmp(base, rewrite[i]->base)) !strncmp(base, rewrite[i]->base, len))
: !strcmp(base, rewrite[i]->base))
return rewrite[i]; return rewrite[i];
} }
ALLOC_GROW(rewrite, rewrite_nr + 1, rewrite_alloc); ALLOC_GROW(rewrite, rewrite_nr + 1, rewrite_alloc);
ret = xcalloc(1, sizeof(struct rewrite)); ret = xcalloc(1, sizeof(struct rewrite));
rewrite[rewrite_nr++] = ret; rewrite[rewrite_nr++] = ret;
if (len) if (len) {
ret->base = xstrndup(base, len); ret->base = xstrndup(base, len);
else ret->baselen = len;
}
else {
ret->base = xstrdup(base); ret->base = xstrdup(base);
ret->baselen = strlen(base);
}
return ret; return ret;
} }
static void add_instead_of(struct rewrite *rewrite, const char *instead_of) 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); 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) static void read_remotes_file(struct remote *remote)