@@ -59,7 +59,7 @@ struct object_array {
/*
* object flag allocation:
- * revision.h: 0---------10 2526
+ * revision.h: 0---------10 25----28
* fetch-pack.c: 01
* negotiator/default.c: 2--5
* walker.c: 0-2
@@ -78,7 +78,7 @@ struct object_array {
* builtin/show-branch.c: 0-------------------------------------------26
* builtin/unpack-objects.c: 2021
*/
-#define FLAG_BITS 27
+#define FLAG_BITS 29
/*
* The object type is stored in 3 bits.
@@ -26,6 +26,7 @@
#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;
@@ -2895,30 +2896,213 @@ static int mark_uninteresting(const struct object_id *oid,
return 0;
}
-struct topo_walk_info {};
+define_commit_slab(indegree_slab, int);
+
+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->max_age != -1 && (c->date < revs->max_age))
+ c->object.flags |= UNINTERESTING;
+
+ if (add_parents_to_list(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)
+{
+ struct topo_walk_info *info = revs->topo_walk_info;
+ struct commit *c;
+ while ((c = prio_queue_peek(&info->explore_queue)) &&
+ c->generation >= gen)
+ 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);
+
+ if (parse_commit_gently(c, 1) < 0)
+ return;
+
+ 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)
+{
+ struct topo_walk_info *info = revs->topo_walk_info;
+ struct commit *c;
+ while ((c = prio_queue_peek(&info->indegree_queue)) &&
+ c->generation >= info->min_generation)
+ 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));
- limit_list(revs);
- sort_in_topological_order(&revs->commits, revs->sort_order);
+ 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;
+ test_flag_and_insert(&info->explore_queue, c, TOPO_WALK_EXPLORED);
+ test_flag_and_insert(&info->indegree_queue, c, TOPO_WALK_INDEGREE);
+
+ if (parse_commit_gently(c, 1))
+ continue;
+ if (c->generation < info->min_generation)
+ info->min_generation = c->generation;
+ }
+
+ for (list = revs->commits; list; list = list->next) {
+ struct commit *c = list->item;
+ *(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);
+
+ 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)
{
- return pop_commit(&revs->commits);
+ 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)
{
- if (add_parents_to_list(revs, commit, &revs->commits, NULL) < 0) {
+ struct commit_list *p;
+ struct topo_walk_info *info = revs->topo_walk_info;
+ if (add_parents_to_list(revs, commit, NULL, NULL) < 0) {
if (!revs->ignore_missing_links)
die("Failed to traverse parents of commit %s",
- oid_to_hex(&commit->object.oid));
+ 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);
+ }
+
+ pi = indegree_slab_at(&info->indegree, parent);
+
+ (*pi)--;
+ if (*pi == 1)
+ prio_queue_put(&info->topo_queue, parent);
+
+ if (revs->first_parent_only)
+ return;
}
}
@@ -24,6 +24,8 @@
#define USER_GIVEN (1u<<25) /* given directly by the user */
#define TRACK_LINEAR (1u<<26)
#define ALL_REV_FLAGS (((1u<<11)-1) | USER_GIVEN | TRACK_LINEAR)
+#define TOPO_WALK_EXPLORED (1u<<27)
+#define TOPO_WALK_INDEGREE (1u<<28)
#define DECORATE_SHORT_REFS 1
#define DECORATE_FULL_REFS 2