git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 01/10] push: change push to fail if short refname does not exist

From
Steffen Prohaska <prohaska@zib.de>
Date
Oct 28, 2007, 17:46 UTC
Message-ID
<11935935812741-git-send-email-prohaska@zib.de>
In-Reply-To
<1193593581312-git-send-email-prohaska@zib.de>

Pushing a short refname used to create a new ref on on the remote side if it did not yet exist. If you specified the wrong branch accidentally it was created. A safety valve that pushes only existing branches may help to avoid errors.

This commit changes push to fail if the remote ref does not yet exist and the refspec does not start with refs/. Remote refs must explicitly be created with their full name. If you specify a branch name that does not yet exist on the remote side, git push will print a suggestion to push the full refname instead.

The new behaviour is more defensive than the old one. You can now explicitly distinguish between "push existing branch" and "create new branch on the remote side". The old implementation allowed the same command line in both cases.

A follow-up patch will add a flag '--create' that provides an alternative to using full refnames if creation of new refs is intended.

Another follow-up patch will support "push origin HEAD". In this case, the existence check is important. If you're on the wrong branch and push HEAD you may be surprised if a new branch is created. This can be avoided by requiring either a full ref or the '--create' flag.

The implementation in this patch is less "clever" and hopefully better readable than an ealier version of the patch. Thanks for the suggestions to Daniel Barkalow <barkalow@iabervon.org>.

Signed-off-by: Steffen Prohaska <prohaska@zib.de>
---
 remote.c              |   25 ++++++++++++++++---------
 t/t5516-fetch-push.sh |   34 ++++++++++++++++++++++++++++++++--
 2 files changed, 48 insertions(+), 11 deletions(-)
