commit-graph: don't call write_graph_chunk_extra_edges() unnecessarily
[gitweb.git] / commit-graph.c
index 7f4519ec3b1a6659551fc4ba792cbef151367a4e..e733ba180aee775f4580185c5b3ffc7de262c115 100644 (file)
 #include "commit-graph.h"
 #include "object-store.h"
 #include "alloc.h"
+#include "hashmap.h"
+#include "replace-object.h"
+#include "progress.h"
 
 #define GRAPH_SIGNATURE 0x43475048 /* "CGPH" */
 #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
 #define GRAPH_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
 #define GRAPH_CHUNKID_DATA 0x43444154 /* "CDAT" */
-#define GRAPH_CHUNKID_LARGEEDGES 0x45444745 /* "EDGE" */
+#define GRAPH_CHUNKID_EXTRAEDGES 0x45444745 /* "EDGE" */
 
 #define GRAPH_DATA_WIDTH 36
 
@@ -30,7 +33,7 @@
 #define GRAPH_OID_VERSION GRAPH_OID_VERSION_SHA1
 #define GRAPH_OID_LEN GRAPH_OID_LEN_SHA1
 
-#define GRAPH_OCTOPUS_EDGES_NEEDED 0x80000000
+#define GRAPH_EXTRA_EDGES_NEEDED 0x80000000
 #define GRAPH_PARENT_MISSING 0x7fffffff
 #define GRAPH_EDGE_LAST_MASK 0x7fffffff
 #define GRAPH_PARENT_NONE 0x70000000
@@ -56,6 +59,28 @@ static struct commit_graph *alloc_commit_graph(void)
        return g;
 }
 
+extern int read_replace_refs;
+
+static int commit_graph_compatible(struct repository *r)
+{
+       if (!r->gitdir)
+               return 0;
+
+       if (read_replace_refs) {
+               prepare_replace_object(r);
+               if (hashmap_get_size(&r->objects->replace_map->map))
+                       return 0;
+       }
+
+       prepare_commit_graft(r);
+       if (r->parsed_objects && r->parsed_objects->grafts_nr)
+               return 0;
+       if (is_repository_shallow(r))
+               return 0;
+
+       return 1;
+}
+
 struct commit_graph *load_commit_graph_one(const char *graph_file)
 {
        void *graph_map;
@@ -152,11 +177,11 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
                                graph->chunk_commit_data = data + chunk_offset;
                        break;
 
-               case GRAPH_CHUNKID_LARGEEDGES:
-                       if (graph->chunk_large_edges)
+               case GRAPH_CHUNKID_EXTRAEDGES:
+                       if (graph->chunk_extra_edges)
                                chunk_repeated = 1;
                        else
-                               graph->chunk_large_edges = data + chunk_offset;
+                               graph->chunk_extra_edges = data + chunk_offset;
                        break;
                }
 
@@ -205,8 +230,7 @@ static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)
  */
 static int prepare_commit_graph(struct repository *r)
 {
-       struct alternate_object_database *alt;
-       char *obj_dir;
+       struct object_directory *odb;
        int config_value;
 
        if (r->objects->commit_graph_attempted)
@@ -224,13 +248,14 @@ static int prepare_commit_graph(struct repository *r)
                 */
                return 0;
 
-       obj_dir = r->objects->objectdir;
-       prepare_commit_graph_one(r, obj_dir);
+       if (!commit_graph_compatible(r))
+               return 0;
+
        prepare_alt_odb(r);
-       for (alt = r->objects->alt_odb_list;
-            !r->objects->commit_graph && alt;
-            alt = alt->next)
-               prepare_commit_graph_one(r, alt->path);
+       for (odb = r->objects->odb;
+            !r->objects->commit_graph && odb;
+            odb = odb->next)
+               prepare_commit_graph_one(r, odb->path);
        return !!r->objects->commit_graph;
 }
 
@@ -252,10 +277,10 @@ int generation_numbers_enabled(struct repository *r)
        return !!first_generation;
 }
 
