git/t/t6301-for-each-ref-errors.sh

72 lines
2 KiB
Bash
Raw Normal View History

#!/bin/sh
test_description='for-each-ref errors for broken refs'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
ZEROS=$ZERO_OID
test_expect_success setup '
MISSING=$(test_oid deadbeef) &&
git commit --allow-empty -m "Initial" &&
git tag testtag &&
git for-each-ref >full-list &&
git for-each-ref --format="%(objectname) %(refname)" >brief-list
'
for-each-ref: report broken references correctly 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>
2015-06-02 15:57:26 +00:00
test_expect_success 'Broken refs are reported correctly' '
r=refs/heads/bogus &&
: >.git/$r &&
test_when_finished "rm -f .git/$r" &&
echo "warning: ignoring broken ref $r" >broken-err &&
git for-each-ref >out 2>err &&
test_cmp full-list out &&
test_cmp broken-err err
'
test_expect_success 'NULL_SHA1 refs are reported correctly' '
r=refs/heads/zeros &&
echo $ZEROS >.git/$r &&
test_when_finished "rm -f .git/$r" &&
echo "warning: ignoring broken ref $r" >zeros-err &&
git for-each-ref >out 2>err &&
test_cmp full-list out &&
test_cmp zeros-err err &&
git for-each-ref --format="%(objectname) %(refname)" >brief-out 2>brief-err &&
test_cmp brief-list brief-out &&
test_cmp zeros-err brief-err
'
test_expect_success 'Missing objects are reported correctly' '
r=refs/heads/missing &&
echo $MISSING >.git/$r &&
test_when_finished "rm -f .git/$r" &&
echo "fatal: missing object $MISSING for $r" >missing-err &&
test_must_fail git for-each-ref 2>err &&
test_cmp missing-err err &&
(
cat brief-list &&
echo "$MISSING $r"
) | sort -k 2 >missing-brief-expected &&
git for-each-ref --format="%(objectname) %(refname)" >brief-out 2>brief-err &&
test_cmp missing-brief-expected brief-out &&
test_must_be_empty brief-err
'
for-each-ref: add ahead-behind format atom The previous change implemented the ahead_behind() method, including an algorithm to compute the ahead/behind values for a number of commit tips relative to a number of commit bases. Now, integrate that algorithm as part of 'git for-each-ref' hidden behind a new format atom, ahead-behind. This naturally extends to 'git branch' and 'git tag' builtins, as well. This format allows specifying multiple bases, if so desired, and all matching references are compared against all of those bases. For this reason, failing to read a reference provided from these atoms results in an error. In order to translate the ahead_behind() method information to the format output code in ref-filter.c, we must populate arrays of ahead_behind_count structs. In struct ref_array, we store the full array that will be passed to ahead_behind(). In struct ref_array_item, we store an array of pointers that point to the relvant items within the full array. In this way, we can pull all relevant ahead/behind values directly when formatting output for a specific item. It also ensures the lifetime of the ahead_behind_count structs matches the time that the array is being used. Add specific tests of the ahead/behind counts in t6600-test-reach.sh, as it has an interesting repository shape. In particular, its merging strategy and its use of different commit-graphs would demonstrate over- counting if the ahead_behind() method did not already account for that possibility. Also add tests for the specific for-each-ref, branch, and tag builtins. In the case of 'git tag', there are intersting cases that happen when some of the selected tips are not commits. This requires careful logic around commits_nr in the second loop of filter_ahead_behind(). Also, the test in t7004 is carefully located to avoid being dependent on the GPG prereq. It also avoids using the test_commit helper, as that will add ticks to the time and disrupt the expected timestamps in later tag tests. Also add performance tests in a new p1300-graph-walks.sh script. This will be useful for more uses in the future, but for now compare the ahead-behind counting algorithm in 'git for-each-ref' to the naive implementation by running 'git rev-list --count' processes for each input. For the Git source code repository, the improvement is already obvious: Test this tree --------------------------------------------------------------- 1500.2: ahead-behind counts: git for-each-ref 0.07(0.07+0.00) 1500.3: ahead-behind counts: git branch 0.07(0.06+0.00) 1500.4: ahead-behind counts: git tag 0.07(0.06+0.00) 1500.5: ahead-behind counts: git rev-list 1.32(1.04+0.27) But the standard performance benchmark is the Linux kernel repository, which demosntrates a significant improvement: Test this tree --------------------------------------------------------------- 1500.2: ahead-behind counts: git for-each-ref 0.27(0.24+0.02) 1500.3: ahead-behind counts: git branch 0.27(0.24+0.03) 1500.4: ahead-behind counts: git tag 0.28(0.27+0.01) 1500.5: ahead-behind counts: git rev-list 4.57(4.03+0.54) The 'git rev-list' test exists in this change as a demonstration, but it will be removed in the next change to avoid wasting time on this comparison. Signed-off-by: Derrick Stolee <derrickstolee@github.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-03-20 11:26:54 +00:00
test_expect_success 'ahead-behind requires an argument' '
test_must_fail git for-each-ref \
--format="%(ahead-behind)" 2>err &&
echo "fatal: expected format: %(ahead-behind:<committish>)" >expect &&
test_cmp expect err
'
test_expect_success 'missing ahead-behind base' '
test_must_fail git for-each-ref \
--format="%(ahead-behind:refs/heads/missing)" 2>err &&
echo "fatal: failed to find '\''refs/heads/missing'\''" >expect &&
test_cmp expect err
'
test_done