Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 34 additions & 97 deletions commit-reach.c
Original file line number Diff line number Diff line change
Expand Up @@ -1013,79 +1013,6 @@ int can_all_from_reach(struct commit_list *from, struct commit_list *to,
return result;
}

struct commit_list *get_reachable_subset(struct commit **from, size_t nr_from,
struct commit **to, size_t nr_to,
unsigned int reachable_flag)
{
struct commit **item;
struct commit *current;
struct commit_list *found_commits = NULL;
struct commit **to_last = to + nr_to;
struct commit **from_last = from + nr_from;
timestamp_t min_generation = GENERATION_NUMBER_INFINITY;
int num_to_find = 0;

struct prio_queue queue = { compare_commits_by_gen_then_commit_date };

for (item = to; item < to_last; item++) {
timestamp_t generation;
struct commit *c = *item;

repo_parse_commit(the_repository, c);
generation = commit_graph_generation(c);
if (generation < min_generation)
min_generation = generation;

if (!(c->object.flags & PARENT1)) {
c->object.flags |= PARENT1;
num_to_find++;
}
}

for (item = from; item < from_last; item++) {
struct commit *c = *item;
if (!(c->object.flags & PARENT2)) {
c->object.flags |= PARENT2;
repo_parse_commit(the_repository, c);

prio_queue_put(&queue, *item);
}
}

while (num_to_find && (current = prio_queue_get(&queue)) != NULL) {
struct commit_list *parents;

if (current->object.flags & PARENT1) {
current->object.flags &= ~PARENT1;
current->object.flags |= reachable_flag;
commit_list_insert(current, &found_commits);
num_to_find--;
}

for (parents = current->parents; parents; parents = parents->next) {
struct commit *p = parents->item;

repo_parse_commit(the_repository, p);

if (commit_graph_generation(p) < min_generation)
continue;

if (p->object.flags & PARENT2)
continue;

p->object.flags |= PARENT2;
prio_queue_put(&queue, p);
}
}

clear_prio_queue(&queue);

clear_commit_marks_many(nr_to, to, PARENT1);
clear_commit_marks_many(nr_from, from, PARENT2);

return found_commits;
}

define_commit_slab(bit_arrays, struct bitmap *);
static struct bit_arrays bit_arrays;

