Skip to content

Commit 79cb645

Browse files
committed
Merge branch 'jt/pushinsteadof'
* jt/pushinsteadof: Add url.<base>.pushInsteadOf: URL rewriting for push only Wrap rewrite globals in a struct in preparation for adding another set
2 parents dc1b0c0 + 1c2eafb commit 79cb645

File tree

5 files changed

+131
-32
lines changed

5 files changed

+131
-32
lines changed

Documentation/config.txt

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

1518+
url.<base>.pushInsteadOf::
1519+
Any URL that starts with this value will not be pushed to;
1520+
instead, it will be rewritten to start with <base>, and the
1521+
resulting URL will be pushed to. In cases where some site serves
1522+
a large number of repositories, and serves them with multiple
1523+
access methods, some of which do not allow push, this feature
1524+
allows people to specify a pull-only URL and have git
1525+
automatically use an appropriate URL to push, even for a
1526+
never-before-seen repository on the site. When more than one
1527+
pushInsteadOf strings match a given URL, the longest match is
1528+
used. If a remote has an explicit pushurl, git will ignore this
1529+
setting for that remote.
1530+
15181531
user.email::
15191532
Your email address to be recorded in any newly created commits.
15201533
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: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ struct rewrite {
2828
int instead_of_nr;
2929
int instead_of_alloc;
3030
};
31+
struct rewrites {
32+
struct rewrite **rewrite;
33+
int rewrite_alloc;
34+
int rewrite_nr;
35+
};
3136

3237
static struct remote **remotes;
3338
static int remotes_alloc;
@@ -41,14 +46,13 @@ static struct branch *current_branch;
4146
static const char *default_remote_name;
4247
static int explicit_default_remote_name;
4348

44-
static struct rewrite **rewrite;
45-
static int rewrite_alloc;
46-
static int rewrite_nr;
49+
static struct rewrites rewrites;
50+
static struct rewrites rewrites_push;
4751

4852
#define BUF_SIZE (2048)
4953
static char buffer[BUF_SIZE];
5054

51-
static const char *alias_url(const char *url)
55+
static const char *alias_url(const char *url, struct rewrites *r)
5256
{
5357
int i, j;
5458
char *ret;
@@ -57,25 +61,25 @@ static const char *alias_url(const char *url)
5761

5862
longest = NULL;
5963
longest_i = -1;
60-
for (i = 0; i < rewrite_nr; i++) {
61-
if (!rewrite[i])
64+
for (i = 0; i < r->rewrite_nr; i++) {
65+
if (!r->rewrite[i])
6266
continue;
63-
for (j = 0; j < rewrite[i]->instead_of_nr; j++) {
64-
if (!prefixcmp(url, rewrite[i]->instead_of[j].s) &&
67+
for (j = 0; j < r->rewrite[i]->instead_of_nr; j++) {
68+
if (!prefixcmp(url, r->rewrite[i]->instead_of[j].s) &&
6569
(!longest ||
66-
longest->len < rewrite[i]->instead_of[j].len)) {
67-
longest = &(rewrite[i]->instead_of[j]);
70+
longest->len < r->rewrite[i]->instead_of[j].len)) {
71+
longest = &(r->rewrite[i]->instead_of[j]);
6872
longest_i = i;
6973
}
7074
}
7175
}
7276
if (!longest)
7377
return url;
7478

75-
ret = xmalloc(rewrite[longest_i]->baselen +
79+
ret = xmalloc(r->rewrite[longest_i]->baselen +
7680
(strlen(url) - longest->len) + 1);
77-
strcpy(ret, rewrite[longest_i]->base);
78-
strcpy(ret + rewrite[longest_i]->baselen, url + longest->len);
81+
strcpy(ret, r->rewrite[longest_i]->base);
82+
strcpy(ret + r->rewrite[longest_i]->baselen, url + longest->len);
7983
return ret;
8084
}
8185

@@ -101,17 +105,25 @@ static void add_url(struct remote *remote, const char *url)
101105
remote->url[remote->url_nr++] = url;
102106
}
103107

104-
static void add_url_alias(struct remote *remote, const char *url)
105-
{
106-
add_url(remote, alias_url(url));
107-
}
108-
109108
static void add_pushurl(struct remote *remote, const char *pushurl)
110109
{
111110
ALLOC_GROW(remote->pushurl, remote->pushurl_nr + 1, remote->pushurl_alloc);
112111
remote->pushurl[remote->pushurl_nr++] = pushurl;
113112
}
114113

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+
115127
static struct remote *make_remote(const char *name, int len)
116128
{
117129
struct remote *ret;
@@ -169,22 +181,22 @@ static struct branch *make_branch(const char *name, int len)
169181
return ret;
170182
}
171183

172-
static struct rewrite *make_rewrite(const char *base, int len)
184+
static struct rewrite *make_rewrite(struct rewrites *r, const char *base, int len)
173185
{
174186
struct rewrite *ret;
175187
int i;
176188

177-
for (i = 0; i < rewrite_nr; i++) {
189+
for (i = 0; i < r->rewrite_nr; i++) {
178190
if (len
179-
? (len == rewrite[i]->baselen &&
180-
!strncmp(base, rewrite[i]->base, len))
181-
: !strcmp(base, rewrite[i]->base))
182-
return rewrite[i];
191+
? (len == r->rewrite[i]->baselen &&
192+
!strncmp(base, r->rewrite[i]->base, len))
193+
: !strcmp(base, r->rewrite[i]->base))
194+
return r->rewrite[i];
183195
}
184196

185-
ALLOC_GROW(rewrite, rewrite_nr + 1, rewrite_alloc);
197+
ALLOC_GROW(r->rewrite, r->rewrite_nr + 1, r->rewrite_alloc);
186198
ret = xcalloc(1, sizeof(struct rewrite));
187-
rewrite[rewrite_nr++] = ret;
199+
r->rewrite[r->rewrite_nr++] = ret;
188200
if (len) {
189201
ret->base = xstrndup(base, len);
190202
ret->baselen = len;
@@ -355,8 +367,13 @@ static int handle_config(const char *key, const char *value, void *cb)
355367
subkey = strrchr(name, '.');
356368
if (!subkey)
357369
return 0;
358-
rewrite = make_rewrite(name, subkey - name);
359370
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);
360377
if (!value)
361378
return config_error_nonbool(key);
362379
add_instead_of(rewrite, xstrdup(value));
@@ -430,13 +447,17 @@ static void alias_all_urls(void)
430447
{
431448
int i, j;
432449
for (i = 0; i < remotes_nr; i++) {
450+
int add_pushurl_aliases;
433451
if (!remotes[i])
434452
continue;
435-
for (j = 0; j < remotes[i]->url_nr; j++) {
436-
remotes[i]->url[j] = alias_url(remotes[i]->url[j]);
437-
}
438453
for (j = 0; j < remotes[i]->pushurl_nr; j++) {
439-
remotes[i]->pushurl[j] = alias_url(remotes[i]->pushurl[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);
440461
}
441462
}
442463
}

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)