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
};
};
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
};
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;
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(),
};
opts.obj_dir = get_object_directory();
if (opts.append)
flags |= COMMIT_GRAPH_APPEND;
+ if (opts.split)
+ flags |= COMMIT_GRAPH_SPLIT;
read_replace_refs = 0;
}
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);
--- /dev/null
+#!/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