diff --git a/Documentation/git-commit-graph.txt b/Documentation/git-commit-graph.txt index b9b4031469c95f..93d50d1e278d6f 100644 --- a/Documentation/git-commit-graph.txt +++ b/Documentation/git-commit-graph.txt @@ -42,6 +42,10 @@ With the `--delete-expired` option, delete the graph files in the pack directory that are not referred to by the graph-latest file. To avoid race conditions, do not delete the file previously referred to by the graph-latest file if it is updated by the `--set-latest` option. ++ +With the `--stdin-packs` option, generate the new commit graph by +walking objects only in the specified packfiles and any commits in +the existing graph-head. 'read':: @@ -68,6 +72,13 @@ $ git commit-graph write $ git commit-graph write --set-latest --delete-expired ------------------------------------------------ +* Write a graph file, extending the current graph file using commits +* in , update graph-latest, and delete stale graph files. ++ +------------------------------------------------ +$ echo | git commit-graph write --set-latest --delete-expired --stdin-packs +------------------------------------------------ + * Read basic information from a graph file. + ------------------------------------------------ diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c index fd99169ff839cb..5f08c403396e5c 100644 --- a/builtin/commit-graph.c +++ b/builtin/commit-graph.c @@ -8,7 +8,7 @@ static char const * const builtin_commit_graph_usage[] = { N_("git commit-graph [--object-dir ]"), N_("git commit-graph read [--object-dir ] [--file=]"), - N_("git commit-graph write [--object-dir ] [--set-latest] [--delete-expired]"), + N_("git commit-graph write [--object-dir ] [--set-latest] [--delete-expired] [--stdin-packs]"), NULL }; @@ -18,7 +18,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 ] [--set-latest] [--delete-expired]"), + N_("git commit-graph write [--object-dir ] [--set-latest] [--delete-expired] [--stdin-packs]"), NULL }; @@ -27,6 +27,7 @@ static struct opts_commit_graph { const char *graph_file; int set_latest; int delete_expired; + int stdin_packs; } opts; static int graph_read(int argc, const char **argv) @@ -149,6 +150,11 @@ static int graph_write(int argc, const char **argv) { char *graph_name; char *old_graph_name; + const char **pack_indexes = NULL; + int nr_packs = 0; + const char **lines = NULL; + int nr_lines = 0; + int alloc_lines = 0; static struct option builtin_commit_graph_write_options[] = { { OPTION_STRING, 'o', "object-dir", &opts.obj_dir, @@ -158,6 +164,8 @@ static int graph_write(int argc, const char **argv) N_("update graph-head to written graph file")), OPT_BOOL('d', "delete-expired", &opts.delete_expired, N_("delete expired head graph file")), + OPT_BOOL('s', "stdin-packs", &opts.stdin_packs, + N_("only scan packfiles listed by stdin")), OPT_END(), }; @@ -170,7 +178,25 @@ static int graph_write(int argc, const char **argv) old_graph_name = get_graph_latest_contents(opts.obj_dir); - graph_name = write_commit_graph(opts.obj_dir); + if (opts.stdin_packs) { + struct strbuf buf = STRBUF_INIT; + nr_lines = 0; + alloc_lines = 128; + ALLOC_ARRAY(lines, alloc_lines); + + while (strbuf_getline(&buf, stdin) != EOF) { + ALLOC_GROW(lines, nr_lines + 1, alloc_lines); + lines[nr_lines++] = buf.buf; + strbuf_detach(&buf, NULL); + } + + pack_indexes = lines; + nr_packs = nr_lines; + } + + graph_name = write_commit_graph(opts.obj_dir, + pack_indexes, + nr_packs); if (graph_name) { if (opts.set_latest) diff --git a/commit-graph.c b/commit-graph.c index ea07b475e2727d..943192c06e88de 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -554,7 +554,9 @@ static void close_reachable(struct packed_oid_list *oids) } } -char *write_commit_graph(const char *obj_dir) +char *write_commit_graph(const char *obj_dir, + const char **pack_indexes, + int nr_packs) { struct packed_oid_list oids; struct packed_commit_list commits; @@ -579,7 +581,27 @@ char *write_commit_graph(const char *obj_dir) oids.alloc = 1024; ALLOC_ARRAY(oids.list, oids.alloc); - for_each_packed_object(if_packed_commit_add_to_list, &oids, 0); + if (pack_indexes) { + struct strbuf packname = STRBUF_INIT; + int dirlen; + strbuf_addf(&packname, "%s/pack/", obj_dir); + dirlen = packname.len; + for (i = 0; i < nr_packs; i++) { + struct packed_git *p; + strbuf_setlen(&packname, dirlen); + strbuf_addstr(&packname, pack_indexes[i]); + p = add_packed_git(packname.buf, packname.len, 1); + if (!p) + 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, if_packed_commit_add_to_list, &oids); + close_pack(p); + } + } + else + for_each_packed_object(if_packed_commit_add_to_list, &oids, 0); + close_reachable(&oids); QSORT(oids.list, oids.nr, commit_compare); diff --git a/commit-graph.h b/commit-graph.h index 48188382aa46af..5617842edb1f87 100644 --- a/commit-graph.h +++ b/commit-graph.h @@ -39,7 +39,9 @@ extern struct commit_graph *load_commit_graph_one(const char *graph_file); extern void prepare_commit_graph(void); -extern char *write_commit_graph(const char *obj_dir); +extern char *write_commit_graph(const char *obj_dir, + const char **pack_indexes, + int nr_packs); #endif diff --git a/packfile.c b/packfile.c index 59648a1820706c..b9ad7b106ac33b 100644 --- a/packfile.c +++ b/packfile.c @@ -299,7 +299,7 @@ void close_pack_index(struct packed_git *p) } } -static void close_pack(struct packed_git *p) +void close_pack(struct packed_git *p) { close_pack_windows(p); close_pack_fd(p); @@ -1839,7 +1839,7 @@ int has_pack_index(const unsigned char *sha1) return 1; } -static int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn cb, void *data) +int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn cb, void *data) { uint32_t i; int r = 0; diff --git a/packfile.h b/packfile.h index 0cdeb54dcd97a6..9281e909d5281f 100644 --- a/packfile.h +++ b/packfile.h @@ -61,6 +61,7 @@ extern void close_pack_index(struct packed_git *); extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *); extern void close_pack_windows(struct packed_git *); +extern void close_pack(struct packed_git *); extern void close_all_packs(void); extern void unuse_pack(struct pack_window **); extern void clear_delta_base_cache(void); @@ -133,6 +134,7 @@ typedef int each_packed_object_fn(const struct object_id *oid, struct packed_git *pack, uint32_t pos, void *data); +extern int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn, void *data); extern int for_each_packed_object(each_packed_object_fn, void *, unsigned flags); #endif diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh index 8c6b510b3ea184..5bd1f772db8894 100755 --- a/t/t5318-commit-graph.sh +++ b/t/t5318-commit-graph.sh @@ -180,6 +180,22 @@ test_expect_success 'write graph with nothing new' ' graph_git_behavior 'cleared graph, commit 8 vs merge 1' commits/8 merge/1 graph_git_behavior 'cleared graph, commit 8 vs merge 2' commits/8 merge/2 +test_expect_success 'build graph from latest pack with closure' ' + rm $objdir/info/graph-latest && + graph5=$(cat new-idx | git commit-graph write --set-latest --delete-expired --stdin-packs) && + test_path_is_file $objdir/info/$graph5 && + test_path_is_missing $objdir/info/$graph4 && + test_path_is_file $objdir/info/graph-latest && + printf $graph5 >expect && + test_cmp expect $objdir/info/graph-latest && + git commit-graph read --file=$graph5 >output && + graph_read_expect "9" "large_edges" && + test_cmp expect output +' + +graph_git_behavior 'graph from pack, commit 8 vs merge 1' commits/8 merge/1 +graph_git_behavior 'graph from pack, commit 8 vs merge 2' commits/8 merge/2 + test_expect_success 'setup bare repo' ' cd .. && git clone --bare --no-local full bare &&