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.
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
---
I still need to work more on the documentation (and I think I'd failed to
come up with an organization for it that I liked), but I'd like to get the
code portion out there are reviewed, at least, since I think last time,
the patch only got as far as a discussion of how I should explain what it
does.
Documentation/config.txt | 22 +++++++++
Documentation/urls.txt | 25 ++++++++++
remote.c | 112 ++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 156 insertions(+), 3 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index f2f6a77..6ccd59f 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -646,6 +646,28 @@ help.format::
Values 'man', 'info', 'web' and 'html' are supported. 'man' is
the default. 'web' and 'html' are the same.
+host.<name>.*::
+ These options provide a way to rewrite URLs when there is a
+ pattern of URLs with a common prefix which should be replaced
+ with a different prefix. For every 'alias' prefix given, any
+ URL git receives that starts with that prefix will be
+ rewritten to have the 'base' prefix instead. 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.
+
+host.<name>.base::
+ The base URL which should be used for this particular
+ host.
+
+host.<name>.rewritebase::
+ Additional base URLs which refer to this host. If a URL
+ matches this, any access to it will use the URL formed with
+ the corresponding base URL instead of the given URL.
+
http.proxy::
Override the HTTP proxy, normally configured using the 'http_proxy'
environment variable (see linkgit:curl[1]). This can be overridden
diff --git a/Documentation/urls.txt b/Documentation/urls.txt
index 81ac17f..11c05db 100644
--- a/Documentation/urls.txt
+++ b/Documentation/urls.txt
@@ -44,3 +44,28 @@ 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:
+
+------------
+ [host "<host>"]
+ base = <actual url base>
+ rewritebase = <other url base>
+------------
+
+If you have a section:
+
+------------
+ [host "xz"]
+ base = git://git.host.xz/
+ rewritebase = host.xz:/path/to/
+ rewritebase = 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 6b56473..59338a3 100644
--- a/remote.c
+++ b/remote.c
@@ -2,6 +2,15 @@
#include "remote.h"
#include "refs.h"
+struct host {
+ const char *name;
+
+ const char *base;
+
+ const char **alias;
+ int alias_nr;
+};
+
static struct remote **remotes;
static int allocated_remotes;
@@ -11,9 +20,32 @@ static int allocated_branches;
static struct branch *current_branch;
static const char *default_remote_name;
+static struct host **hosts;
+static int allocated_hosts;
+
#define BUF_SIZE (2048)
static char buffer[BUF_SIZE];
+static const char *alias_url(const char *url)
+{
+ int i, j;
+ for (i = 0; i < allocated_hosts; i++) {
+ if (!hosts[i])
+ continue;
+ for (j = 0; j < hosts[i]->alias_nr; j++) {
+ if (!prefixcmp(url, hosts[i]->alias[j])) {
+ char *ret = malloc(strlen(hosts[i]->base) -
+ strlen(hosts[i]->alias[j]) +
+ strlen(url) + 1);
+ strcpy(ret, hosts[i]->base);
+ strcat(ret, url + strlen(hosts[i]->alias[j]));
+ return ret;
+ }
+ }
+ }
+ return url;
+}
+
static void add_push_refspec(struct remote *remote, const char *ref)
{
int nr = remote->push_refspec_nr + 1;
@@ -41,6 +73,11 @@ static void add_url(struct remote *remote, const char *url)
remote->url_nr = nr;
}
+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;
@@ -121,6 +158,48 @@ static struct branch *make_branch(const char *name, int len)
return branches[empty];
}
+static struct host *make_host(const char *name, int len)
+{
+ int i, empty = -1;
+
+ for (i = 0; i < allocated_hosts; i++) {
+ if (!hosts[i]) {
+ if (empty < 0)
+ empty = i;
+ } else {
+ if (len ? (!strncmp(name, hosts[i]->name, len) &&
+ !hosts[i]->name[len]) :
+ !strcmp(name, hosts[i]->name))
+ return hosts[i];
+ }
+ }
+
+ if (empty < 0) {
+ empty = allocated_hosts;
+ allocated_hosts += allocated_hosts ? allocated_hosts : 1;
+ hosts = xrealloc(hosts,
+ sizeof(*hosts) * allocated_hosts);
+ memset(hosts + empty, 0,
+ (allocated_hosts - empty) * sizeof(*hosts));
+ }
+ hosts[empty] = xcalloc(1, sizeof(struct host));
+ if (len)
+ hosts[empty]->name = xstrndup(name, len);
+ else
+ hosts[empty]->name = xstrdup(name);
+
+ return hosts[empty];
+}
+
+static void add_alias(struct host *host, const char *name)
+{
+ int nr = host->alias_nr + 1;
+ host->alias =
+ xrealloc(host->alias, nr * sizeof(char *));
+ host->alias[nr-1] = name;
+ host->alias_nr = nr;
+}
+
static void read_remotes_file(struct remote *remote)
{
FILE *f = fopen(git_path("remotes/%s", remote->name), "r");
@@ -154,7 +233,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 +285,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 +315,20 @@ static int handle_config(const char *key, const char *value)
}
return 0;
}
+ if (!prefixcmp(key, "host.")) {
+ struct host *host;
+ name = key + 5;
+ subkey = strrchr(name, '.');
+ if (!subkey)
+ return 0;
+ host = make_host(name, subkey - name);
+ if (!value)
+ return 0;
+ if (!strcmp(subkey, ".base"))
+ host->base = xstrdup(value);
+ else if (!strcmp(subkey, ".rewritebase"))
+ add_alias(host, xstrdup(value));
+ }
if (prefixcmp(key, "remote."))
return 0;
name = key + 7;
@@ -287,6 +380,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 < allocated_remotes; 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 +408,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 +474,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);
--
1.5.4.1.1350.g2b9ee
-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html