Merge branch 'jt/commit-graph-per-object-store'

The singleton commit-graph in-core instance is made per in-core
repository instance.

* jt/commit-graph-per-object-store:
  commit-graph: add repo arg to graph readers
  commit-graph: store graph in struct object_store
  commit-graph: add free_commit_graph
  commit-graph: add missing forward declaration
  object-store: add missing include
  commit-graph: refactor preparing commit graph
maint
Junio C Hamano 2018-08-02 15:30:47 -07:00
commit 78a72ad4f8
16 changed files with 207 additions and 62 deletions

View File

@ -719,6 +719,7 @@ TEST_BUILTINS_OBJS += test-prio-queue.o
TEST_BUILTINS_OBJS += test-read-cache.o TEST_BUILTINS_OBJS += test-read-cache.o
TEST_BUILTINS_OBJS += test-ref-store.o TEST_BUILTINS_OBJS += test-ref-store.o
TEST_BUILTINS_OBJS += test-regex.o TEST_BUILTINS_OBJS += test-regex.o
TEST_BUILTINS_OBJS += test-repository.o
TEST_BUILTINS_OBJS += test-revision-walking.o TEST_BUILTINS_OBJS += test-revision-walking.o
TEST_BUILTINS_OBJS += test-run-command.o TEST_BUILTINS_OBJS += test-run-command.o
TEST_BUILTINS_OBJS += test-scrap-cache-tree.o TEST_BUILTINS_OBJS += test-scrap-cache-tree.o

View File

@ -115,6 +115,8 @@ static int graph_read(int argc, const char **argv)
printf(" large_edges"); printf(" large_edges");
printf("\n"); printf("\n");


free_commit_graph(graph);

return 0; return 0;
} }



View File

@ -830,7 +830,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)


check_connectivity(); check_connectivity();


if (core_commit_graph) { if (!git_config_get_bool("core.commitgraph", &i) && i) {
struct child_process commit_graph_verify = CHILD_PROCESS_INIT; struct child_process commit_graph_verify = CHILD_PROCESS_INIT;
const char *verify_argv[] = { "commit-graph", "verify", NULL, NULL, NULL }; const char *verify_argv[] = { "commit-graph", "verify", NULL, NULL, NULL };



View File

@ -877,7 +877,6 @@ extern char *git_replace_ref_base;


extern int fsync_object_files; extern int fsync_object_files;
extern int core_preload_index; extern int core_preload_index;
extern int core_commit_graph;
extern int core_apply_sparse_checkout; extern int core_apply_sparse_checkout;
extern int precomposed_unicode; extern int precomposed_unicode;
extern int protect_hfs; extern int protect_hfs;

View File

@ -183,53 +183,60 @@ cleanup_fail:
exit(1); exit(1);
} }


/* global storage */ static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)
static struct commit_graph *commit_graph = NULL;

static void prepare_commit_graph_one(const char *obj_dir)
{ {
char *graph_name; char *graph_name;


if (commit_graph) if (r->objects->commit_graph)
return; return;


graph_name = get_commit_graph_filename(obj_dir); graph_name = get_commit_graph_filename(obj_dir);
commit_graph = load_commit_graph_one(graph_name); r->objects->commit_graph =
load_commit_graph_one(graph_name);


FREE_AND_NULL(graph_name); FREE_AND_NULL(graph_name);
} }


