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>
This commit is contained in:
parent
d071d94296
commit
1c2eafb89b
@ -1500,6 +1500,19 @@ url.<base>.insteadOf::
|
||||
never-before-seen repository on the site. When more than one
|
||||
insteadOf strings match a given URL, the longest match is used.
|
||||
|
||||
url.<base>.pushInsteadOf::
|
||||
Any URL that starts with this value will not be pushed to;
|
||||
instead, it will be rewritten to start with <base>, and the
|
||||
resulting URL will be pushed to. In cases where some site serves
|
||||
a large number of repositories, and serves them with multiple
|
||||
access methods, some of which do not allow push, this feature
|
||||
allows people to specify a pull-only URL and have git
|
||||
automatically use an appropriate URL to push, even for a
|
||||
never-before-seen repository on the site. When more than one
|
||||
pushInsteadOf strings match a given URL, the longest match is
|
||||
used. If a remote has an explicit pushurl, git will ignore this
|
||||
setting for that remote.
|
||||
|
||||
user.email::
|
||||
Your email address to be recorded in any newly created commits.
|
||||
Can be overridden by the 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_EMAIL', and
|
||||
|
@ -67,3 +67,21 @@ For example, with this:
|
||||
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".
|
||||
|
||||
If you want to rewrite URLs for push only, you can create a
|
||||
configuration section of the form:
|
||||
|
||||
------------
|
||||
[url "<actual url base>"]
|
||||
pushInsteadOf = <other url base>
|
||||
------------
|
||||
|
||||
For example, with this:
|
||||
|
||||
------------
|
||||
[url "ssh://example.org/"]
|
||||
pushInsteadOf = git://example.org/
|
||||
------------
|
||||
|
||||
a URL like "git://example.org/path/to/repo.git" will be rewritten to
|
||||
"ssh://example.org/path/to/repo.git" for pushes, but pulls will still
|
||||
use the original URL.
|
||||
|
@ -1532,7 +1532,7 @@ _git_config ()
|
||||
url.*.*)
|
||||
local pfx="${cur%.*}."
|
||||
cur="${cur##*.}"
|
||||
__gitcomp "insteadof" "$pfx" "$cur"
|
||||
__gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur"
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
36
remote.c
36
remote.c
@ -47,6 +47,7 @@ static const char *default_remote_name;
|
||||
static int explicit_default_remote_name;
|
||||
|
||||
static struct rewrites rewrites;
|
||||
static struct rewrites rewrites_push;
|
||||
|
||||
#define BUF_SIZE (2048)
|
||||
static char buffer[BUF_SIZE];
|
||||
@ -104,17 +105,25 @@ static void add_url(struct remote *remote, const char *url)
|
||||
remote->url[remote->url_nr++] = url;
|
||||
}
|
||||
|
||||
static void add_url_alias(struct remote *remote, const char *url)
|
||||
{
|
||||
add_url(remote, alias_url(url, &rewrites));
|
||||
}
|
||||
|
||||
static void add_pushurl(struct remote *remote, const char *pushurl)
|
||||
{
|
||||
ALLOC_GROW(remote->pushurl, remote->pushurl_nr + 1, remote->pushurl_alloc);
|
||||
remote->pushurl[remote->pushurl_nr++] = pushurl;
|
||||
}
|
||||
|
||||
static void add_pushurl_alias(struct remote *remote, const char *url)
|
||||
{
|
||||
const char *pushurl = alias_url(url, &rewrites_push);
|
||||
if (pushurl != url)
|
||||
add_pushurl(remote, pushurl);
|
||||
}
|
||||
|
||||
static void add_url_alias(struct remote *remote, const char *url)
|
||||
{
|
||||
add_url(remote, alias_url(url, &rewrites));
|
||||
add_pushurl_alias(remote, url);
|
||||
}
|
||||
|
||||
static struct remote *make_remote(const char *name, int len)
|
||||
{
|
||||
struct remote *ret;
|
||||
@ -358,8 +367,13 @@ static int handle_config(const char *key, const char *value, void *cb)
|
||||
subkey = strrchr(name, '.');
|
||||
if (!subkey)
|
||||
return 0;
|
||||
rewrite = make_rewrite(&rewrites, name, subkey - name);
|
||||
if (!strcmp(subkey, ".insteadof")) {
|
||||
rewrite = make_rewrite(&rewrites, name, subkey - name);
|
||||
if (!value)
|
||||
return config_error_nonbool(key);
|
||||
add_instead_of(rewrite, xstrdup(value));
|
||||
} else if (!strcmp(subkey, ".pushinsteadof")) {
|
||||
rewrite = make_rewrite(&rewrites_push, name, subkey - name);
|
||||
if (!value)
|
||||
return config_error_nonbool(key);
|
||||
add_instead_of(rewrite, xstrdup(value));
|
||||
@ -433,14 +447,18 @@ static void alias_all_urls(void)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 0; i < remotes_nr; i++) {
|
||||
int add_pushurl_aliases;
|
||||
if (!remotes[i])
|
||||
continue;
|
||||
for (j = 0; j < remotes[i]->url_nr; j++) {
|
||||
remotes[i]->url[j] = alias_url(remotes[i]->url[j], &rewrites);
|
||||
}
|
||||
for (j = 0; j < remotes[i]->pushurl_nr; j++) {
|
||||
remotes[i]->pushurl[j] = alias_url(remotes[i]->pushurl[j], &rewrites);
|
||||
}
|
||||
add_pushurl_aliases = remotes[i]->pushurl_nr == 0;
|
||||
for (j = 0; j < remotes[i]->url_nr; j++) {
|
||||
if (add_pushurl_aliases)
|
||||
add_pushurl_alias(remotes[i], remotes[i]->url[j]);
|
||||
remotes[i]->url[j] = alias_url(remotes[i]->url[j], &rewrites);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,6 +122,23 @@ test_expect_success 'fetch with insteadOf' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'fetch with pushInsteadOf (should not rewrite)' '
|
||||
mk_empty &&
|
||||
(
|
||||
TRASH=$(pwd)/ &&
|
||||
cd testrepo &&
|
||||
git config "url.trash/.pushInsteadOf" "$TRASH" &&
|
||||
git config remote.up.url "$TRASH." &&
|
||||
git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
|
||||
git fetch up &&
|
||||
|
||||
r=$(git show-ref -s --verify refs/remotes/origin/master) &&
|
||||
test "z$r" = "z$the_commit" &&
|
||||
|
||||
test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'push without wildcard' '
|
||||
mk_empty &&
|
||||
|
||||
@ -162,6 +179,36 @@ test_expect_success 'push with insteadOf' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'push with pushInsteadOf' '
|
||||
mk_empty &&
|
||||
TRASH="$(pwd)/" &&
|
||||
git config "url.$TRASH.pushInsteadOf" trash/ &&
|
||||
git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
|
||||
(
|
||||
cd testrepo &&
|
||||
r=$(git show-ref -s --verify refs/remotes/origin/master) &&
|
||||
test "z$r" = "z$the_commit" &&
|
||||
|
||||
test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'push with pushInsteadOf and explicit pushurl (pushInsteadOf should not rewrite)' '
|
||||
mk_empty &&
|
||||
TRASH="$(pwd)/" &&
|
||||
git config "url.trash2/.pushInsteadOf" trash/ &&
|
||||
git config remote.r.url trash/wrong &&
|
||||
git config remote.r.pushurl "$TRASH/testrepo" &&
|
||||
git push r refs/heads/master:refs/remotes/origin/master &&
|
||||
(
|
||||
cd testrepo &&
|
||||
r=$(git show-ref -s --verify refs/remotes/origin/master) &&
|
||||
test "z$r" = "z$the_commit" &&
|
||||
|
||||
test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'push with matching heads' '
|
||||
|
||||
mk_test heads/master &&
|
||||
|
Loading…
Reference in New Issue
Block a user