Merge branch 'ab/commit-graph-progress'
authorJunio C Hamano <gitster@pobox.com>
Tue, 16 Oct 2018 07:15:58 +0000 (16:15 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 16 Oct 2018 07:15:58 +0000 (16:15 +0900)
Generation of (experimental) commit-graph files have so far been
fairly silent, even though it takes noticeable amount of time in a
meaningfully large repository. The users will now see progress
output.

* ab/commit-graph-progress:
gc: fix regression in 7b0f229222 impacting --quiet
commit-graph verify: add progress output
commit-graph write: add progress output

1  2 
builtin/commit.c
builtin/gc.c
commit-graph.c
commit-graph.h
diff --combined builtin/commit.c
index b57d8e4b82052bdb85287b5fcb675261a68c639b,0d9828e29ebe89f037e68761d5cd5b407339cd48..3f33ae60430d5d0ba52a6956539b1dadbb10e309
@@@ -33,8 -33,6 +33,8 @@@
  #include "sequencer.h"
  #include "mailmap.h"
  #include "help.h"
 +#include "commit-reach.h"
 +#include "commit-graph.h"
  
  static const char * const builtin_commit_usage[] = {
        N_("git commit [<options>] [--] <pathspec>..."),
@@@ -1653,9 -1651,6 +1653,9 @@@ int cmd_commit(int argc, const char **a
                      "new_index file. Check that disk is not full and quota is\n"
                      "not exceeded, and then \"git reset HEAD\" to recover."));
  
-               write_commit_graph_reachable(get_object_directory(), 0);
 +      if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
++              write_commit_graph_reachable(get_object_directory(), 0, 0);
 +
        rerere(0);
        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
        run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
diff --combined builtin/gc.c
index 2b592260e9ad0de18e8eaae4df20cc6f886a4681,91ffb1a803db63206e75cc68df2d671ba4d5e97b..6591ddbe83c46b22aa514cd962a1087294cd1caa
@@@ -183,7 -183,7 +183,7 @@@ static struct packed_git *find_base_pac
  {
        struct packed_git *p, *base = NULL;
  
 -      for (p = get_packed_git(the_repository); p; p = p->next) {
 +      for (p = get_all_packs(the_repository); p; p = p->next) {
                if (!p->pack_local)
                        continue;
                if (limit) {
@@@ -208,7 -208,7 +208,7 @@@ static int too_many_packs(void
        if (gc_auto_pack_limit <= 0)
                return 0;
  
 -      for (cnt = 0, p = get_packed_git(the_repository); p; p = p->next) {
 +      for (cnt = 0, p = get_all_packs(the_repository); p; p = p->next) {
                if (!p->pack_local)
                        continue;
                if (p->pack_keep)
@@@ -646,7 -646,8 +646,8 @@@ int cmd_gc(int argc, const char **argv
                clean_pack_garbage();
  
        if (gc_write_commit_graph)
-               write_commit_graph_reachable(get_object_directory(), 0);
+               write_commit_graph_reachable(get_object_directory(), 0,
+                                            !quiet && !daemonized);
  
        if (auto_gc && too_many_loose_objects())
                warning(_("There are too many unreachable loose objects; "
diff --combined commit-graph.c
index 7f4519ec3b1a6659551fc4ba792cbef151367a4e,2a24eb8b5a60bfbb66597b4b8dd113eeb0f4715f..5908bd4e3429fe9bc187db82fb2d14625f1bbd1c
@@@ -13,6 -13,7 +13,7 @@@
  #include "commit-graph.h"
  #include "object-store.h"
  #include "alloc.h"
+ #include "progress.h"
  
  #define GRAPH_SIGNATURE 0x43475048 /* "CGPH" */
  #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
@@@ -213,9 -214,8 +214,9 @@@ static int prepare_commit_graph(struct 
                return !!r->objects->commit_graph;
        r->objects->commit_graph_attempted = 1;
  
 -      if (repo_config_get_bool(r, "core.commitgraph", &config_value) ||
 -          !config_value)
 +      if (!git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&
 +          (repo_config_get_bool(r, "core.commitgraph", &config_value) ||
 +          !config_value))
                /*
                 * This repository is not configured to use commit graphs, so
                 * do not load one. (But report commit_graph_attempted anyway
        return !!r->objects->commit_graph;
  }
  
 +int generation_numbers_enabled(struct repository *r)
 +{
 +      uint32_t first_generation;
 +      struct commit_graph *g;
 +      if (!prepare_commit_graph(r))
 +             return 0;
 +
 +      g = r->objects->commit_graph;
 +
 +      if (!g->num_commits)
 +              return 0;
 +
 +      first_generation = get_be32(g->chunk_commit_data +
 +                                  g->hash_len + 8) >> 2;
 +
 +      return !!first_generation;
 +}
 +
  static void close_commit_graph(void)
  {
        free_commit_graph(the_repository->objects->commit_graph);
@@@ -567,6 -549,8 +568,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,
        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,12 -593,18 +612,18 @@@ static void add_missing_parents(struct 
        }
  }
  
- static void close_reachable(struct packed_oid_list *oids)
+ static void close_reachable(struct packed_oid_list *oids, int report_progress)
  {
        int i;
        struct commit *commit;
+       struct progress *progress = NULL;
+       int j = 0;
  
+       if (report_progress)
+               progress = start_delayed_progress(
+                       _("Annotating commits in commit graph"), 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;
         * closure.
         */
        for (i = 0; i < oids->nr; i++) {
+               display_progress(progress, ++j);
                commit = lookup_commit(the_repository, &oids->list[i]);
  
                if (commit && !parse_commit(commit))
        }
  
        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;
                        }
                }
        }
+       stop_progress(&progress);
  }
  
  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;
  
        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);
  }
  
  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;
        int num_chunks;
        int num_extra_edges;
        struct commit_list *parent;
+       struct progress *progress = NULL;
  
        oids.nr = 0;
        oids.alloc = approximate_object_count() / 4;
+       oids.progress = NULL;
+       oids.progress_done = 0;
  
        if (append) {
                prepare_commit_graph_one(the_repository, 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);
                        for_each_object_in_pack(p, add_packed_commits, &oids, 0);
                        close_pack(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;
                                oids.nr++;
                        }
                }
+               stop_progress(&progress);
        }
  
-       if (!pack_indexes && !commit_hex)
+       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, 0);
+               stop_progress(&oids.progress);
+       }
  
-       close_reachable(&oids);
+       close_reachable(&oids, report_progress);
  
        QSORT(oids.list, oids.nr, commit_compare);
  
        count_distinct = 1;
        for (i = 1; i < oids.nr; i++) {
 -              if (oidcmp(&oids.list[i-1], &oids.list[i]))
 +              if (!oideq(&oids.list[i - 1], &oids.list[i]))
                        count_distinct++;
        }
  
        num_extra_edges = 0;
        for (i = 0; i < oids.nr; i++) {
                int num_parents = 0;
 -              if (i > 0 && !oidcmp(&oids.list[i-1], &oids.list[i]))
 +              if (i > 0 && oideq(&oids.list[i - 1], &oids.list[i]))
                        continue;
  
                commits.list[commits.nr] = lookup_commit(the_repository, &oids.list[i]);
        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))
@@@ -897,6 -922,7 +941,7 @@@ int verify_commit_graph(struct reposito
        int generation_zero = 0;
        struct hashfile *f;
        int devnull;
+       struct progress *progress = NULL;
  
        if (!g) {
                graph_report("no commit-graph file loaded");
        f = hashfd(devnull, NULL);
        hashwrite(f, g->data, g->data_len - g->hash_len);
        finalize_hashfile(f, checksum.hash, CSUM_CLOSE);
 -      if (hashcmp(checksum.hash, g->data + g->data_len - g->hash_len)) {
 +      if (!hasheq(checksum.hash, g->data + g->data_len - g->hash_len)) {
                graph_report(_("the commit-graph file has incorrect checksum and is likely corrupt"));
                verify_commit_graph_error = VERIFY_COMMIT_GRAPH_ERROR_HASH;
        }
        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);
                        continue;
                }
  
 -              if (oidcmp(&get_commit_tree_in_graph_one(g, graph_commit)->object.oid,
 +              if (!oideq(&get_commit_tree_in_graph_one(g, graph_commit)->object.oid,
                           get_commit_tree_oid(odb_commit)))
                        graph_report("root tree OID for commit %s in commit-graph is %s != %s",
                                     oid_to_hex(&cur_oid),
                                break;
                        }
  
 -                      if (oidcmp(&graph_parents->item->object.oid, &odb_parents->item->object.oid))
 +                      if (!oideq(&graph_parents->item->object.oid, &odb_parents->item->object.oid))
                                graph_report("commit-graph parent for %s is %s != %s",
                                             oid_to_hex(&cur_oid),
                                             oid_to_hex(&graph_parents->item->object.oid),
                                     graph_commit->date,
                                     odb_commit->date);
        }
+       stop_progress(&progress);
  
        return verify_commit_graph_error;
  }
diff --combined commit-graph.h
index b05047676514e4d031ac90de50510eb33a4b6db6,f50712a973a2a88d84cbce35acf54276bb3ed021..5678a8f4cad2ddec9c7c50a31f0a54abc026f951
@@@ -6,8 -6,6 +6,8 @@@
  #include "string-list.h"
  #include "cache.h"
  
 +#define GIT_TEST_COMMIT_GRAPH "GIT_TEST_COMMIT_GRAPH"
 +
  struct commit;
  
  char *get_commit_graph_filename(const char *obj_dir);
@@@ -54,17 -52,12 +54,18 @@@ struct commit_graph 
  
  struct commit_graph *load_commit_graph_one(const char *graph_file);
  
- void write_commit_graph_reachable(const char *obj_dir, int append);
 +/*
 + * Return 1 if and only if the repository has a commit-graph
 + * file and generation numbers are computed in that file.
 + */
 +int generation_numbers_enabled(struct repository *r);
 +
+ void write_commit_graph_reachable(const char *obj_dir, int append,
+                                 int report_progress);
  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);
  
  int verify_commit_graph(struct repository *r, struct commit_graph *g);