commit-graph: add --split option to builtin
authorDerrick Stolee <dstolee@microsoft.com>
Tue, 18 Jun 2019 18:14:28 +0000 (11:14 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 20 Jun 2019 03:46:26 +0000 (20:46 -0700)
Add a new "--split" option to the 'git commit-graph write' subcommand. This
option allows the optional behavior of writing a commit-graph chain.

The current behavior will add a tip commit-graph containing any commits that
are not in the existing commit-graph or commit-graph chain. Later changes
will allow merging the chain and expiring out-dated files.

Add a new test script (t5324-split-commit-graph.sh) that demonstrates this
behavior.

Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/commit-graph.c
commit-graph.c
t/t5324-split-commit-graph.sh [new file with mode: 0755]
index d8efa5bab276a816bef48e9cb3891ccc0cb81aad..ff8bc3c8b9e01760abcb45bbc9ddbfeada866ace 100644 (file)
@@ -10,7 +10,7 @@ static char const * const builtin_commit_graph_usage[] = {
        N_("git commit-graph [--object-dir <objdir>]"),
        N_("git commit-graph read [--object-dir <objdir>]"),
        N_("git commit-graph verify [--object-dir <objdir>]"),
-       N_("git commit-graph write [--object-dir <objdir>] [--append] [--reachable|--stdin-packs|--stdin-commits]"),
+       N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits]"),
        NULL
 };
 
@@ -25,7 +25,7 @@ static const char * const builtin_commit_graph_read_usage[] = {
 };
 
 static const char * const builtin_commit_graph_write_usage[] = {
-       N_("git commit-graph write [--object-dir <objdir>] [--append] [--reachable|--stdin-packs|--stdin-commits]"),
+       N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits]"),
        NULL
 };
 
@@ -35,9 +35,9 @@ static struct opts_commit_graph {
        int stdin_packs;
        int stdin_commits;
        int append;
+       int split;
 } opts;
 
-
 static int graph_verify(int argc, const char **argv)
 {
        struct commit_graph *graph = NULL;
@@ -156,6 +156,8 @@ static int graph_write(int argc, const char **argv)
                        N_("start walk at commits listed by stdin")),
                OPT_BOOL(0, "append", &opts.append,
                        N_("include all commits already in the commit-graph file")),
+               OPT_BOOL(0, "split", &opts.split,
+                       N_("allow writing an incremental commit-graph file")),
                OPT_END(),
        };
 
@@ -169,6 +171,8 @@ static int graph_write(int argc, const char **argv)
                opts.obj_dir = get_object_directory();
        if (opts.append)
                flags |= COMMIT_GRAPH_APPEND;
+       if (opts.split)
+               flags |= COMMIT_GRAPH_SPLIT;
 
        read_replace_refs = 0;
 
