Skip to content

Commit 6d8f8eb

Browse files
committed
Merge branch 'ds/commit-graph-with-grafts'
The recently introduced commit-graph auxiliary data is incompatible with mechanisms such as replace & grafts that "breaks" immutable nature of the object reference relationship. Disable optimizations based on its use (and updating existing commit-graph) when these incompatible features are in use in the repository. * ds/commit-graph-with-grafts: commit-graph: close_commit_graph before shallow walk commit-graph: not compatible with uninitialized repo commit-graph: not compatible with grafts commit-graph: not compatible with replace objects test-repository: properly init repo commit-graph: update design document refs.c: upgrade for_each_replace_ref to be a each_repo_ref_fn callback refs.c: migrate internal ref iteration to pass thru repository argument
2 parents 36d767d + 829a321 commit 6d8f8eb

16 files changed

+194
-30
lines changed

Documentation/technical/commit-graph.txt

+15-3
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,24 @@ Design Details
112112
- The file format includes parameters for the object ID hash function,
113113
so a future change of hash algorithm does not require a change in format.
114114

115+
- Commit grafts and replace objects can change the shape of the commit
116+
history. The latter can also be enabled/disabled on the fly using
117+
`--no-replace-objects`. This leads to difficultly storing both possible
118+
interpretations of a commit id, especially when computing generation
119+
numbers. The commit-graph will not be read or written when
120+
replace-objects or grafts are present.
121+
122+
- Shallow clones create grafts of commits by dropping their parents. This
123+
leads the commit-graph to think those commits have generation number 1.
124+
If and when those commits are made unshallow, those generation numbers
125+
become invalid. Since shallow clones are intended to restrict the commit
126+
history to a very small set of commits, the commit-graph feature is less
127+
helpful for these clones, anyway. The commit-graph will not be read or
128+
written when shallow commits are present.
129+
115130
Future Work
116131
-----------
117132

118-
- The commit graph feature currently does not honor commit grafts. This can
119-
be remedied by duplicating or refactoring the current graft logic.
120-
121133
- After computing and storing generation numbers, we must make graph
122134
walks aware of generation numbers to gain the performance benefits they
123135
enable. This will mostly be accomplished by swapping a commit-date-ordered

builtin/commit-graph.c

+4
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ static int graph_read(int argc, const char **argv)
120120
return 0;
121121
}
122122

