push: use remote.$name.push as a refmap

Since f2690487 (fetch: opportunistically update tracking refs,
2013-05-11), we stopped taking a non-storing refspec given on the
command line of "git fetch" literally, and instead started mapping
it via remote.$name.fetch refspecs.  This allows

    $ git fetch origin master

from the 'origin' repository, which is configured with

    [remote "origin"]
        fetch = +refs/heads/*:refs/remotes/origin/*

to update refs/remotes/origin/master with the result, as if the
command line were

    $ git fetch origin +master:refs/remotes/origin/master

to reduce surprises and improve usability.  Before that change, a
refspec on the command line without a colon was only to fetch the
history and leave the result in FETCH_HEAD, without updating the
remote-tracking branches.

When you are simulating a fetch from you by your mothership with a
push by you into your mothership, instead of having:

    [remote "satellite"]
        fetch = +refs/heads/*:refs/remotes/satellite/*

on the mothership repository and running:

    mothership$ git fetch satellite

you would have:

    [remote "mothership"]
        push = +refs/heads/*:refs/remotes/satellite/*

on your satellite machine, and run:

    satellite$ git push mothership

Because we so far did not make the corresponding change to the push
side, this command:

    satellite$ git push mothership master

does _not_ allow you on the satellite to only push 'master' out but
still to the usual destination (i.e. refs/remotes/satellite/master).

Implement the logic to map an unqualified refspec given on the
command line via the remote.$name.push refspec.  This will bring a
bit more symmetry between "fetch" and "push".

Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Junio C Hamano
2013-12-03 15:41:15 -08:00
parent 50d829c11a
commit ca02465b41
5 changed files with 96 additions and 8 deletions

View File

@ -35,9 +35,38 @@ static void add_refspec(const char *ref)
refspec[refspec_nr-1] = ref;
}
static void set_refspecs(const char **refs, int nr)
static const char *map_refspec(const char *ref,
struct remote *remote, struct ref *local_refs)
{
struct ref *matched = NULL;
/* Does "ref" uniquely name our ref? */
if (count_refspec_match(ref, local_refs, &matched) != 1)
return ref;
if (remote->push) {
struct refspec query;
memset(&query, 0, sizeof(struct refspec));
query.src = matched->name;
if (!query_refspecs(remote->push, remote->push_refspec_nr, &query) &&
query.dst) {
struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, "%s%s:%s",
query.force ? "+" : "",
query.src, query.dst);
return strbuf_detach(&buf, NULL);
}
}
return ref;
}
static void set_refspecs(const char **refs, int nr, const char *repo)
{
struct remote *remote = NULL;
struct ref *local_refs = NULL;
int i;
for (i = 0; i < nr; i++) {
const char *ref = refs[i];
if (!strcmp("tag", ref)) {
@ -56,6 +85,13 @@ static void set_refspecs(const char **refs, int nr)
die(_("--delete only accepts plain target ref names"));
strbuf_addf(&delref, ":%s", ref);
ref = strbuf_detach(&delref, NULL);
} else if (!strchr(ref, ':')) {
if (!remote) {
/* lazily grab remote and local_refs */
remote = remote_get(repo);
local_refs = get_local_heads();
}
ref = map_refspec(ref, remote, local_refs);
}
add_refspec(ref);
}
@ -487,7 +523,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
if (argc > 0) {
repo = argv[0];
set_refspecs(argv + 1, argc - 1);
set_refspecs(argv + 1, argc - 1, repo);
}
rc = do_push(repo, flags);