static int prepare_commit_graph_run_once = 0; /*
static void prepare_commit_graph(void) * Return 1 if commit_graph is non-NULL, and 0 otherwise.
*
* On the first invocation, this function attemps to load the commit
* graph if the_repository is configured to have one.
*/
static int prepare_commit_graph(struct repository *r)
{ {
struct alternate_object_database *alt; struct alternate_object_database *alt;
char *obj_dir; char *obj_dir;
int config_value;


if (prepare_commit_graph_run_once) if (r->objects->commit_graph_attempted)
return; return !!r->objects->commit_graph;
prepare_commit_graph_run_once = 1; r->objects->commit_graph_attempted = 1;


obj_dir = get_object_directory(); if (repo_config_get_bool(r, "core.commitgraph", &config_value) ||
prepare_commit_graph_one(obj_dir); !config_value)
prepare_alt_odb(the_repository); /*
for (alt = the_repository->objects->alt_odb_list; * This repository is not configured to use commit graphs, so
!commit_graph && alt; * do not load one. (But report commit_graph_attempted anyway
* so that commit graph loading is not attempted again for this
* repository.)
*/
return 0;

obj_dir = r->objects->objectdir;
prepare_commit_graph_one(r, obj_dir);
prepare_alt_odb(r);
for (alt = r->objects->alt_odb_list;
!r->objects->commit_graph && alt;
alt = alt->next) alt = alt->next)
prepare_commit_graph_one(alt->path); prepare_commit_graph_one(r, alt->path);
return !!r->objects->commit_graph;
} }


static void close_commit_graph(void) static void close_commit_graph(void)
{ {
if (!commit_graph) free_commit_graph(the_repository->objects->commit_graph);
return; the_repository->objects->commit_graph = NULL;

if (commit_graph->graph_fd >= 0) {
munmap((void *)commit_graph->data, commit_graph->data_len);
commit_graph->data = NULL;
close(commit_graph->graph_fd);
}

FREE_AND_NULL(commit_graph);
} }


