@matheusbsbs / matheus-bagliano / commits / 1c2eafb89b
Fork 0

Add url.<base>.pushInsteadOf: URL rewriting for push only

This configuration option allows systematically rewriting fetch-only URLs to push-capable URLs when used with push. For instance: [url "ssh://example.org/"] pushInsteadOf = "git://example.org/" This will allow clones of "git://example.org/path/to/repo" to subsequently push to "ssh://example.org/path/to/repo", without manually configuring pushurl for that remote. Includes documentation for the new option, bash completion updates, and test cases (both that pushInsteadOf applies to push, that it does not apply to fetch, and that it is ignored when pushURL is already defined). Signed-off-by: Josh Triplett <josh@joshtriplett.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Josh Triplett committed Sep 7, 2009 at 01:56 UTC 1c2eafb89bcaf2ddbf4dfb93df19673c0fadaaeb
5 files changed +106 -10
Documentation/config.txt
+13
@@ -1500,6 +1500,19 @@ url.<base>.insteadOf::
1500 never-before-seen repository on the site. When more than one
1501 insteadOf strings match a given URL, the longest match is used.
1502
1503 +url.<base>.pushInsteadOf::
1504 + Any URL that starts with this value will not be pushed to;
1505 + instead, it will be rewritten to start with <base>, and the
1506 + resulting URL will be pushed to. In cases where some site serves
1507 + a large number of repositories, and serves them with multiple
1508 + access methods, some of which do not allow push, this feature
1509 + allows people to specify a pull-only URL and have git
1510 + automatically use an appropriate URL to push, even for a
1511 + never-before-seen repository on the site. When more than one
1512 + pushInsteadOf strings match a given URL, the longest match is
1513 + used. If a remote has an explicit pushurl, git will ignore this
1514 + setting for that remote.
1515 +
1516 user.email::
1517 Your email address to be recorded in any newly created commits.
1518 Can be overridden by the 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_EMAIL', and
Documentation/urls.txt
+18
@@ -67,3 +67,21 @@ For example, with this:
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
70 +If you want to rewrite URLs for push only, you can create a
71 +configuration section of the form:
72 +
73 +------------
74 + [url "<actual url base>"]
75 + pushInsteadOf = <other url base>
76 +------------
77 +
78 +For example, with this:
79 +
80 +------------
81 + [url "ssh://example.org/"]
82 + pushInsteadOf = git://example.org/
83 +------------
84 +
85 +a URL like "git://example.org/path/to/repo.git" will be rewritten to
86 +"ssh://example.org/path/to/repo.git" for pushes, but pulls will still
87 +use the original URL.
contrib/completion/git-completion.bash
+1 -1
@@ -1532,7 +1532,7 @@ _git_config ()
1532 url.*.*)
1533 local pfx="${cur%.*}."
1534 cur="${cur##*.}"
1535 - __gitcomp "insteadof" "$pfx" "$cur"
1535 + __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur"
1536 return
1537 ;;
1538 esac
remote.c
+27 -9
@@ -47,6 +47,7 @@ static const char *default_remote_name;
47 static int explicit_default_remote_name;
48
49 static struct rewrites rewrites;
50 +static struct rewrites rewrites_push;
51
52 #define BUF_SIZE (2048)
53 static char buffer[BUF_SIZE];
@@ -104,17 +105,25 @@ static void add_url(struct remote *remote, const char *url)
105 remote->url[remote->url_nr++] = url;
106 }
107
107 -static void add_url_alias(struct remote *remote, const char *url)
108 -{
109 - add_url(remote, alias_url(url, &rewrites));
110 -}
111 -
108 static void add_pushurl(struct remote *remote, const char *pushurl)
109 {
110 ALLOC_GROW(remote->pushurl, remote->pushurl_nr + 1, remote->pushurl_alloc);
111 remote->pushurl[remote->pushurl_nr++] = pushurl;
112 }
113
114 +static void add_pushurl_alias(struct remote *remote, const char *url)
115 +{
116 + const char *pushurl = alias_url(url, &rewrites_push);
117 + if (pushurl != url)
118 + add_pushurl(remote, pushurl);
119 +}
120 +
121 +static void add_url_alias(struct remote *remote, const char *url)
122 +{
123 + add_url(remote, alias_url(url, &rewrites));
124 + add_pushurl_alias(remote, url);
125 +}
126 +
127 static struct remote *make_remote(const char *name, int len)
128 {
129 struct remote *ret;
@@ -358,8 +367,13 @@ static int handle_config(const char *key, const char *value, void *cb)
367 subkey = strrchr(name, '.');
368 if (!subkey)
369 return 0;
361 - rewrite = make_rewrite(&rewrites, name, subkey - name);
370 if (!strcmp(subkey, ".insteadof")) {
371 + rewrite = make_rewrite(&rewrites, name, subkey - name);
372 + if (!value)
373 + return config_error_nonbool(key);
374 + add_instead_of(rewrite, xstrdup(value));
375 + } else if (!strcmp(subkey, ".pushinsteadof")) {
376 + rewrite = make_rewrite(&rewrites_push, name, subkey - name);
377 if (!value)
378 return config_error_nonbool(key);
379 add_instead_of(rewrite, xstrdup(value));
@@ -433,14 +447,18 @@ static void alias_all_urls(void)
447 {
448 int i, j;
449 for (i = 0; i < remotes_nr; i++) {
450 + int add_pushurl_aliases;
451 if (!remotes[i])
452 continue;
438 - for (j = 0; j < remotes[i]->url_nr; j++) {
439 - remotes[i]->url[j] = alias_url(remotes[i]->url[j], &rewrites);
440 - }
453 for (j = 0; j < remotes[i]->pushurl_nr; j++) {
454 remotes[i]->pushurl[j] = alias_url(remotes[i]->pushurl[j], &rewrites);
455 }
456 + add_pushurl_aliases = remotes[i]->pushurl_nr == 0;
457 + for (j = 0; j < remotes[i]->url_nr; j++) {
458 + if (add_pushurl_aliases)
459 + add_pushurl_alias(remotes[i], remotes[i]->url[j]);
460 + remotes[i]->url[j] = alias_url(remotes[i]->url[j], &rewrites);
461 + }
462 }
463 }
464
t/t5516-fetch-push.sh
+47
@@ -122,6 +122,23 @@ test_expect_success 'fetch with insteadOf' '
122 )
123 '
124
125 +test_expect_success 'fetch with pushInsteadOf (should not rewrite)' '
126 + mk_empty &&
127 + (
128 + TRASH=$(pwd)/ &&
129 + cd testrepo &&
130 + git config "url.trash/.pushInsteadOf" "$TRASH" &&
131 + git config remote.up.url "$TRASH." &&
132 + git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
133 + git fetch up &&
134 +
135 + r=$(git show-ref -s --verify refs/remotes/origin/master) &&
136 + test "z$r" = "z$the_commit" &&
137 +
138 + test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
139 + )
140 +'
141 +
142 test_expect_success 'push without wildcard' '
143 mk_empty &&
144
@@ -162,6 +179,36 @@ test_expect_success 'push with insteadOf' '
179 )
180 '
181
182 +test_expect_success 'push with pushInsteadOf' '
183 + mk_empty &&
184 + TRASH="$(pwd)/" &&
185 + git config "url.$TRASH.pushInsteadOf" trash/ &&
186 + git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
187 + (
188 + cd testrepo &&
189 + r=$(git show-ref -s --verify refs/remotes/origin/master) &&
190 + test "z$r" = "z$the_commit" &&
191 +
192 + test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
193 + )
194 +'
195 +
196 +test_expect_success 'push with pushInsteadOf and explicit pushurl (pushInsteadOf should not rewrite)' '
197 + mk_empty &&
198 + TRASH="$(pwd)/" &&
199 + git config "url.trash2/.pushInsteadOf" trash/ &&
200 + git config remote.r.url trash/wrong &&
201 + git config remote.r.pushurl "$TRASH/testrepo" &&
202 + git push r refs/heads/master:refs/remotes/origin/master &&
203 + (
204 + cd testrepo &&
205 + r=$(git show-ref -s --verify refs/remotes/origin/master) &&
206 + test "z$r" = "z$the_commit" &&
207 +
208 + test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
209 + )
210 +'
211 +
212 test_expect_success 'push with matching heads' '
213
214 mk_test heads/master &&