From 2d31347ba5c56d43d64dfdfe04a924178ee55b75 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Mon, 18 Feb 2008 23:41:41 -0500 Subject: [PATCH 1/3] Use ALLOC_GROW in remote.{c,h} Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- remote.c | 118 ++++++++++++++++++++++--------------------------------- remote.h | 4 ++ 2 files changed, 50 insertions(+), 72 deletions(-) diff --git a/remote.c b/remote.c index 6b56473f5b..457d8a44c7 100644 --- a/remote.c +++ b/remote.c @@ -3,10 +3,12 @@ #include "refs.h" 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; @@ -16,109 +18,81 @@ static char buffer[BUF_SIZE]; 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 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 branches[empty]; + return ret; } static void read_remotes_file(struct remote *remote) @@ -380,7 +354,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; diff --git a/remote.h b/remote.h index 86e036d610..0f6033fb25 100644 --- a/remote.h +++ b/remote.h @@ -6,14 +6,17 @@ struct remote { const char **url; int url_nr; + int url_alloc; const char **push_refspec; struct refspec *push; int push_refspec_nr; + int push_refspec_alloc; const char **fetch_refspec; struct refspec *fetch; int fetch_refspec_nr; + int fetch_refspec_alloc; /* * -1 to never fetch tags @@ -100,6 +103,7 @@ struct branch { const char **merge_name; struct refspec **merge; int merge_nr; + int merge_alloc; }; struct branch *branch_get(const char *name); From 55029ae4dac07942437c0c715ea7c8ac60dd3576 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Wed, 20 Feb 2008 13:43:53 -0500 Subject: [PATCH 2/3] Add support for url aliases in config files This allows users with different preferences for access methods to the same remote repositories to rewrite each other's URLs by pattern matching across a large set of similiarly set up repositories to each get the desired access. For example, if you don't have a kernel.org account, you might want settings like: [url "git://git.kernel.org/pub/"] insteadOf = master.kernel.org:/pub Then, if you give git a URL like: master.kernel.org:/pub/scm/linux/kernel/git/linville/wireless-2.6.git it will act like you gave it: git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6.git and you can cut-and-paste pull requests in email without fixing them by hand, for example. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- Documentation/config.txt | 12 +++++ Documentation/urls.txt | 23 ++++++++++ remote.c | 97 ++++++++++++++++++++++++++++++++++++++-- t/t5516-fetch-push.sh | 31 +++++++++++++ 4 files changed, 160 insertions(+), 3 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index f2f6a774e0..298138917a 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -886,6 +886,18 @@ tar.umask:: archiving user's umask will be used instead. See umask(2) and linkgit:git-archive[1]. +url..insteadOf:: + Any URL that starts with this value will be rewritten to + start, instead, with . In cases where some site serves a + large number of repositories, and serves them with multiple + access methods, and some users need to use different access + 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 + `` match to an URL is undefined. + user.email:: Your email address to be recorded in any newly created commits. Can be overridden by the 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_EMAIL', and diff --git a/Documentation/urls.txt b/Documentation/urls.txt index 81ac17f32a..fa34c67471 100644 --- a/Documentation/urls.txt +++ b/Documentation/urls.txt @@ -44,3 +44,26 @@ endif::git-clone[] ifdef::git-clone[] They are equivalent, except the former implies --local option. endif::git-clone[] + + +If there are a large number of similarly-named remote repositories and +you want to use a different format for them (such that the URLs you +use will be rewritten into URLs that work), you can create a +configuration section of the form: + +------------ + [url ""] + insteadOf = +------------ + +For example, with this: + +------------ + [url "git://git.host.xz/"] + insteadOf = host.xz:/path/to/ + insteadOf = work: +------------ + +a URL like "work:repo.git" or like "host.xz:/path/to/repo.git" will be +rewritten in any context that takes a URL to be "git://git.host.xz/repo.git". + diff --git a/remote.c b/remote.c index 457d8a44c7..0012954b6f 100644 --- a/remote.c +++ b/remote.c @@ -2,6 +2,13 @@ #include "remote.h" #include "refs.h" +struct rewrite { + const char *base; + const char **instead_of; + int instead_of_nr; + int instead_of_alloc; +}; + static struct remote **remotes; static int remotes_alloc; static int remotes_nr; @@ -13,9 +20,33 @@ 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; + 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; + } + } + } + return url; +} + static void add_push_refspec(struct remote *remote, const char *ref) { ALLOC_GROW(remote->push_refspec, @@ -38,6 +69,11 @@ static void add_url(struct remote *remote, const char *url) 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) { struct remote *ret; @@ -95,6 +131,35 @@ static struct branch *make_branch(const char *name, int len) 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 ? (!strncmp(base, rewrite[i]->base, len) && + !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) + ret->base = xstrndup(base, len); + else + ret->base = xstrdup(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; +} + static void read_remotes_file(struct remote *remote) { FILE *f = fopen(git_path("remotes/%s", remote->name), "r"); @@ -128,7 +193,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)); @@ -180,7 +245,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 */ } @@ -210,6 +275,19 @@ static int handle_config(const char *key, const char *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; @@ -261,6 +339,18 @@ static int handle_config(const char *key, const char *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]; @@ -277,6 +367,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) @@ -342,7 +433,7 @@ struct remote *remote_get(const char *name) 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); diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 9d2dc33cbd..9023ba05af 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -100,6 +100,23 @@ test_expect_success 'fetch with wildcard' ' ) ' +test_expect_success 'fetch with insteadOf' ' + mk_empty && + ( + TRASH=$(pwd) && + cd testrepo && + git config url./$TRASH/.insteadOf trash/ + git config remote.up.url trash/. && + git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" && + git fetch up && + + r=$(git show-ref -s --verify refs/remotes/origin/master) && + test "z$r" = "z$the_commit" && + + test 1 = $(git for-each-ref refs/remotes/origin | wc -l) + ) +' + test_expect_success 'push without wildcard' ' mk_empty && @@ -126,6 +143,20 @@ test_expect_success 'push with wildcard' ' ) ' +test_expect_success 'push with insteadOf' ' + mk_empty && + TRASH=$(pwd) && + git config url./$TRASH/.insteadOf trash/ && + git push trash/testrepo refs/heads/master:refs/remotes/origin/master && + ( + cd testrepo && + r=$(git show-ref -s --verify refs/remotes/origin/master) && + test "z$r" = "z$the_commit" && + + test 1 = $(git for-each-ref refs/remotes/origin | wc -l) + ) +' + test_expect_success 'push with matching heads' ' mk_test heads/master && From 844112cac07d6413b9bb42d38527d2f7ca7c7801 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 24 Feb 2008 22:25:04 -0800 Subject: [PATCH 3/3] 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 --- Documentation/config.txt | 5 ++-- remote.c | 54 ++++++++++++++++++++++++++++------------ 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 298138917a..57b9b99863 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -894,9 +894,8 @@ url..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 - `` 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. diff --git a/remote.c b/remote.c index 0012954b6f..1f83696758 100644 --- 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)