commit-graph: load commit-graph chains
[gitweb.git] / commit-graph.c
index 96b07a674e4d653e7ebd704687d4f05805fd3009..f7dfc6aecd46890147961a560738844760e8251f 100644 (file)
@@ -45,6 +45,19 @@ char *get_commit_graph_filename(const char *obj_dir)
        return xstrfmt("%s/info/commit-graph", obj_dir);
 }
 
+static char *get_split_graph_filename(const char *obj_dir,
+                                     const char *oid_hex)
+{
+       return xstrfmt("%s/info/commit-graphs/graph-%s.graph",
+                      obj_dir,
+                      oid_hex);
+}
+
+static char *get_chain_filename(const char *obj_dir)
+{
+       return xstrfmt("%s/info/commit-graphs/commit-graph-chain", obj_dir);
+}
+
 static uint8_t oid_version(void)
 {
        return 1;
@@ -286,18 +299,105 @@ static struct commit_graph *load_commit_graph_one(const char *graph_file)
        return load_commit_graph_one_fd_st(fd, &st);
 }
 
+static struct commit_graph *load_commit_graph_v1(struct repository *r, const char *obj_dir)
+{
+       char *graph_name = get_commit_graph_filename(obj_dir);
+       struct commit_graph *g = load_commit_graph_one(graph_name);
+       free(graph_name);
+
+       return g;
+}
+
+static int add_graph_to_chain(struct commit_graph *g,
+                             struct commit_graph *chain,
+                             struct object_id *oids,
+                             int n)
+{
+       struct commit_graph *cur_g = chain;
+
+       while (n) {
+               n--;
+               cur_g = cur_g->base_graph;
+       }
+
+       g->base_graph = chain;
+
+       if (chain)
+               g->num_commits_in_base = chain->num_commits + chain->num_commits_in_base;
+
+       return 1;
+}
+
+static struct commit_graph *load_commit_graph_chain(struct repository *r, const char *obj_dir)
+{
+       struct commit_graph *graph_chain = NULL;
+       struct strbuf line = STRBUF_INIT;
+       struct stat st;
+       struct object_id *oids;
+       int i = 0, valid = 1, count;
+       char *chain_name = get_chain_filename(obj_dir);
+       FILE *fp;
+       int stat_res;
+
+       fp = fopen(chain_name, "r");
+       stat_res = stat(chain_name, &st);
+       free(chain_name);
+
+       if (!fp ||
+           stat_res ||
+           st.st_size <= the_hash_algo->hexsz)
+               return NULL;
+
+       count = st.st_size / (the_hash_algo->hexsz + 1);
+       oids = xcalloc(count, sizeof(struct object_id));
+
+       for (i = 0; i < count && valid; i++) {
+               char *graph_name;
+               struct commit_graph *g;
+
+               if (strbuf_getline_lf(&line, fp) == EOF)
+                       break;
+
+               if (get_oid_hex(line.buf, &oids[i])) {
+                       warning(_("invalid commit-graph chain: line '%s' not a hash"),
+                               line.buf);
+                       valid = 0;
+                       break;
+               }
+
+               graph_name = get_split_graph_filename(obj_dir, line.buf);
+               g = load_commit_graph_one(graph_name);
+               free(graph_name);
+
+               if (g && add_graph_to_chain(g, graph_chain, oids, i))
+                       graph_chain = g;
+               else
+                       valid = 0;
+       }
+
+       free(oids);
+       fclose(fp);
+
+       return graph_chain;
+}
+
+static struct commit_graph *read_commit_graph_one(struct repository *r, const char *obj_dir)
+{
+       struct commit_graph *g = load_commit_graph_v1(r, obj_dir);
+
+       if (!g)
+               g = load_commit_graph_chain(r, obj_dir);
+
+       return g;
+}
+
 static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)
 {
-       char *graph_name;
 
        if (r->objects->commit_graph)
                return;
 
-       graph_name = get_commit_graph_filename(obj_dir);
-       r->objects->commit_graph =
-               load_commit_graph_one(graph_name);
-
-       FREE_AND_NULL(graph_name);
+       r->objects->commit_graph = read_commit_graph_one(r, obj_dir);
 }
 
 /*