Expand Down Expand Up @@ -1212,22 +1139,26 @@ static int compare_commit_and_index_by_generation(const void *va, const void *vb
void tips_reachable_from_bases(struct repository *r,
struct commit_list *bases,
struct commit **tips, size_t tips_nr,
int mark)
int mark, enum tips_reachable_mode mode)
{
struct commit_and_index *commits;
struct commit_list *p;
struct commit *c;
size_t min_generation_index = 0;
timestamp_t min_generation;
struct commit_list *stack = NULL;
struct prio_queue queue = { NULL };

if (!bases || !tips || !tips_nr)
return;

/*
* Do a depth-first search starting at 'bases' to search for the
* tips. Stop at the lowest (un-found) generation number. When
* finding the lowest commit, increase the minimum generation
* number to the next lowest (un-found) generation number.
* Search starting at 'bases' looking for the tips. Stop at the
* lowest un-found generation number, raising the floor as tips
* are found. Use DFS by default; with TIPS_REACHABLE_PQ,
* use a priority queue ordered by generation then commit date.
*/
if (mode == TIPS_REACHABLE_PQ)
queue.compare = compare_commits_by_gen_then_commit_date;

CALLOC_ARRAY(commits, tips_nr);

Expand All @@ -1245,14 +1176,19 @@ void tips_reachable_from_bases(struct repository *r,

while (bases) {
repo_parse_commit(r, bases->item);
commit_list_insert(bases->item, &stack);
bases->item->object.flags |= SEEN;
prio_queue_put(&queue, bases->item);
bases = bases->next;
}

while (stack) {
int explored_all_parents = 1;
struct commit_list *p;
struct commit *c = stack->item;
while ((c = prio_queue_get(&queue))) {
struct commit *first_parent = NULL;

repo_parse_commit(r, c);

/* Skip if below the current generation floor. */
if (commit_graph_generation(c) < min_generation)
continue;

/* Does it match any of our tips? */
{
Expand All @@ -1276,33 +1212,34 @@ void tips_reachable_from_bases(struct repository *r,
}

for (p = c->parents; p; p = p->next) {
repo_parse_commit(r, p->item);

/* Have we already explored this parent? */
if (p->item->object.flags & SEEN)
continue;

/* Is it below the current minimum generation? */
if (commit_graph_generation(p->item) < min_generation)
continue;

/* Ok, we will explore from here on. */
p->item->object.flags |= SEEN;
explored_all_parents = 0;
commit_list_insert(p->item, &stack);
break;
/* Parse before pushing in PQ mode for ordering. */
if (mode == TIPS_REACHABLE_PQ)
repo_parse_commit(r, p->item);
if (!first_parent)
first_parent = p->item;
else
prio_queue_put(&queue, p->item);
}

if (explored_all_parents)
pop_commit(&stack);
/*
* Add the first parent last so that it is on top of
* the LIFO queue, maintaining first-parent DFS order.
*/
if (first_parent)
prio_queue_put(&queue, first_parent);
}

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);
clear_prio_queue(&queue);
}

/*
Expand Down
19 changes: 5 additions & 14 deletions commit-reach.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,19 +96,6 @@ int can_all_from_reach_with_flag(struct object_array *from,
int can_all_from_reach(struct commit_list *from, struct commit_list *to,
int commit_date_cutoff);


/*
* Return a list of commits containing the commits in the 'to' array
* that are reachable from at least one commit in the 'from' array.
* Also add the given 'flag' to each of the commits in the returned list.
*
* This method uses the PARENT1 and PARENT2 flags during its operation,
* so be sure these flags are not set before calling the method.
*/
struct commit_list *get_reachable_subset(struct commit **from, size_t nr_from,
struct commit **to, size_t nr_to,
unsigned int reachable_flag);

struct ahead_behind_count {
/**
* As input, the *_index members indicate which positions in
Expand Down Expand Up @@ -144,10 +131,14 @@ void ahead_behind(struct repository *r,
* For all tip commits, add 'mark' to their flags if and only if they
* are reachable from one of the commits in 'bases'.
*/
enum tips_reachable_mode {
TIPS_REACHABLE_DFS,
TIPS_REACHABLE_PQ,
};
void tips_reachable_from_bases(struct repository *r,
struct commit_list *bases,
struct commit **tips, size_t tips_nr,
int mark);
int mark, enum tips_reachable_mode mode);

/*
* Given a 'tip' commit and a list potential 'bases', return the index 'i' that
Expand Down
2 changes: 1 addition & 1 deletion ref-filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -3157,7 +3157,7 @@ static void reach_filter(struct ref_array *array,
tips_reachable_from_bases(the_repository,
*check_reachable,
to_clear, array->nr,
UNINTERESTING);
UNINTERESTING, TIPS_REACHABLE_DFS);

old_nr = array->nr;
array->nr = 0;
Expand Down
20 changes: 10 additions & 10 deletions remote.c
Original file line number Diff line number Diff line change
Expand Up @@ -1459,9 +1459,8 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
* sent to the other side.
*/
if (sent_tips.nr) {
const int reachable_flag = 1;
struct commit_list *found_commits;
struct commit_stack src_commits = COMMIT_STACK_INIT;
struct commit_list *bases = NULL;

for_each_string_list_item(item, &src_tag) {
struct ref *ref = item->util;
Expand All @@ -1479,11 +1478,13 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
commit_stack_push(&src_commits, commit);
}

found_commits = get_reachable_subset(sent_tips.items,
sent_tips.nr,
src_commits.items,
src_commits.nr,
reachable_flag);
for (size_t i = 0; i < sent_tips.nr; i++)
commit_list_insert(sent_tips.items[i], &bases);
tips_reachable_from_bases(the_repository,
bases, src_commits.items,
src_commits.nr, TMP_MARK,
TIPS_REACHABLE_PQ);
commit_list_free(bases);

for_each_string_list_item(item, &src_tag) {
struct ref *dst_ref;
Expand All @@ -1503,7 +1504,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
* Is this tag, which they do not have, reachable from
* any of the commits we are sending?
*/
if (!(commit->object.flags & reachable_flag))
if (!(commit->object.flags & TMP_MARK))
continue;

/* Add it in */
Expand All @@ -1513,9 +1514,8 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
}

clear_commit_marks_many(src_commits.nr, src_commits.items,
reachable_flag);
TMP_MARK);
commit_stack_clear(&src_commits);
commit_list_free(found_commits);
}

string_list_clear(&src_tag, 0);
Expand Down
44 changes: 23 additions & 21 deletions t/helper/test-reach.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "hex.h"
#include "object-name.h"
#include "ref-filter.h"
#include "revision.h"
#include "setup.h"
#include "string-list.h"
#include "tag.h"
Expand Down Expand Up @@ -149,30 +150,31 @@ int cmd__reach(int ac, const char **av)

printf("%s(_,A,X,_):%d\n", av[1], commit_contains(&filter, A, X, &cache));
clear_contains_cache(&cache);
} else if (!strcmp(av[1], "get_reachable_subset")) {
const int reachable_flag = 1;
int count = 0;
struct commit_list *current;
struct commit_list *list = get_reachable_subset(X_stack.items, X_stack.nr,
Y_stack.items, Y_stack.nr,
reachable_flag);
printf("get_reachable_subset(X,Y)\n");
for (current = list; current; current = current->next) {
if (!(list->item->object.flags & reachable_flag))
die(_("commit %s is not marked reachable"),
oid_to_hex(&list->item->object.oid));
count++;
}
} else if (!strcmp(av[1], "tips_reachable_from_bases") ||
!strcmp(av[1], "tips_reachable_from_bases_pq")) {
enum tips_reachable_mode mode =
!strcmp(av[1], "tips_reachable_from_bases_pq")
? TIPS_REACHABLE_PQ : TIPS_REACHABLE_DFS;
struct commit_list *bases = NULL;
struct commit_list *result = NULL;

for (size_t i = 0; i < X_stack.nr; i++)
commit_list_insert(X_stack.items[i], &bases);
tips_reachable_from_bases(the_repository,
bases, Y_stack.items,
Y_stack.nr, TMP_MARK,
mode);
commit_list_free(bases);

printf("tips_reachable_from_bases(X,Y)\n");
for (size_t i = 0; i < Y_stack.nr; i++) {
if (Y_stack.items[i]->object.flags & reachable_flag)
count--;
if (Y_stack.items[i]->object.flags & TMP_MARK)
commit_list_insert(Y_stack.items[i], &result);
}
print_sorted_commit_ids(result);

if (count < 0)
die(_("too many commits marked reachable"));

print_sorted_commit_ids(list);
commit_list_free(list);
clear_commit_marks_many(Y_stack.nr, Y_stack.items, TMP_MARK);
commit_list_free(result);
}

object_array_clear(&X_obj);
Expand Down
Loading
Loading