Merge branch 'ds/reachable-topo-order'
The revision walker machinery learned to take advantage of the commit generation numbers stored in the commit-graph file. * ds/reachable-topo-order: t6012: make rev-list tests more interesting revision.c: generation-based topo-order algorithm commit/revisions: bookkeeping before refactoring revision.c: begin refactoring --topo-order logic test-reach: add rev-list tests test-reach: add run_three_modes method prio-queue: add 'peek' operation
This commit is contained in:
243
revision.c
243
revision.c
@ -25,6 +25,8 @@
|
||||
#include "worktree.h"
|
||||
#include "argv-array.h"
|
||||
#include "commit-reach.h"
|
||||
#include "commit-graph.h"
|
||||
#include "prio-queue.h"
|
||||
|
||||
volatile show_early_output_fn_t show_early_output;
|
||||
|
||||
@ -767,8 +769,8 @@ static void commit_list_insert_by_date_cached(struct commit *p, struct commit_li
|
||||
*cache = new_entry;
|
||||
}
|
||||
|
||||
static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
|
||||
struct commit_list **list, struct commit_list **cache_ptr)
|
||||
static int process_parents(struct rev_info *revs, struct commit *commit,
|
||||
struct commit_list **list, struct commit_list **cache_ptr)
|
||||
{
|
||||
struct commit_list *parent = commit->parents;
|
||||
unsigned left_flag;
|
||||
@ -807,7 +809,8 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
|
||||
if (p->object.flags & SEEN)
|
||||
continue;
|
||||
p->object.flags |= SEEN;
|
||||
commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
|
||||
if (list)
|
||||
commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -846,7 +849,8 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
|
||||
p->object.flags |= left_flag;
|
||||
if (!(p->object.flags & SEEN)) {
|
||||
p->object.flags |= SEEN;
|
||||
commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
|
||||
if (list)
|
||||
commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
|
||||
}
|
||||
if (revs->first_parent_only)
|
||||
break;
|
||||
@ -1090,7 +1094,7 @@ static int limit_list(struct rev_info *revs)
|
||||
|
||||
if (revs->max_age != -1 && (commit->date < revs->max_age))
|
||||
obj->flags |= UNINTERESTING;
|
||||
if (add_parents_to_list(revs, commit, &list, NULL) < 0)
|
||||
if (process_parents(revs, commit, &list, NULL) < 0)
|
||||
return -1;
|
||||
if (obj->flags & UNINTERESTING) {
|
||||
mark_parents_uninteresting(commit);
|
||||
@ -2468,7 +2472,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
|
||||
if (revs->diffopt.objfind)
|
||||
revs->simplify_history = 0;
|
||||
|
||||
if (revs->topo_order)
|
||||
if (revs->topo_order && !generation_numbers_enabled(the_repository))
|
||||
revs->limited = 1;
|
||||
|
||||
if (revs->prune_data.nr) {
|
||||
@ -2907,6 +2911,217 @@ static int mark_uninteresting(const struct object_id *oid,
|
||||
return 0;
|
||||
}
|
||||
|
||||
define_commit_slab(indegree_slab, int);
|
||||
define_commit_slab(author_date_slab, timestamp_t);
|
||||
|
||||
struct topo_walk_info {
|
||||
uint32_t min_generation;
|
||||
struct prio_queue explore_queue;
|
||||
struct prio_queue indegree_queue;
|
||||
struct prio_queue topo_queue;
|
||||
struct indegree_slab indegree;
|
||||
struct author_date_slab author_date;
|
||||
};
|
||||
|
||||
static inline void test_flag_and_insert(struct prio_queue *q, struct commit *c, int flag)
|
||||
{
|
||||
if (c->object.flags & flag)
|
||||
return;
|
||||
|
||||
c->object.flags |= flag;
|
||||
prio_queue_put(q, c);
|
||||
}
|
||||
|
||||
static void explore_walk_step(struct rev_info *revs)
|
||||
{
|
||||
struct topo_walk_info *info = revs->topo_walk_info;
|
||||
struct commit_list *p;
|
||||
struct commit *c = prio_queue_get(&info->explore_queue);
|
||||
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
if (parse_commit_gently(c, 1) < 0)
|
||||
return;
|
||||
|
||||
if (revs->sort_order == REV_SORT_BY_AUTHOR_DATE)
|
||||
record_author_date(&info->author_date, c);
|
||||
|
||||
if (revs->max_age != -1 && (c->date < revs->max_age))
|
||||
c->object.flags |= UNINTERESTING;
|
||||
|
||||
if (process_parents(revs, c, NULL, NULL) < 0)
|
||||
return;
|
||||
|
||||
if (c->object.flags & UNINTERESTING)
|
||||
mark_parents_uninteresting(c);
|
||||
|
||||
for (p = c->parents; p; p = p->next)
|
||||
test_flag_and_insert(&info->explore_queue, p->item, TOPO_WALK_EXPLORED);
|
||||
}
|
||||
|
||||
static void explore_to_depth(struct rev_info *revs,
|
||||
uint32_t gen_cutoff)
|
||||
{
|
||||
struct topo_walk_info *info = revs->topo_walk_info;
|
||||
struct commit *c;
|
||||
while ((c = prio_queue_peek(&info->explore_queue)) &&
|
||||
c->generation >= gen_cutoff)
|
||||
explore_walk_step(revs);
|
||||
}
|
||||
|
||||
static void indegree_walk_step(struct rev_info *revs)
|
||||
{
|
||||
struct commit_list *p;
|
||||
struct topo_walk_info *info = revs->topo_walk_info;
|
||||
struct commit *c = prio_queue_get(&info->indegree_queue);
|
||||
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
if (parse_commit_gently(c, 1) < 0)
|
||||
return;
|
||||
|
||||
explore_to_depth(revs, c->generation);
|
||||
|
||||
for (p = c->parents; p; p = p->next) {
|
||||
struct commit *parent = p->item;
|
||||
int *pi = indegree_slab_at(&info->indegree, parent);
|
||||
|
||||
if (*pi)
|
||||
(*pi)++;
|
||||
else
|
||||
*pi = 2;
|
||||
|
||||
test_flag_and_insert(&info->indegree_queue, parent, TOPO_WALK_INDEGREE);
|
||||
|
||||
if (revs->first_parent_only)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void compute_indegrees_to_depth(struct rev_info *revs,
|
||||
uint32_t gen_cutoff)
|
||||
{
|
||||
struct topo_walk_info *info = revs->topo_walk_info;
|
||||
struct commit *c;
|
||||
while ((c = prio_queue_peek(&info->indegree_queue)) &&
|
||||
c->generation >= gen_cutoff)
|
||||
indegree_walk_step(revs);
|
||||
}
|
||||
|
||||
static void init_topo_walk(struct rev_info *revs)
|
||||
{
|
||||
struct topo_walk_info *info;
|
||||
struct commit_list *list;
|
||||
revs->topo_walk_info = xmalloc(sizeof(struct topo_walk_info));
|
||||
info = revs->topo_walk_info;
|
||||
memset(info, 0, sizeof(struct topo_walk_info));
|
||||
|
||||
init_indegree_slab(&info->indegree);
|
||||
memset(&info->explore_queue, 0, sizeof(info->explore_queue));
|
||||
memset(&info->indegree_queue, 0, sizeof(info->indegree_queue));
|
||||
memset(&info->topo_queue, 0, sizeof(info->topo_queue));
|
||||
|
||||
switch (revs->sort_order) {
|
||||
default: /* REV_SORT_IN_GRAPH_ORDER */
|
||||
info->topo_queue.compare = NULL;
|
||||
break;
|
||||
case REV_SORT_BY_COMMIT_DATE:
|
||||
info->topo_queue.compare = compare_commits_by_commit_date;
|
||||
break;
|
||||
case REV_SORT_BY_AUTHOR_DATE:
|
||||
init_author_date_slab(&info->author_date);
|
||||
info->topo_queue.compare = compare_commits_by_author_date;
|
||||
info->topo_queue.cb_data = &info->author_date;
|
||||
break;
|
||||
}
|
||||
|
||||
info->explore_queue.compare = compare_commits_by_gen_then_commit_date;
|
||||
info->indegree_queue.compare = compare_commits_by_gen_then_commit_date;
|
||||
|
||||
info->min_generation = GENERATION_NUMBER_INFINITY;
|
||||
for (list = revs->commits; list; list = list->next) {
|
||||
struct commit *c = list->item;
|
||||
|
||||
if (parse_commit_gently(c, 1))
|
||||
continue;
|
||||
|
||||
test_flag_and_insert(&info->explore_queue, c, TOPO_WALK_EXPLORED);
|
||||
test_flag_and_insert(&info->indegree_queue, c, TOPO_WALK_INDEGREE);
|
||||
|
||||
if (c->generation < info->min_generation)
|
||||
info->min_generation = c->generation;
|
||||
|
||||
*(indegree_slab_at(&info->indegree, c)) = 1;
|
||||
|
||||
if (revs->sort_order == REV_SORT_BY_AUTHOR_DATE)
|
||||
record_author_date(&info->author_date, c);
|
||||
}
|
||||
compute_indegrees_to_depth(revs, info->min_generation);
|
||||
|
||||
for (list = revs->commits; list; list = list->next) {
|
||||
struct commit *c = list->item;
|
||||
|
||||
if (*(indegree_slab_at(&info->indegree, c)) == 1)
|
||||
prio_queue_put(&info->topo_queue, c);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is unfortunate; the initial tips need to be shown
|
||||
* in the order given from the revision traversal machinery.
|
||||
*/
|
||||
if (revs->sort_order == REV_SORT_IN_GRAPH_ORDER)
|
||||
prio_queue_reverse(&info->topo_queue);
|
||||
}
|
||||
|
||||
static struct commit *next_topo_commit(struct rev_info *revs)
|
||||
{
|
||||
struct commit *c;
|
||||
struct topo_walk_info *info = revs->topo_walk_info;
|
||||
|
||||
/* pop next off of topo_queue */
|
||||
c = prio_queue_get(&info->topo_queue);
|
||||
|
||||
if (c)
|
||||
*(indegree_slab_at(&info->indegree, c)) = 0;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static void expand_topo_walk(struct rev_info *revs, struct commit *commit)
|
||||
{
|
||||
struct commit_list *p;
|
||||
struct topo_walk_info *info = revs->topo_walk_info;
|
||||
if (process_parents(revs, commit, NULL, NULL) < 0) {
|
||||
if (!revs->ignore_missing_links)
|
||||
die("Failed to traverse parents of commit %s",
|
||||
oid_to_hex(&commit->object.oid));
|
||||
}
|
||||
|
||||
for (p = commit->parents; p; p = p->next) {
|
||||
struct commit *parent = p->item;
|
||||
int *pi;
|
||||
|
||||
if (parse_commit_gently(parent, 1) < 0)
|
||||
continue;
|
||||
|
||||
if (parent->generation < info->min_generation) {
|
||||
info->min_generation = parent->generation;
|
||||
compute_indegrees_to_depth(revs, info->min_generation);
|
||||
}
|
||||
|
||||
pi = indegree_slab_at(&info->indegree, parent);
|
||||
|
||||
(*pi)--;
|
||||
if (*pi == 1)
|
||||
prio_queue_put(&info->topo_queue, parent);
|
||||
|
||||
if (revs->first_parent_only)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int prepare_revision_walk(struct rev_info *revs)
|
||||
{
|
||||
int i;
|
||||
@ -2943,11 +3158,13 @@ int prepare_revision_walk(struct rev_info *revs)
|
||||
commit_list_sort_by_date(&revs->commits);
|
||||
if (revs->no_walk)
|
||||
return 0;
|
||||
if (revs->limited)
|
||||
if (revs->limited) {
|
||||
if (limit_list(revs) < 0)
|
||||
return -1;
|
||||
if (revs->topo_order)
|
||||
sort_in_topological_order(&revs->commits, revs->sort_order);
|
||||
if (revs->topo_order)
|
||||
sort_in_topological_order(&revs->commits, revs->sort_order);
|
||||
} else if (revs->topo_order)
|
||||
init_topo_walk(revs);
|
||||
if (revs->line_level_traverse)
|
||||
line_log_filter(revs);
|
||||
if (revs->simplify_merges)
|
||||
@ -2964,7 +3181,7 @@ static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp
|
||||
for (;;) {
|
||||
struct commit *p = *pp;
|
||||
if (!revs->limited)
|
||||
if (add_parents_to_list(revs, p, &revs->commits, &cache) < 0)
|
||||
if (process_parents(revs, p, &revs->commits, &cache) < 0)
|
||||
return rewrite_one_error;
|
||||
if (p->object.flags & UNINTERESTING)
|
||||
return rewrite_one_ok;
|
||||
@ -3272,6 +3489,8 @@ static struct commit *get_revision_1(struct rev_info *revs)
|
||||
|
||||
if (revs->reflog_info)
|
||||
commit = next_reflog_entry(revs->reflog_info);
|
||||
else if (revs->topo_walk_info)
|
||||
commit = next_topo_commit(revs);
|
||||
else
|
||||
commit = pop_commit(&revs->commits);
|
||||
|
||||
@ -3293,7 +3512,9 @@ static struct commit *get_revision_1(struct rev_info *revs)
|
||||
|
||||
if (revs->reflog_info)
|
||||
try_to_simplify_commit(revs, commit);
|
||||
else if (add_parents_to_list(revs, commit, &revs->commits, NULL) < 0) {
|
||||
else if (revs->topo_walk_info)
|
||||
expand_topo_walk(revs, commit);
|
||||
else if (process_parents(revs, commit, &revs->commits, NULL) < 0) {
|
||||
if (!revs->ignore_missing_links)
|
||||
die("Failed to traverse parents of commit %s",
|
||||
oid_to_hex(&commit->object.oid));
|
||||
|
Reference in New Issue
Block a user