Skip to content

Commit c343f9c

Browse files
committed
Merge branch 'ds/rev-list-maximal-only-optim'
"git rev-list --maximal-only" has been optimized by borrowing the logic used by "git show-branch --independent", which computes the same kind of information much more efficiently. * ds/rev-list-maximal-only-optim: rev-list: use reduce_heads() for --maximal-only p6011: add perf test for rev-list --maximal-only t6600: test --maximal-only and --independent
2 parents 8e04162 + b0ba57d commit c343f9c

File tree

4 files changed

+164
-0
lines changed

4 files changed

+164
-0
lines changed

builtin/rev-list.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "oidset.h"
2626
#include "oidmap.h"
2727
#include "packfile.h"
28+
#include "commit-reach.h"
2829
#include "quote.h"
2930
#include "strbuf.h"
3031

@@ -633,6 +634,61 @@ static int try_bitmap_disk_usage(struct rev_info *revs,
633634
return 0;
634635
}
635636

637+
/*
638+
* If revs->maximal_only is set and no other walk modifiers are provided,
639+
* run a faster computation to filter the independent commits and prepare
640+
* them for output. Set revs->no_walk to prevent later walking.
641+
*
642+
* If this algorithm doesn't apply, then no changes are made to revs.
643+
*/
644+
static void prepare_maximal_independent(struct rev_info *revs)
645+
{
646+
struct commit_list *c;
647+
648+
if (!revs->maximal_only)
649+
return;
650+
651+
for (c = revs->commits; c; c = c->next) {
652+
if (c->item->object.flags & UNINTERESTING)
653+
return;
654+
}
655+
656+
if (revs->limited ||
657+
revs->topo_order ||
658+
revs->first_parent_only ||
659+
revs->reverse ||
660+
revs->max_count >= 0 ||
661+
revs->skip_count >= 0 ||
662+
revs->min_age != (timestamp_t)-1 ||
663+
revs->max_age != (timestamp_t)-1 ||
664+
revs->min_parents > 0 ||
665+
revs->max_parents >= 0 ||
666+
revs->prune_data.nr ||
667+
revs->count ||
668+
revs->left_right ||
669+
revs->boundary ||
670+
revs->tag_objects ||
671+
revs->tree_objects ||
672+
revs->blob_objects ||
673+
revs->filter.choice ||
674+
revs->reflog_info ||
675+
revs->diff ||
676+
revs->grep_filter.pattern_list ||
677+
revs->grep_filter.header_list ||
678+
revs->verbose_header ||
679+
revs->print_parents ||
680+
revs->edge_hint ||
681+
revs->unpacked ||
682+
revs->no_kept_objects ||
683+
revs->line_level_traverse)
684+
return;
685+
686+
reduce_heads_replace(&revs->commits);
687+
688+
/* Modify 'revs' to only output this commit list. */
689+
revs->no_walk = 1;
690+
}
691+
636692
int cmd_rev_list(int argc,
637693
const char **argv,
638694
const char *prefix,
@@ -875,6 +931,9 @@ int cmd_rev_list(int argc,
875931

876932
if (prepare_revision_walk(&revs))
877933
die("revision walk setup failed");
934+
935+
prepare_maximal_independent(&revs);
936+
878937
if (revs.tree_objects)
879938
mark_edges_uninteresting(&revs, show_edge, 0);
880939

t/perf/p6011-rev-list-maximal.sh

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/bin/sh
2+
3+
test_description='Test --maximal-only and --independent options'
4+
5+
. ./perf-lib.sh
6+
7+
test_perf_default_repo
8+
9+
test_expect_success 'setup' '
10+
git for-each-ref --format="%(*objecttype) %(objecttype) %(objectname)" \
11+
"refs/heads/*" "refs/tags/*" |
12+
sed -n -e "s/^commit commit //p" -e "s/^ commit //p" |
13+
head -n 50 >commits &&
14+
git commit-graph write --reachable
15+
'
16+
17+
test_perf 'merge-base --independent' '
18+
git merge-base --independent $(cat commits) >/dev/null
19+
'
20+
21+
test_perf 'rev-list --maximal-only' '
22+
git rev-list --maximal-only $(cat commits) >/dev/null
23+
'
24+
25+
test_perf 'rev-list --maximal-only --since' '
26+
git rev-list --maximal-only --since=2000-01-01 $(cat commits) >/dev/null
27+
'
28+
29+
test_done

t/t6000-rev-list-misc.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,4 +263,35 @@ test_expect_success 'rev-list --boundary incompatible with --maximal-only' '
263263
test_grep "cannot be used together" err
264264
'
265265

266+
test_expect_success 'rev-list --maximal-only and --pretty' '
267+
test_when_finished rm -rf repo &&
268+
269+
git init repo &&
270+
test_commit -C repo 1 &&
271+
oid1=$(git -C repo rev-parse HEAD) &&
272+
test_commit -C repo 2 &&
273+
oid2=$(git -C repo rev-parse HEAD) &&
274+
git -C repo checkout --detach HEAD~1 &&
275+
test_commit -C repo 3 &&
276+
oid3=$(git -C repo rev-parse HEAD) &&
277+
278+
cat >expect <<-EOF &&
279+
commit $oid3
280+
$oid3
281+
commit $oid2
282+
$oid2
283+
EOF
284+
285+
git -C repo rev-list --pretty="%H" --maximal-only $oid1 $oid2 $oid3 >out &&
286+
test_cmp expect out &&
287+
288+
cat >expect <<-EOF &&
289+
$oid3
290+
$oid2
291+
EOF
292+
293+
git -C repo log --pretty="%H" --maximal-only $oid1 $oid2 $oid3 >out &&
294+
test_cmp expect out
295+
'
296+
266297
test_done

t/t6600-test-reach.sh

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,4 +837,49 @@ test_expect_success 'rev-list --maximal-only (range)' '
837837
--first-parent --exclude-first-parent-only
838838
'
839839

840+
test_expect_success 'rev-list --maximal-only matches merge-base --independent' '
841+
# Mix of independent and dependent
842+
git merge-base --independent \
843+
refs/heads/commit-5-2 \
844+
refs/heads/commit-3-2 \
845+
refs/heads/commit-2-5 >expect &&
846+
sort expect >expect.sorted &&
847+
git rev-list --maximal-only \
848+
refs/heads/commit-5-2 \
849+
refs/heads/commit-3-2 \
850+
refs/heads/commit-2-5 >actual &&
851+
sort actual >actual.sorted &&
852+
test_cmp expect.sorted actual.sorted &&
853+
854+
# All independent commits.
855+
git merge-base --independent \
856+
refs/heads/commit-5-2 \
857+
refs/heads/commit-4-3 \
858+
refs/heads/commit-3-4 \
859+
refs/heads/commit-2-5 >expect &&
860+
sort expect >expect.sorted &&
861+
git rev-list --maximal-only \
862+
refs/heads/commit-5-2 \
863+
refs/heads/commit-4-3 \
864+
refs/heads/commit-3-4 \
865+
refs/heads/commit-2-5 >actual &&
866+
sort actual >actual.sorted &&
867+
test_cmp expect.sorted actual.sorted &&
868+
869+
# Only one independent.
870+
git merge-base --independent \
871+
refs/heads/commit-1-1 \
872+
refs/heads/commit-4-2 \
873+
refs/heads/commit-4-4 \
874+
refs/heads/commit-8-4 >expect &&
875+
sort expect >expect.sorted &&
876+
git rev-list --maximal-only \
877+
refs/heads/commit-1-1 \
878+
refs/heads/commit-4-2 \
879+
refs/heads/commit-4-4 \
880+
refs/heads/commit-8-4 >actual &&
881+
sort actual >actual.sorted &&
882+
test_cmp expect.sorted actual.sorted
883+
'
884+
840885
test_done

0 commit comments

Comments
 (0)