Skip to content

Commit fcb2c07

Browse files
derrickstoleegitster
authored andcommitted
commit-reach: implement get_reachable_subset
The existing reachability algorithms in commit-reach.c focus on finding merge-bases or determining if all commits in a set X can reach at least one commit in a set Y. However, for two commits sets X and Y, we may also care about which commits in Y are reachable from at least one commit in X. Implement get_reachable_subset() which answers this question. Given two arrays of commits, 'from' and 'to', return a commit_list with every commit from the 'to' array that is reachable from at least one commit in the 'from' array. The algorithm is a simple walk starting at the 'from' commits, using the PARENT2 flag to indicate "this commit has already been added to the walk queue". By marking the 'to' commits with the PARENT1 flag, we can determine when we see a commit from the 'to' array. We remove the PARENT1 flag as we add that commit to the result list to avoid duplicates. The order of the resulting list is a reverse of the order that the commits are discovered in the walk. There are a couple shortcuts to avoid walking more than we need: 1. We determine the minimum generation number of commits in the 'to' array. We do not walk commits with generation number below this minimum. 2. We count how many distinct commits are in the 'to' array, and decrement this count when we discover a 'to' commit during the walk. If this number reaches zero, then we can terminate the walk. Tests will be added using the 'test-tool reach' helper in a subsequent commit. Signed-off-by: Derrick Stolee <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent d582ea2 commit fcb2c07

File tree

2 files changed

+82
-0
lines changed

2 files changed

+82
-0
lines changed

commit-reach.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,3 +690,72 @@ int can_all_from_reach(struct commit_list *from, struct commit_list *to,
690690
object_array_clear(&from_objs);
691691
return result;
692692
}
693+
694+
struct commit_list *get_reachable_subset(struct commit **from, int nr_from,
695+
struct commit **to, int nr_to,
696+
unsigned int reachable_flag)
697+
{
698+
struct commit **item;
699+
struct commit *current;
700+
struct commit_list *found_commits = NULL;
701+
struct commit **to_last = to + nr_to;
702+
struct commit **from_last = from + nr_from;
703+
uint32_t min_generation = GENERATION_NUMBER_INFINITY;
704+
int num_to_find = 0;
705+
706+
struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
707+
708+
for (item = to; item < to_last; item++) {
709+
struct commit *c = *item;
710+
711+
parse_commit(c);
712+
if (c->generation < min_generation)
713+
min_generation = c->generation;
714+
715+
if (!(c->object.flags & PARENT1)) {
716+
c->object.flags |= PARENT1;
717+
num_to_find++;
718+
}
719+
}
720+
721+
for (item = from; item < from_last; item++) {
722+
struct commit *c = *item;
723+
if (!(c->object.flags & PARENT2)) {
724+
c->object.flags |= PARENT2;
725+
parse_commit(c);
726+
727+
prio_queue_put(&queue, *item);
728+
}
729+
}
730+
731+
while (num_to_find && (current = prio_queue_get(&queue)) != NULL) {
732+
struct commit_list *parents;
733+
734+
if (current->object.flags & PARENT1) {
735+
current->object.flags &= ~PARENT1;
736+
current->object.flags |= reachable_flag;
737+
commit_list_insert(current, &found_commits);
738+
num_to_find--;
739+
}
740+
741+
for (parents = current->parents; parents; parents = parents->next) {
742+
struct commit *p = parents->item;
743+
744+
parse_commit(p);
745+
746+
if (p->generation < min_generation)
747+
continue;
748+
749+
if (p->object.flags & PARENT2)
750+
continue;
751+
752+
p->object.flags |= PARENT2;
753+
prio_queue_put(&queue, p);
754+
}
755+
}
756+
757+
clear_commit_marks_many(nr_to, to, PARENT1);
758+
clear_commit_marks_many(nr_from, from, PARENT2);
759+
760+
return found_commits;
761+
}

commit-reach.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,17 @@ int can_all_from_reach_with_flag(struct object_array *from,
7474
int can_all_from_reach(struct commit_list *from, struct commit_list *to,
7575
int commit_date_cutoff);
7676

77+
78+
/*
79+
* Return a list of commits containing the commits in the 'to' array
80+
* that are reachable from at least one commit in the 'from' array.
81+
* Also add the given 'flag' to each of the commits in the returned list.
82+
*
83+
* This method uses the PARENT1 and PARENT2 flags during its operation,
84+
* so be sure these flags are not set before calling the method.
85+
*/
86+
struct commit_list *get_reachable_subset(struct commit **from, int nr_from,
87+
struct commit **to, int nr_to,
88+
unsigned int reachable_flag);
89+
7790
#endif

0 commit comments

Comments
 (0)