Skip to content

Commit

Permalink
Merge branch 'ds/commit-graph-with-grafts' into maint
Browse files Browse the repository at this point in the history
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
  • Loading branch information
gitster committed Nov 21, 2018
2 parents a357dae + 829a321 commit e60e38a
Show file tree
Hide file tree
Showing 16 changed files with 194 additions and 30 deletions.
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. The latter 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
4 changes: 4 additions & 0 deletions builtin/commit-graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ static int graph_read(int argc, const char **argv)
return 0;
}

extern int read_replace_refs;

static int graph_write(int argc, const char **argv)
{
struct string_list *pack_indexes = NULL;
Expand Down Expand Up @@ -150,6 +152,8 @@ static int graph_write(int argc, const char **argv)
if (!opts.obj_dir)
opts.obj_dir = get_object_directory();

read_replace_refs = 0;

if (opts.reachable) {
write_commit_graph_reachable(opts.obj_dir, opts.append);
return 0;
Expand Down
8 changes: 4 additions & 4 deletions builtin/replace.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ struct show_data {
enum replace_format format;
};

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

obj_type = oid_object_info(the_repository, &object,
NULL);
repl_type = oid_object_info(the_repository, oid, NULL);
obj_type = oid_object_info(r, &object, NULL);
repl_type = oid_object_info(r, oid, NULL);

printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type),
oid_to_hex(oid), type_name(repl_type));
Expand Down
38 changes: 34 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,28 @@ static struct commit_graph *alloc_commit_graph(void)
return g;
}

extern int read_replace_refs;

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

if (read_replace_refs) {
prepare_replace_object(r);
if (hashmap_get_size(&r->objects->replace_map->map))
return 0;
}

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

return 1;
}

struct commit_graph *load_commit_graph_one(const char *graph_file)
{
void *graph_map;
Expand Down Expand Up @@ -223,6 +247,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 +260,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 +720,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 +875,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 @@ -60,6 +60,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
48 changes: 41 additions & 7 deletions refs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1394,17 +1394,50 @@ struct ref_iterator *refs_ref_iterator_begin(
* non-zero value, stop the iteration and return that value;
* otherwise, return 0.
*/
static int do_for_each_repo_ref(struct repository *r, const char *prefix,
each_repo_ref_fn fn, int trim, int flags,
void *cb_data)
{
struct ref_iterator *iter;
struct ref_store *refs = get_main_ref_store(r);

if (!refs)
return 0;

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

return do_for_each_repo_ref_iterator(r, iter, fn, cb_data);
}

struct do_for_each_ref_help {
each_ref_fn *fn;
void *cb_data;
};

static int do_for_each_ref_helper(struct repository *r,
const char *refname,
const struct object_id *oid,
int flags,
void *cb_data)
{
struct do_for_each_ref_help *hp = cb_data;

return hp->fn(refname, oid, flags, hp->cb_data);
}

static int do_for_each_ref(struct ref_store *refs, const char *prefix,
each_ref_fn fn, int trim, int flags, void *cb_data)
{
struct ref_iterator *iter;
struct do_for_each_ref_help hp = { fn, cb_data };

if (!refs)
return 0;

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

return do_for_each_ref_iterator(iter, fn, cb_data);
return do_for_each_repo_ref_iterator(the_repository, iter,
do_for_each_ref_helper, &hp);
}

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

int for_each_replace_ref(struct repository *r, each_ref_fn fn, void *cb_data)
int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data)
{
return do_for_each_ref(get_main_ref_store(r),
git_replace_ref_base, fn,
strlen(git_replace_ref_base),
DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
return do_for_each_repo_ref(r, git_replace_ref_base, fn,
strlen(git_replace_ref_base),
DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
}

int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
Expand Down Expand Up @@ -2033,10 +2065,12 @@ int refs_verify_refname_available(struct ref_store *refs,
int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
struct ref_iterator *iter;
struct do_for_each_ref_help hp = { fn, cb_data };

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

return do_for_each_ref_iterator(iter, fn, cb_data);
return do_for_each_repo_ref_iterator(the_repository, iter,
do_for_each_ref_helper, &hp);
}

int for_each_reflog(each_ref_fn fn, void *cb_data)
Expand Down
12 changes: 11 additions & 1 deletion refs.h
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,16 @@ struct ref_transaction;
typedef int each_ref_fn(const char *refname,
const struct object_id *oid, int flags, void *cb_data);

/*
* The same as each_ref_fn, but also with a repository argument that
* contains the repository associated with the callback.
*/
typedef int each_repo_ref_fn(struct repository *r,
const char *refname,
const struct object_id *oid,
int flags,
void *cb_data);

/*
* The following functions invoke the specified callback function for
* each reference indicated. If the function ever returns a nonzero
Expand Down Expand Up @@ -309,7 +319,7 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,
int for_each_tag_ref(each_ref_fn fn, void *cb_data);
int for_each_branch_ref(each_ref_fn fn, void *cb_data);
int for_each_remote_ref(each_ref_fn fn, void *cb_data);
int for_each_replace_ref(struct repository *r, each_ref_fn fn, void *cb_data);
int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data);
int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
const char *prefix, void *cb_data);
Expand Down
6 changes: 3 additions & 3 deletions refs/iterator.c
Original file line number Diff line number Diff line change
Expand Up @@ -407,15 +407,15 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,

struct ref_iterator *current_ref_iter = NULL;

int do_for_each_ref_iterator(struct ref_iterator *iter,
each_ref_fn fn, void *cb_data)
int do_for_each_repo_ref_iterator(struct repository *r, struct ref_iterator *iter,
each_repo_ref_fn fn, void *cb_data)
{
int retval = 0, ok;
struct ref_iterator *old_ref_iter = current_ref_iter;

current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
retval = fn(iter->refname, iter->oid, iter->flags, cb_data);
retval = fn(r, iter->refname, iter->oid, iter->flags, cb_data);
if (retval) {
/*
* If ref_iterator_abort() returns ITER_ERROR,
Expand Down
5 changes: 3 additions & 2 deletions refs/refs-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -474,8 +474,9 @@ extern struct ref_iterator *current_ref_iter;
* adapter between the callback style of reference iteration and the
* iterator style.
*/
int do_for_each_ref_iterator(struct ref_iterator *iter,
each_ref_fn fn, void *cb_data);
int do_for_each_repo_ref_iterator(struct repository *r,
struct ref_iterator *iter,
each_repo_ref_fn fn, void *cb_data);

/*
* Only include per-worktree refs in a do_for_each_ref*() iteration.
Expand Down
7 changes: 4 additions & 3 deletions replace-object.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
#include "repository.h"
#include "commit.h"

static int register_replace_ref(const char *refname,
static int register_replace_ref(struct repository *r,
const char *refname,
const struct object_id *oid,
int flag, void *cb_data)
{
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 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
Loading

0 comments on commit e60e38a

Please sign in to comment.