Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.kernel.org/pub/scm/git/git.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'commit-reach.c')
-rw-r--r--commit-reach.c113
1 files changed, 113 insertions, 0 deletions
diff --git a/commit-reach.c b/commit-reach.c
index cd990dce16..c1edeb4610 100644
--- a/commit-reach.c
+++ b/commit-reach.c
@@ -1044,3 +1044,116 @@ void ahead_behind(struct repository *r,
clear_bit_arrays(&bit_arrays);
clear_prio_queue(&queue);
}
+
+struct commit_and_index {
+ struct commit *commit;
+ unsigned int index;
+ timestamp_t generation;
+};
+
+static int compare_commit_and_index_by_generation(const void *va, const void *vb)
+{
+ const struct commit_and_index *a = (const struct commit_and_index *)va;
+ const struct commit_and_index *b = (const struct commit_and_index *)vb;
+
+ if (a->generation > b->generation)
+ return 1;
+ if (a->generation < b->generation)
+ return -1;
+ return 0;
+}
+
+void tips_reachable_from_bases(struct repository *r,
+ struct commit_list *bases,
+ struct commit **tips, size_t tips_nr,
+ int mark)
+{
+ struct commit_and_index *commits;
+ size_t min_generation_index = 0;
+ timestamp_t min_generation;
+ struct commit_list *stack = 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.
+ */
+
+ CALLOC_ARRAY(commits, tips_nr);
+
+ 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]);
+ }
+
+ /* Sort with generation number ascending. */
+ QSORT(commits, tips_nr, compare_commit_and_index_by_generation);
+ min_generation = commits[0].generation;
+
+ while (bases) {
+ repo_parse_commit(r, bases->item);
+ commit_list_insert(bases->item, &stack);
+ bases = bases->next;
+ }
+
+ while (stack) {
+ 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 (commits[j].commit == c) {
+ tips[commits[j].index]->object.flags |= mark;
+
+ if (j == min_generation_index) {
+ unsigned int k = j + 1;
+ while (k < tips_nr &&
+ (tips[commits[k].index]->object.flags & mark))
+ k++;
+
+ /* Terminate early if all found. */
+ if (k >= tips_nr)
+ goto done;
+
+ min_generation_index = k;
+ min_generation = commits[k].generation;
+ }
+ }
+ }
+
+ 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;
+ }
+
+ if (explored_all_parents)
+ pop_commit(&stack);
+ }
+
+done:
+ free(commits);
+ repo_clear_commit_marks(r, SEEN);
+}