Skip to content

Clarify commit-graph and grafts/replace/shallow incompatibilities #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions Documentation/technical/commit-graph.txt
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,24 @@ Design Details
- The file format includes parameters for the object ID hash function,
so a future change of hash algorithm does not require a change in format.

- Commit grafts and replace objects can change the shape of the commit
history. These can also be enabled/disabled on the fly using
`--no-replace-objects`. This leads to difficultly storing both possible
interpretations of a commit id, especially when computing generation
numbers. The commit-graph will not be read or written when
replace-objects or grafts are present.

- Shallow clones create grafts of commits by dropping their parents. This
leads the commit-graph to think those commits have generation number 1.
If and when those commits are made unshallow, those generation numbers
become invalid. Since shallow clones are intended to restrict the commit
history to a very small set of commits, the commit-graph feature is less
helpful for these clones, anyway. The commit-graph will not be read or
written when shallow commits are present.

Future Work
-----------

- The commit graph feature currently does not honor commit grafts. This can
be remedied by duplicating or refactoring the current graft logic.

- After computing and storing generation numbers, we must make graph
walks aware of generation numbers to gain the performance benefits they
enable. This will mostly be accomplished by swapping a commit-date-ordered
Expand Down
35 changes: 31 additions & 4 deletions commit-graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include "commit-graph.h"
#include "object-store.h"
#include "alloc.h"
#include "hashmap.h"
#include "replace-object.h"

#define GRAPH_SIGNATURE 0x43475048 /* "CGPH" */
#define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
Expand Down Expand Up @@ -56,6 +58,25 @@ static struct commit_graph *alloc_commit_graph(void)
return g;
}

static int commit_graph_compatible(struct repository *r)
{
if (!r->gitdir)
return 0;

prepare_commit_graft(r);
if (r->parsed_objects && r->parsed_objects->grafts_nr)
return 0;
if (is_repository_shallow(r))
return 0;

prepare_replace_object(r);
if (check_replace_refs &&
hashmap_get_size(&r->objects->replace_map->map))
return 0;

return 1;
}

struct commit_graph *load_commit_graph_one(const char *graph_file)
{
void *graph_map;
Expand Down Expand Up @@ -223,6 +244,9 @@ static int prepare_commit_graph(struct repository *r)
*/
return 0;

if (!commit_graph_compatible(r))
return 0;

obj_dir = r->objects->objectdir;
prepare_commit_graph_one(r, obj_dir);
prepare_alt_odb(r);
Expand All @@ -233,10 +257,10 @@ static int prepare_commit_graph(struct repository *r)
return !!r->objects->commit_graph;
}

static void close_commit_graph(void)
void close_commit_graph(struct repository *r)
{
free_commit_graph(the_repository->objects->commit_graph);
the_repository->objects->commit_graph = NULL;
free_commit_graph(r->objects->commit_graph);
r->objects->commit_graph = NULL;
}

