зеркало из https://github.com/microsoft/git.git
Merge branch 'db/host-alias'
* db/host-alias: url rewriting: take longest and first match Add support for url aliases in config files Use ALLOC_GROW in remote.{c,h}
This commit is contained in:
Коммит
5372715ed2
|
@ -899,6 +899,17 @@ 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. 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.
|
||||
Can be overridden by the 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_EMAIL', and
|
||||
|
|
|
@ -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".
|
||||
|
||||
|
|
237
remote.c
237
remote.c
|
@ -2,123 +2,184 @@
|
|||
#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 branches[empty];
|
||||
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];
|
||||
}
|
||||
|
||||
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 */
|
||||
}
|
||||
|
@ -236,6 +297,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;
|
||||
|
@ -287,6 +361,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];
|
||||
|
@ -303,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)
|
||||
|
@ -368,7 +455,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);
|
||||
|
@ -380,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;
|
||||
|
|
4
remote.h
4
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);
|
||||
|
|
|
@ -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 &&
|
||||
|
|
Загрузка…
Ссылка в новой задаче