While preparing commits to be written into a commit-graph file, compute
the generation numbers using a depth-first strategy.

The only commits that are walked in this depth-first search are those
without a precomputed generation number. Thus, computation time will be
relative to the number of new commits to the commit-graph file.

Signed-off-by: Derrick Stolee <dsto...@microsoft.com>
---
 commit-graph.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 commit.h       |  1 +
 2 files changed, 47 insertions(+)

diff --git a/commit-graph.c b/commit-graph.c
index d24b947525..b80c8ad80e 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -419,6 +419,13 @@ static void write_graph_chunk_data(struct hashfile *f, int 
hash_len,
                else
                        packedDate[0] = 0;
 
+               if ((*list)->generation != GENERATION_NUMBER_UNDEF) {
+                       if ((*list)->generation > GENERATION_NUMBER_MAX)
+                               die("generation number %u is too large to store 
in commit-graph",
+                                   (*list)->generation);
+                       packedDate[0] |= htonl((*list)->generation << 2);
+               }
+
                packedDate[1] = htonl((*list)->date);
                hashwrite(f, packedDate, 8);
 
@@ -551,6 +558,43 @@ static void close_reachable(struct packed_oid_list *oids)
        }
 }
 
+static void compute_generation_numbers(struct commit** commits,
+                                      int nr_commits)
+{
+       int i;
+       struct commit_list *list = NULL;
+
+       for (i = 0; i < nr_commits; i++) {
+               if (commits[i]->generation != GENERATION_NUMBER_UNDEF &&
+                   commits[i]->generation != GENERATION_NUMBER_NONE)
+                       continue;
+
+               commit_list_insert(commits[i], &list);
+               while (list) {
+                       struct commit *current = list->item;
+                       struct commit_list *parent;
+                       int all_parents_computed = 1;
+                       uint32_t max_generation = 0;
+
+                       for (parent = current->parents; parent; parent = 
parent->next) {
+                               if (parent->item->generation == 
GENERATION_NUMBER_UNDEF ||
+                                   parent->item->generation == 
GENERATION_NUMBER_NONE) {
+                                       all_parents_computed = 0;
+                                       commit_list_insert(parent->item, &list);
+                                       break;
+                               } else if (parent->item->generation > 
max_generation) {
+                                       max_generation = 
parent->item->generation;
+                               }
+                       }
+
+                       if (all_parents_computed) {
+                               current->generation = max_generation + 1;
+                               pop_commit(&list);
+                       }
+               }
+       }
+}
+
 void write_commit_graph(const char *obj_dir,
                        const char **pack_indexes,
                        int nr_packs,
@@ -674,6 +718,8 @@ void write_commit_graph(const char *obj_dir,
        if (commits.nr >= GRAPH_PARENT_MISSING)
                die(_("too many commits to write graph"));
 
+       compute_generation_numbers(commits.list, commits.nr);
+
        graph_name = get_commit_graph_filename(obj_dir);
        fd = hold_lock_file_for_update(&lk, graph_name, 0);
 
diff --git a/commit.h b/commit.h
index 3cadd386f3..bc7a3186c5 100644
--- a/commit.h
+++ b/commit.h
@@ -11,6 +11,7 @@
 
 #define COMMIT_NOT_FROM_GRAPH 0xFFFFFFFF
 #define GENERATION_NUMBER_UNDEF 0xFFFFFFFF
+#define GENERATION_NUMBER_MAX 0x3FFFFFFF
 #define GENERATION_NUMBER_NONE 0
 
 struct commit_list {
-- 
2.17.0.rc0

Reply via email to