index f0698b05996757ae38562ad038400f6fb8889f17..1224309e5feb48a6cef4b86c41f5b4fecfcce604 100644 (file)
@@ -1472,12 +1472,16 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
                }
 
                if (ctx->base_graph_name) {
-                       result = rename(ctx->base_graph_name,
-                                       ctx->commit_graph_filenames_after[ctx->num_commit_graphs_after - 2]);
+                       const char *dest = ctx->commit_graph_filenames_after[
+                                               ctx->num_commit_graphs_after - 2];
 
-                       if (result) {
-                               error(_("failed to rename base commit-graph file"));
-                               return -1;
+                       if (strcmp(ctx->base_graph_name, dest)) {
+                               result = rename(ctx->base_graph_name, dest);
+
+                               if (result) {
+                                       error(_("failed to rename base commit-graph file"));
+                                       return -1;
+                               }
                        }
                } else {
                        char *graph_name = get_commit_graph_filename(ctx->obj_dir);
diff --git a/t/t5324-split-commit-graph.sh b/t/t5324-split-commit-graph.sh
new file mode 100755 (executable)
index 0000000..ccd24bd
--- /dev/null
@@ -0,0 +1,122 @@
+#!/bin/sh
+
+test_description='split commit graph'
+. ./test-lib.sh
+
+GIT_TEST_COMMIT_GRAPH=0
+
+test_expect_success 'setup repo' '
+       git init &&
+       git config core.commitGraph true &&
+       infodir=".git/objects/info" &&
+       graphdir="$infodir/commit-graphs" &&
+       test_oid_init
+'
+
+graph_read_expect() {
+       NUM_BASE=0
+       if test ! -z $2
+       then
+               NUM_BASE=$2
+       fi
+       cat >expect <<- EOF
+       header: 43475048 1 1 3 $NUM_BASE
+       num_commits: $1
+       chunks: oid_fanout oid_lookup commit_metadata
+       EOF
+       git commit-graph read >output &&
+       test_cmp expect output
+}
+
+test_expect_success 'create commits and write commit-graph' '
+       for i in $(test_seq 3)
+       do
+               test_commit $i &&
+               git branch commits/$i || return 1
+       done &&
+       git commit-graph write --reachable &&
+       test_path_is_file $infodir/commit-graph &&
+       graph_read_expect 3
+'
+
+graph_git_two_modes() {
+       git -c core.commitGraph=true $1 >output
+       git -c core.commitGraph=false $1 >expect
+       test_cmp expect output
+}
+
+graph_git_behavior() {
+       MSG=$1
+       BRANCH=$2
+       COMPARE=$3
+       test_expect_success "check normal git operations: $MSG" '
+               graph_git_two_modes "log --oneline $BRANCH" &&
+               graph_git_two_modes "log --topo-order $BRANCH" &&
+               graph_git_two_modes "log --graph $COMPARE..$BRANCH" &&
+               graph_git_two_modes "branch -vv" &&
+               graph_git_two_modes "merge-base -a $BRANCH $COMPARE"
+       '
+}
+
+graph_git_behavior 'graph exists' commits/3 commits/1
+
+verify_chain_files_exist() {
+       for hash in $(cat $1/commit-graph-chain)
+       do
+               test_path_is_file $1/graph-$hash.graph || return 1
+       done
+}
+
+test_expect_success 'add more commits, and write a new base graph' '
+       git reset --hard commits/1 &&
+       for i in $(test_seq 4 5)
+       do
+               test_commit $i &&
+               git branch commits/$i || return 1
+       done &&
+       git reset --hard commits/2 &&
+       for i in $(test_seq 6 10)
+       do
+               test_commit $i &&
+               git branch commits/$i || return 1
+       done &&
+       git reset --hard commits/2 &&
+       git merge commits/4 &&
+       git branch merge/1 &&
+       git reset --hard commits/4 &&
+       git merge commits/6 &&
+       git branch merge/2 &&
+       git commit-graph write --reachable &&
+       graph_read_expect 12
+'
+
+test_expect_success 'add three more commits, write a tip graph' '
+       git reset --hard commits/3 &&
+       git merge merge/1 &&
+       git merge commits/5 &&
+       git merge merge/2 &&
+       git branch merge/3 &&
+       git commit-graph write --reachable --split &&
+       test_path_is_missing $infodir/commit-graph &&
+       test_path_is_file $graphdir/commit-graph-chain &&
+       ls $graphdir/graph-*.graph >graph-files &&
+       test_line_count = 2 graph-files &&
+       verify_chain_files_exist $graphdir
+'
+
+graph_git_behavior 'split commit-graph: merge 3 vs 2' merge/3 merge/2
+
+test_expect_success 'add one commit, write a tip graph' '
+       test_commit 11 &&
+       git branch commits/11 &&
+       git commit-graph write --reachable --split &&
+       test_path_is_missing $infodir/commit-graph &&
+       test_path_is_file $graphdir/commit-graph-chain &&
+       ls $graphdir/graph-*.graph >graph-files &&
+       test_line_count = 3 graph-files &&
+       verify_chain_files_exist $graphdir
+'
+
+graph_git_behavior 'three-layer commit-graph: commit 11 vs 6' commits/11 commits/6
+
+test_done