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

[PATCH v2 1/2] checkout: allow dwim for branch creation for "git checkout $branch --"

From
Matthieu Moy <matthieu.moy@imag.fr>
Date
Sep 25, 2013, 19:31 UTC
Message-ID
<1380137471-26972-1-git-send-email-Matthieu.Moy@imag.fr>

The "--" notation disambiguates files and branches, but as a side-effect of the previous implementation, also disabled the branch auto-creation when $branch does not exist.

A possible scenario is then:

git checkout $branch => fails if $branch is both a ref and a file, and suggests --

git checkout $branch -- => refuses to create the $branch

This patch allows the second form to create $branch, and since the -- is provided, it does not look for file named $branch.

Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>
---
Since v1: added a paragraph in the block comment.
 builtin/checkout.c       | 38 ++++++++++++++++++++++++++++++--------
 t/t2024-checkout-dwim.sh | 22 ++++++++++++++++++++++
 2 files changed, 52 insertions(+), 8 deletions(-)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 0f57397..a5a12f6 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -863,6 +863,14 @@ static const char *unique_tracking_name(const char *name, unsigned char *sha1)
 	return NULL;
 }
 
+static int error_invalid_ref(const char *arg, int has_dash_dash, int argcount)
+{
+	if (has_dash_dash)
+		die(_("invalid reference: %s"), arg);
+	else
+		return argcount;
+}
+
 static int parse_branchname_arg(int argc, const char **argv,
 				int dwim_new_local_branch_ok,
 				struct branch_info *new,
@@ -881,6 +889,12 @@ static int parse_branchname_arg(int argc, const char **argv,
 	 *   <ref> must be a valid tree, everything after the '--' must be
 	 *   a path.
 	 *
+	 *   A sub-case of (1) is "git checkout <ref> --". In this
+	 *   case, checkout behaves like case (3), except that it does
+	 *   not attempt to understand <ref> as a file (hence, the
+	 *   short-hand to create branch <ref> works even if <ref>
+	 *   exists as a filename).
+	 *
 	 * case 2: git checkout -- [<paths>]
 	 *
 	 *   everything after the '--' must be paths.
@@ -916,20 +930,28 @@ static int parse_branchname_arg(int argc, const char **argv,
 	if (!strcmp(arg, "-"))
 		arg = "@{-1}";
 
-	if (get_sha1_mb(arg, rev)) {
-		if (has_dash_dash)          /* case (1) */
-			die(_("invalid reference: %s"), arg);
-		if (dwim_new_local_branch_ok &&
-		    !check_filename(NULL, arg) &&
-		    argc == 1) {
+	if (get_sha1_mb(arg, rev)) { /* case (1)? */
+		int try_dwim = dwim_new_local_branch_ok;
+
+		if (check_filename(NULL, arg) && !has_dash_dash)
+			try_dwim = 0;
+		/*
+		 * Accept "git checkout foo" and "git checkout foo --"
+		 * as candidates for dwim.
+		 */
+		if (!(argc == 1 && !has_dash_dash) &&
+		    !(argc == 2 && has_dash_dash))
+			try_dwim = 0;
+
+		if (try_dwim) {
 			const char *remote = unique_tracking_name(arg, rev);
 			if (!remote)
-				return argcount;
+				return error_invalid_ref(arg, has_dash_dash, argcount);
 			*new_branch = arg;
 			arg = remote;
 			/* DWIMmed to create local branch */
 		} else {
-			return argcount;
+			return error_invalid_ref(arg, has_dash_dash, argcount);
 		}
 	}
 
diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh
index 094b92e..d9afdb2 100755
--- a/t/t2024-checkout-dwim.sh
+++ b/t/t2024-checkout-dwim.sh
@@ -164,4 +164,26 @@ test_expect_success 'checkout of branch from a single remote succeeds #4' '
 	test_branch_upstream eggs repo_d eggs
 '
 
+test_expect_success 'checkout of branch with a file having the same name fails' '
+	git checkout -B master &&
+	test_might_fail git branch -D spam &&
+
+	>spam &&
+	test_must_fail git checkout spam &&
+	test_must_fail git checkout spam &&
+	test_must_fail git rev-parse --verify refs/heads/spam &&
+	test_branch master
+'
+
+test_expect_success 'checkout <branch> -- succeeds, even if a file with the same name exists' '
+	git checkout -B master &&
+	test_might_fail git branch -D spam &&
+
+	>spam &&
+	git checkout spam -- &&
+	test_branch spam &&
+	test_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&
+	test_branch_upstream spam repo_c spam
+'
+
 test_done
-- 
1.8.4.474.g128a96c
Next: Matthieu Moy
Message 1 of 7 in “checkout: allow dwim for branch creation for "git checkout $branch --"”
  1. 1/2 checkout: allow dwim for branch creation for "git checkout $branch --"Matthieu Moy, Sep 25, 2013
  2. 2/2 checkout: proper error message on 'git checkout foo bar --'Matthieu Moy, Sep 25, 2013
  3. Jonathan NiederSep 25, 2013
  4. Matthieu MoySep 26, 2013
  5. Jonathan NiederSep 25, 2013
  6. Matthieu MoySep 26, 2013
  7. Matthieu MoySep 26, 2013

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.