summaryrefslogtreecommitdiff
path: root/commit-reach.c
diff options
context:
space:
mode:
Diffstat (limited to 'commit-reach.c')
-rw-r--r--commit-reach.c122
1 files changed, 75 insertions, 47 deletions
diff --git a/commit-reach.c b/commit-reach.c
index f5858944fd..bc522d6840 100644
--- a/commit-reach.c
+++ b/commit-reach.c
@@ -514,66 +514,87 @@ int commit_contains(struct ref_filter *filter, struct commit *commit,
return is_descendant_of(commit, list);
}
-int reachable(struct commit *from, unsigned int with_flag,
- unsigned int assign_flag, time_t min_commit_date)
+static int compare_commits_by_gen(const void *_a, const void *_b)
{
- struct prio_queue work = { compare_commits_by_commit_date };
+ const struct commit *a = (const struct commit *)_a;
+ const struct commit *b = (const struct commit *)_b;
- prio_queue_put(&work, from);
- while (work.nr) {
- struct commit_list *list;
- struct commit *commit = prio_queue_get(&work);
-
- if (commit->object.flags & with_flag) {
- from->object.flags |= assign_flag;
- break;
- }
- if (!commit->object.parsed)
- parse_object(the_repository, &commit->object.oid);
- if (commit->object.flags & REACHABLE)
- continue;
- commit->object.flags |= REACHABLE;
- if (commit->date < min_commit_date)
- continue;
- for (list = commit->parents; list; list = list->next) {
- struct commit *parent = list->item;
- if (!(parent->object.flags & REACHABLE))
- prio_queue_put(&work, parent);
- }
- }
- from->object.flags |= REACHABLE;
- clear_commit_marks(from, REACHABLE);
- clear_prio_queue(&work);
- return (from->object.flags & assign_flag);
+ if (a->generation < b->generation)
+ return -1;
+ if (a->generation > b->generation)
+ return 1;
+ return 0;
}
int can_all_from_reach_with_flag(struct object_array *from,
unsigned int with_flag,
unsigned int assign_flag,
- time_t min_commit_date)
+ time_t min_commit_date,
+ uint32_t min_generation)
{
+ struct commit **list = NULL;
int i;
+ int result = 1;
+ ALLOC_ARRAY(list, from->nr);
for (i = 0; i < from->nr; i++) {
- struct object *from_one = from->objects[i].item;
+ list[i] = (struct commit *)from->objects[i].item;
- if (from_one->flags & assign_flag)
- continue;
- from_one = deref_tag(the_repository, from_one, "a from object", 0);
- if (!from_one || from_one->type != OBJ_COMMIT) {
- /* no way to tell if this is reachable by
- * looking at the ancestry chain alone, so
- * leave a note to ourselves not to worry about
- * this object anymore.
- */
- from->objects[i].item->flags |= assign_flag;
- continue;
- }
- if (!reachable((struct commit *)from_one, with_flag, assign_flag,
- min_commit_date))
+ if (parse_commit(list[i]) ||
+ list[i]->generation < min_generation)
return 0;
}
- return 1;
+
+ QSORT(list, from->nr, compare_commits_by_gen);
+
+ for (i = 0; i < from->nr; i++) {
+ /* DFS from list[i] */
+ struct commit_list *stack = NULL;
+
+ list[i]->object.flags |= assign_flag;
+ commit_list_insert(list[i], &stack);
+
+ while (stack) {
+ struct commit_list *parent;
+
+ if (stack->item->object.flags & with_flag) {
+ pop_commit(&stack);
+ continue;
+ }
+
+ for (parent = stack->item->parents; parent; parent = parent->next) {
+ if (parent->item->object.flags & (with_flag | RESULT))
+ stack->item->object.flags |= RESULT;
+
+ if (!(parent->item->object.flags & assign_flag)) {
+ parent->item->object.flags |= assign_flag;
+
+ if (parse_commit(parent->item) ||
+ parent->item->date < min_commit_date ||
+ parent->item->generation < min_generation)
+ continue;
+
+ commit_list_insert(parent->item, &stack);
+ break;
+ }
+ }
+
+ if (!parent)
+ pop_commit(&stack);
+ }
+
+ if (!(list[i]->object.flags & (with_flag | RESULT))) {
+ result = 0;
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ for (i = 0; i < from->nr; i++) {
+ clear_commit_marks(list[i], RESULT);
+ clear_commit_marks(list[i], assign_flag);
+ }
+ return result;
}
int can_all_from_reach(struct commit_list *from, struct commit_list *to,
@@ -583,6 +604,7 @@ int can_all_from_reach(struct commit_list *from, struct commit_list *to,
time_t min_commit_date = cutoff_by_min_date ? from->item->date : 0;
struct commit_list *from_iter = from, *to_iter = to;
int result;
+ uint32_t min_generation = GENERATION_NUMBER_INFINITY;
while (from_iter) {
add_object_array(&from_iter->item->object, NULL, &from_objs);
@@ -590,6 +612,9 @@ int can_all_from_reach(struct commit_list *from, struct commit_list *to,
if (!parse_commit(from_iter->item)) {
if (from_iter->item->date < min_commit_date)
min_commit_date = from_iter->item->date;
+
+ if (from_iter->item->generation < min_generation)
+ min_generation = from_iter->item->generation;
}
from_iter = from_iter->next;
@@ -599,6 +624,9 @@ int can_all_from_reach(struct commit_list *from, struct commit_list *to,
if (!parse_commit(to_iter->item)) {
if (to_iter->item->date < min_commit_date)
min_commit_date = to_iter->item->date;
+
+ if (to_iter->item->generation < min_generation)
+ min_generation = to_iter->item->generation;
}
to_iter->item->object.flags |= PARENT2;
@@ -607,7 +635,7 @@ int can_all_from_reach(struct commit_list *from, struct commit_list *to,
}
result = can_all_from_reach_with_flag(&from_objs, PARENT2, PARENT1,
- min_commit_date);
+ min_commit_date, min_generation);
while (from) {
clear_commit_marks(from->item, PARENT1);