Skip to content

Commit 8a6ac28

Browse files
ttaylorrgitster
authored andcommitted
builtin/commit-graph.c: introduce split strategy 'replace'
When using split commit-graphs, it is sometimes useful to completely replace the commit-graph chain with a new base. For example, consider a scenario in which a repository builds a new commit-graph incremental for each push. Occasionally (say, after some fixed number of pushes), they may wish to rebuild the commit-graph chain with all reachable commits. They can do so with $ git commit-graph write --reachable but this removes the chain entirely and replaces it with a single commit-graph in 'objects/info/commit-graph'. Unfortunately, this means that the next push will have to move this commit-graph into the first layer of a new chain, and then write its new commits on top. Avoid such copying entirely by allowing the caller to specify that they wish to replace the entirety of their commit-graph chain, while also specifying that the new commit-graph should become the basis of a fresh, length-one chain. This addresses the above situation by making it possible for the caller to instead write: $ git commit-graph write --reachable --split=replace which writes a new length-one chain to 'objects/info/commit-graphs', making the commit-graph incremental generated by the subsequent push relatively cheap by avoiding the aforementioned copy. In order to do this, remove an assumption in 'write_commit_graph_file' that chains are always at least two incrementals long. Signed-off-by: Taylor Blau <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent fdbde82 commit 8a6ac28

File tree

5 files changed

+66
-18
lines changed

5 files changed

+66
-18
lines changed