-static void close_commit_graph(void)
+void close_commit_graph(struct repository *r)
 {
-       free_commit_graph(the_repository->objects->commit_graph);
-       the_repository->objects->commit_graph = NULL;
+       free_commit_graph(r->objects->commit_graph);
+       r->objects->commit_graph = NULL;
 }
 
 static int bsearch_graph(struct commit_graph *g, struct object_id *oid, uint32_t *pos)
@@ -318,12 +343,12 @@ static int fill_commit_in_graph(struct commit *item, struct commit_graph *g, uin
        edge_value = get_be32(commit_data + g->hash_len + 4);
        if (edge_value == GRAPH_PARENT_NONE)
                return 1;
-       if (!(edge_value & GRAPH_OCTOPUS_EDGES_NEEDED)) {
+       if (!(edge_value & GRAPH_EXTRA_EDGES_NEEDED)) {
                pptr = insert_parent_or_die(g, edge_value, pptr);
                return 1;
        }
 
-       parent_data_ptr = (uint32_t*)(g->chunk_large_edges +
+       parent_data_ptr = (uint32_t*)(g->chunk_extra_edges +
                          4 * (uint64_t)(edge_value & GRAPH_EDGE_LAST_MASK));
        do {
                edge_value = get_be32(parent_data_ptr);
@@ -479,7 +504,7 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
                if (!parent)
                        edge_value = GRAPH_PARENT_NONE;
                else if (parent->next)
-                       edge_value = GRAPH_OCTOPUS_EDGES_NEEDED | num_extra_edges;
+                       edge_value = GRAPH_EXTRA_EDGES_NEEDED | num_extra_edges;
                else {
                        edge_value = sha1_pos(parent->item->object.oid.hash,
                                              commits,
@@ -491,7 +516,7 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
 
                hashwrite_be32(f, edge_value);
 
-               if (edge_value & GRAPH_OCTOPUS_EDGES_NEEDED) {
+               if (edge_value & GRAPH_EXTRA_EDGES_NEEDED) {
                        do {
                                num_extra_edges++;
                                parent = parent->next;
@@ -512,7 +537,7 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
        }
 }
 
-static void write_graph_chunk_large_edges(struct hashfile *f,
+static void write_graph_chunk_extra_edges(struct hashfile *f,
                                          struct commit **commits,
                                          int nr_commits)
 {
@@ -567,6 +592,8 @@ struct packed_oid_list {
        struct object_id *list;
        int nr;
        int alloc;
+       struct progress *progress;
+       int progress_done;
 };
 
 static int add_packed_commits(const struct object_id *oid,
@@ -579,6 +606,9 @@ static int add_packed_commits(const struct object_id *oid,
        off_t offset = nth_packed_object_offset(pack, pos);
        struct object_info oi = OBJECT_INFO_INIT;
 
+       if (list->progress)
+               display_progress(list->progress, ++list->progress_done);
+
        oi.typep = &type;
        if (packed_object_info(the_repository, pack, offset, &oi) < 0)
                die(_("unable to get type of object %s"), oid_to_hex(oid));
@@ -606,43 +636,66 @@ static void add_missing_parents(struct packed_oid_list *oids, struct commit *com
        }
 }
 
-static void close_reachable(struct packed_oid_list *oids)
+static void close_reachable(struct packed_oid_list *oids, int report_progress)
 {
-       int i;
+       int i, j;
        struct commit *commit;
+       struct progress *progress = NULL;
 
+       if (report_progress)
+               progress = start_delayed_progress(
+                       _("Loading known commits in commit graph"), j = 0);
        for (i = 0; i < oids->nr; i++) {
+               display_progress(progress, ++j);
                commit = lookup_commit(the_repository, &oids->list[i]);
                if (commit)
                        commit->object.flags |= UNINTERESTING;
        }
+       stop_progress(&progress);
 
        /*
         * As this loop runs, oids->nr may grow, but not more
         * than the number of missing commits in the reachable
         * closure.
         */
+       if (report_progress)
+               progress = start_delayed_progress(
+                       _("Expanding reachable commits in commit graph"), j = 0);
        for (i = 0; i < oids->nr; i++) {
+               display_progress(progress, ++j);
                commit = lookup_commit(the_repository, &oids->list[i]);
 
                if (commit && !parse_commit(commit))
                        add_missing_parents(oids, commit);
        }
+       stop_progress(&progress);
 
+       if (report_progress)
+               progress = start_delayed_progress(
+                       _("Clearing commit marks in commit graph"), j = 0);
        for (i = 0; i < oids->nr; i++) {
+               display_progress(progress, ++j);
                commit = lookup_commit(the_repository, &oids->list[i]);
 
                if (commit)
                        commit->object.flags &= ~UNINTERESTING;
        }
+       stop_progress(&progress);
 }
 
-static void compute_generation_numbers(struct packed_commit_list* commits)
+static void compute_generation_numbers(struct packed_commit_list* commits,
+                                      int report_progress)
 {
        int i;
        struct commit_list *list = NULL;
+       struct progress *progress = NULL;
 
+       if (report_progress)
+               progress = start_progress(
+                       _("Computing commit graph generation numbers"),
+                       commits->nr);
        for (i = 0; i < commits->nr; i++) {
+               display_progress(progress, i + 1);
                if (commits->list[i]->generation != GENERATION_NUMBER_INFINITY &&
                    commits->list[i]->generation != GENERATION_NUMBER_ZERO)
                        continue;
@@ -674,6 +727,7 @@ static void compute_generation_numbers(struct packed_commit_list* commits)
                        }
                }
        }
+       stop_progress(&progress);
 }
 
 static int add_ref_to_list(const char *refname,
@@ -686,19 +740,21 @@ static int add_ref_to_list(const char *refname,
        return 0;
 }
 
-void write_commit_graph_reachable(const char *obj_dir, int append)
+void write_commit_graph_reachable(const char *obj_dir, int append,
+                                 int report_progress)
 {
-       struct string_list list;
+       struct string_list list = STRING_LIST_INIT_DUP;
 
-       string_list_init(&list, 1);
        for_each_ref(add_ref_to_list, &list);
-       write_commit_graph(obj_dir, NULL, &list, append);
+       write_commit_graph(obj_dir, NULL, &list, append, report_progress);
+
+       string_list_clear(&list, 0);
 }
 
 void write_commit_graph(const char *obj_dir,
                        struct string_list *pack_indexes,
                        struct string_list *commit_hex,
-                       int append)
+                       int append, int report_progress)
 {
        struct packed_oid_list oids;
        struct packed_commit_list commits;
@@ -711,9 +767,15 @@ void write_commit_graph(const char *obj_dir,
        int num_chunks;
        int num_extra_edges;
        struct commit_list *parent;
+       struct progress *progress = NULL;
+
+       if (!commit_graph_compatible(the_repository))
+               return;
 
        oids.nr = 0;
-       oids.alloc = approximate_object_count() / 4;
+       oids.alloc = approximate_object_count() / 32;
+       oids.progress = NULL;
+       oids.progress_done = 0;
 
        if (append) {
                prepare_commit_graph_one(the_repository, obj_dir);
@@ -740,6 +802,11 @@ void write_commit_graph(const char *obj_dir,
                int dirlen;
                strbuf_addf(&packname, "%s/pack/", obj_dir);
                dirlen = packname.len;
+               if (report_progress) {
+                       oids.progress = start_delayed_progress(
+                               _("Finding commits for commit graph"), 0);
+                       oids.progress_done = 0;
+               }
                for (i = 0; i < pack_indexes->nr; i++) {
                        struct packed_git *p;
                        strbuf_setlen(&packname, dirlen);
@@ -749,18 +816,26 @@ void write_commit_graph(const char *obj_dir,
                                die(_("error adding pack %s"), packname.buf);
                        if (open_pack_index(p))
                                die(_("error opening index for %s"), packname.buf);
-                       for_each_object_in_pack(p, add_packed_commits, &oids, 0);
+                       for_each_object_in_pack(p, add_packed_commits, &oids,
+                                               FOR_EACH_OBJECT_PACK_ORDER);
                        close_pack(p);
+                       free(p);
                }
+               stop_progress(&oids.progress);
                strbuf_release(&packname);
        }
 
        if (commit_hex) {
+               if (report_progress)
+                       progress = start_delayed_progress(
+                               _("Finding commits for commit graph"),
+                               commit_hex->nr);
                for (i = 0; i < commit_hex->nr; i++) {
                        const char *end;
                        struct object_id oid;
                        struct commit *result;
 
+                       display_progress(progress, i + 1);
                        if (commit_hex->items[i].string &&
                            parse_oid_hex(commit_hex->items[i].string, &oid, &end))
                                continue;
@@ -773,12 +848,19 @@ void write_commit_graph(const char *obj_dir,
                                oids.nr++;
                        }
                }
+               stop_progress(&progress);
        }
 
-       if (!pack_indexes && !commit_hex)
-               for_each_packed_object(add_packed_commits, &oids, 0);
+       if (!pack_indexes && !commit_hex) {
+               if (report_progress)
+                       oids.progress = start_delayed_progress(
+                               _("Finding commits for commit graph"), 0);
+               for_each_packed_object(add_packed_commits, &oids,
+                                      FOR_EACH_OBJECT_PACK_ORDER);
+               stop_progress(&oids.progress);
+       }
 
-       close_reachable(&oids);
+       close_reachable(&oids, report_progress);
 
        QSORT(oids.list, oids.nr, commit_compare);
 
@@ -818,12 +900,14 @@ 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);
+       compute_generation_numbers(&commits, report_progress);
 
        graph_name = get_commit_graph_filename(obj_dir);
-       if (safe_create_leading_directories(graph_name))
+       if (safe_create_leading_directories(graph_name)) {
+               UNLEAK(graph_name);
                die_errno(_("unable to create leading directories of %s"),
                          graph_name);
+       }
 
        hold_lock_file_for_update(&lk, graph_name, LOCK_DIE_ON_ERROR);
        f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);
@@ -839,7 +923,7 @@ void write_commit_graph(const char *obj_dir,
        chunk_ids[1] = GRAPH_CHUNKID_OIDLOOKUP;
        chunk_ids[2] = GRAPH_CHUNKID_DATA;
        if (num_extra_edges)
-               chunk_ids[3] = GRAPH_CHUNKID_LARGEEDGES;
+               chunk_ids[3] = GRAPH_CHUNKID_EXTRAEDGES;
        else
                chunk_ids[3] = 0;
        chunk_ids[4] = 0;
@@ -862,15 +946,16 @@ void write_commit_graph(const char *obj_dir,
        write_graph_chunk_fanout(f, commits.list, commits.nr);
        write_graph_chunk_oids(f, GRAPH_OID_LEN, commits.list, commits.nr);
        write_graph_chunk_data(f, GRAPH_OID_LEN, commits.list, commits.nr);
-       write_graph_chunk_large_edges(f, commits.list, commits.nr);
+       if (num_extra_edges)
+               write_graph_chunk_extra_edges(f, commits.list, commits.nr);
 
-       close_commit_graph();
+       close_commit_graph(the_repository);
        finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
        commit_lock_file(&lk);
 
+       free(graph_name);
+       free(commits.list);
        free(oids.list);
-       oids.alloc = 0;
-       oids.nr = 0;
 }
 
 #define VERIFY_COMMIT_GRAPH_ERROR_HASH 2
@@ -897,6 +982,7 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g)
        int generation_zero = 0;
        struct hashfile *f;
        int devnull;
+       struct progress *progress = NULL;
 
        if (!g) {
                graph_report("no commit-graph file loaded");
@@ -964,11 +1050,14 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g)
        if (verify_commit_graph_error & ~VERIFY_COMMIT_GRAPH_ERROR_HASH)
                return verify_commit_graph_error;
 
+       progress = start_progress(_("Verifying commits in commit graph"),
+                                 g->num_commits);
        for (i = 0; i < g->num_commits; i++) {
                struct commit *graph_commit, *odb_commit;
                struct commit_list *graph_parents, *odb_parents;
                uint32_t max_generation = 0;
 
+               display_progress(progress, i + 1);
                hashcpy(cur_oid.hash, g->chunk_oid_lookup + g->hash_len * i);
 
                graph_commit = lookup_commit(r, &cur_oid);
@@ -1045,6 +1134,7 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g)
                                     graph_commit->date,
                                     odb_commit->date);
        }
+       stop_progress(&progress);
 
        return verify_commit_graph_error;
 }