* [PATCH 0/3] Teaching "git push" to map pushed refs @ 2013-12-04 0:39 Junio C Hamano 2013-12-04 0:39 ` [PATCH 1/3] builtin/push.c: use strbuf instead of manual allocation Junio C Hamano ` (3 more replies) 0 siblings, 4 replies; 8+ messages in thread From: Junio C Hamano @ 2013-12-04 0:39 UTC (permalink / raw) To: git Earlier, Peff taught "git fetch origin master" to update a remote-tracking branch by internally mapping the colon-less refspec 'master' to '+refs/heads/master:refs/remotes/origin/master'. Both git fetch origin git fetch origin master would update the same refs/remotes/origin/master, which avoids surprises. However, we did not have a similar refspec mapping on the push side. This three-patch series does just that, and would help triangular workflow by making these two: git checkout master && git push origin git push origin master update the same ref at the 'origin'. Junio C Hamano (3): builtin/push.c: use strbuf instead of manual allocation push: use remote.$name.push as a refmap push: also use "upstream" mapping when pushing a single ref Documentation/git-push.txt | 9 ++++-- builtin/push.c | 79 ++++++++++++++++++++++++++++++++-------------- remote.c | 8 ++--- remote.h | 2 ++ t/t5516-fetch-push.sh | 75 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 144 insertions(+), 29 deletions(-) -- 1.8.5.1-400-gbc1da41 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/3] builtin/push.c: use strbuf instead of manual allocation 2013-12-04 0:39 [PATCH 0/3] Teaching "git push" to map pushed refs Junio C Hamano @ 2013-12-04 0:39 ` Junio C Hamano 2013-12-04 0:39 ` [PATCH 2/3] push: use remote.$name.push as a refmap Junio C Hamano ` (2 subsequent siblings) 3 siblings, 0 replies; 8+ messages in thread From: Junio C Hamano @ 2013-12-04 0:39 UTC (permalink / raw) To: git The command line arguments given to "git push" are massaged into a list of refspecs in set_refspecs() function. This was implemented using xmalloc, strcpy and friends, but it is much easier to read if done using strbuf. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- builtin/push.c | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/builtin/push.c b/builtin/push.c index 7b1b66c..76e4400 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -41,29 +41,22 @@ static void set_refspecs(const char **refs, int nr) for (i = 0; i < nr; i++) { const char *ref = refs[i]; if (!strcmp("tag", ref)) { - char *tag; - int len; + struct strbuf tagref = STRBUF_INIT; if (nr <= ++i) die(_("tag shorthand without <tag>")); - len = strlen(refs[i]) + 11; - if (deleterefs) { - tag = xmalloc(len+1); - strcpy(tag, ":refs/tags/"); - } else { - tag = xmalloc(len); - strcpy(tag, "refs/tags/"); - } - strcat(tag, refs[i]); - ref = tag; - } else if (deleterefs && !strchr(ref, ':')) { - char *delref; - int len = strlen(ref)+1; - delref = xmalloc(len+1); - strcpy(delref, ":"); - strcat(delref, ref); - ref = delref; - } else if (deleterefs) - die(_("--delete only accepts plain target ref names")); + ref = refs[i]; + if (deleterefs) + strbuf_addf(&tagref, ":refs/tags/%s", ref); + else + strbuf_addf(&tagref, "refs/tags/%s", ref); + ref = strbuf_detach(&tagref, NULL); + } else if (deleterefs) { + struct strbuf delref = STRBUF_INIT; + if (strchr(ref, ':')) + die(_("--delete only accepts plain target ref names")); + strbuf_addf(&delref, ":%s", ref); + ref = strbuf_detach(&delref, NULL); + } add_refspec(ref); } } -- 1.8.5.1-400-gbc1da41 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/3] push: use remote.$name.push as a refmap 2013-12-04 0:39 [PATCH 0/3] Teaching "git push" to map pushed refs Junio C Hamano 2013-12-04 0:39 ` [PATCH 1/3] builtin/push.c: use strbuf instead of manual allocation Junio C Hamano @ 2013-12-04 0:39 ` Junio C Hamano 2013-12-04 0:39 ` [PATCH 3/3] push: also use "upstream" mapping when pushing a single ref Junio C Hamano 2013-12-05 1:27 ` [PATCH v2 0/3] Teaching "git push" to map pushed refs Junio C Hamano 3 siblings, 0 replies; 8+ messages in thread From: Junio C Hamano @ 2013-12-04 0:39 UTC (permalink / raw) To: git 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. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- Documentation/git-push.txt | 9 +++++++-- builtin/push.c | 35 ++++++++++++++++++++++++++++++++--- remote.c | 8 ++++---- remote.h | 2 ++ t/t5516-fetch-push.sh | 45 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 9 deletions(-) diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 9eec740..2b7f4f9 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -56,8 +56,13 @@ it can be any arbitrary "SHA-1 expression", such as `master~4` or + The <dst> tells which ref on the remote side is updated with this push. Arbitrary expressions cannot be used here, an actual ref must -be named. If `:`<dst> is omitted, the same ref as <src> will be -updated. +be named. +If `git push [<repository>]` without any `<refspec>` argument is set to +update some ref at the destination with `<src>` with +`remote.<repository>.push` configuration variable, `:<dst>` part can +be omitted---such a push will update a ref that `<src>` normally updates +without any `<refspec>` on the command line. Otherwise, missing +`:<dst>` means to update the same ref as the `<src>`. + The object referenced by <src> is used to update the <dst> reference on the remote side. By default this is only allowed if <dst> is not diff --git a/builtin/push.c b/builtin/push.c index 76e4400..bdc1fc1 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -35,7 +35,34 @@ 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, + struct remote *remote, struct ref *local_refs) { int i; for (i = 0; i < nr; i++) { @@ -56,7 +83,8 @@ 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, ':')) + ref = map_refspec(ref, remote, local_refs); add_refspec(ref); } } @@ -487,7 +515,8 @@ 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, + remote_get(repo), get_local_heads()); } rc = do_push(repo, flags); diff --git a/remote.c b/remote.c index 9f1a8aa..6ffa149 100644 --- a/remote.c +++ b/remote.c @@ -821,7 +821,7 @@ static int match_name_with_pattern(const char *key, const char *name, return ret; } -static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query) +int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query) { int i; int find_src = !query->src; @@ -955,9 +955,9 @@ void sort_ref_list(struct ref **l, int (*cmp)(const void *, const void *)) *l = llist_mergesort(*l, ref_list_get_next, ref_list_set_next, cmp); } -static int count_refspec_match(const char *pattern, - struct ref *refs, - struct ref **matched_ref) +int count_refspec_match(const char *pattern, + struct ref *refs, + struct ref **matched_ref) { int patlen = strlen(pattern); struct ref *matched_weak = NULL; diff --git a/remote.h b/remote.h index 131130a..f7d43b4 100644 --- a/remote.h +++ b/remote.h @@ -128,6 +128,7 @@ struct ref *alloc_ref(const char *name); struct ref *copy_ref(const struct ref *ref); struct ref *copy_ref_list(const struct ref *ref); void sort_ref_list(struct ref **, int (*cmp)(const void *, const void *)); +extern int count_refspec_match(const char *, struct ref *refs, struct ref **matched_ref); int ref_compare_name(const void *, const void *); int check_ref_type(const struct ref *ref, int flags); @@ -158,6 +159,7 @@ struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec); void free_refspec(int nr_refspec, struct refspec *refspec); +extern int query_refspecs(struct refspec *specs, int nr, struct refspec *query); char *apply_refspecs(struct refspec *refspecs, int nr_refspec, const char *name); diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 99c32d7..6d7f102 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -1126,6 +1126,51 @@ test_expect_success 'fetch follows tags by default' ' test_cmp expect actual ' +test_expect_success 'pushing a specific ref applies remote.$name.push as refmap' ' + mk_test testrepo heads/master && + rm -fr src dst && + git init src && + git init --bare dst && + ( + cd src && + git pull ../testrepo master && + git branch next && + git config remote.dst.url ../dst && + git config remote.dst.push "+refs/heads/*:refs/remotes/src/*" && + git push dst master && + git show-ref refs/heads/master | + sed -e "s|refs/heads/|refs/remotes/src/|" >../dst/expect + ) && + ( + cd dst && + test_must_fail git show-ref refs/heads/next && + test_must_fail git show-ref refs/heads/master && + git show-ref refs/remotes/src/master >actual + ) && + test_cmp dst/expect dst/actual +' + +test_expect_success 'with no remote.$name.push, it is not used as refmap' ' + mk_test testrepo heads/master && + rm -fr src dst && + git init src && + git init --bare dst && + ( + cd src && + git pull ../testrepo master && + git branch next && + git config remote.dst.url ../dst && + git push dst master && + git show-ref refs/heads/master >../dst/expect + ) && + ( + cd dst && + test_must_fail git show-ref refs/heads/next && + git show-ref refs/heads/master >actual + ) && + test_cmp dst/expect dst/actual +' + test_expect_success 'push does not follow tags by default' ' mk_test testrepo heads/master && rm -fr src dst && -- 1.8.5.1-400-gbc1da41 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 3/3] push: also use "upstream" mapping when pushing a single ref 2013-12-04 0:39 [PATCH 0/3] Teaching "git push" to map pushed refs Junio C Hamano 2013-12-04 0:39 ` [PATCH 1/3] builtin/push.c: use strbuf instead of manual allocation Junio C Hamano 2013-12-04 0:39 ` [PATCH 2/3] push: use remote.$name.push as a refmap Junio C Hamano @ 2013-12-04 0:39 ` Junio C Hamano 2013-12-05 1:27 ` [PATCH v2 0/3] Teaching "git push" to map pushed refs Junio C Hamano 3 siblings, 0 replies; 8+ messages in thread From: Junio C Hamano @ 2013-12-04 0:39 UTC (permalink / raw) To: git When the user is using the 'upstream' mode, these commands: $ git push $ git push origin would find the 'upstream' branch for the current branch, and then push the current branch to update it. However, pushing a single branch explicitly, i.e. $ git push origin $(git symbolic-ref --short HEAD) would not go through the same ref mapping process, and ends up updating the branch at 'origin' of the same name, which may not necessarily be the upstream of the branch being pushed. In the spirit similar to the previous one, map a colon-less refspec using the upstream mapping logic. Note that this is deliberately done after the logic to map refspec via the remote.$name.push configuration did not find any. This is because most of those that employ a simple 'upstream' mode do not have the push refspec, and checking with remote.$name.push refspec first will allow it to be used as a mechanism to override the usual 'upstream' push mapping (which is primarily useful for centralized workflow) by those who want to use a triangular workflow. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- builtin/push.c | 11 +++++++++++ t/t5516-fetch-push.sh | 30 ++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/builtin/push.c b/builtin/push.c index bdc1fc1..71c081e 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -58,6 +58,17 @@ static const char *map_refspec(const char *ref, } } + if (push_default == PUSH_DEFAULT_UPSTREAM && + !prefixcmp(matched->name, "refs/heads/")) { + struct branch *branch = branch_get(matched->name + 11); + if (branch->merge_nr == 1 && branch->merge[0]->src) { + struct strbuf buf = STRBUF_INIT; + strbuf_addf(&buf, "%s:%s", + ref, branch->merge[0]->src); + return strbuf_detach(&buf, NULL); + } + } + return ref; } diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 6d7f102..926e7f6 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -1160,6 +1160,7 @@ test_expect_success 'with no remote.$name.push, it is not used as refmap' ' git pull ../testrepo master && git branch next && git config remote.dst.url ../dst && + git config push.default matching && git push dst master && git show-ref refs/heads/master >../dst/expect ) && @@ -1171,6 +1172,35 @@ test_expect_success 'with no remote.$name.push, it is not used as refmap' ' test_cmp dst/expect dst/actual ' +test_expect_success 'with no remote.$name.push, upstream mapping is used' ' + mk_test testrepo heads/master && + rm -fr src dst && + git init src && + git init --bare dst && + ( + cd src && + git pull ../testrepo master && + git branch next && + git config remote.dst.url ../dst && + git config remote.dst.fetch "+refs/heads/*:refs/remotes/dst/*" && + git config push.default upstream && + + git config branch.master.merge refs/heads/trunk && + git config branch.master.remote dst && + + git push dst master && + git show-ref refs/heads/master | + sed -e "s|refs/heads/master|refs/heads/trunk|" >../dst/expect + ) && + ( + cd dst && + test_must_fail git show-ref refs/heads/master && + test_must_fail git show-ref refs/heads/next && + git show-ref refs/heads/trunk >actual + ) && + test_cmp dst/expect dst/actual +' + test_expect_success 'push does not follow tags by default' ' mk_test testrepo heads/master && rm -fr src dst && -- 1.8.5.1-400-gbc1da41 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 0/3] Teaching "git push" to map pushed refs 2013-12-04 0:39 [PATCH 0/3] Teaching "git push" to map pushed refs Junio C Hamano ` (2 preceding siblings ...) 2013-12-04 0:39 ` [PATCH 3/3] push: also use "upstream" mapping when pushing a single ref Junio C Hamano @ 2013-12-05 1:27 ` Junio C Hamano 2013-12-05 1:27 ` [PATCH v2 1/3] builtin/push.c: use strbuf instead of manual allocation Junio C Hamano ` (2 more replies) 3 siblings, 3 replies; 8+ messages in thread From: Junio C Hamano @ 2013-12-05 1:27 UTC (permalink / raw) To: git The 'master' refspec in "git fetch origin master" used to mean "We grab their 'master' branch, but we do not store it in any of our remote-tracking branches." but in modern Git, the remote-tracking branch that would receive updates from 'master' with a corresponding "git fetch origin" (without any specific refs on the command line) is updated. Updating the same refs/remotes/origin/master with git fetch origin git fetch origin master avoids surprises. However, we did not have a similar refspec mapping on the push side. This three-patch series does just that. It would help triangular workflow by making these two: git checkout master && git push origin git push origin master update the same ref at the 'origin'. It also would help those who need to emulate a fetch run on mothership from satellite with a push run on satellite into mothership (due to e.g. network connectivity issues). The second round avoids instantiating the remote and the list of local heads until the very last minute when they are actually needed (the change is in the second patch). Junio C Hamano (3): builtin/push.c: use strbuf instead of manual allocation push: use remote.$name.push as a refmap push: also use "upstream" mapping when pushing a single ref Documentation/git-push.txt | 9 +++-- builtin/push.c | 84 ++++++++++++++++++++++++++++++++++------------ remote.c | 8 ++--- remote.h | 2 ++ t/t5516-fetch-push.sh | 75 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 150 insertions(+), 28 deletions(-) -- 1.8.5.1-402-gdd8f092 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2 1/3] builtin/push.c: use strbuf instead of manual allocation 2013-12-05 1:27 ` [PATCH v2 0/3] Teaching "git push" to map pushed refs Junio C Hamano @ 2013-12-05 1:27 ` Junio C Hamano 2013-12-05 1:27 ` [PATCH v2 2/3] push: use remote.$name.push as a refmap Junio C Hamano 2013-12-05 1:27 ` [PATCH v2 3/3] push: also use "upstream" mapping when pushing a single ref Junio C Hamano 2 siblings, 0 replies; 8+ messages in thread From: Junio C Hamano @ 2013-12-05 1:27 UTC (permalink / raw) To: git The command line arguments given to "git push" are massaged into a list of refspecs in set_refspecs() function. This was implemented using xmalloc, strcpy and friends, but it is much easier to read if done using strbuf. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- builtin/push.c | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/builtin/push.c b/builtin/push.c index 7b1b66c..76e4400 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -41,29 +41,22 @@ static void set_refspecs(const char **refs, int nr) for (i = 0; i < nr; i++) { const char *ref = refs[i]; if (!strcmp("tag", ref)) { - char *tag; - int len; + struct strbuf tagref = STRBUF_INIT; if (nr <= ++i) die(_("tag shorthand without <tag>")); - len = strlen(refs[i]) + 11; - if (deleterefs) { - tag = xmalloc(len+1); - strcpy(tag, ":refs/tags/"); - } else { - tag = xmalloc(len); - strcpy(tag, "refs/tags/"); - } - strcat(tag, refs[i]); - ref = tag; - } else if (deleterefs && !strchr(ref, ':')) { - char *delref; - int len = strlen(ref)+1; - delref = xmalloc(len+1); - strcpy(delref, ":"); - strcat(delref, ref); - ref = delref; - } else if (deleterefs) - die(_("--delete only accepts plain target ref names")); + ref = refs[i]; + if (deleterefs) + strbuf_addf(&tagref, ":refs/tags/%s", ref); + else + strbuf_addf(&tagref, "refs/tags/%s", ref); + ref = strbuf_detach(&tagref, NULL); + } else if (deleterefs) { + struct strbuf delref = STRBUF_INIT; + if (strchr(ref, ':')) + die(_("--delete only accepts plain target ref names")); + strbuf_addf(&delref, ":%s", ref); + ref = strbuf_detach(&delref, NULL); + } add_refspec(ref); } } -- 1.8.5.1-402-gdd8f092 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 2/3] push: use remote.$name.push as a refmap 2013-12-05 1:27 ` [PATCH v2 0/3] Teaching "git push" to map pushed refs Junio C Hamano 2013-12-05 1:27 ` [PATCH v2 1/3] builtin/push.c: use strbuf instead of manual allocation Junio C Hamano @ 2013-12-05 1:27 ` Junio C Hamano 2013-12-05 1:27 ` [PATCH v2 3/3] push: also use "upstream" mapping when pushing a single ref Junio C Hamano 2 siblings, 0 replies; 8+ messages in thread From: Junio C Hamano @ 2013-12-05 1:27 UTC (permalink / raw) To: git 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> --- Documentation/git-push.txt | 9 +++++++-- builtin/push.c | 40 ++++++++++++++++++++++++++++++++++++++-- remote.c | 8 ++++---- remote.h | 2 ++ t/t5516-fetch-push.sh | 45 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 8 deletions(-) diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 9eec740..2b7f4f9 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -56,8 +56,13 @@ it can be any arbitrary "SHA-1 expression", such as `master~4` or + The <dst> tells which ref on the remote side is updated with this push. Arbitrary expressions cannot be used here, an actual ref must -be named. If `:`<dst> is omitted, the same ref as <src> will be -updated. +be named. +If `git push [<repository>]` without any `<refspec>` argument is set to +update some ref at the destination with `<src>` with +`remote.<repository>.push` configuration variable, `:<dst>` part can +be omitted---such a push will update a ref that `<src>` normally updates +without any `<refspec>` on the command line. Otherwise, missing +`:<dst>` means to update the same ref as the `<src>`. + The object referenced by <src> is used to update the <dst> reference on the remote side. By default this is only allowed if <dst> is not diff --git a/builtin/push.c b/builtin/push.c index 76e4400..857f76d 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -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); diff --git a/remote.c b/remote.c index 9f1a8aa..6ffa149 100644 --- a/remote.c +++ b/remote.c @@ -821,7 +821,7 @@ static int match_name_with_pattern(const char *key, const char *name, return ret; } -static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query) +int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query) { int i; int find_src = !query->src; @@ -955,9 +955,9 @@ void sort_ref_list(struct ref **l, int (*cmp)(const void *, const void *)) *l = llist_mergesort(*l, ref_list_get_next, ref_list_set_next, cmp); } -static int count_refspec_match(const char *pattern, - struct ref *refs, - struct ref **matched_ref) +int count_refspec_match(const char *pattern, + struct ref *refs, + struct ref **matched_ref) { int patlen = strlen(pattern); struct ref *matched_weak = NULL; diff --git a/remote.h b/remote.h index 131130a..f7d43b4 100644 --- a/remote.h +++ b/remote.h @@ -128,6 +128,7 @@ struct ref *alloc_ref(const char *name); struct ref *copy_ref(const struct ref *ref); struct ref *copy_ref_list(const struct ref *ref); void sort_ref_list(struct ref **, int (*cmp)(const void *, const void *)); +extern int count_refspec_match(const char *, struct ref *refs, struct ref **matched_ref); int ref_compare_name(const void *, const void *); int check_ref_type(const struct ref *ref, int flags); @@ -158,6 +159,7 @@ struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec); void free_refspec(int nr_refspec, struct refspec *refspec); +extern int query_refspecs(struct refspec *specs, int nr, struct refspec *query); char *apply_refspecs(struct refspec *refspecs, int nr_refspec, const char *name); diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 99c32d7..6d7f102 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -1126,6 +1126,51 @@ test_expect_success 'fetch follows tags by default' ' test_cmp expect actual ' +test_expect_success 'pushing a specific ref applies remote.$name.push as refmap' ' + mk_test testrepo heads/master && + rm -fr src dst && + git init src && + git init --bare dst && + ( + cd src && + git pull ../testrepo master && + git branch next && + git config remote.dst.url ../dst && + git config remote.dst.push "+refs/heads/*:refs/remotes/src/*" && + git push dst master && + git show-ref refs/heads/master | + sed -e "s|refs/heads/|refs/remotes/src/|" >../dst/expect + ) && + ( + cd dst && + test_must_fail git show-ref refs/heads/next && + test_must_fail git show-ref refs/heads/master && + git show-ref refs/remotes/src/master >actual + ) && + test_cmp dst/expect dst/actual +' + +test_expect_success 'with no remote.$name.push, it is not used as refmap' ' + mk_test testrepo heads/master && + rm -fr src dst && + git init src && + git init --bare dst && + ( + cd src && + git pull ../testrepo master && + git branch next && + git config remote.dst.url ../dst && + git push dst master && + git show-ref refs/heads/master >../dst/expect + ) && + ( + cd dst && + test_must_fail git show-ref refs/heads/next && + git show-ref refs/heads/master >actual + ) && + test_cmp dst/expect dst/actual +' + test_expect_success 'push does not follow tags by default' ' mk_test testrepo heads/master && rm -fr src dst && -- 1.8.5.1-402-gdd8f092 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 3/3] push: also use "upstream" mapping when pushing a single ref 2013-12-05 1:27 ` [PATCH v2 0/3] Teaching "git push" to map pushed refs Junio C Hamano 2013-12-05 1:27 ` [PATCH v2 1/3] builtin/push.c: use strbuf instead of manual allocation Junio C Hamano 2013-12-05 1:27 ` [PATCH v2 2/3] push: use remote.$name.push as a refmap Junio C Hamano @ 2013-12-05 1:27 ` Junio C Hamano 2 siblings, 0 replies; 8+ messages in thread From: Junio C Hamano @ 2013-12-05 1:27 UTC (permalink / raw) To: git When the user is using the 'upstream' mode, these commands: $ git push $ git push origin would find the 'upstream' branch for the current branch, and then push the current branch to update it. However, pushing a single branch explicitly, i.e. $ git push origin $(git symbolic-ref --short HEAD) would not go through the same ref mapping process, and ends up updating the branch at 'origin' of the same name, which may not necessarily be the upstream of the branch being pushed. In the spirit similar to the previous one, map a colon-less refspec using the upstream mapping logic. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- builtin/push.c | 11 +++++++++++ t/t5516-fetch-push.sh | 30 ++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/builtin/push.c b/builtin/push.c index 857f76d..9e47c29 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -58,6 +58,17 @@ static const char *map_refspec(const char *ref, } } + if (push_default == PUSH_DEFAULT_UPSTREAM && + !prefixcmp(matched->name, "refs/heads/")) { + struct branch *branch = branch_get(matched->name + 11); + if (branch->merge_nr == 1 && branch->merge[0]->src) { + struct strbuf buf = STRBUF_INIT; + strbuf_addf(&buf, "%s:%s", + ref, branch->merge[0]->src); + return strbuf_detach(&buf, NULL); + } + } + return ref; } diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 6d7f102..926e7f6 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -1160,6 +1160,7 @@ test_expect_success 'with no remote.$name.push, it is not used as refmap' ' git pull ../testrepo master && git branch next && git config remote.dst.url ../dst && + git config push.default matching && git push dst master && git show-ref refs/heads/master >../dst/expect ) && @@ -1171,6 +1172,35 @@ test_expect_success 'with no remote.$name.push, it is not used as refmap' ' test_cmp dst/expect dst/actual ' +test_expect_success 'with no remote.$name.push, upstream mapping is used' ' + mk_test testrepo heads/master && + rm -fr src dst && + git init src && + git init --bare dst && + ( + cd src && + git pull ../testrepo master && + git branch next && + git config remote.dst.url ../dst && + git config remote.dst.fetch "+refs/heads/*:refs/remotes/dst/*" && + git config push.default upstream && + + git config branch.master.merge refs/heads/trunk && + git config branch.master.remote dst && + + git push dst master && + git show-ref refs/heads/master | + sed -e "s|refs/heads/master|refs/heads/trunk|" >../dst/expect + ) && + ( + cd dst && + test_must_fail git show-ref refs/heads/master && + test_must_fail git show-ref refs/heads/next && + git show-ref refs/heads/trunk >actual + ) && + test_cmp dst/expect dst/actual +' + test_expect_success 'push does not follow tags by default' ' mk_test testrepo heads/master && rm -fr src dst && -- 1.8.5.1-402-gdd8f092 ^ permalink raw reply related [flat|nested] 8+ messages in thread
end of thread, other threads:[~2013-12-05 1:27 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2013-12-04 0:39 [PATCH 0/3] Teaching "git push" to map pushed refs Junio C Hamano 2013-12-04 0:39 ` [PATCH 1/3] builtin/push.c: use strbuf instead of manual allocation Junio C Hamano 2013-12-04 0:39 ` [PATCH 2/3] push: use remote.$name.push as a refmap Junio C Hamano 2013-12-04 0:39 ` [PATCH 3/3] push: also use "upstream" mapping when pushing a single ref Junio C Hamano 2013-12-05 1:27 ` [PATCH v2 0/3] Teaching "git push" to map pushed refs Junio C Hamano 2013-12-05 1:27 ` [PATCH v2 1/3] builtin/push.c: use strbuf instead of manual allocation Junio C Hamano 2013-12-05 1:27 ` [PATCH v2 2/3] push: use remote.$name.push as a refmap Junio C Hamano 2013-12-05 1:27 ` [PATCH v2 3/3] push: also use "upstream" mapping when pushing a single ref Junio C Hamano
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).