Documentation/git-commit-graph.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,10 @@ strategy and other splitting options. The new commits not already in the
6464
commit-graph are added in a new "tip" file. This file is merged with the
6565
existing file if the following merge conditions are met:
6666
* If `--split=no-merge` is specified, a merge is never performed, and
67-
the remaining options are ignored. A bare `--split` defers to the
68-
remaining options. (Note that merging a chain of commit graphs replaces
69-
the existing chain with a length-1 chain where the first and only
67+
the remaining options are ignored. `--split=replace` overwrites the
68+
existing chain with a new one. A bare `--split` defers to the remaining
69+
options. (Note that merging a chain of commit graphs replaces the
70+
existing chain with a length-1 chain where the first and only
7071
incremental holds the entire graph).
7172
+
7273
* If `--size-multiple=<X>` is not specified, let `X` equal 2. If the new

builtin/commit-graph.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ static int write_option_parse_split(const struct option *opt, const char *arg,
126126

127127
if (!strcmp(arg, "no-merge"))
128128
*flags = COMMIT_GRAPH_SPLIT_MERGE_PROHIBITED;
129+
else if (!strcmp(arg, "replace"))
130+
*flags = COMMIT_GRAPH_SPLIT_REPLACE;
129131
else
130132
die(_("unrecognized --split argument, %s"), arg);
131133

commit-graph.c

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -866,7 +866,7 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
866866

867867
if (edge_value >= 0)
868868
edge_value += ctx->new_num_commits_in_base;
869-
else {
869+
else if (ctx->new_base_graph) {
870870
uint32_t pos;
871871
if (find_commit_in_graph(parent->item,
872872
ctx->new_base_graph,
@@ -897,7 +897,7 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
897897

898898
if (edge_value >= 0)
899899
edge_value += ctx->new_num_commits_in_base;
900-
else {
900+
else if (ctx->new_base_graph) {
901901
uint32_t pos;
902902
if (find_commit_in_graph(parent->item,
903903
ctx->new_base_graph,
@@ -964,7 +964,7 @@ static void write_graph_chunk_extra_edges(struct hashfile *f,
964964

965965
if (edge_value >= 0)
966966
edge_value += ctx->new_num_commits_in_base;
967-
else {
967+
else if (ctx->new_base_graph) {
968968
uint32_t pos;
969969
if (find_commit_in_graph(parent->item,
970970
ctx->new_base_graph,
@@ -1037,6 +1037,8 @@ static void close_reachable(struct write_commit_graph_context *ctx)
10371037
{
10381038
int i;
10391039
struct commit *commit;
1040+
enum commit_graph_split_flags flags = ctx->split_opts ?
1041+
ctx->split_opts->flags : COMMIT_GRAPH_SPLIT_UNSPECIFIED;
10401042

10411043
if (ctx->report_progress)
10421044
ctx->progress = start_delayed_progress(
@@ -1066,8 +1068,9 @@ static void close_reachable(struct write_commit_graph_context *ctx)
10661068
if (!commit)
10671069
continue;
10681070
if (ctx->split) {
1069-
if (!parse_commit(commit) &&
1070-
commit->graph_pos == COMMIT_NOT_FROM_GRAPH)
1071+
if ((!parse_commit(commit) &&
1072+
commit->graph_pos == COMMIT_NOT_FROM_GRAPH) ||
1073+
flags == COMMIT_GRAPH_SPLIT_REPLACE)
10711074
add_missing_parents(ctx, commit);
10721075
} else if (!parse_commit_no_graph(commit))
10731076
add_missing_parents(ctx, commit);
@@ -1287,6 +1290,8 @@ static uint32_t count_distinct_commits(struct write_commit_graph_context *ctx)
12871290
static void copy_oids_to_commits(struct write_commit_graph_context *ctx)
12881291
{
12891292
uint32_t i;
1293+
enum commit_graph_split_flags flags = ctx->split_opts ?
1294+
ctx->split_opts->flags : COMMIT_GRAPH_SPLIT_UNSPECIFIED;
12901295

12911296
ctx->num_extra_edges = 0;
12921297
if (ctx->report_progress)
@@ -1303,11 +1308,14 @@ static void copy_oids_to_commits(struct write_commit_graph_context *ctx)
13031308
ALLOC_GROW(ctx->commits.list, ctx->commits.nr + 1, ctx->commits.alloc);
13041309
ctx->commits.list[ctx->commits.nr] = lookup_commit(ctx->r, &ctx->oids.list[i]);
13051310

1306-
if (ctx->split &&
1311+
if (ctx->split && flags != COMMIT_GRAPH_SPLIT_REPLACE &&
13071312
ctx->commits.list[ctx->commits.nr]->graph_pos != COMMIT_NOT_FROM_GRAPH)
13081313
continue;
13091314

1310-
parse_commit_no_graph(ctx->commits.list[ctx->commits.nr]);
1315+
if (ctx->split && flags == COMMIT_GRAPH_SPLIT_REPLACE)
1316+
parse_commit(ctx->commits.list[ctx->commits.nr]);
1317+
else
1318+
parse_commit_no_graph(ctx->commits.list[ctx->commits.nr]);
13111319

13121320
num_parents = commit_list_count(ctx->commits.list[ctx->commits.nr]->parents);
13131321
if (num_parents > 2)
@@ -1488,8 +1496,12 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
14881496
}
14891497

14901498
if (ctx->base_graph_name) {
1491-
const char *dest = ctx->commit_graph_filenames_after[
1492-
ctx->num_commit_graphs_after - 2];
1499+
const char *dest;
1500+
int idx = ctx->num_commit_graphs_after - 1;
1501+
if (ctx->num_commit_graphs_after > 1)
1502+
idx--;
1503+
1504+
dest = ctx->commit_graph_filenames_after[idx];
14931505

14941506
if (strcmp(ctx->base_graph_name, dest)) {
14951507
result = rename(ctx->base_graph_name, dest);
@@ -1546,9 +1558,13 @@ static void split_graph_merge_strategy(struct write_commit_graph_context *ctx)
15461558

15471559
g = ctx->r->objects->commit_graph;
15481560
num_commits = ctx->commits.nr;
1549-
ctx->num_commit_graphs_after = ctx->num_commit_graphs_before + 1;
1561+
if (flags == COMMIT_GRAPH_SPLIT_REPLACE)
1562+
ctx->num_commit_graphs_after = 1;
1563+
else
1564+
ctx->num_commit_graphs_after = ctx->num_commit_graphs_before + 1;
15501565

1551-
if (flags != COMMIT_GRAPH_SPLIT_MERGE_PROHIBITED) {
1566+
if (flags != COMMIT_GRAPH_SPLIT_MERGE_PROHIBITED &&
1567+
flags != COMMIT_GRAPH_SPLIT_REPLACE) {
15521568
while (g && (g->num_commits <= size_mult * num_commits ||
15531569
(max_commits && num_commits > max_commits))) {
15541570
if (g->odb != ctx->odb)
@@ -1561,7 +1577,11 @@ static void split_graph_merge_strategy(struct write_commit_graph_context *ctx)
15611577
}
15621578
}
15631579

1564-
ctx->new_base_graph = g;
1580+
if (flags != COMMIT_GRAPH_SPLIT_REPLACE)
1581+
ctx->new_base_graph = g;
1582+
else if (ctx->num_commit_graphs_after != 1)
1583+
BUG("split_graph_merge_strategy: num_commit_graphs_after "
1584+
"should be 1 with --split=replace");
15651585

15661586
if (ctx->num_commit_graphs_after == 2) {
15671587
char *old_graph_name = get_commit_graph_filename(g->odb);
@@ -1772,6 +1792,7 @@ int write_commit_graph(struct object_directory *odb,
17721792
struct write_commit_graph_context *ctx;
17731793
uint32_t i, count_distinct = 0;
17741794
int res = 0;
1795+
int replace = 0;
17751796

17761797
if (!commit_graph_compatible(the_repository))
17771798
return 0;
@@ -1806,6 +1827,9 @@ int write_commit_graph(struct object_directory *odb,
18061827
g = g->base_graph;
18071828
}
18081829
}
1830+
1831+
if (ctx->split_opts)
1832+
replace = ctx->split_opts->flags & COMMIT_GRAPH_SPLIT_REPLACE;
18091833
}
18101834

18111835
ctx->approx_nr_objects = approximate_object_count();
@@ -1866,13 +1890,14 @@ int write_commit_graph(struct object_directory *odb,
18661890
goto cleanup;
18671891
}
18681892

1869-
if (!ctx->commits.nr)
1893+
if (!ctx->commits.nr && !replace)
18701894
goto cleanup;
18711895

18721896
if (ctx->split) {
18731897
split_graph_merge_strategy(ctx);
18741898

1875-
merge_commit_graphs(ctx);
1899+
if (!replace)
1900+
merge_commit_graphs(ctx);
18761901
} else
18771902
ctx->num_commit_graphs_after = 1;
18781903

commit-graph.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ enum commit_graph_write_flags {
8484

8585
enum commit_graph_split_flags {
8686
COMMIT_GRAPH_SPLIT_UNSPECIFIED = 0,
87-
COMMIT_GRAPH_SPLIT_MERGE_PROHIBITED = 1
87+
COMMIT_GRAPH_SPLIT_MERGE_PROHIBITED = 1,
88+
COMMIT_GRAPH_SPLIT_REPLACE = 2
8889
};
8990

9091
struct split_commit_graph_opts {

t/t5324-split-commit-graph.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,4 +355,23 @@ test_expect_success '--split=no-merge always writes an incremental' '
355355
test_line_count = 2 $graphdir/commit-graph-chain
356356
'
357357

358+
test_expect_success '--split=replace replaces the chain' '
359+
rm -rf $graphdir $infodir/commit-graph &&
360+
git reset --hard commits/3 &&
361+
git rev-list -1 HEAD~2 >a &&
362+
git rev-list -1 HEAD~1 >b &&
363+
git rev-list -1 HEAD >c &&
364+
git commit-graph write --split=no-merge --stdin-commits <a &&
365+
git commit-graph write --split=no-merge --stdin-commits <b &&
366+
git commit-graph write --split=no-merge --stdin-commits <c &&
367+
test_line_count = 3 $graphdir/commit-graph-chain &&
368+
git commit-graph write --stdin-commits --split=replace <b &&
369+
test_path_is_missing $infodir/commit-graph &&
370+
test_path_is_file $graphdir/commit-graph-chain &&
371+
ls $graphdir/graph-*.graph >graph-files &&
372+
test_line_count = 1 graph-files &&
373+
verify_chain_files_exist $graphdir &&
374+
graph_read_expect 2
375+
'
376+
358377
test_done

0 commit comments

Comments
 (0)