]> git.ipfire.org Git - thirdparty/git.git/commitdiff
maintenance: add commit-graph task
authorDerrick Stolee <dstolee@microsoft.com>
Thu, 17 Sep 2020 18:11:46 +0000 (18:11 +0000)
committerJunio C Hamano <gitster@pobox.com>
Thu, 17 Sep 2020 18:30:05 +0000 (11:30 -0700)
The first new task in the 'git maintenance' builtin is the
'commit-graph' task. This updates the commit-graph file
incrementally with the command

git commit-graph write --reachable --split

By writing an incremental commit-graph file using the "--split"
option we minimize the disruption from this operation. The default
behavior is to merge layers until the new "top" layer is less than
half the size of the layer below. This provides quick writes most
of the time, with the longer writes following a power law
distribution.

Most importantly, concurrent Git processes only look at the
commit-graph-chain file for a very short amount of time, so they
will verly likely not be holding a handle to the file when we try
to replace it. (This only matters on Windows.)

If a concurrent process reads the old commit-graph-chain file, but
our job expires some of the .graph files before they can be read,
then those processes will see a warning message (but not fail).
This could be avoided by a future update to use the --expire-time
argument when writing the commit-graph.

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/git-maintenance.txt
builtin/gc.c
commit-graph.c
commit-graph.h
t/t7900-maintenance.sh

index 04fa0fe3298e7793bf0f865c5dccbb500d09bf20..fc5dbcf0b9efd3ab6630dd2de94f8d1e8fcc16e5 100644 (file)
@@ -35,6 +35,14 @@ run::
 TASKS
 -----
 
+commit-graph::
+       The `commit-graph` job updates the `commit-graph` files incrementally,
+       then verifies that the written data is correct. The incremental
+       write is safe to run alongside concurrent Git processes since it
+       will not expire `.graph` files that were in the previous
+       `commit-graph-chain` file. They will be deleted by a later run based
+       on the expiration delay.
+
 gc::
        Clean up unnecessary files and optimize the local repository. "GC"
        stands for "garbage collection," but this task performs many
index 904fb2d9aa320adf8a4621c574dd81822428af44..86b807a008e1951b2b83d784c03e5d358d90574e 100644 (file)
@@ -710,6 +710,31 @@ struct maintenance_run_opts {
        int quiet;
 };
 
+static int run_write_commit_graph(struct maintenance_run_opts *opts)
+{
+       struct child_process child = CHILD_PROCESS_INIT;
+
+       child.git_cmd = 1;
+       strvec_pushl(&child.args, "commit-graph", "write",
+                    "--split", "--reachable", NULL);
+
+       if (opts->quiet)
+               strvec_push(&child.args, "--no-progress");
+
+       return !!run_command(&child);
+}
+
+static int maintenance_task_commit_graph(struct maintenance_run_opts *opts)
+{
+       close_object_store(the_repository->objects);
+       if (run_write_commit_graph(opts)) {
+               error(_("failed to write commit-graph"));
+               return 1;
+       }
+
+       return 0;
+}
+
 static int maintenance_task_gc(struct maintenance_run_opts *opts)
 {
        struct child_process child = CHILD_PROCESS_INIT;
@@ -738,6 +763,7 @@ struct maintenance_task {
 
 enum maintenance_task_label {
        TASK_GC,
+       TASK_COMMIT_GRAPH,
 
        /* Leave as final value */
        TASK__COUNT
@@ -749,6 +775,10 @@ static struct maintenance_task tasks[] = {
                maintenance_task_gc,
                1,
        },
+       [TASK_COMMIT_GRAPH] = {
+               "commit-graph",
+               maintenance_task_commit_graph,
+       },
 };
 
 static int maintenance_run_tasks(struct maintenance_run_opts *opts)
index e51c91dd5b0af4e4946091e5ca7f06707b3a0c5b..a9b0db7d4ac4c1e3ef7e45f6fee202ec86b1cbb5 100644 (file)
@@ -172,7 +172,7 @@ static char *get_split_graph_filename(struct object_directory *odb,
                       oid_hex);
 }
 
-static char *get_chain_filename(struct object_directory *odb)
+char *get_commit_graph_chain_filename(struct object_directory *odb)
 {
        return xstrfmt("%s/info/commit-graphs/commit-graph-chain", odb->path);
 }
@@ -516,7 +516,7 @@ static struct commit_graph *load_commit_graph_chain(struct repository *r,
        struct stat st;
        struct object_id *oids;
        int i = 0, valid = 1, count;
-       char *chain_name = get_chain_filename(odb);
+       char *chain_name = get_commit_graph_chain_filename(odb);
        FILE *fp;
        int stat_res;
 
@@ -1668,7 +1668,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
        }
 
        if (ctx->split) {
-               char *lock_name = get_chain_filename(ctx->odb);
+               char *lock_name = get_commit_graph_chain_filename(ctx->odb);
 
                hold_lock_file_for_update_mode(&lk, lock_name,
                                               LOCK_DIE_ON_ERROR, 0444);
@@ -2038,7 +2038,7 @@ static void expire_commit_graphs(struct write_commit_graph_context *ctx)
        if (ctx->split_opts && ctx->split_opts->expire_time)
                expire_time = ctx->split_opts->expire_time;
        if (!ctx->split) {
-               char *chain_file_name = get_chain_filename(ctx->odb);
+               char *chain_file_name = get_commit_graph_chain_filename(ctx->odb);
                unlink(chain_file_name);
                free(chain_file_name);
                ctx->num_commit_graphs_after = 0;
index 09a97030dcd8a2ea570da097d6f27a5d0cbaf934..765221cdcc11410271ac93e5bf275e5ae21027cb 100644 (file)
@@ -25,6 +25,7 @@ struct raw_object_store;
 struct string_list;
 
 char *get_commit_graph_filename(struct object_directory *odb);
+char *get_commit_graph_chain_filename(struct object_directory *odb);
 int open_commit_graph(const char *graph_file, int *fd, struct stat *st);
 
 /*
index 5637053bf658209d598ac5b31ea7028f620cc44d..505a1b4d60acf8ffc24b205b1698151c72848960 100755 (executable)
@@ -4,6 +4,8 @@ test_description='git maintenance builtin'
 
 . ./test-lib.sh
 
+GIT_TEST_COMMIT_GRAPH=0
+
 test_expect_success 'help text' '
        test_expect_code 129 git maintenance -h 2>err &&
        test_i18ngrep "usage: git maintenance run" err &&