123+
extern int read_replace_refs;
124+
123125
static int graph_write(int argc, const char **argv)
124126
{
125127
struct string_list *pack_indexes = NULL;
@@ -150,6 +152,8 @@ static int graph_write(int argc, const char **argv)
150152
if (!opts.obj_dir)
151153
opts.obj_dir = get_object_directory();
152154

155+
read_replace_refs = 0;
156+
153157
if (opts.reachable) {
154158
write_commit_graph_reachable(opts.obj_dir, opts.append, 1);
155159
return 0;

builtin/replace.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ struct show_data {
3939
enum replace_format format;
4040
};
4141

42-
static int show_reference(const char *refname, const struct object_id *oid,
42+
static int show_reference(struct repository *r, const char *refname,
43+
const struct object_id *oid,
4344
int flag, void *cb_data)
4445
{
4546
struct show_data *data = cb_data;
@@ -56,9 +57,8 @@ static int show_reference(const char *refname, const struct object_id *oid,
5657
if (get_oid(refname, &object))
5758
return error(_("failed to resolve '%s' as a valid ref"), refname);
5859

59-
obj_type = oid_object_info(the_repository, &object,
60-
NULL);
61-
repl_type = oid_object_info(the_repository, oid, NULL);
60+
obj_type = oid_object_info(r, &object, NULL);
61+
repl_type = oid_object_info(r, oid, NULL);
6262

6363
printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type),
6464
oid_to_hex(oid), type_name(repl_type));

commit-graph.c

+34-4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include "commit-graph.h"
1414
#include "object-store.h"
1515
#include "alloc.h"
16+
#include "hashmap.h"
17+
#include "replace-object.h"
1618
#include "progress.h"
1719

1820
#define GRAPH_SIGNATURE 0x43475048 /* "CGPH" */
@@ -57,6 +59,28 @@ static struct commit_graph *alloc_commit_graph(void)
5759
return g;
5860
}
5961

62+
extern int read_replace_refs;
63+
64+
static int commit_graph_compatible(struct repository *r)
65+
{
66+
if (!r->gitdir)
67+
return 0;
68+
69+
if (read_replace_refs) {
70+
prepare_replace_object(r);
71+
if (hashmap_get_size(&r->objects->replace_map->map))
72+
return 0;
73+
}
74+
75+
prepare_commit_graft(r);
76+
if (r->parsed_objects && r->parsed_objects->grafts_nr)
77+
return 0;
78+
if (is_repository_shallow(r))
79+
return 0;
80+
81+
return 1;
82+
}
83+
6084
struct commit_graph *load_commit_graph_one(const char *graph_file)
6185
{
6286
void *graph_map;
@@ -225,6 +249,9 @@ static int prepare_commit_graph(struct repository *r)
225249
*/
226250
return 0;
227251

252+
if (!commit_graph_compatible(r))
253+
return 0;
254+
228255
obj_dir = r->objects->objectdir;
229256
prepare_commit_graph_one(r, obj_dir);
230257
prepare_alt_odb(r);
@@ -253,10 +280,10 @@ int generation_numbers_enabled(struct repository *r)
253280
return !!first_generation;
254281
}
255282

256-
static void close_commit_graph(void)
283+
void close_commit_graph(struct repository *r)
257284
{
258-
free_commit_graph(the_repository->objects->commit_graph);
259-
the_repository->objects->commit_graph = NULL;
285+
free_commit_graph(r->objects->commit_graph);
286+
r->objects->commit_graph = NULL;
260287
}
261288

262289
static int bsearch_graph(struct commit_graph *g, struct object_id *oid, uint32_t *pos)
@@ -737,6 +764,9 @@ void write_commit_graph(const char *obj_dir,
737764
struct commit_list *parent;
738765
struct progress *progress = NULL;
739766

767+
if (!commit_graph_compatible(the_repository))
768+
return;
769+
740770
oids.nr = 0;
741771
oids.alloc = approximate_object_count() / 4;
742772
oids.progress = NULL;
@@ -908,7 +938,7 @@ void write_commit_graph(const char *obj_dir,
908938
write_graph_chunk_data(f, GRAPH_OID_LEN, commits.list, commits.nr);
909939
write_graph_chunk_large_edges(f, commits.list, commits.nr);
910940

911-
close_commit_graph();
941+
close_commit_graph(the_repository);
912942
finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
913943
commit_lock_file(&lk);
914944

commit-graph.h

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ void write_commit_graph(const char *obj_dir,
6969

7070
int verify_commit_graph(struct repository *r, struct commit_graph *g);
7171

72+
void close_commit_graph(struct repository *);
7273
void free_commit_graph(struct commit_graph *);
7374

7475
#endif

commit.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ static int read_graft_file(struct repository *r, const char *graft_file)
209209
return 0;
210210
}
211211

212-
static void prepare_commit_graft(struct repository *r)
212+
void prepare_commit_graft(struct repository *r)
213213
{
214214
char *graft_file;
215215

commit.h

+1
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);
202202

203203
struct commit_graft *read_graft_line(struct strbuf *line);
204204
int register_commit_graft(struct repository *r, struct commit_graft *, int);
205+
void prepare_commit_graft(struct repository *r);
205206
struct commit_graft *lookup_commit_graft(struct repository *r, const struct object_id *oid);
206207

207208
/* largest positive number a signed 32-bit integer can contain */

refs.c

+41-7
Original file line numberDiff line numberDiff line change
@@ -1394,17 +1394,50 @@ struct ref_iterator *refs_ref_iterator_begin(
13941394
* non-zero value, stop the iteration and return that value;
13951395
* otherwise, return 0.
13961396
*/
1397+
static int do_for_each_repo_ref(struct repository *r, const char *prefix,
1398+
each_repo_ref_fn fn, int trim, int flags,
1399+
void *cb_data)
1400+
{
1401+
struct ref_iterator *iter;
1402+
struct ref_store *refs = get_main_ref_store(r);
1403+
1404+
if (!refs)
1405+
return 0;
1406+
1407+
iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
1408+
1409+
return do_for_each_repo_ref_iterator(r, iter, fn, cb_data);
1410+
}
1411+
1412+
struct do_for_each_ref_help {
1413+
each_ref_fn *fn;
1414+
void *cb_data;
1415+
};
1416+
1417+
static int do_for_each_ref_helper(struct repository *r,
1418+
const char *refname,
1419+
const struct object_id *oid,
1420+
int flags,
1421+
void *cb_data)
1422+
{
1423+
struct do_for_each_ref_help *hp = cb_data;
1424+
1425+
return hp->fn(refname, oid, flags, hp->cb_data);
1426+
}
1427+
13971428
static int do_for_each_ref(struct ref_store *refs, const char *prefix,
13981429
each_ref_fn fn, int trim, int flags, void *cb_data)
13991430
{
14001431
struct ref_iterator *iter;
1432+
struct do_for_each_ref_help hp = { fn, cb_data };
14011433

14021434
if (!refs)
14031435
return 0;
14041436

14051437
iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
14061438

1407-
return do_for_each_ref_iterator(iter, fn, cb_data);
1439+
return do_for_each_repo_ref_iterator(the_repository, iter,
1440+
do_for_each_ref_helper, &hp);
14081441
}
14091442

14101443
int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
@@ -1449,12 +1482,11 @@ int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
14491482
return do_for_each_ref(refs, prefix, fn, 0, flag, cb_data);
14501483
}
14511484

1452-
int for_each_replace_ref(struct repository *r, each_ref_fn fn, void *cb_data)
1485+
int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data)
14531486
{
1454-
return do_for_each_ref(get_main_ref_store(r),
1455-
git_replace_ref_base, fn,
1456-
strlen(git_replace_ref_base),
1457-
DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
1487+
return do_for_each_repo_ref(r, git_replace_ref_base, fn,
1488+
strlen(git_replace_ref_base),
1489+
DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
14581490
}
14591491

14601492
int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
@@ -2033,10 +2065,12 @@ int refs_verify_refname_available(struct ref_store *refs,
20332065
int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)
20342066
{
20352067
struct ref_iterator *iter;
2068+
struct do_for_each_ref_help hp = { fn, cb_data };
20362069

20372070
iter = refs->be->reflog_iterator_begin(refs);
20382071

2039-
return do_for_each_ref_iterator(iter, fn, cb_data);
2072+
return do_for_each_repo_ref_iterator(the_repository, iter,
2073+
do_for_each_ref_helper, &hp);
20402074
}
20412075

20422076
int for_each_reflog(each_ref_fn fn, void *cb_data)

refs.h

+11-1
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,16 @@ struct ref_transaction;
276276
typedef int each_ref_fn(const char *refname,
277277
const struct object_id *oid, int flags, void *cb_data);
278278

279+
/*
280+
* The same as each_ref_fn, but also with a repository argument that
281+
* contains the repository associated with the callback.
282+
*/
283+
typedef int each_repo_ref_fn(struct repository *r,
284+
const char *refname,
285+
const struct object_id *oid,
286+
int flags,
287+
void *cb_data);
288+
279289
/*
280290
* The following functions invoke the specified callback function for
281291
* each reference indicated. If the function ever returns a nonzero
@@ -309,7 +319,7 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,
309319
int for_each_tag_ref(each_ref_fn fn, void *cb_data);
310320
int for_each_branch_ref(each_ref_fn fn, void *cb_data);
311321
int for_each_remote_ref(each_ref_fn fn, void *cb_data);
312-
int for_each_replace_ref(struct repository *r, each_ref_fn fn, void *cb_data);
322+
int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data);
313323
int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
314324
int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
315325
const char *prefix, void *cb_data);

refs/iterator.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -407,15 +407,15 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
407407

408408
struct ref_iterator *current_ref_iter = NULL;
409409

410-
int do_for_each_ref_iterator(struct ref_iterator *iter,
411-
each_ref_fn fn, void *cb_data)
410+
int do_for_each_repo_ref_iterator(struct repository *r, struct ref_iterator *iter,
411+
each_repo_ref_fn fn, void *cb_data)
412412
{
413413
int retval = 0, ok;
414414
struct ref_iterator *old_ref_iter = current_ref_iter;
415415

416416
current_ref_iter = iter;
417417
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
418-
retval = fn(iter->refname, iter->oid, iter->flags, cb_data);
418+
retval = fn(r, iter->refname, iter->oid, iter->flags, cb_data);
419419
if (retval) {
420420
/*
421421
* If ref_iterator_abort() returns ITER_ERROR,

refs/refs-internal.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -474,8 +474,9 @@ extern struct ref_iterator *current_ref_iter;
474474
* adapter between the callback style of reference iteration and the
475475
* iterator style.
476476
*/
477-
int do_for_each_ref_iterator(struct ref_iterator *iter,
478-
each_ref_fn fn, void *cb_data);
477+
int do_for_each_repo_ref_iterator(struct repository *r,
478+
struct ref_iterator *iter,
479+
each_repo_ref_fn fn, void *cb_data);
479480

480481
/*
481482
* Only include per-worktree refs in a do_for_each_ref*() iteration.

replace-object.c

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
#include "repository.h"
77
#include "commit.h"
88

9-
static int register_replace_ref(const char *refname,
9+
static int register_replace_ref(struct repository *r,
10+
const char *refname,
1011
const struct object_id *oid,
1112
int flag, void *cb_data)
1213
{
@@ -25,13 +26,13 @@ static int register_replace_ref(const char *refname,
2526
oidcpy(&repl_obj->replacement, oid);
2627

2728
/* Register new object */
28-
if (oidmap_put(the_repository->objects->replace_map, repl_obj))
29+
if (oidmap_put(r->objects->replace_map, repl_obj))
2930
die(_("duplicate replace ref: %s"), refname);
3031

3132
return 0;
3233
}
3334

34-
static void prepare_replace_object(struct repository *r)
35+
void prepare_replace_object(struct repository *r)
3536
{
3637
if (r->objects->replace_map)
3738
return;

replace-object.h

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ struct replace_object {
1010
struct object_id replacement;
1111
};
1212

13+
void prepare_replace_object(struct repository *r);
14+
1315
/*
1416
* This internal function is only declared here for the benefit of
1517
* lookup_replace_object(). Please do not call it directly.

t/helper/test-repository.c

+8-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ static void test_parse_commit_in_graph(const char *gitdir, const char *worktree,
1515
struct commit *c;
1616
struct commit_list *parent;
1717

18-
repo_init(&r, gitdir, worktree);
18+
setup_git_env(gitdir);
19+
20+
if (repo_init(&r, gitdir, worktree))
21+
die("Couldn't init repo");
1922

2023
c = lookup_commit(&r, commit_oid);
2124

@@ -38,7 +41,10 @@ static void test_get_commit_tree_in_graph(const char *gitdir,
3841
struct commit *c;
3942
struct tree *tree;
4043

41-
repo_init(&r, gitdir, worktree);
44+
setup_git_env(gitdir);
45+
46+
if (repo_init(&r, gitdir, worktree))
47+
die("Couldn't init repo");
4248

4349
c = lookup_commit(&r, commit_oid);
4450

0 commit comments

Comments
 (0)