static int bsearch_graph(struct commit_graph *g, struct object_id *oid, uint32_t *pos)
Expand Down Expand Up @@ -693,6 +717,9 @@ void write_commit_graph(const char *obj_dir,
int num_extra_edges;
struct commit_list *parent;

if (!commit_graph_compatible(the_repository))
return;

oids.nr = 0;
oids.alloc = approximate_object_count() / 4;

Expand Down Expand Up @@ -845,7 +872,7 @@ void write_commit_graph(const char *obj_dir,
write_graph_chunk_data(f, GRAPH_OID_LEN, commits.list, commits.nr);
write_graph_chunk_large_edges(f, commits.list, commits.nr);

close_commit_graph();
close_commit_graph(the_repository);
finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
commit_lock_file(&lk);

Expand Down
1 change: 1 addition & 0 deletions commit-graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ void write_commit_graph(const char *obj_dir,

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

void close_commit_graph(struct repository *);
void free_commit_graph(struct commit_graph *);

#endif
2 changes: 1 addition & 1 deletion commit.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ static int read_graft_file(struct repository *r, const char *graft_file)
return 0;
}

static void prepare_commit_graft(struct repository *r)
void prepare_commit_graft(struct repository *r)
{
char *graft_file;

Expand Down
1 change: 1 addition & 0 deletions commit.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);

struct commit_graft *read_graft_line(struct strbuf *line);
int register_commit_graft(struct repository *r, struct commit_graft *, int);
void prepare_commit_graft(struct repository *r);
struct commit_graft *lookup_commit_graft(struct repository *r, const struct object_id *oid);

extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2);
Expand Down
7 changes: 4 additions & 3 deletions replace-object.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ static int register_replace_ref(const char *refname,
const char *slash = strrchr(refname, '/');
const char *hash = slash ? slash + 1 : refname;
struct replace_object *repl_obj = xmalloc(sizeof(*repl_obj));
struct repository *r = (struct repository *)cb_data;

if (get_oid_hex(hash, &repl_obj->original.oid)) {
free(repl_obj);
Expand All @@ -25,13 +26,13 @@ static int register_replace_ref(const char *refname,
oidcpy(&repl_obj->replacement, oid);

/* Register new object */
if (oidmap_put(the_repository->objects->replace_map, repl_obj))
if (oidmap_put(r->objects->replace_map, repl_obj))
die("duplicate replace ref: %s", refname);

return 0;
}

static void prepare_replace_object(struct repository *r)
void prepare_replace_object(struct repository *r)
{
if (r->objects->replace_map)
return;
Expand All @@ -40,7 +41,7 @@ static void prepare_replace_object(struct repository *r)
xmalloc(sizeof(*r->objects->replace_map));
oidmap_init(r->objects->replace_map, 0);

for_each_replace_ref(r, register_replace_ref, NULL);
for_each_replace_ref(r, register_replace_ref, r);
}

/* We allow "recursive" replacement. Only within reason, though */
Expand Down
2 changes: 2 additions & 0 deletions replace-object.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ struct replace_object {
struct object_id replacement;
};

void prepare_replace_object(struct repository *r);

/*
* This internal function is only declared here for the benefit of
* lookup_replace_object(). Please do not call it directly.
Expand Down
10 changes: 8 additions & 2 deletions t/helper/test-repository.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ static void test_parse_commit_in_graph(const char *gitdir, const char *worktree,
struct commit *c;
struct commit_list *parent;

repo_init(&r, gitdir, worktree);
setup_git_env(gitdir);

if (repo_init(&r, gitdir, worktree))
die("Couldn't init repo");

c = lookup_commit(&r, commit_oid);

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

repo_init(&r, gitdir, worktree);
setup_git_env(gitdir);

if (repo_init(&r, gitdir, worktree))
die("Couldn't init repo");

c = lookup_commit(&r, commit_oid);

Expand Down
58 changes: 58 additions & 0 deletions t/t5318-commit-graph.sh
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,64 @@ test_expect_success 'check that gc computes commit-graph' '
test_cmp commit-graph-after-gc $objdir/info/commit-graph
'

test_expect_success 'replace-objects invalidates commit-graph' '
cd "$TRASH_DIRECTORY" &&
test_when_finished rm -rf replace &&
git clone full replace &&
(
cd replace &&
git commit-graph write --reachable &&
test_path_is_file .git/objects/info/commit-graph &&
git replace HEAD~1 HEAD~2 &&
git -c core.commitGraph=false log >expect &&
git -c core.commitGraph=true log >actual &&
test_cmp expect actual &&
git commit-graph write --reachable &&
git -c core.commitGraph=false --no-replace-objects log >expect &&
git -c core.commitGraph=true --no-replace-objects log >actual &&
test_cmp expect actual &&
rm -rf .git/objects/info/commit-graph &&
git commit-graph write --reachable &&
test_path_is_missing .git/objects/info/commit-graph
)
'

test_expect_success 'commit grafts invalidate commit-graph' '
cd "$TRASH_DIRECTORY" &&
test_when_finished rm -rf graft &&
git clone full graft &&
(
cd graft &&
git commit-graph write --reachable &&
test_path_is_file .git/objects/info/commit-graph &&
git replace --graft HEAD~1 HEAD~3 &&
git -c core.commitGraph=false log >expect &&
git -c core.commitGraph=true log >actual &&
test_cmp expect actual &&
git commit-graph write --reachable &&
git -c core.commitGraph=false --no-replace-objects log >expect &&
git -c core.commitGraph=true --no-replace-objects log >actual &&
test_cmp expect actual &&
rm -rf .git/objects/info/commit-graph &&
git commit-graph write --reachable &&
test_path_is_missing .git/objects/info/commit-graph
)
'

test_expect_success 'replace-objects invalidates commit-graph' '
cd "$TRASH_DIRECTORY" &&
test_when_finished rm -rf shallow &&
git clone --depth 2 "file://$TRASH_DIRECTORY/full" shallow &&
(
cd shallow &&
git commit-graph write --reachable &&
test_path_is_missing .git/objects/info/commit-graph &&
git fetch origin --unshallow &&
git commit-graph write --reachable &&
test_path_is_file .git/objects/info/commit-graph
)
'

# the verify tests below expect the commit-graph to contain
# exactly the commits reachable from the commits/8 branch.
# If the file changes the set of commits in the list, then the
Expand Down
2 changes: 2 additions & 0 deletions upload-pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "quote.h"
#include "upload-pack.h"
#include "serve.h"
#include "commit-graph.h"

/* Remember to update object flag allocation in object.h */
#define THEY_HAVE (1u << 11)
Expand Down Expand Up @@ -739,6 +740,7 @@ static void deepen_by_rev_list(int ac, const char **av,
{
struct commit_list *result;

close_commit_graph(the_repository);
result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW);
send_shallow(result);
free_commit_list(result);
Expand Down