@matheusbsbs / matheus-bagliano / commits / 55029ae4da
Fork 0

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>

Daniel Barkalow committed Feb 20, 2008 at 13:43 UTC 55029ae4dac07942437c0c715ea7c8ac60dd3576
4 files changed +160 -3
Documentation/config.txt
+12
@@ -886,6 +886,18 @@ tar.umask::
886 archiving user's umask will be used instead. See umask(2) and
887 linkgit:git-archive[1].
888
889 +url.<base>.insteadOf::
890 + Any URL that starts with this value will be rewritten to
891 + start, instead, with <base>. In cases where some site serves a
892 + large number of repositories, and serves them with multiple
893 + access methods, and some users need to use different access
894 + methods, this feature allows people to specify any of the
895 + equivalent URLs and have git automatically rewrite the URL to
896 + the best alternative for the particular user, even for a
897 + never-before-seen repository on the site. The effect of
898 + having multiple `insteadOf` values from different
899 + `<base>` match to an URL is undefined.
900 +
901 user.email::
902 Your email address to be recorded in any newly created commits.
903 Can be overridden by the 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_EMAIL', and
Documentation/urls.txt
+23
@@ -44,3 +44,26 @@ endif::git-clone[]
44 ifdef::git-clone[]
45 They are equivalent, except the former implies --local option.
46 endif::git-clone[]
47 +
48 +
49 +If there are a large number of similarly-named remote repositories and
50 +you want to use a different format for them (such that the URLs you
51 +use will be rewritten into URLs that work), you can create a
52 +configuration section of the form:
53 +
54 +------------
55 + [url "<actual url base>"]
56 + insteadOf = <other url base>
57 +------------
58 +
59 +For example, with this:
60 +
61 +------------
62 + [url "git://git.host.xz/"]
63 + insteadOf = host.xz:/path/to/
64 + insteadOf = work:
65 +------------
66 +
67 +a URL like "work:repo.git" or like "host.xz:/path/to/repo.git" will be
68 +rewritten in any context that takes a URL to be "git://git.host.xz/repo.git".
69 +
remote.c
+94 -3
@@ -2,6 +2,13 @@
2 #include "remote.h"
3 #include "refs.h"
4
5 +struct rewrite {
6 + const char *base;
7 + const char **instead_of;
8 + int instead_of_nr;
9 + int instead_of_alloc;
10 +};
11 +
12 static struct remote **remotes;
13 static int remotes_alloc;
14 static int remotes_nr;
@@ -13,9 +20,33 @@ static int branches_nr;
20 static struct branch *current_branch;
21 static const char *default_remote_name;
22
23 +static struct rewrite **rewrite;
24 +static int rewrite_alloc;
25 +static int rewrite_nr;
26 +
27 #define BUF_SIZE (2048)
28 static char buffer[BUF_SIZE];
29
30 +static const char *alias_url(const char *url)
31 +{
32 + int i, j;
33 + for (i = 0; i < rewrite_nr; i++) {
34 + if (!rewrite[i])
35 + continue;
36 + for (j = 0; j < rewrite[i]->instead_of_nr; j++) {
37 + if (!prefixcmp(url, rewrite[i]->instead_of[j])) {
38 + char *ret = malloc(strlen(rewrite[i]->base) -
39 + strlen(rewrite[i]->instead_of[j]) +
40 + strlen(url) + 1);
41 + strcpy(ret, rewrite[i]->base);
42 + strcat(ret, url + strlen(rewrite[i]->instead_of[j]));
43 + return ret;
44 + }
45 + }
46 + }
47 + return url;
48 +}
49 +
50 static void add_push_refspec(struct remote *remote, const char *ref)
51 {
52 ALLOC_GROW(remote->push_refspec,
@@ -38,6 +69,11 @@ static void add_url(struct remote *remote, const char *url)
69 remote->url[remote->url_nr++] = url;
70 }
71
72 +static void add_url_alias(struct remote *remote, const char *url)
73 +{
74 + add_url(remote, alias_url(url));
75 +}
76 +
77 static struct remote *make_remote(const char *name, int len)
78 {
79 struct remote *ret;
@@ -95,6 +131,35 @@ static struct branch *make_branch(const char *name, int len)
131 return ret;
132 }
133
134 +static struct rewrite *make_rewrite(const char *base, int len)
135 +{
136 + struct rewrite *ret;
137 + int i;
138 +
139 + for (i = 0; i < rewrite_nr; i++) {
140 + if (len ? (!strncmp(base, rewrite[i]->base, len) &&
141 + !rewrite[i]->base[len]) :
142 + !strcmp(base, rewrite[i]->base))
143 + return rewrite[i];
144 + }
145 +
146 + ALLOC_GROW(rewrite, rewrite_nr + 1, rewrite_alloc);
147 + ret = xcalloc(1, sizeof(struct rewrite));
148 + rewrite[rewrite_nr++] = ret;
149 + if (len)
150 + ret->base = xstrndup(base, len);
151 + else
152 + ret->base = xstrdup(base);
153 +
154 + return ret;
155 +}
156 +
157 +static void add_instead_of(struct rewrite *rewrite, const char *instead_of)
158 +{
159 + ALLOC_GROW(rewrite->instead_of, rewrite->instead_of_nr + 1, rewrite->instead_of_alloc);
160 + rewrite->instead_of[rewrite->instead_of_nr++] = instead_of;
161 +}
162 +
163 static void read_remotes_file(struct remote *remote)
164 {
165 FILE *f = fopen(git_path("remotes/%s", remote->name), "r");
@@ -128,7 +193,7 @@ static void read_remotes_file(struct remote *remote)
193
194 switch (value_list) {
195 case 0:
131 - add_url(remote, xstrdup(s));
196 + add_url_alias(remote, xstrdup(s));
197 break;
198 case 1:
199 add_push_refspec(remote, xstrdup(s));
@@ -180,7 +245,7 @@ static void read_branches_file(struct remote *remote)
245 } else {
246 branch = "refs/heads/master";
247 }
183 - add_url(remote, p);
248 + add_url_alias(remote, p);
249 add_fetch_refspec(remote, branch);
250 remote->fetch_tags = 1; /* always auto-follow */
251 }
@@ -210,6 +275,19 @@ static int handle_config(const char *key, const char *value)
275 }
276 return 0;
277 }
278 + if (!prefixcmp(key, "url.")) {
279 + struct rewrite *rewrite;
280 + name = key + 5;
281 + subkey = strrchr(name, '.');
282 + if (!subkey)
283 + return 0;
284 + rewrite = make_rewrite(name, subkey - name);
285 + if (!strcmp(subkey, ".insteadof")) {
286 + if (!value)
287 + return config_error_nonbool(key);
288 + add_instead_of(rewrite, xstrdup(value));
289 + }
290 + }
291 if (prefixcmp(key, "remote."))
292 return 0;
293 name = key + 7;
@@ -261,6 +339,18 @@ static int handle_config(const char *key, const char *value)
339 return 0;
340 }
341
342 +static void alias_all_urls(void)
343 +{
344 + int i, j;
345 + for (i = 0; i < remotes_nr; i++) {
346 + if (!remotes[i])
347 + continue;
348 + for (j = 0; j < remotes[i]->url_nr; j++) {
349 + remotes[i]->url[j] = alias_url(remotes[i]->url[j]);
350 + }
351 + }
352 +}
353 +
354 static void read_config(void)
355 {
356 unsigned char sha1[20];
@@ -277,6 +367,7 @@ static void read_config(void)
367 make_branch(head_ref + strlen("refs/heads/"), 0);
368 }
369 git_config(handle_config);
370 + alias_all_urls();
371 }
372
373 struct refspec *parse_ref_spec(int nr_refspec, const char **refspec)
@@ -342,7 +433,7 @@ struct remote *remote_get(const char *name)
433 read_branches_file(ret);
434 }
435 if (!ret->url)
345 - add_url(ret, name);
436 + add_url_alias(ret, name);
437 if (!ret->url)
438 return NULL;
439 ret->fetch = parse_ref_spec(ret->fetch_refspec_nr, ret->fetch_refspec);
t/t5516-fetch-push.sh
+31
@@ -100,6 +100,23 @@ test_expect_success 'fetch with wildcard' '
100 )
101 '
102
103 +test_expect_success 'fetch with insteadOf' '
104 + mk_empty &&
105 + (
106 + TRASH=$(pwd) &&
107 + cd testrepo &&
108 + git config url./$TRASH/.insteadOf trash/
109 + git config remote.up.url trash/. &&
110 + git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
111 + git fetch up &&
112 +
113 + r=$(git show-ref -s --verify refs/remotes/origin/master) &&
114 + test "z$r" = "z$the_commit" &&
115 +
116 + test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
117 + )
118 +'
119 +
120 test_expect_success 'push without wildcard' '
121 mk_empty &&
122
@@ -126,6 +143,20 @@ test_expect_success 'push with wildcard' '
143 )
144 '
145
146 +test_expect_success 'push with insteadOf' '
147 + mk_empty &&
148 + TRASH=$(pwd) &&
149 + git config url./$TRASH/.insteadOf trash/ &&
150 + git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
151 + (
152 + cd testrepo &&
153 + r=$(git show-ref -s --verify refs/remotes/origin/master) &&
154 + test "z$r" = "z$the_commit" &&
155 +
156 + test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
157 + )
158 +'
159 +
160 test_expect_success 'push with matching heads' '
161
162 mk_test heads/master &&