diff --git a/remote.c b/remote.c
index 170015a..cf6441a 100644
--- a/remote.c
+++ b/remote.c
@@ -610,7 +610,8 @@ static int match_explicit(struct ref *src, struct ref *dst,
 {
 	struct ref *matched_src, *matched_dst;
 
-	const char *dst_value = rs->dst;
+	const char *lit_dst_value;
+	const char *search_dst_value;
 
 	if (rs->pattern)
 		return errs;
@@ -637,27 +638,33 @@ static int match_explicit(struct ref *src, struct ref *dst,
 	if (!matched_src)
 		errs = 1;
 
-	if (!dst_value) {
+	if (rs->dst) {
+		lit_dst_value = search_dst_value = rs->dst;
+	} else {
 		if (!matched_src)
 			return errs;
-		dst_value = matched_src->name;
+		lit_dst_value = rs->src;
+		search_dst_value = matched_src->name;
 	}
 
-	switch (count_refspec_match(dst_value, dst, &matched_dst)) {
+	switch (count_refspec_match(search_dst_value, dst, &matched_dst)) {
 	case 1:
 		break;
 	case 0:
-		if (!memcmp(dst_value, "refs/", 5))
-			matched_dst = make_linked_ref(dst_value, dst_tail);
-		else
+		if (!memcmp(lit_dst_value , "refs/", 5))
+			matched_dst = make_linked_ref(lit_dst_value, dst_tail);
+		else {
 			error("dst refspec %s does not match any "
 			      "existing ref on the remote and does "
-			      "not start with refs/.", dst_value);
+			      "not start with refs/.", lit_dst_value);
+			if (!rs->dst)
+				error("Did you mean %s?\n", search_dst_value);
+		}
 		break;
 	default:
 		matched_dst = NULL;
 		error("dst refspec %s matches more than one.",
-		      dst_value);
+		      lit_dst_value);
 		break;
 	}
 	if (errs || !matched_dst)
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 4fbd5b1..5ba09e2 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -126,6 +126,36 @@ test_expect_success 'push with wildcard' '
 	)
 '
 
+test_expect_success 'push nonexisting (1)' '
+
+	mk_test &&
+	if git push testrepo master
+	then
+		echo "Oops, should have failed"
+		false
+	fi
+
+'
+
+test_expect_success 'push nonexisting (2)' '
+
+	mk_test &&
+	if git push testrepo heads/master
+	then
+		echo "Oops, should have failed"
+		false
+	fi
+
+'
+
+test_expect_success 'push nonexisting (3)' '
+
+	mk_test &&
+	git push testrepo refs/heads/master &&
+	check_push_result $the_commit heads/master
+
+'
+
 test_expect_success 'push with matching heads' '
 
 	mk_test heads/master &&
@@ -225,7 +255,7 @@ test_expect_success 'push with colon-less refspec (3)' '
 		git tag -d frotz
 	fi &&
 	git branch -f frotz master &&
-	git push testrepo frotz &&
+	git push testrepo refs/heads/frotz &&
 	check_push_result $the_commit heads/frotz &&
 	test 1 = $( cd testrepo && git show-ref | wc -l )
 '
@@ -238,7 +268,7 @@ test_expect_success 'push with colon-less refspec (4)' '
 		git branch -D frotz
 	fi &&
 	git tag -f frotz &&
-	git push testrepo frotz &&
+	git push testrepo refs/tags/frotz &&
 	check_push_result $the_commit tags/frotz &&
 	test 1 = $( cd testrepo && git show-ref | wc -l )
 
-- 
1.5.3.4.439.ge8b49
Previous: Steffen ProhaskaNext: Steffen Prohaska
Message 2 of 53 in “improve refspec handling in push”
  1. 0/10 improve refspec handling in pushSteffen Prohaska, Oct 28, 2007
  2. 01/10 push: change push to fail if short refname does not existSteffen Prohaska, Oct 28, 2007
  3. 02/10 push: teach push new flag --createSteffen Prohaska, Oct 28, 2007
  4. 03/10 push: support pushing HEAD to real branch nameSteffen Prohaska, Oct 28, 2007
  5. 04/10 push: add "git push HEAD" shorthand for 'push current branch to default repo'Steffen Prohaska, Oct 28, 2007
  6. 05/10 rename ref_matches_abbrev() to ref_abbrev_matches_full_with_fetch_rules()Steffen Prohaska, Oct 28, 2007
  7. 06/10 add ref_abbrev_matches_full_with_rev_parse_rules() comparing abbrev with full ref nameSteffen Prohaska, Oct 28, 2007
  8. 07/10 push: use same rules as git-rev-parse to resolve refspecsSteffen Prohaska, Oct 28, 2007
  9. 08/10 push: teach push to accept --verbose optionSteffen Prohaska, Oct 28, 2007
  10. 09/10 push: teach push to pass --verbose option to transport layerSteffen Prohaska, Oct 28, 2007
  11. 10/10 push: teach push to be quiet if local ref is strict subset of remote refSteffen Prohaska, Oct 28, 2007
  12. Junio C HamanoOct 30, 2007
  13. Steffen ProhaskaOct 30, 2007
  14. Andreas EricssonOct 30, 2007
  15. Steffen ProhaskaOct 30, 2007
  16. Junio C HamanoOct 30, 2007
  17. Steffen ProhaskaOct 31, 2007
  18. Junio C HamanoOct 31, 2007
  19. Junio C HamanoOct 31, 2007
  20. Steffen ProhaskaOct 31, 2007
  21. Junio C HamanoOct 31, 2007
  22. Steffen ProhaskaOct 31, 2007
  23. Junio C HamanoOct 31, 2007
  24. Steffen ProhaskaNov 1, 2007
  25. Andreas EricssonNov 1, 2007
  26. Steffen ProhaskaNov 1, 2007
  27. Junio C HamanoNov 1, 2007
  28. Steffen ProhaskaNov 2, 2007
  29. Junio C HamanoNov 2, 2007
  30. Steffen ProhaskaNov 2, 2007
  31. Junio C HamanoNov 2, 2007
  32. Steffen ProhaskaNov 2, 2007
  33. Andreas EricssonNov 2, 2007
  34. Tom PrinceNov 2, 2007
  35. Andreas EricssonNov 2, 2007
  36. Steffen ProhaskaNov 2, 2007
  37. Junio C HamanoNov 2, 2007
  38. Junio C HamanoNov 2, 2007
  39. Andreas EricssonNov 1, 2007
  40. Steffen ProhaskaNov 1, 2007
  41. Andreas EricssonNov 1, 2007
  42. Wincent ColaiutaNov 2, 2007
  43. Johannes SchindelinNov 2, 2007
  44. Steffen ProhaskaNov 2, 2007
  45. Wincent ColaiutaNov 2, 2007
  46. Daniel BarkalowOct 30, 2007
  47. Junio C HamanoOct 30, 2007
  48. Steffen ProhaskaOct 30, 2007
  49. Junio C HamanoOct 30, 2007
  50. Junio C HamanoOct 30, 2007
  51. Junio C HamanoOct 30, 2007
  52. Steffen ProhaskaOct 30, 2007
  53. Junio C HamanoOct 30, 2007

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.