for-each-ref: report broken references correctly
authorMichael Haggerty <mhagger@alum.mit.edu>
Tue, 2 Jun 2015 15:57:26 +0000 (17:57 +0200)
committerJunio C Hamano <gitster@pobox.com>
Tue, 2 Jun 2015 20:09:16 +0000 (13:09 -0700)
If there is a loose reference file with invalid contents, "git
for-each-ref" incorrectly reports the problem as being a missing
object with name NULL_SHA1:

$ echo '12345678' >.git/refs/heads/nonsense
$ git for-each-ref
fatal: missing object 0000000000000000000000000000000000000000 for refs/heads/nonsense

With an explicit "--format" string, it can even report that the
reference validly points at NULL_SHA1:

$ git for-each-ref --format='%(objectname) %(refname)'
0000000000000000000000000000000000000000 refs/heads/nonsense
$ echo $?
0

This has been broken since

b7dd2d2 for-each-ref: Do not lookup objects when they will not be used (2009-05-27)

, which changed for-each-ref from using for_each_ref() to using
git_for_each_rawref() in order to avoid looking up the referred-to
objects unnecessarily. (When "git for-each-ref" is given a "--format"
string that doesn't include information about the pointed-to object,
it does not look up the object at all, which makes it considerably
faster. Iterating with DO_FOR_EACH_INCLUDE_BROKEN is essential to this
optimization because otherwise for_each_ref() would itself need to
check whether the object exists as part of its brokenness test.)

But for_each_rawref() includes broken references in the iteration, and
"git for-each-ref" doesn't itself reject references with REF_ISBROKEN.
The result is that broken references are processed *as if* they had
the value NULL_SHA1, which is the value stored in entries for broken
references.

Change "git for-each-ref" to emit warnings for references that are
REF_ISBROKEN but to otherwise skip them.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/for-each-ref.c
t/t6301-for-each-ref-errors.sh
index 008513c2f135b5a77d1642ff88e272a7e2f9f9d5..4a15f568f116fe797d257274ce0782be9e4e8aeb 100644 (file)
@@ -851,6 +851,11 @@ static int grab_single_ref(const char *refname, const unsigned char *sha1, int f
                  return 0;
        }
 
                  return 0;
        }
 
+       if (flag & REF_ISBROKEN) {
+                 warning("ignoring broken ref %s", refname);
+                 return 0;
+       }
+
        if (*cb->grab_pattern) {
                const char **pattern;
                int namelen = strlen(refname);
        if (*cb->grab_pattern) {
                const char **pattern;
                int namelen = strlen(refname);
index cf25244c26eff1b75601be8dc5617635fa1022b9..72d2397e1219a704a599cd860cd60f09c75db568 100755 (executable)
@@ -14,7 +14,7 @@ test_expect_success setup '
        git for-each-ref --format="%(objectname) %(refname)" >brief-list
 '
 
        git for-each-ref --format="%(objectname) %(refname)" >brief-list
 '
 
-test_expect_failure 'Broken refs are reported correctly' '
+test_expect_success 'Broken refs are reported correctly' '
        r=refs/heads/bogus &&
        : >.git/$r &&
        test_when_finished "rm -f .git/$r" &&
        r=refs/heads/bogus &&
        : >.git/$r &&
        test_when_finished "rm -f .git/$r" &&