@matheusbsbs / matheus-bagliano / commits / f8aae12034
Fork 0

push: allow unqualified dest refspecs to DWIM

Previously, a push like: git push remote src:dst would go through the following steps: 1. check for an unambiguous 'dst' on the remote; if it exists, then push to that ref 2. otherwise, check if 'dst' begins with 'refs/'; if it does, create a new ref 3. otherwise, complain because we don't know where in the refs hierarchy to put 'dst' However, in some cases, we can guess about the ref type of 'dst' based on the ref type of 'src'. Specifically, before complaining we now check: 2.5. if 'src' resolves to a ref starting with refs/heads or refs/tags, then prepend that to 'dst' So now this creates a new branch on the remote, whereas it previously failed with an error message: git push master:newbranch Note that, by design, we limit this DWIM behavior only to source refs which resolve exactly (including symrefs which resolve to existing refs). We still complain on a partial destination refspec if the source is a raw sha1, or a ref expression such as 'master~10'. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Apr 23, 2008 at 05:16 UTC f8aae120345f511e59bb008e8de2a8f6e65cf377
2 files changed +69 -3
remote.c
+29 -3
@@ -812,6 +812,26 @@ static struct ref *make_linked_ref(const char *name, struct ref ***tail)
812 return ret;
813 }
814
815 +static char *guess_ref(const char *name, struct ref *peer)
816 +{
817 + struct strbuf buf = STRBUF_INIT;
818 + unsigned char sha1[20];
819 +
820 + const char *r = resolve_ref(peer->name, sha1, 1, NULL);
821 + if (!r)
822 + return NULL;
823 +
824 + if (!prefixcmp(r, "refs/heads/"))
825 + strbuf_addstr(&buf, "refs/heads/");
826 + else if (!prefixcmp(r, "refs/tags/"))
827 + strbuf_addstr(&buf, "refs/tags/");
828 + else
829 + return NULL;
830 +
831 + strbuf_addstr(&buf, name);
832 + return strbuf_detach(&buf, NULL);
833 +}
834 +
835 static int match_explicit(struct ref *src, struct ref *dst,
836 struct ref ***dst_tail,
837 struct refspec *rs,
@@ -820,6 +840,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
840 struct ref *matched_src, *matched_dst;
841
842 const char *dst_value = rs->dst;
843 + char *dst_guess;
844
845 if (rs->pattern)
846 return errs;
@@ -866,10 +887,15 @@ static int match_explicit(struct ref *src, struct ref *dst,
887 case 0:
888 if (!memcmp(dst_value, "refs/", 5))
889 matched_dst = make_linked_ref(dst_value, dst_tail);
890 + else if((dst_guess = guess_ref(dst_value, matched_src)))
891 + matched_dst = make_linked_ref(dst_guess, dst_tail);
892 else
870 - error("dst refspec %s does not match any "
871 - "existing ref on the remote and does "
872 - "not start with refs/.", dst_value);
893 + error("unable to push to unqualified destination: %s\n"
894 + "The destination refspec neither matches an "
895 + "existing ref on the remote nor\n"
896 + "begins with refs/, and we are unable to "
897 + "guess a prefix based on the source ref.",
898 + dst_value);
899 break;
900 default:
901 matched_dst = NULL;
t/t5516-fetch-push.sh
+40
@@ -273,6 +273,37 @@ test_expect_success 'push with colon-less refspec (4)' '
273
274 '
275
276 +test_expect_success 'push head with non-existant, incomplete dest' '
277 +
278 + mk_test &&
279 + git push testrepo master:branch &&
280 + check_push_result $the_commit heads/branch
281 +
282 +'
283 +
284 +test_expect_success 'push tag with non-existant, incomplete dest' '
285 +
286 + mk_test &&
287 + git tag -f v1.0 &&
288 + git push testrepo v1.0:tag &&
289 + check_push_result $the_commit tags/tag
290 +
291 +'
292 +
293 +test_expect_success 'push sha1 with non-existant, incomplete dest' '
294 +
295 + mk_test &&
296 + test_must_fail git push testrepo `git rev-parse master`:foo
297 +
298 +'
299 +
300 +test_expect_success 'push ref expression with non-existant, incomplete dest' '
301 +
302 + mk_test &&
303 + test_must_fail git push testrepo master^:branch
304 +
305 +'
306 +
307 test_expect_success 'push with HEAD' '
308
309 mk_test heads/master &&
@@ -311,6 +342,15 @@ test_expect_success 'push with +HEAD' '
342
343 '
344
345 +test_expect_success 'push HEAD with non-existant, incomplete dest' '
346 +
347 + mk_test &&
348 + git checkout master &&
349 + git push testrepo HEAD:branch &&
350 + check_push_result $the_commit heads/branch
351 +
352 +'
353 +
354 test_expect_success 'push with config remote.*.push = HEAD' '
355
356 mk_test heads/local &&