aboutsummaryrefslogtreecommitdiffstats
path: root/t
diff options
context:
space:
mode:
authorDerrick Stolee <derrickstolee@github.com>2023-03-20 11:26:54 +0000
committerJunio C Hamano <gitster@pobox.com>2023-03-20 12:17:33 -0700
commit49abcd21da65f12b4ae873433e5f6220bfaae1da (patch)
tree184f76c2d1bcf778c8b3faa8606afd75a9842812 /t
parentfd67d149bde24c44bc342d43ce621f36cf495929 (diff)
downloadgit-49abcd21da65f12b4ae873433e5f6220bfaae1da.tar.gz
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>
Diffstat (limited to 't')
-rwxr-xr-xt/perf/p1500-graph-walks.sh45
-rwxr-xr-xt/t3203-branch-output.sh14
-rwxr-xr-xt/t6301-for-each-ref-errors.sh14
-rwxr-xr-xt/t6600-test-reach.sh86
-rwxr-xr-xt/t7004-tag.sh28
5 files changed, 187 insertions, 0 deletions
diff --git a/t/perf/p1500-graph-walks.sh b/t/perf/p1500-graph-walks.sh
new file mode 100755
index 0000000000..439a448c2e
--- /dev/null
+++ b/t/perf/p1500-graph-walks.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+test_description='Commit walk performance tests'
+. ./perf-lib.sh
+
+test_perf_large_repo
+
+test_expect_success 'setup' '
+ git for-each-ref --format="%(refname)" "refs/heads/*" "refs/tags/*" >allrefs &&
+ sort -r allrefs | head -n 50 >refs &&
+ for ref in $(cat refs)
+ do
+ git branch -f ref-$ref $ref &&
+ echo ref-$ref ||
+ return 1
+ done >branches &&
+ for ref in $(cat refs)
+ do
+ git tag -f tag-$ref $ref &&
+ echo tag-$ref ||
+ return 1
+ done >tags &&
+ git commit-graph write --reachable
+'
+
+test_perf 'ahead-behind counts: git for-each-ref' '
+ git for-each-ref --format="%(ahead-behind:HEAD)" --stdin <refs
+'
+
+test_perf 'ahead-behind counts: git branch' '
+ xargs git branch -l --format="%(ahead-behind:HEAD)" <branches
+'
+
+test_perf 'ahead-behind counts: git tag' '
+ xargs git tag -l --format="%(ahead-behind:HEAD)" <tags
+'
+
+test_perf 'ahead-behind counts: git rev-list' '
+ for r in $(cat refs)
+ do
+ git rev-list --count "HEAD..$r" || return 1
+ done
+'
+
+test_done
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index d34d77f893..1c0f7ea24e 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -337,6 +337,20 @@ test_expect_success 'git branch --format option' '
test_cmp expect actual
'
+test_expect_success 'git branch --format with ahead-behind' '
+ cat >expect <<-\EOF &&
+ (HEAD detached from fromtag) 0 0
+ refs/heads/ambiguous 0 0
+ refs/heads/branch-one 1 0
+ refs/heads/branch-two 0 0
+ refs/heads/main 1 0
+ refs/heads/ref-to-branch 1 0
+ refs/heads/ref-to-remote 1 0
+ EOF
+ git branch --format="%(refname) %(ahead-behind:HEAD)" >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'git branch with --format=%(rest) must fail' '
test_must_fail git branch --format="%(rest)" >actual
'
diff --git a/t/t6301-for-each-ref-errors.sh b/t/t6301-for-each-ref-errors.sh
index bfda1f46ad..2667dd13fe 100755
--- a/t/t6301-for-each-ref-errors.sh
+++ b/t/t6301-for-each-ref-errors.sh
@@ -54,4 +54,18 @@ test_expect_success 'Missing objects are reported correctly' '
test_must_be_empty brief-err
'
+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
diff --git a/t/t6600-test-reach.sh b/t/t6600-test-reach.sh
index 338a9c46a2..0cb50797ef 100755
--- a/t/t6600-test-reach.sh
+++ b/t/t6600-test-reach.sh
@@ -443,4 +443,90 @@ test_expect_success 'get_reachable_subset:none' '
test_all_modes get_reachable_subset
'
+test_expect_success 'for-each-ref ahead-behind:linear' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-1-3
+ refs/heads/commit-1-5
+ refs/heads/commit-1-8
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1 0 8
+ refs/heads/commit-1-3 0 6
+ refs/heads/commit-1-5 0 4
+ refs/heads/commit-1-8 0 1
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-1-9)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:all' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-2-4
+ refs/heads/commit-4-2
+ refs/heads/commit-4-4
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1 0 24
+ refs/heads/commit-2-4 0 17
+ refs/heads/commit-4-2 0 17
+ refs/heads/commit-4-4 0 9
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-5-5)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:some' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-5-3
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1 0 53
+ refs/heads/commit-4-8 8 30
+ refs/heads/commit-5-3 0 39
+ refs/heads/commit-9-9 27 0
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-9-6)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:some, multibase' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-5-3
+ refs/heads/commit-7-8
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1 0 53 0 53
+ refs/heads/commit-4-8 8 30 0 22
+ refs/heads/commit-5-3 0 39 0 39
+ refs/heads/commit-7-8 14 12 8 6
+ refs/heads/commit-9-9 27 0 27 0
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-9-6) %(ahead-behind:commit-6-9)" \
+ --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:none' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-7-5
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-4-8 16 16
+ refs/heads/commit-7-5 7 4
+ refs/heads/commit-9-9 49 0
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-8-4)" --stdin
+'
+
test_done
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 9aa1660651..04a4b44183 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -792,6 +792,34 @@ test_expect_success 'annotations for blobs are empty' '
test_cmp expect actual
'
+# Run this before doing any signing, so the test has the same results
+# regardless of the GPG prereq.
+test_expect_success 'git tag --format with ahead-behind' '
+ test_when_finished git reset --hard tag-one-line &&
+ git commit --allow-empty -m "left" &&
+ git tag -a -m left tag-left &&
+ git reset --hard HEAD~1 &&
+ git commit --allow-empty -m "right" &&
+ git tag -a -m left tag-right &&
+
+ # Use " !" at the end to demonstrate whitespace
+ # around empty ahead-behind token for tag-blob.
+ cat >expect <<-EOF &&
+ refs/tags/tag-blob !
+ refs/tags/tag-left 1 1 !
+ refs/tags/tag-lines 0 1 !
+ refs/tags/tag-one-line 0 1 !
+ refs/tags/tag-right 0 0 !
+ refs/tags/tag-zero-lines 0 1 !
+ EOF
+ git tag -l --format="%(refname) %(ahead-behind:HEAD) !" >actual 2>err &&
+ grep "refs/tags/tag" actual >actual.focus &&
+ test_cmp expect actual.focus &&
+
+ # Error reported for tags that point to non-commits.
+ grep "error: object [0-9a-f]* is a blob, not a commit" err
+'
+
# trying to verify annotated non-signed tags:
test_expect_success GPG \