get_sha1: warn about full or short object names that look like refs

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 <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Nguyễn Thái Ngọc Duy committed May 29, 2013 at 19:12 UTC 798c35fcd8a71a094ca68ac05d81e08c5ac8166d
4 files changed +44 -2
advice.c
+2
@@ -12,6 +12,7 @@ int advice_commit_before_merge = 1;
12 int advice_resolve_conflict = 1;
13 int advice_implicit_identity = 1;
14 int advice_detached_head = 1;
15 +int advice_object_name_warning = 1;
16
17 static struct {
18 const char *name;
@@ -29,6 +30,7 @@ static struct {
30 { "resolveconflict", &advice_resolve_conflict },
31 { "implicitidentity", &advice_implicit_identity },
32 { "detachedhead", &advice_detached_head },
33 + { "object_name_warning", &advice_object_name_warning },
34
35 /* make this an alias for backward compatibility */
36 { "pushnonfastforward", &advice_push_update_rejected }
advice.h
+1
@@ -15,6 +15,7 @@ extern int advice_commit_before_merge;
15 extern int advice_resolve_conflict;
16 extern int advice_implicit_identity;
17 extern int advice_detached_head;
18 +extern int advice_object_name_warning;
19
20 int git_default_advice_config(const char *var, const char *value);
21 void advise(const char *advice, ...);
sha1_name.c
+23 -2
@@ -435,12 +435,31 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned l
435 static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
436 {
437 static const char *warn_msg = "refname '%.*s' is ambiguous.";
438 + static const char *object_name_msg = N_(
439 + "Git normally never creates a ref that ends with 40 hex characters\n"
440 + "because it will be ignored when you just specify 40-hex. These refs\n"
441 + "may be created by mistake. For example,\n"
442 + "\n"
443 + " git checkout -b $br $(git rev-parse ...)\n"
444 + "\n"
445 + "where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
446 + "examine these refs and maybe delete them. Turn this message off by\n"
447 + "running \"git config advice.object_name_warning false\"");
448 + unsigned char tmp_sha1[20];
449 char *real_ref = NULL;
450 int refs_found = 0;
451 int at, reflog_len;
452
442 - if (len == 40 && !get_sha1_hex(str, sha1))
453 + if (len == 40 && !get_sha1_hex(str, sha1)) {
454 + refs_found = dwim_ref(str, len, tmp_sha1, &real_ref);
455 + if (refs_found > 0 && warn_ambiguous_refs) {
456 + warning(warn_msg, len, str);
457 + if (advice_object_name_warning)
458 + fprintf(stderr, "%s\n", _(object_name_msg));
459 + }
460 + free(real_ref);
461 return 0;
462 + }
463
464 /* basic@{time or number or -number} format to query ref-log */
465 reflog_len = at = 0;
@@ -481,7 +500,9 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
500 if (!refs_found)
501 return -1;
502
484 - if (warn_ambiguous_refs && refs_found > 1)
503 + if (warn_ambiguous_refs &&
504 + (refs_found > 1 ||
505 + !get_short_sha1(str, len, tmp_sha1, GET_SHA1_QUIETLY)))
506 warning(warn_msg, len, str);
507
508 if (reflog_len) {
t/t1512-rev-parse-disambiguation.sh
+18
@@ -261,4 +261,22 @@ test_expect_success 'rev-parse --disambiguate' '
261 test "$(sed -e "s/^\(.........\).*/\1/" actual | sort -u)" = 000000000
262 '
263
264 +test_expect_success 'ambiguous 40-hex ref' '
265 + TREE=$(git mktree </dev/null) &&
266 + REF=`git rev-parse HEAD` &&
267 + VAL=$(git commit-tree $TREE </dev/null) &&
268 + git update-ref refs/heads/$REF $VAL &&
269 + test `git rev-parse $REF 2>err` = $REF &&
270 + grep "refname.*${REF}.*ambiguous" err
271 +'
272 +
273 +test_expect_success 'ambiguous short sha1 ref' '
274 + TREE=$(git mktree </dev/null) &&
275 + REF=`git rev-parse --short HEAD` &&
276 + VAL=$(git commit-tree $TREE </dev/null) &&
277 + git update-ref refs/heads/$REF $VAL &&
278 + test `git rev-parse $REF 2>err` = $VAL &&
279 + grep "refname.*${REF}.*ambiguous" err
280 +'
281 +
282 test_done