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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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)
 {
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]