static int bsearch_graph(struct commit_graph *g, struct object_id *oid, uint32_t *pos) static int bsearch_graph(struct commit_graph *g, struct object_id *oid, uint32_t *pos)
@ -324,8 +331,6 @@ static int parse_commit_in_graph_one(struct commit_graph *g, struct commit *item
{ {
uint32_t pos; uint32_t pos;


if (!core_commit_graph)
return 0;
if (item->object.parsed) if (item->object.parsed)
return 1; return 1;


@ -335,25 +340,20 @@ static int parse_commit_in_graph_one(struct commit_graph *g, struct commit *item
return 0; return 0;
} }


int parse_commit_in_graph(struct commit *item) int parse_commit_in_graph(struct repository *r, struct commit *item)
{ {
if (!core_commit_graph) if (!prepare_commit_graph(r))
return 0;

prepare_commit_graph();
if (commit_graph)
return parse_commit_in_graph_one(commit_graph, item);
return 0; return 0;
return parse_commit_in_graph_one(r->objects->commit_graph, item);
} }


void load_commit_graph_info(struct commit *item) void load_commit_graph_info(struct repository *r, struct commit *item)
{ {
uint32_t pos; uint32_t pos;
if (!core_commit_graph) if (!prepare_commit_graph(r))
return; return;
prepare_commit_graph(); if (find_commit_in_graph(item, r->objects->commit_graph, &pos))
if (commit_graph && find_commit_in_graph(item, commit_graph, &pos)) fill_commit_graph_info(item, r->objects->commit_graph, pos);
fill_commit_graph_info(item, commit_graph, pos);
} }


static struct tree *load_tree_for_commit(struct commit_graph *g, struct commit *c) static struct tree *load_tree_for_commit(struct commit_graph *g, struct commit *c)
@ -379,9 +379,9 @@ static struct tree *get_commit_tree_in_graph_one(struct commit_graph *g,
return load_tree_for_commit(g, (struct commit *)c); return load_tree_for_commit(g, (struct commit *)c);
} }


struct tree *get_commit_tree_in_graph(const struct commit *c) struct tree *get_commit_tree_in_graph(struct repository *r, const struct commit *c)
{ {
return get_commit_tree_in_graph_one(commit_graph, c); return get_commit_tree_in_graph_one(r->objects->commit_graph, c);
} }


static void write_graph_chunk_fanout(struct hashfile *f, static void write_graph_chunk_fanout(struct hashfile *f,
@ -697,16 +697,18 @@ void write_commit_graph(const char *obj_dir,
oids.alloc = approximate_object_count() / 4; oids.alloc = approximate_object_count() / 4;


if (append) { if (append) {
prepare_commit_graph_one(obj_dir); prepare_commit_graph_one(the_repository, obj_dir);
if (commit_graph) if (the_repository->objects->commit_graph)
oids.alloc += commit_graph->num_commits; oids.alloc += the_repository->objects->commit_graph->num_commits;
} }


if (oids.alloc < 1024) if (oids.alloc < 1024)
oids.alloc = 1024; oids.alloc = 1024;
ALLOC_ARRAY(oids.list, oids.alloc); ALLOC_ARRAY(oids.list, oids.alloc);


if (append && commit_graph) { if (append && the_repository->objects->commit_graph) {
struct commit_graph *commit_graph =
the_repository->objects->commit_graph;
for (i = 0; i < commit_graph->num_commits; i++) { for (i = 0; i < commit_graph->num_commits; i++) {
const unsigned char *hash = commit_graph->chunk_oid_lookup + const unsigned char *hash = commit_graph->chunk_oid_lookup +
commit_graph->hash_len * i; commit_graph->hash_len * i;
@ -1027,3 +1029,15 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g)


return verify_commit_graph_error; return verify_commit_graph_error;
} }

void free_commit_graph(struct commit_graph *g)
{
if (!g)
return;
if (g->graph_fd >= 0) {
munmap((void *)g->data, g->data_len);
g->data = NULL;
close(g->graph_fd);
}
free(g);
}

View File

@ -5,6 +5,8 @@
#include "repository.h" #include "repository.h"
#include "string-list.h" #include "string-list.h"


struct commit;

char *get_commit_graph_filename(const char *obj_dir); char *get_commit_graph_filename(const char *obj_dir);


/* /*
@ -17,7 +19,7 @@ char *get_commit_graph_filename(const char *obj_dir);
* *
* See parse_commit_buffer() for the fallback after this call. * See parse_commit_buffer() for the fallback after this call.
*/ */
int parse_commit_in_graph(struct commit *item); int parse_commit_in_graph(struct repository *r, struct commit *item);


/* /*
* It is possible that we loaded commit contents from the commit buffer, * It is possible that we loaded commit contents from the commit buffer,
@ -25,9 +27,10 @@ int parse_commit_in_graph(struct commit *item);
* checked and filled. Fill the graph_pos and generation members of * checked and filled. Fill the graph_pos and generation members of
* the given commit. * the given commit.
*/ */
void load_commit_graph_info(struct commit *item); void load_commit_graph_info(struct repository *r, struct commit *item);


struct tree *get_commit_tree_in_graph(const struct commit *c); struct tree *get_commit_tree_in_graph(struct repository *r,
const struct commit *c);


struct commit_graph { struct commit_graph {
int graph_fd; int graph_fd;
@ -56,4 +59,6 @@ void write_commit_graph(const char *obj_dir,


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


void free_commit_graph(struct commit_graph *);

#endif #endif

View File

@ -342,7 +342,7 @@ struct tree *get_commit_tree(const struct commit *commit)
if (commit->graph_pos == COMMIT_NOT_FROM_GRAPH) if (commit->graph_pos == COMMIT_NOT_FROM_GRAPH)
BUG("commit has NULL tree, but was not loaded from commit-graph"); BUG("commit has NULL tree, but was not loaded from commit-graph");


return get_commit_tree_in_graph(commit); return get_commit_tree_in_graph(the_repository, commit);
} }


struct object_id *get_commit_tree_oid(const struct commit *commit) struct object_id *get_commit_tree_oid(const struct commit *commit)
@ -438,7 +438,7 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b
item->date = parse_commit_date(bufptr, tail); item->date = parse_commit_date(bufptr, tail);


if (check_graph) if (check_graph)
load_commit_graph_info(item); load_commit_graph_info(the_repository, item);


return 0; return 0;
} }
@ -454,7 +454,7 @@ int parse_commit_internal(struct commit *item, int quiet_on_missing, int use_com
return -1; return -1;
if (item->object.parsed) if (item->object.parsed)
return 0; return 0;
if (use_commit_graph && parse_commit_in_graph(item)) if (use_commit_graph && parse_commit_in_graph(the_repository, item))
return 0; return 0;
buffer = read_object_file(&item->object.oid, &type, &size); buffer = read_object_file(&item->object.oid, &type, &size);
if (!buffer) if (!buffer)

View File

@ -1320,11 +1320,6 @@ static int git_default_core_config(const char *var, const char *value)
return 0; return 0;
} }


if (!strcmp(var, "core.commitgraph")) {
core_commit_graph = git_config_bool(var, value);
return 0;
}

if (!strcmp(var, "core.sparsecheckout")) { if (!strcmp(var, "core.sparsecheckout")) {
core_apply_sparse_checkout = git_config_bool(var, value); core_apply_sparse_checkout = git_config_bool(var, value);
return 0; return 0;

View File

@ -66,7 +66,6 @@ enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE; enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;
char *notes_ref_name; char *notes_ref_name;
int grafts_replace_parents = 1; int grafts_replace_parents = 1;
int core_commit_graph;
int core_apply_sparse_checkout; int core_apply_sparse_checkout;
int merge_log_config = -1; int merge_log_config = -1;
int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */ int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */

View File

@ -2,6 +2,9 @@
#define OBJECT_STORE_H #define OBJECT_STORE_H


#include "oidmap.h" #include "oidmap.h"
#include "list.h"
#include "sha1-array.h"
#include "strbuf.h"


struct alternate_object_database { struct alternate_object_database {
struct alternate_object_database *next; struct alternate_object_database *next;
@ -103,6 +106,9 @@ struct raw_object_store {
*/ */
struct oidmap *replace_map; struct oidmap *replace_map;


struct commit_graph *commit_graph;
unsigned commit_graph_attempted : 1; /* if loading has been attempted */

/* /*
* private data * private data
* *

View File

@ -9,6 +9,7 @@
#include "alloc.h" #include "alloc.h"
#include "object-store.h" #include "object-store.h"
#include "packfile.h" #include "packfile.h"
#include "commit-graph.h"


unsigned int get_max_object_index(void) unsigned int get_max_object_index(void)
{ {
@ -507,6 +508,10 @@ void raw_object_store_clear(struct raw_object_store *o)
oidmap_free(o->replace_map, 1); oidmap_free(o->replace_map, 1);
FREE_AND_NULL(o->replace_map); FREE_AND_NULL(o->replace_map);


free_commit_graph(o->commit_graph);
o->commit_graph = NULL;
o->commit_graph_attempted = 0;

free_alt_odbs(o); free_alt_odbs(o);
o->alt_odb_tail = NULL; o->alt_odb_tail = NULL;



View File

@ -1713,7 +1713,7 @@ static enum contains_result contains_tag_algo(struct commit *candidate,


for (p = want; p; p = p->next) { for (p = want; p; p = p->next) {
struct commit *c = p->item; struct commit *c = p->item;
load_commit_graph_info(c); load_commit_graph_info(the_repository, c);
if (c->generation < cutoff) if (c->generation < cutoff)
cutoff = c->generation; cutoff = c->generation;
} }

View File

@ -0,0 +1,82 @@
#include "test-tool.h"
#include "cache.h"
#include "commit-graph.h"
#include "commit.h"
#include "config.h"
#include "object-store.h"
#include "object.h"
#include "repository.h"
#include "tree.h"

static void test_parse_commit_in_graph(const char *gitdir, const char *worktree,
const struct object_id *commit_oid)
{
struct repository r;
struct commit *c;
struct commit_list *parent;

repo_init(&r, gitdir, worktree);

c = lookup_commit(&r, commit_oid);

if (!parse_commit_in_graph(&r, c))
die("Couldn't parse commit");

printf("%"PRItime, c->date);
for (parent = c->parents; parent; parent = parent->next)
printf(" %s", oid_to_hex(&parent->item->object.oid));
printf("\n");

repo_clear(&r);
}

static void test_get_commit_tree_in_graph(const char *gitdir,
const char *worktree,
const struct object_id *commit_oid)
{
struct repository r;
struct commit *c;
struct tree *tree;

repo_init(&r, gitdir, worktree);

c = lookup_commit(&r, commit_oid);

/*
* get_commit_tree_in_graph does not automatically parse the commit, so
* parse it first.
*/
if (!parse_commit_in_graph(&r, c))
die("Couldn't parse commit");
tree = get_commit_tree_in_graph(&r, c);
if (!tree)
die("Couldn't get commit tree");

printf("%s\n", oid_to_hex(&tree->object.oid));

repo_clear(&r);
}

int cmd__repository(int argc, const char **argv)
{
if (argc < 2)
die("must have at least 2 arguments");
if (!strcmp(argv[1], "parse_commit_in_graph")) {
struct object_id oid;
if (argc < 5)
die("not enough arguments");
if (parse_oid_hex(argv[4], &oid, &argv[4]))
die("cannot parse oid '%s'", argv[4]);
test_parse_commit_in_graph(argv[2], argv[3], &oid);
} else if (!strcmp(argv[1], "get_commit_tree_in_graph")) {
struct object_id oid;
if (argc < 5)
die("not enough arguments");
if (parse_oid_hex(argv[4], &oid, &argv[4]))
die("cannot parse oid '%s'", argv[4]);
test_get_commit_tree_in_graph(argv[2], argv[3], &oid);
} else {
die("unrecognized '%s'", argv[1]);
}
return 0;
}

View File

@ -29,6 +29,7 @@ static struct test_cmd cmds[] = {
{ "read-cache", cmd__read_cache }, { "read-cache", cmd__read_cache },
{ "ref-store", cmd__ref_store }, { "ref-store", cmd__ref_store },
{ "regex", cmd__regex }, { "regex", cmd__regex },
{ "repository", cmd__repository },
{ "revision-walking", cmd__revision_walking }, { "revision-walking", cmd__revision_walking },
{ "run-command", cmd__run_command }, { "run-command", cmd__run_command },
{ "scrap-cache-tree", cmd__scrap_cache_tree }, { "scrap-cache-tree", cmd__scrap_cache_tree },

View File

@ -23,6 +23,7 @@ int cmd__prio_queue(int argc, const char **argv);
int cmd__read_cache(int argc, const char **argv); int cmd__read_cache(int argc, const char **argv);
int cmd__ref_store(int argc, const char **argv); int cmd__ref_store(int argc, const char **argv);
int cmd__regex(int argc, const char **argv); int cmd__regex(int argc, const char **argv);
int cmd__repository(int argc, const char **argv);
int cmd__revision_walking(int argc, const char **argv); int cmd__revision_walking(int argc, const char **argv);
int cmd__run_command(int argc, const char **argv); int cmd__run_command(int argc, const char **argv);
int cmd__scrap_cache_tree(int argc, const char **argv); int cmd__scrap_cache_tree(int argc, const char **argv);

View File

@ -431,4 +431,39 @@ test_expect_success 'git fsck (checks commit-graph)' '
test_must_fail git fsck test_must_fail git fsck
' '


test_expect_success 'setup non-the_repository tests' '
rm -rf repo &&
git init repo &&
test_commit -C repo one &&
test_commit -C repo two &&
git -C repo config core.commitGraph true &&
git -C repo rev-parse two | \
git -C repo commit-graph write --stdin-commits
'

test_expect_success 'parse_commit_in_graph works for non-the_repository' '
test-tool repository parse_commit_in_graph \
repo/.git repo "$(git -C repo rev-parse two)" >actual &&
echo $(git -C repo log --pretty="%ct" -1) \
$(git -C repo rev-parse one) >expect &&
test_cmp expect actual &&

test-tool repository parse_commit_in_graph \
repo/.git repo "$(git -C repo rev-parse one)" >actual &&
echo $(git -C repo log --pretty="%ct" -1 one) >expect &&
test_cmp expect actual
'

test_expect_success 'get_commit_tree_in_graph works for non-the_repository' '
test-tool repository get_commit_tree_in_graph \
repo/.git repo "$(git -C repo rev-parse two)" >actual &&
echo $(git -C repo rev-parse two^{tree}) >expect &&
test_cmp expect actual &&

test-tool repository get_commit_tree_in_graph \
repo/.git repo "$(git -C repo rev-parse one)" >actual &&
echo $(git -C repo rev-parse one^{tree}) >expect &&
test_cmp expect actual
'

test_done test_done