Merge branch 'kk/tips-reachable-from-bases-optim'

Revision traversal optimization.

* kk/tips-reachable-from-bases-optim:
  t6600: add tests for duplicate tips in tips_reachable_from_bases()
  commit-reach: use object flags for tips_reachable_from_bases()
main
Junio C Hamano 2026-05-31 10:00:37 +09:00
commit a0ce168def
2 changed files with 56 additions and 12 deletions

View File

@ -1143,7 +1143,6 @@ void ahead_behind(struct repository *r,

struct commit_and_index {
struct commit *commit;
unsigned int index;
timestamp_t generation;
};

@ -1183,7 +1182,6 @@ void tips_reachable_from_bases(struct repository *r,

for (size_t i = 0; i < tips_nr; i++) {
commits[i].commit = tips[i];
commits[i].index = i;
commits[i].generation = commit_graph_generation(tips[i]);
}

@ -1191,6 +1189,9 @@ void tips_reachable_from_bases(struct repository *r,
QSORT(commits, tips_nr, compare_commit_and_index_by_generation);
min_generation = commits[0].generation;

for (size_t i = 0; i < tips_nr; i++)
commits[i].commit->object.flags |= RESULT;

while (bases) {
repo_parse_commit(r, bases->item);
commit_list_insert(bases->item, &stack);
@ -1201,20 +1202,16 @@ void tips_reachable_from_bases(struct repository *r,
int explored_all_parents = 1;
struct commit_list *p;
struct commit *c = stack->item;
timestamp_t c_gen = commit_graph_generation(c);

/* Does it match any of our tips? */
for (size_t j = min_generation_index; j < tips_nr; j++) {
if (c_gen < commits[j].generation)
break;
{
if (c->object.flags & RESULT) {
c->object.flags |= mark;

if (commits[j].commit == c) {
tips[commits[j].index]->object.flags |= mark;

if (j == min_generation_index) {
unsigned int k = j + 1;
if (commits[min_generation_index].commit->object.flags & mark) {
unsigned int k = min_generation_index + 1;
while (k < tips_nr &&
(tips[commits[k].index]->object.flags & mark))
(commits[k].commit->object.flags & mark))
k++;

/* Terminate early if all found. */
@ -1250,6 +1247,8 @@ void tips_reachable_from_bases(struct repository *r,
}

done:
for (size_t i = 0; i < tips_nr; i++)
commits[i].commit->object.flags &= ~RESULT;
free(commits);
repo_clear_commit_marks(r, SEEN);
commit_list_free(stack);

View File

@ -612,6 +612,51 @@ test_expect_success 'for-each-ref merged:none' '
--format="%(refname)" --stdin
'

test_expect_success 'for-each-ref merged:duplicate, all reachable' '
git branch dup-a commit-3-3 &&
git branch dup-b commit-3-3 &&
cat >input <<-\EOF &&
refs/heads/commit-1-1
refs/heads/dup-a
refs/heads/dup-b
EOF
cat >expect <<-\EOF &&
refs/heads/commit-1-1
refs/heads/dup-a
refs/heads/dup-b
EOF
run_all_modes git for-each-ref --merged=commit-5-5 \
--format="%(refname)" --stdin
'

test_expect_success 'for-each-ref merged:duplicate, none reachable' '
cat >input <<-\EOF &&
refs/heads/dup-a
refs/heads/dup-b
refs/heads/commit-9-9
EOF
>expect &&
run_all_modes git for-each-ref --merged=commit-2-2 \
--format="%(refname)" --stdin
'

test_expect_success 'for-each-ref merged:duplicate at min generation' '
git branch dup-c commit-1-1 &&
git branch dup-d commit-1-1 &&
cat >input <<-\EOF &&
refs/heads/dup-c
refs/heads/dup-d
refs/heads/commit-5-5
EOF
cat >expect <<-\EOF &&
refs/heads/commit-5-5
refs/heads/dup-c
refs/heads/dup-d
EOF
run_all_modes git for-each-ref --merged=commit-5-5 \
--format="%(refname)" --stdin
'

# For get_branch_base_for_tip, we only care about
# first-parent history. Here is the test graph with
# second parents removed: