Browse Source

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 <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
maint
Daniel Barkalow 17 years ago committed by Junio C Hamano
parent
commit
55029ae4da
  1. 12
      Documentation/config.txt
  2. 23
      Documentation/urls.txt
  3. 97
      remote.c
  4. 31
      t/t5516-fetch-push.sh

12
Documentation/config.txt

@ -886,6 +886,18 @@ tar.umask:: @@ -886,6 +886,18 @@ tar.umask::
archiving user's umask will be used instead. See umask(2) and
linkgit:git-archive[1].

url.<base>.insteadOf::
Any URL that starts with this value will be rewritten to
start, instead, with <base>. 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
`<base>` 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

23
Documentation/urls.txt

@ -44,3 +44,26 @@ endif::git-clone[] @@ -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 "<actual url base>"]
insteadOf = <other url base>
------------

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".


97
remote.c

@ -2,6 +2,13 @@ @@ -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; @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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);

31
t/t5516-fetch-push.sh

@ -100,6 +100,23 @@ test_expect_success 'fetch with wildcard' ' @@ -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' ' @@ -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 &&

Loading…
Cancel
Save