Skip to content

Commit 1c2eafb

Browse files
joshtriplettgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent d071d94 commit 1c2eafb

File tree

5 files changed

+106
-10
lines changed

5 files changed

+106
-10
lines changed

Documentation/config.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1500,6 +1500,19 @@ url.<base>.insteadOf::
15001500
never-before-seen repository on the site. When more than one
15011501
insteadOf strings match a given URL, the longest match is used.
15021502

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+
15031516
user.email::
15041517
Your email address to be recorded in any newly created commits.
15051518
Can be overridden by the 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_EMAIL', and

Documentation/urls.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,21 @@ For example, with this:
6767
a URL like "work:repo.git" or like "host.xz:/path/to/repo.git" will be
6868
rewritten in any context that takes a URL to be "git://git.host.xz/repo.git".
6969

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1532,7 +1532,7 @@ _git_config ()
15321532
url.*.*)
15331533
local pfx="${cur%.*}."
15341534
cur="${cur##*.}"
1535-
__gitcomp "insteadof" "$pfx" "$cur"
1535+
__gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur"
15361536
return
15371537
;;
15381538
esac

remote.c

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ static const char *default_remote_name;
4747
static int explicit_default_remote_name;
4848

4949
static struct rewrites rewrites;
50+
static struct rewrites rewrites_push;
5051

5152
#define BUF_SIZE (2048)
5253
static char buffer[BUF_SIZE];
@@ -104,17 +105,25 @@ static void add_url(struct remote *remote, const char *url)
104105
remote->url[remote->url_nr++] = url;
105106
}
106107

107-
static void add_url_alias(struct remote *remote, const char *url)
108-
{
109-
add_url(remote, alias_url(url, &rewrites));
110-
}
111-
112108
static void add_pushurl(struct remote *remote, const char *pushurl)
113109
{
114110
ALLOC_GROW(remote->pushurl, remote->pushurl_nr + 1, remote->pushurl_alloc);
115111
remote->pushurl[remote->pushurl_nr++] = pushurl;
116112
}
117113

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+
118127
static struct remote *make_remote(const char *name, int len)
119128
{
120129
struct remote *ret;
@@ -358,8 +367,13 @@ static int handle_config(const char *key, const char *value, void *cb)
358367
subkey = strrchr(name, '.');
359368
if (!subkey)
360369
return 0;
361-
rewrite = make_rewrite(&rewrites, name, subkey - name);
362370
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);
363377
if (!value)
364378
return config_error_nonbool(key);
365379
add_instead_of(rewrite, xstrdup(value));
@@ -433,14 +447,18 @@ static void alias_all_urls(void)
433447
{
434448
int i, j;
435449
for (i = 0; i < remotes_nr; i++) {
450+
int add_pushurl_aliases;
436451
if (!remotes[i])
437452
continue;
438-
for (j = 0; j < remotes[i]->url_nr; j++) {
439-
remotes[i]->url[j] = alias_url(remotes[i]->url[j], &rewrites);
440-
}
441453
for (j = 0; j < remotes[i]->pushurl_nr; j++) {
442454
remotes[i]->pushurl[j] = alias_url(remotes[i]->pushurl[j], &rewrites);
443455
}
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+
}
444462
}
445463
}
446464

t/t5516-fetch-push.sh

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,23 @@ test_expect_success 'fetch with insteadOf' '
122122
)
123123
'
124124

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+
125142
test_expect_success 'push without wildcard' '
126143
mk_empty &&
127144
@@ -162,6 +179,36 @@ test_expect_success 'push with insteadOf' '
162179
)
163180
'
164181

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+
165212
test_expect_success 'push with matching heads' '
166213
167214
mk_test heads/master &&

0 commit comments

Comments
 (0)