Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.kernel.org/pub/scm/git/git.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'commit-graph.c')
-rw-r--r--commit-graph.c758
1 files changed, 438 insertions, 320 deletions
diff --git a/commit-graph.c b/commit-graph.c
index 47e9be0a3a..8cc1d1d6c3 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -80,25 +80,30 @@ static int commit_graph_compatible(struct repository *r)
return 1;
}
-struct commit_graph *load_commit_graph_one(const char *graph_file)
+int open_commit_graph(const char *graph_file, int *fd, struct stat *st)
+{
+ *fd = git_open(graph_file);
+ if (*fd < 0)
+ return 0;
+ if (fstat(*fd, st)) {
+ close(*fd);
+ return 0;
+ }
+ return 1;
+}
+
+struct commit_graph *load_commit_graph_one_fd_st(int fd, struct stat *st)
{
void *graph_map;
size_t graph_size;
- struct stat st;
struct commit_graph *ret;
- int fd = git_open(graph_file);
- if (fd < 0)
- return NULL;
- if (fstat(fd, &st)) {
- close(fd);
- return NULL;
- }
- graph_size = xsize_t(st.st_size);
+ graph_size = xsize_t(st->st_size);
if (graph_size < GRAPH_MIN_SIZE) {
close(fd);
- die(_("graph file %s is too small"), graph_file);
+ error(_("commit-graph file is too small"));
+ return NULL;
}
graph_map = xmmap(NULL, graph_size, PROT_READ, MAP_PRIVATE, fd, 0);
ret = parse_commit_graph(graph_map, fd, graph_size);
@@ -106,12 +111,41 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
if (!ret) {
munmap(graph_map, graph_size);
close(fd);
- exit(1);
}
return ret;
}
+static int verify_commit_graph_lite(struct commit_graph *g)
+{
+ /*
+ * Basic validation shared between parse_commit_graph()
+ * which'll be called every time the graph is used, and the
+ * much more expensive verify_commit_graph() used by
+ * "commit-graph verify".
+ *
+ * There should only be very basic checks here to ensure that
+ * we don't e.g. segfault in fill_commit_in_graph(), but
+ * because this is a very hot codepath nothing that e.g. loops
+ * over g->num_commits, or runs a checksum on the commit-graph
+ * itself.
+ */
+ if (!g->chunk_oid_fanout) {
+ error("commit-graph is missing the OID Fanout chunk");
+ return 1;
+ }
+ if (!g->chunk_oid_lookup) {
+ error("commit-graph is missing the OID Lookup chunk");
+ return 1;
+ }
+ if (!g->chunk_commit_data) {
+ error("commit-graph is missing the Commit Data chunk");
+ return 1;
+ }
+
+ return 0;
+}
+
struct commit_graph *parse_commit_graph(void *graph_map, int fd,
size_t graph_size)
{
@@ -133,21 +167,21 @@ struct commit_graph *parse_commit_graph(void *graph_map, int fd,
graph_signature = get_be32(data);
if (graph_signature != GRAPH_SIGNATURE) {
- error(_("graph signature %X does not match signature %X"),
+ error(_("commit-graph signature %X does not match signature %X"),
graph_signature, GRAPH_SIGNATURE);
return NULL;
}
graph_version = *(unsigned char*)(data + 4);
if (graph_version != GRAPH_VERSION) {
- error(_("graph version %X does not match version %X"),
+ error(_("commit-graph version %X does not match version %X"),
graph_version, GRAPH_VERSION);
return NULL;
}
hash_version = *(unsigned char*)(data + 5);
if (hash_version != oid_version()) {
- error(_("hash version %X does not match version %X"),
+ error(_("commit-graph hash version %X does not match version %X"),
hash_version, oid_version());
return NULL;
}
@@ -170,7 +204,7 @@ struct commit_graph *parse_commit_graph(void *graph_map, int fd,
if (data + graph_size - chunk_lookup <
GRAPH_CHUNKLOOKUP_WIDTH) {
- error(_("chunk lookup table entry missing; graph file may be incomplete"));
+ error(_("commit-graph chunk lookup table entry missing; file may be incomplete"));
free(graph);
return NULL;
}
@@ -181,7 +215,7 @@ struct commit_graph *parse_commit_graph(void *graph_map, int fd,
chunk_lookup += GRAPH_CHUNKLOOKUP_WIDTH;
if (chunk_offset > graph_size - the_hash_algo->rawsz) {
- error(_("improper chunk offset %08x%08x"), (uint32_t)(chunk_offset >> 32),
+ error(_("commit-graph improper chunk offset %08x%08x"), (uint32_t)(chunk_offset >> 32),
(uint32_t)chunk_offset);
free(graph);
return NULL;
@@ -218,7 +252,7 @@ struct commit_graph *parse_commit_graph(void *graph_map, int fd,
}
if (chunk_repeated) {
- error(_("chunk id %08x appears multiple times"), chunk_id);
+ error(_("commit-graph chunk id %08x appears multiple times"), chunk_id);
free(graph);
return NULL;
}
@@ -233,9 +267,27 @@ struct commit_graph *parse_commit_graph(void *graph_map, int fd,
last_chunk_offset = chunk_offset;
}
+ if (verify_commit_graph_lite(graph)) {
+ free(graph);
+ return NULL;
+ }
+
return graph;
}
+static struct commit_graph *load_commit_graph_one(const char *graph_file)
+{
+
+ struct stat st;
+ int fd;
+ int open_ok = open_commit_graph(graph_file, &fd, &st);
+
+ if (!open_ok)
+ return NULL;
+
+ return load_commit_graph_one_fd_st(fd, &st);
+}
+
static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)
{
char *graph_name;
@@ -261,6 +313,10 @@ static int prepare_commit_graph(struct repository *r)
struct object_directory *odb;
int config_value;
+ if (git_env_bool(GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD, 0))
+ die("dying as requested by the '%s' variable on commit-graph load!",
+ GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD);
+
if (r->objects->commit_graph_attempted)
return !!r->objects->commit_graph;
r->objects->commit_graph_attempted = 1;
@@ -305,10 +361,10 @@ int generation_numbers_enabled(struct repository *r)
return !!first_generation;
}
-void close_commit_graph(struct repository *r)
+void close_commit_graph(struct raw_object_store *o)
{
- free_commit_graph(r->objects->commit_graph);
- r->objects->commit_graph = NULL;
+ free_commit_graph(o->commit_graph);
+ o->commit_graph = NULL;
}
static int bsearch_graph(struct commit_graph *g, struct object_id *oid, uint32_t *pos)
@@ -343,6 +399,11 @@ static void fill_commit_graph_info(struct commit *item, struct commit_graph *g,
item->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
}
+static inline void set_commit_tree(struct commit *c, struct tree *t)
+{
+ c->maybe_tree = t;
+}
+
static int fill_commit_in_graph(struct repository *r,
struct commit *item,
struct commit_graph *g, uint32_t pos)
@@ -356,7 +417,7 @@ static int fill_commit_in_graph(struct repository *r,
item->object.parsed = 1;
item->graph_pos = pos;
- item->maybe_tree = NULL;
+ set_commit_tree(item, NULL);
date_high = get_be32(commit_data + g->hash_len + 8) & 0x3;
date_low = get_be32(commit_data + g->hash_len + 12);
@@ -442,7 +503,7 @@ static struct tree *load_tree_for_commit(struct repository *r,
GRAPH_DATA_WIDTH * (c->graph_pos);
hashcpy(oid.hash, commit_data);
- c->maybe_tree = lookup_tree(r, &oid);
+ set_commit_tree(c, lookup_tree(r, &oid));
return c->maybe_tree;
}
@@ -464,14 +525,38 @@ struct tree *get_commit_tree_in_graph(struct repository *r, const struct commit
return get_commit_tree_in_graph_one(r, r->objects->commit_graph, c);
}
+struct packed_commit_list {
+ struct commit **list;
+ int nr;
+ int alloc;
+};
+
+struct packed_oid_list {
+ struct object_id *list;
+ int nr;
+ int alloc;
+};
+
+struct write_commit_graph_context {
+ struct repository *r;
+ const char *obj_dir;
+ char *graph_name;
+ struct packed_oid_list oids;
+ struct packed_commit_list commits;
+ int num_extra_edges;
+ unsigned long approx_nr_objects;
+ struct progress *progress;
+ int progress_done;
+ uint64_t progress_cnt;
+ unsigned append:1,
+ report_progress:1;
+};
+
static void write_graph_chunk_fanout(struct hashfile *f,
- struct commit **commits,
- int nr_commits,
- struct progress *progress,
- uint64_t *progress_cnt)
+ struct write_commit_graph_context *ctx)
{
int i, count = 0;
- struct commit **list = commits;
+ struct commit **list = ctx->commits.list;
/*
* Write the first-level table (the list is sorted,
@@ -479,10 +564,10 @@ static void write_graph_chunk_fanout(struct hashfile *f,
* having to do eight extra binary search iterations).
*/
for (i = 0; i < 256; i++) {
- while (count < nr_commits) {
+ while (count < ctx->commits.nr) {
if ((*list)->object.oid.hash[0] != i)
break;
- display_progress(progress, ++*progress_cnt);
+ display_progress(ctx->progress, ++ctx->progress_cnt);
count++;
list++;
}
@@ -492,14 +577,12 @@ static void write_graph_chunk_fanout(struct hashfile *f,
}
static void write_graph_chunk_oids(struct hashfile *f, int hash_len,
- struct commit **commits, int nr_commits,
- struct progress *progress,
- uint64_t *progress_cnt)
+ struct write_commit_graph_context *ctx)
{
- struct commit **list = commits;
+ struct commit **list = ctx->commits.list;
int count;
- for (count = 0; count < nr_commits; count++, list++) {
- display_progress(progress, ++*progress_cnt);
+ for (count = 0; count < ctx->commits.nr; count++, list++) {
+ display_progress(ctx->progress, ++ctx->progress_cnt);
hashwrite(f, (*list)->object.oid.hash, (int)hash_len);
}
}
@@ -511,21 +594,19 @@ static const unsigned char *commit_to_sha1(size_t index, void *table)
}
static void write_graph_chunk_data(struct hashfile *f, int hash_len,
- struct commit **commits, int nr_commits,
- struct progress *progress,
- uint64_t *progress_cnt)
+ struct write_commit_graph_context *ctx)
{
- struct commit **list = commits;
- struct commit **last = commits + nr_commits;
+ struct commit **list = ctx->commits.list;
+ struct commit **last = ctx->commits.list + ctx->commits.nr;
uint32_t num_extra_edges = 0;
while (list < last) {
struct commit_list *parent;
int edge_value;
uint32_t packedDate[2];
- display_progress(progress, ++*progress_cnt);
+ display_progress(ctx->progress, ++ctx->progress_cnt);
- parse_commit(*list);
+ parse_commit_no_graph(*list);
hashwrite(f, get_commit_tree_oid(*list)->hash, hash_len);
parent = (*list)->parents;
@@ -534,8 +615,8 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
edge_value = GRAPH_PARENT_NONE;
else {
edge_value = sha1_pos(parent->item->object.oid.hash,
- commits,
- nr_commits,
+ ctx->commits.list,
+ ctx->commits.nr,
commit_to_sha1);
if (edge_value < 0)
@@ -555,8 +636,8 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
edge_value = GRAPH_EXTRA_EDGES_NEEDED | num_extra_edges;
else {
edge_value = sha1_pos(parent->item->object.oid.hash,
- commits,
- nr_commits,
+ ctx->commits.list,
+ ctx->commits.nr,
commit_to_sha1);
if (edge_value < 0)
BUG("missing parent %s for commit %s",
@@ -588,19 +669,16 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
}
static void write_graph_chunk_extra_edges(struct hashfile *f,
- struct commit **commits,
- int nr_commits,
- struct progress *progress,
- uint64_t *progress_cnt)
+ struct write_commit_graph_context *ctx)
{
- struct commit **list = commits;
- struct commit **last = commits + nr_commits;
+ struct commit **list = ctx->commits.list;
+ struct commit **last = ctx->commits.list + ctx->commits.nr;
struct commit_list *parent;
while (list < last) {
int num_parents = 0;
- display_progress(progress, ++*progress_cnt);
+ display_progress(ctx->progress, ++ctx->progress_cnt);
for (parent = (*list)->parents; num_parents < 3 && parent;
parent = parent->next)
@@ -614,8 +692,8 @@ static void write_graph_chunk_extra_edges(struct hashfile *f,
/* Since num_parents > 2, this initializer is safe. */
for (parent = (*list)->parents->next; parent; parent = parent->next) {
int edge_value = sha1_pos(parent->item->object.oid.hash,
- commits,
- nr_commits,
+ ctx->commits.list,
+ ctx->commits.nr,
commit_to_sha1);
if (edge_value < 0)
@@ -639,125 +717,111 @@ static int commit_compare(const void *_a, const void *_b)
return oidcmp(a, b);
}
-struct packed_commit_list {
- struct commit **list;
- int nr;
- int alloc;
-};
-
-struct packed_oid_list {
- struct object_id *list;
- int nr;
- int alloc;
- struct progress *progress;
- int progress_done;
-};
-
static int add_packed_commits(const struct object_id *oid,
struct packed_git *pack,
uint32_t pos,
void *data)
{
- struct packed_oid_list *list = (struct packed_oid_list*)data;
+ struct write_commit_graph_context *ctx = (struct write_commit_graph_context*)data;
enum object_type type;
off_t offset = nth_packed_object_offset(pack, pos);
struct object_info oi = OBJECT_INFO_INIT;
- if (list->progress)
- display_progress(list->progress, ++list->progress_done);
+ if (ctx->progress)
+ display_progress(ctx->progress, ++ctx->progress_done);
oi.typep = &type;
- if (packed_object_info(the_repository, pack, offset, &oi) < 0)
+ if (packed_object_info(ctx->r, pack, offset, &oi) < 0)
die(_("unable to get type of object %s"), oid_to_hex(oid));
if (type != OBJ_COMMIT)
return 0;
- ALLOC_GROW(list->list, list->nr + 1, list->alloc);
- oidcpy(&(list->list[list->nr]), oid);
- list->nr++;
+ ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
+ oidcpy(&(ctx->oids.list[ctx->oids.nr]), oid);
+ ctx->oids.nr++;
return 0;
}
-static void add_missing_parents(struct packed_oid_list *oids, struct commit *commit)
+static void add_missing_parents(struct write_commit_graph_context *ctx, struct commit *commit)
{
struct commit_list *parent;
for (parent = commit->parents; parent; parent = parent->next) {
if (!(parent->item->object.flags & UNINTERESTING)) {
- ALLOC_GROW(oids->list, oids->nr + 1, oids->alloc);
- oidcpy(&oids->list[oids->nr], &(parent->item->object.oid));
- oids->nr++;
+ ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
+ oidcpy(&ctx->oids.list[ctx->oids.nr], &(parent->item->object.oid));
+ ctx->oids.nr++;
parent->item->object.flags |= UNINTERESTING;
}
}
}
-static void close_reachable(struct packed_oid_list *oids, int report_progress)
+static void close_reachable(struct write_commit_graph_context *ctx)
{
int i;
struct commit *commit;
- struct progress *progress = NULL;
- if (report_progress)
- progress = start_delayed_progress(
- _("Loading known commits in commit graph"), oids->nr);
- for (i = 0; i < oids->nr; i++) {
- display_progress(progress, i + 1);
- commit = lookup_commit(the_repository, &oids->list[i]);
+ if (ctx->report_progress)
+ ctx->progress = start_delayed_progress(
+ _("Loading known commits in commit graph"),
+ ctx->oids.nr);
+ for (i = 0; i < ctx->oids.nr; i++) {
+ display_progress(ctx->progress, i + 1);
+ commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
if (commit)
commit->object.flags |= UNINTERESTING;
}
- stop_progress(&progress);
+ stop_progress(&ctx->progress);
/*
- * As this loop runs, oids->nr may grow, but not more
+ * As this loop runs, ctx->oids.nr may grow, but not more
* than the number of missing commits in the reachable
* closure.
*/
- if (report_progress)
- progress = start_delayed_progress(
- _("Expanding reachable commits in commit graph"), oids->nr);
- for (i = 0; i < oids->nr; i++) {
- display_progress(progress, i + 1);
- commit = lookup_commit(the_repository, &oids->list[i]);
-
- if (commit && !parse_commit(commit))
- add_missing_parents(oids, commit);
+ if (ctx->report_progress)
+ ctx->progress = start_delayed_progress(
+ _("Expanding reachable commits in commit graph"),
+ ctx->oids.nr);
+ for (i = 0; i < ctx->oids.nr; i++) {
+ display_progress(ctx->progress, i + 1);
+ commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
+
+ if (commit && !parse_commit_no_graph(commit))
+ add_missing_parents(ctx, commit);
}
- stop_progress(&progress);
+ stop_progress(&ctx->progress);
- if (report_progress)
- progress = start_delayed_progress(
- _("Clearing commit marks in commit graph"), oids->nr);
- for (i = 0; i < oids->nr; i++) {
- display_progress(progress, i + 1);
- commit = lookup_commit(the_repository, &oids->list[i]);
+ if (ctx->report_progress)
+ ctx->progress = start_delayed_progress(
+ _("Clearing commit marks in commit graph"),
+ ctx->oids.nr);
+ for (i = 0; i < ctx->oids.nr; i++) {
+ display_progress(ctx->progress, i + 1);
+ commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
if (commit)
commit->object.flags &= ~UNINTERESTING;
}
- stop_progress(&progress);
+ stop_progress(&ctx->progress);
}
-static void compute_generation_numbers(struct packed_commit_list* commits,
- int report_progress)
+static void compute_generation_numbers(struct write_commit_graph_context *ctx)
{
int i;
struct commit_list *list = NULL;
- struct progress *progress = NULL;
- if (report_progress)
- progress = start_progress(
- _("Computing commit graph generation numbers"),
- commits->nr);
- for (i = 0; i < commits->nr; i++) {
- display_progress(progress, i + 1);
- if (commits->list[i]->generation != GENERATION_NUMBER_INFINITY &&
- commits->list[i]->generation != GENERATION_NUMBER_ZERO)
+ if (ctx->report_progress)
+ ctx->progress = start_progress(
+ _("Computing commit graph generation numbers"),
+ ctx->commits.nr);
+ for (i = 0; i < ctx->commits.nr; i++) {
+ display_progress(ctx->progress, i + 1);
+ if (ctx->commits.list[i]->generation != GENERATION_NUMBER_INFINITY &&
+ ctx->commits.list[i]->generation != GENERATION_NUMBER_ZERO)
continue;
- commit_list_insert(commits->list[i], &list);
+ commit_list_insert(ctx->commits.list[i], &list);
while (list) {
struct commit *current = list->item;
struct commit_list *parent;
@@ -784,7 +848,7 @@ static void compute_generation_numbers(struct packed_commit_list* commits,
}
}
}
- stop_progress(&progress);
+ stop_progress(&ctx->progress);
}
static int add_ref_to_list(const char *refname,
@@ -797,207 +861,187 @@ static int add_ref_to_list(const char *refname,
return 0;
}
-void write_commit_graph_reachable(const char *obj_dir, int append,
- int report_progress)
+int write_commit_graph_reachable(const char *obj_dir, unsigned int flags)
{
struct string_list list = STRING_LIST_INIT_DUP;
+ int result;
for_each_ref(add_ref_to_list, &list);
- write_commit_graph(obj_dir, NULL, &list, append, report_progress);
+ result = write_commit_graph(obj_dir, NULL, &list,
+ flags);
string_list_clear(&list, 0);
+ return result;
}
-void write_commit_graph(const char *obj_dir,
- struct string_list *pack_indexes,
- struct string_list *commit_hex,
- int append, int report_progress)
+static int fill_oids_from_packs(struct write_commit_graph_context *ctx,
+ struct string_list *pack_indexes)
{
- struct packed_oid_list oids;
- struct packed_commit_list commits;
- struct hashfile *f;
- uint32_t i, count_distinct = 0;
- char *graph_name;
- struct lock_file lk = LOCK_INIT;
- uint32_t chunk_ids[5];
- uint64_t chunk_offsets[5];
- int num_chunks;
- int num_extra_edges;
- struct commit_list *parent;
- struct progress *progress = NULL;
- const unsigned hashsz = the_hash_algo->rawsz;
- uint64_t progress_cnt = 0;
+ uint32_t i;
struct strbuf progress_title = STRBUF_INIT;
- unsigned long approx_nr_objects;
-
- if (!commit_graph_compatible(the_repository))
- return;
-
- oids.nr = 0;
- approx_nr_objects = approximate_object_count();
- oids.alloc = approx_nr_objects / 32;
- oids.progress = NULL;
- oids.progress_done = 0;
+ struct strbuf packname = STRBUF_INIT;
+ int dirlen;
- if (append) {
- prepare_commit_graph_one(the_repository, obj_dir);
- if (the_repository->objects->commit_graph)
- oids.alloc += the_repository->objects->commit_graph->num_commits;
+ strbuf_addf(&packname, "%s/pack/", ctx->obj_dir);
+ dirlen = packname.len;
+ if (ctx->report_progress) {
+ strbuf_addf(&progress_title,
+ Q_("Finding commits for commit graph in %d pack",
+ "Finding commits for commit graph in %d packs",
+ pack_indexes->nr),
+ pack_indexes->nr);
+ ctx->progress = start_delayed_progress(progress_title.buf, 0);
+ ctx->progress_done = 0;
}
-
- if (oids.alloc < 1024)
- oids.alloc = 1024;
- ALLOC_ARRAY(oids.list, oids.alloc);
-
- 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++) {
- const unsigned char *hash = commit_graph->chunk_oid_lookup +
- commit_graph->hash_len * i;
- hashcpy(oids.list[oids.nr++].hash, hash);
+ for (i = 0; i < pack_indexes->nr; i++) {
+ struct packed_git *p;
+ strbuf_setlen(&packname, dirlen);
+ strbuf_addstr(&packname, pack_indexes->items[i].string);
+ p = add_packed_git(packname.buf, packname.len, 1);
+ if (!p) {
+ error(_("error adding pack %s"), packname.buf);
+ return -1;
+ }
+ if (open_pack_index(p)) {
+ error(_("error opening index for %s"), packname.buf);
+ return -1;
}
+ for_each_object_in_pack(p, add_packed_commits, ctx,
+ FOR_EACH_OBJECT_PACK_ORDER);
+ close_pack(p);
+ free(p);
}
- if (pack_indexes) {
- struct strbuf packname = STRBUF_INIT;
- int dirlen;
- strbuf_addf(&packname, "%s/pack/", obj_dir);
- dirlen = packname.len;
- if (report_progress) {
- strbuf_addf(&progress_title,
- Q_("Finding commits for commit graph in %d pack",
- "Finding commits for commit graph in %d packs",
- pack_indexes->nr),
- pack_indexes->nr);
- oids.progress = start_delayed_progress(progress_title.buf, 0);
- oids.progress_done = 0;
- }
- for (i = 0; i < pack_indexes->nr; i++) {
- struct packed_git *p;
- strbuf_setlen(&packname, dirlen);
- strbuf_addstr(&packname, pack_indexes->items[i].string);
- p = add_packed_git(packname.buf, packname.len, 1);
- if (!p)
- die(_("error adding pack %s"), packname.buf);
- if (open_pack_index(p))
- die(_("error opening index for %s"), packname.buf);
- for_each_object_in_pack(p, add_packed_commits, &oids,
- FOR_EACH_OBJECT_PACK_ORDER);
- close_pack(p);
- free(p);
- }
- stop_progress(&oids.progress);
- strbuf_reset(&progress_title);
- strbuf_release(&packname);
+ stop_progress(&ctx->progress);
+ strbuf_reset(&progress_title);
+ strbuf_release(&packname);
+
+ return 0;
+}
+
+static void fill_oids_from_commit_hex(struct write_commit_graph_context *ctx,
+ struct string_list *commit_hex)
+{
+ uint32_t i;
+ struct strbuf progress_title = STRBUF_INIT;
+
+ if (ctx->report_progress) {
+ strbuf_addf(&progress_title,
+ Q_("Finding commits for commit graph from %d ref",
+ "Finding commits for commit graph from %d refs",
+ commit_hex->nr),
+ commit_hex->nr);
+ ctx->progress = start_delayed_progress(
+ progress_title.buf,
+ commit_hex->nr);
}
+ for (i = 0; i < commit_hex->nr; i++) {
+ const char *end;
+ struct object_id oid;
+ struct commit *result;
+
+ display_progress(ctx->progress, i + 1);
+ if (commit_hex->items[i].string &&
+ parse_oid_hex(commit_hex->items[i].string, &oid, &end))
+ continue;
- if (commit_hex) {
- if (report_progress) {
- strbuf_addf(&progress_title,
- Q_("Finding commits for commit graph from %d ref",
- "Finding commits for commit graph from %d refs",
- commit_hex->nr),
- commit_hex->nr);
- progress = start_delayed_progress(progress_title.buf,
- commit_hex->nr);
- }
- for (i = 0; i < commit_hex->nr; i++) {
- const char *end;
- struct object_id oid;
- struct commit *result;
-
- display_progress(progress, i + 1);
- if (commit_hex->items[i].string &&
- parse_oid_hex(commit_hex->items[i].string, &oid, &end))
- continue;
-
- result = lookup_commit_reference_gently(the_repository, &oid, 1);
-
- if (result) {
- ALLOC_GROW(oids.list, oids.nr + 1, oids.alloc);
- oidcpy(&oids.list[oids.nr], &(result->object.oid));
- oids.nr++;
- }
+ result = lookup_commit_reference_gently(ctx->r, &oid, 1);
+
+ if (result) {
+ ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
+ oidcpy(&ctx->oids.list[ctx->oids.nr], &(result->object.oid));
+ ctx->oids.nr++;
}
- stop_progress(&progress);
- strbuf_reset(&progress_title);
}
+ stop_progress(&ctx->progress);
+ strbuf_release(&progress_title);
+}
- if (!pack_indexes && !commit_hex) {
- if (report_progress)
- oids.progress = start_delayed_progress(
- _("Finding commits for commit graph among packed objects"),
- approx_nr_objects);
- for_each_packed_object(add_packed_commits, &oids,
- FOR_EACH_OBJECT_PACK_ORDER);
- if (oids.progress_done < approx_nr_objects)
- display_progress(oids.progress, approx_nr_objects);
- stop_progress(&oids.progress);
- }
+static void fill_oids_from_all_packs(struct write_commit_graph_context *ctx)
+{
+ if (ctx->report_progress)
+ ctx->progress = start_delayed_progress(
+ _("Finding commits for commit graph among packed objects"),
+ ctx->approx_nr_objects);
+ for_each_packed_object(add_packed_commits, ctx,
+ FOR_EACH_OBJECT_PACK_ORDER);
+ if (ctx->progress_done < ctx->approx_nr_objects)
+ display_progress(ctx->progress, ctx->approx_nr_objects);
+ stop_progress(&ctx->progress);
+}
- close_reachable(&oids, report_progress);
+static uint32_t count_distinct_commits(struct write_commit_graph_context *ctx)
+{
+ uint32_t i, count_distinct = 1;
- if (report_progress)
- progress = start_delayed_progress(
+ if (ctx->report_progress)
+ ctx->progress = start_delayed_progress(
_("Counting distinct commits in commit graph"),
- oids.nr);
- display_progress(progress, 0); /* TODO: Measure QSORT() progress */
- QSORT(oids.list, oids.nr, commit_compare);
- count_distinct = 1;
- for (i = 1; i < oids.nr; i++) {
- display_progress(progress, i + 1);
- if (!oideq(&oids.list[i - 1], &oids.list[i]))
+ ctx->oids.nr);
+ display_progress(ctx->progress, 0); /* TODO: Measure QSORT() progress */
+ QSORT(ctx->oids.list, ctx->oids.nr, commit_compare);
+
+ for (i = 1; i < ctx->oids.nr; i++) {
+ display_progress(ctx->progress, i + 1);
+ if (!oideq(&ctx->oids.list[i - 1], &ctx->oids.list[i]))
count_distinct++;
}
- stop_progress(&progress);
+ stop_progress(&ctx->progress);
- if (count_distinct >= GRAPH_EDGE_LAST_MASK)
- die(_("the commit graph format cannot write %d commits"), count_distinct);
+ return count_distinct;
+}
- commits.nr = 0;
- commits.alloc = count_distinct;
- ALLOC_ARRAY(commits.list, commits.alloc);
+static void copy_oids_to_commits(struct write_commit_graph_context *ctx)
+{
+ uint32_t i;
+ struct commit_list *parent;
- num_extra_edges = 0;
- if (report_progress)
- progress = start_delayed_progress(
+ ctx->num_extra_edges = 0;
+ if (ctx->report_progress)
+ ctx->progress = start_delayed_progress(
_("Finding extra edges in commit graph"),
- oids.nr);
- for (i = 0; i < oids.nr; i++) {
+ ctx->oids.nr);
+ for (i = 0; i < ctx->oids.nr; i++) {
int num_parents = 0;
- display_progress(progress, i + 1);
- if (i > 0 && oideq(&oids.list[i - 1], &oids.list[i]))
+ display_progress(ctx->progress, i + 1);
+ if (i > 0 && oideq(&ctx->oids.list[i - 1], &ctx->oids.list[i]))
continue;
- commits.list[commits.nr] = lookup_commit(the_repository, &oids.list[i]);
- parse_commit(commits.list[commits.nr]);
+ ctx->commits.list[ctx->commits.nr] = lookup_commit(ctx->r, &ctx->oids.list[i]);
+ parse_commit_no_graph(ctx->commits.list[ctx->commits.nr]);
- for (parent = commits.list[commits.nr]->parents;
+ for (parent = ctx->commits.list[ctx->commits.nr]->parents;
parent; parent = parent->next)
num_parents++;
if (num_parents > 2)
- num_extra_edges += num_parents - 1;
+ ctx->num_extra_edges += num_parents - 1;
- commits.nr++;
+ ctx->commits.nr++;
}
- num_chunks = num_extra_edges ? 4 : 3;
- stop_progress(&progress);
-
- if (commits.nr >= GRAPH_EDGE_LAST_MASK)
- die(_("too many commits to write graph"));
-
- compute_generation_numbers(&commits, report_progress);
+ stop_progress(&ctx->progress);
+}
- graph_name = get_commit_graph_filename(obj_dir);
- if (safe_create_leading_directories(graph_name)) {
- UNLEAK(graph_name);
- die_errno(_("unable to create leading directories of %s"),
- graph_name);
+static int write_commit_graph_file(struct write_commit_graph_context *ctx)
+{
+ uint32_t i;
+ struct hashfile *f;
+ struct lock_file lk = LOCK_INIT;
+ uint32_t chunk_ids[5];
+ uint64_t chunk_offsets[5];
+ const unsigned hashsz = the_hash_algo->rawsz;
+ struct strbuf progress_title = STRBUF_INIT;
+ int num_chunks = ctx->num_extra_edges ? 4 : 3;
+
+ ctx->graph_name = get_commit_graph_filename(ctx->obj_dir);
+ if (safe_create_leading_directories(ctx->graph_name)) {
+ UNLEAK(ctx->graph_name);
+ error(_("unable to create leading directories of %s"),
+ ctx->graph_name);
+ return -1;
}
- hold_lock_file_for_update(&lk, graph_name, LOCK_DIE_ON_ERROR);
+ hold_lock_file_for_update(&lk, ctx->graph_name, LOCK_DIE_ON_ERROR);
f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);
hashwrite_be32(f, GRAPH_SIGNATURE);
@@ -1010,7 +1054,7 @@ void write_commit_graph(const char *obj_dir,
chunk_ids[0] = GRAPH_CHUNKID_OIDFANOUT;
chunk_ids[1] = GRAPH_CHUNKID_OIDLOOKUP;
chunk_ids[2] = GRAPH_CHUNKID_DATA;
- if (num_extra_edges)
+ if (ctx->num_extra_edges)
chunk_ids[3] = GRAPH_CHUNKID_EXTRAEDGES;
else
chunk_ids[3] = 0;
@@ -1018,9 +1062,9 @@ void write_commit_graph(const char *obj_dir,
chunk_offsets[0] = 8 + (num_chunks + 1) * GRAPH_CHUNKLOOKUP_WIDTH;
chunk_offsets[1] = chunk_offsets[0] + GRAPH_FANOUT_SIZE;
- chunk_offsets[2] = chunk_offsets[1] + hashsz * commits.nr;
- chunk_offsets[3] = chunk_offsets[2] + (hashsz + 16) * commits.nr;
- chunk_offsets[4] = chunk_offsets[3] + 4 * num_extra_edges;
+ chunk_offsets[2] = chunk_offsets[1] + hashsz * ctx->commits.nr;
+ chunk_offsets[3] = chunk_offsets[2] + (hashsz + 16) * ctx->commits.nr;
+ chunk_offsets[4] = chunk_offsets[3] + 4 * ctx->num_extra_edges;
for (i = 0; i <= num_chunks; i++) {
uint32_t chunk_write[3];
@@ -1031,31 +1075,113 @@ void write_commit_graph(const char *obj_dir,
hashwrite(f, chunk_write, 12);
}
- if (report_progress) {
+ if (ctx->report_progress) {
strbuf_addf(&progress_title,
Q_("Writing out commit graph in %d pass",
"Writing out commit graph in %d passes",
num_chunks),
num_chunks);
- progress = start_delayed_progress(
+ ctx->progress = start_delayed_progress(
progress_title.buf,
- num_chunks * commits.nr);
+ num_chunks * ctx->commits.nr);
}
- write_graph_chunk_fanout(f, commits.list, commits.nr, progress, &progress_cnt);
- write_graph_chunk_oids(f, hashsz, commits.list, commits.nr, progress, &progress_cnt);
- write_graph_chunk_data(f, hashsz, commits.list, commits.nr, progress, &progress_cnt);
- if (num_extra_edges)
- write_graph_chunk_extra_edges(f, commits.list, commits.nr, progress, &progress_cnt);
- stop_progress(&progress);
+ write_graph_chunk_fanout(f, ctx);
+ write_graph_chunk_oids(f, hashsz, ctx);
+ write_graph_chunk_data(f, hashsz, ctx);
+ if (ctx->num_extra_edges)
+ write_graph_chunk_extra_edges(f, ctx);
+ stop_progress(&ctx->progress);
strbuf_release(&progress_title);
- close_commit_graph(the_repository);
+ close_commit_graph(ctx->r->objects);
finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
commit_lock_file(&lk);
- free(graph_name);
- free(commits.list);
- free(oids.list);
+ return 0;
+}
+
+int write_commit_graph(const char *obj_dir,
+ struct string_list *pack_indexes,
+ struct string_list *commit_hex,
+ unsigned int flags)
+{
+ struct write_commit_graph_context *ctx;
+ uint32_t i, count_distinct = 0;
+ int res = 0;
+
+ if (!commit_graph_compatible(the_repository))
+ return 0;
+
+ ctx = xcalloc(1, sizeof(struct write_commit_graph_context));
+ ctx->r = the_repository;
+ ctx->obj_dir = obj_dir;
+ ctx->append = flags & COMMIT_GRAPH_APPEND ? 1 : 0;
+ ctx->report_progress = flags & COMMIT_GRAPH_PROGRESS ? 1 : 0;
+
+ ctx->approx_nr_objects = approximate_object_count();
+ ctx->oids.alloc = ctx->approx_nr_objects / 32;
+
+ if (ctx->append) {
+ prepare_commit_graph_one(ctx->r, ctx->obj_dir);
+ if (ctx->r->objects->commit_graph)
+ ctx->oids.alloc += ctx->r->objects->commit_graph->num_commits;
+ }
+
+ if (ctx->oids.alloc < 1024)
+ ctx->oids.alloc = 1024;
+ ALLOC_ARRAY(ctx->oids.list, ctx->oids.alloc);
+
+ if (ctx->append && ctx->r->objects->commit_graph) {
+ struct commit_graph *g = ctx->r->objects->commit_graph;
+ for (i = 0; i < g->num_commits; i++) {
+ const unsigned char *hash = g->chunk_oid_lookup + g->hash_len * i;
+ hashcpy(ctx->oids.list[ctx->oids.nr++].hash, hash);
+ }
+ }
+
+ if (pack_indexes) {
+ if ((res = fill_oids_from_packs(ctx, pack_indexes)))
+ goto cleanup;
+ }
+
+ if (commit_hex)
+ fill_oids_from_commit_hex(ctx, commit_hex);
+
+ if (!pack_indexes && !commit_hex)
+ fill_oids_from_all_packs(ctx);
+
+ close_reachable(ctx);
+
+ count_distinct = count_distinct_commits(ctx);
+
+ if (count_distinct >= GRAPH_EDGE_LAST_MASK) {
+ error(_("the commit graph format cannot write %d commits"), count_distinct);
+ res = -1;
+ goto cleanup;
+ }
+
+ ctx->commits.alloc = count_distinct;
+ ALLOC_ARRAY(ctx->commits.list, ctx->commits.alloc);
+
+ copy_oids_to_commits(ctx);
+
+ if (ctx->commits.nr >= GRAPH_EDGE_LAST_MASK) {
+ error(_("too many commits to write graph"));
+ res = -1;
+ goto cleanup;
+ }
+
+ compute_generation_numbers(ctx);
+
+ res = write_commit_graph_file(ctx);
+
+cleanup:
+ free(ctx->graph_name);
+ free(ctx->commits.list);
+ free(ctx->oids.list);
+ free(ctx);
+
+ return res;
}
#define VERIFY_COMMIT_GRAPH_ERROR_HASH 2
@@ -1089,15 +1215,7 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g)
return 1;
}
- verify_commit_graph_error = 0;
-
- if (!g->chunk_oid_fanout)
- graph_report("commit-graph is missing the OID Fanout chunk");
- if (!g->chunk_oid_lookup)
- graph_report("commit-graph is missing the OID Lookup chunk");
- if (!g->chunk_commit_data)
- graph_report("commit-graph is missing the Commit Data chunk");
-
+ verify_commit_graph_error = verify_commit_graph_lite(g);
if (verify_commit_graph_error)
return verify_commit_graph_error;
@@ -1116,7 +1234,7 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g)
hashcpy(cur_oid.hash, g->chunk_oid_lookup + g->hash_len * i);
if (i && oidcmp(&prev_oid, &cur_oid) >= 0)
- graph_report("commit-graph has incorrect OID order: %s then %s",
+ graph_report(_("commit-graph has incorrect OID order: %s then %s"),
oid_to_hex(&prev_oid),
oid_to_hex(&cur_oid));
@@ -1126,14 +1244,14 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g)
uint32_t fanout_value = get_be32(g->chunk_oid_fanout + cur_fanout_pos);
if (i != fanout_value)
- graph_report("commit-graph has incorrect fanout value: fanout[%d] = %u != %u",
+ graph_report(_("commit-graph has incorrect fanout value: fanout[%d] = %u != %u"),
cur_fanout_pos, fanout_value, i);
cur_fanout_pos++;
}
graph_commit = lookup_commit(r, &cur_oid);
if (!parse_commit_in_graph_one(r, g, graph_commit))
- graph_report("failed to parse %s from commit-graph",
+ graph_report(_("failed to parse commit %s from commit-graph"),
oid_to_hex(&cur_oid));
}
@@ -1141,7 +1259,7 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g)
uint32_t fanout_value = get_be32(g->chunk_oid_fanout + cur_fanout_pos);
if (g->num_commits != fanout_value)
- graph_report("commit-graph has incorrect fanout value: fanout[%d] = %u != %u",
+ graph_report(_("commit-graph has incorrect fanout value: fanout[%d] = %u != %u"),
cur_fanout_pos, fanout_value, i);
cur_fanout_pos++;
@@ -1161,16 +1279,16 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g)
hashcpy(cur_oid.hash, g->chunk_oid_lookup + g->hash_len * i);
graph_commit = lookup_commit(r, &cur_oid);
- odb_commit = (struct commit *)create_object(r, cur_oid.hash, alloc_commit_node(r));
+ odb_commit = (struct commit *)create_object(r, &cur_oid, alloc_commit_node(r));
if (parse_commit_internal(odb_commit, 0, 0)) {
- graph_report("failed to parse %s from object database",
+ graph_report(_("failed to parse commit %s from object database for commit-graph"),
oid_to_hex(&cur_oid));
continue;
}
if (!oideq(&get_commit_tree_in_graph_one(r, g, graph_commit)->object.oid,
get_commit_tree_oid(odb_commit)))
- graph_report("root tree OID for commit %s in commit-graph is %s != %s",
+ graph_report(_("root tree OID for commit %s in commit-graph is %s != %s"),
oid_to_hex(&cur_oid),
oid_to_hex(get_commit_tree_oid(graph_commit)),
oid_to_hex(get_commit_tree_oid(odb_commit)));
@@ -1180,13 +1298,13 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g)
while (graph_parents) {
if (odb_parents == NULL) {
- graph_report("commit-graph parent list for commit %s is too long",
+ graph_report(_("commit-graph parent list for commit %s is too long"),
oid_to_hex(&cur_oid));
break;
}
if (!oideq(&graph_parents->item->object.oid, &odb_parents->item->object.oid))
- graph_report("commit-graph parent for %s is %s != %s",
+ graph_report(_("commit-graph parent for %s is %s != %s"),
oid_to_hex(&cur_oid),
oid_to_hex(&graph_parents->item->object.oid),
oid_to_hex(&odb_parents->item->object.oid));
@@ -1199,16 +1317,16 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g)
}
if (odb_parents != NULL)
- graph_report("commit-graph parent list for commit %s terminates early",
+ graph_report(_("commit-graph parent list for commit %s terminates early"),
oid_to_hex(&cur_oid));
if (!graph_commit->generation) {
if (generation_zero == GENERATION_NUMBER_EXISTS)
- graph_report("commit-graph has generation number zero for commit %s, but non-zero elsewhere",
+ graph_report(_("commit-graph has generation number zero for commit %s, but non-zero elsewhere"),
oid_to_hex(&cur_oid));
generation_zero = GENERATION_ZERO_EXISTS;
} else if (generation_zero == GENERATION_ZERO_EXISTS)
- graph_report("commit-graph has non-zero generation number for commit %s, but zero elsewhere",
+ graph_report(_("commit-graph has non-zero generation number for commit %s, but zero elsewhere"),
oid_to_hex(&cur_oid));
if (generation_zero == GENERATION_ZERO_EXISTS)
@@ -1223,13 +1341,13 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g)
max_generation--;
if (graph_commit->generation != max_generation + 1)
- graph_report("commit-graph generation for commit %s is %u != %u",
+ graph_report(_("commit-graph generation for commit %s is %u != %u"),
oid_to_hex(&cur_oid),
graph_commit->generation,
max_generation + 1);
if (graph_commit->date != odb_commit->date)
- graph_report("commit date for commit %s in commit-graph is %"PRItime" != %"PRItime,
+ graph_report(_("commit date for commit %s in commit-graph is %"PRItime" != %"PRItime),
oid_to_hex(&cur_oid),
graph_commit->date,
odb_commit->date);