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

Re* [BUG] "git checkout -b" erronously thinks a branch already exists

From
Junio C Hamano <gitster@pobox.com>
Date
Jun 6, 2011, 01:38 UTC
Message-ID
<7v62ojbuyh.fsf@alter.siamese.dyndns.org>
In-Reply-To
<201106051305.13723.stefano.lattarini@gmail.com>
Interesting.

This is because "git checkout -b <name>" tries to make sure that "refs/heads/<name>" does not exist, and the way it does it to pass it to get_sha1() and make sure it fails.

It almost always works, except when that string is passed from get_sha1() to get_describe_name(), which takes "ANYTHING-g<hexstring>" and as long as hexstring names an existing object, it says "Yup, I found one."

I think the right fix is to make sure that "refs/heads/<name>" does not exist by checking exactly that.

Perhaps something like this. I am not sure if we want to use the "yield non zero to signal not an error but an early return" trick like I did in this patch, though.

---
 builtin/checkout.c |    2 +-
 refs.c             |   14 ++++++++++++++
 refs.h             |    1 +
 3 files changed, 16 insertions(+), 1 deletions(-)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 28cdc51..af1e7b5 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1071,7 +1071,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		if (strbuf_check_branch_ref(&buf, opts.new_branch))
 			die(_("git checkout: we do not like '%s' as a branch name."),
 			    opts.new_branch);
-		if (!get_sha1(buf.buf, rev)) {
+		if (ref_exists(buf.buf)) {
 			opts.branch_exists = 1;
 			if (!opts.new_branch_force)
 				die(_("git checkout: branch %s already exists"),
diff --git a/refs.c b/refs.c
index e3c0511..138b1a0 100644
--- a/refs.c
+++ b/refs.c
@@ -1826,6 +1826,20 @@ int update_ref(const char *action, const char *refname,
 	return 0;
 }
 
+static int compare_ref_name(const char *refname,
+			    const unsigned char *sha1, int flag,
+			    void *cb_data)
+{
+	if (!strcmp(cb_data, refname))
+		return -1; /* early return */
+	return 0;
+}
+
+int ref_exists(char *refname)
+{
+	return for_each_ref(compare_ref_name, refname) != 0;
+}
+
 struct ref *find_ref_by_name(const struct ref *list, const char *name)
 {
 	for ( ; list; list = list->next)
diff --git a/refs.h b/refs.h
index 5e7a9a5..5839487 100644
--- a/refs.h
+++ b/refs.h
@@ -35,6 +35,7 @@ extern int for_each_ref_in_submodule(const char *submodule, const char *prefix,
 extern int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
 extern int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
 extern int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+extern int ref_exists(char *);
 
 static inline const char *has_glob_specials(const char *pattern)
 {
Previous: Stefano LattariniNext: Junio C Hamano
Message 2 of 5 in “[BUG] "git checkout -b" erronously thinks a branch already exists”
  1. Stefano LattariniJun 5, 2011
  2. Re* [BUG] "git checkout -b" erronously thinks a branch already existsJunio C Hamano, Jun 6, 2011
  3. Junio C HamanoJun 6, 2011
  4. Stefano LattariniJun 6, 2011
  5. Jeff KingJun 6, 2011

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.