Subject: [PATCH v2] get_sha1: warn about full or short object names that look like refs

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Wed, 29 May 2013 19:12:42 +0700

When we get 40 hex digits, we immediately assume it's an SHA-1. This
is the right thing to do because we have no way else to specify an
object. If there is a ref with the same object name, it will be
ignored. Warn the user about this case because the ref with full
object name is likely a mistake, for example

    git checkout -b $empty_var $(git rev-parse something)

advice.object_name_warning is not documented because frankly people
should not be aware about it until they encounter this situation.

While at there, warn about ambiguation with abbreviated SHA-1 too.

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 advice.c                            |  2 ++
 advice.h                            |  1 +
 sha1_name.c                         | 25 +++++++++++++++++++++++--
 t/t1512-rev-parse-disambiguation.sh | 18 ++++++++++++++++++
 4 files changed, 44 insertions(+), 2 deletions(-)

diff --git a/advice.c b/advice.c
index 780f58d..22abde9 100644
--- a/advice.c
+++ b/advice.c
@@ -12,6 +12,7 @@ int advice_commit_before_merge = 1;
 int advice_resolve_conflict = 1;
 int advice_implicit_identity = 1;
 int advice_detached_head = 1;
+int advice_object_name_warning = 1;
 
 static struct {
 	const char *name;
@@ -29,6 +30,7 @@ static struct {
 	{ "resolveconflict", &advice_resolve_conflict },
 	{ "implicitidentity", &advice_implicit_identity },
 	{ "detachedhead", &advice_detached_head },
+	{ "object_name_warning", &advice_object_name_warning },
 
 	/* make this an alias for backward compatibility */
 	{ "pushnonfastforward", &advice_push_update_rejected }
diff --git a/advice.h b/advice.h
index fad36df..24d5420 100644
--- a/advice.h
+++ b/advice.h
@@ -15,6 +15,7 @@ extern int advice_commit_before_merge;
 extern int advice_resolve_conflict;
 extern int advice_implicit_identity;
 extern int advice_detached_head;
+extern int advice_object_name_warning;
 
 int git_default_advice_config(const char *var, const char *value);
 void advise(const char *advice, ...);
diff --git a/sha1_name.c b/sha1_name.c
index 95003c7..502d107 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -435,12 +435,31 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned l
 static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 {
 	static const char *warn_msg = "refname '%.*s' is ambiguous.";
+	static const char *object_name_msg = N_(
+	"Git normally never creates a ref that ends with 40 hex characters\n"
+	"because it will be ignored when you just specify 40-hex. These refs\n"
+	"may be created by mistake. For example,\n"
+	"\n"
+	"  git checkout -b $br $(git rev-parse ...)\n"
+	"\n"
+	"where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
+	"examine these refs and maybe delete them. Turn this message off by\n"
+	"running \"git config advice.object_name_warning false\"");
+	unsigned char tmp_sha1[20];
 	char *real_ref = NULL;
 	int refs_found = 0;
 	int at, reflog_len;
 
-	if (len == 40 && !get_sha1_hex(str, sha1))
+	if (len == 40 && !get_sha1_hex(str, sha1)) {
+		refs_found = dwim_ref(str, len, tmp_sha1, &real_ref);
+		if (refs_found > 0 && warn_ambiguous_refs) {
+			warning(warn_msg, len, str);
+			if (advice_object_name_warning)
+				fprintf(stderr, "%s\n", _(object_name_msg));
+		}
+		free(real_ref);
 		return 0;
+	}
 
 	/* basic@{time or number or -number} format to query ref-log */
 	reflog_len = at = 0;
@@ -481,7 +500,9 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 	if (!refs_found)
 		return -1;
 
-	if (warn_ambiguous_refs && refs_found > 1)
+	if (warn_ambiguous_refs &&
+	    (refs_found > 1 ||
+	     !get_short_sha1(str, len, tmp_sha1, GET_SHA1_QUIETLY)))
 		warning(warn_msg, len, str);
 
 	if (reflog_len) {
diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh
index 6b3d797..db22808 100755
--- a/t/t1512-rev-parse-disambiguation.sh
+++ b/t/t1512-rev-parse-disambiguation.sh
@@ -261,4 +261,22 @@ test_expect_success 'rev-parse --disambiguate' '
 	test "$(sed -e "s/^\(.........\).*/\1/" actual | sort -u)" = 000000000
 '
 
+test_expect_success 'ambiguous 40-hex ref' '
+	TREE=$(git mktree </dev/null) &&
+	REF=`git rev-parse HEAD` &&
+	VAL=$(git commit-tree $TREE </dev/null) &&
+	git update-ref refs/heads/$REF $VAL &&
+	test `git rev-parse $REF 2>err` = $REF &&
+	grep "refname.*${REF}.*ambiguous" err
+'
+
+test_expect_success 'ambiguous short sha1 ref' '
+	TREE=$(git mktree </dev/null) &&
+	REF=`git rev-parse --short HEAD` &&
+	VAL=$(git commit-tree $TREE </dev/null) &&
+	git update-ref refs/heads/$REF $VAL &&
+	test `git rev-parse $REF 2>err` = $VAL &&
+	grep "refname.*${REF}.*ambiguous" err
+'
+
 test_done
-- 
1.8.2.83.gc99314b