|
|
|
@ -525,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,
|
|
|
|
@ -540,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++;
|
|
|
|
|
}
|
|
|
|
@ -553,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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -572,19 +594,17 @@ 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_no_graph(*list);
|
|
|
|
|
hashwrite(f, get_commit_tree_oid(*list)->hash, hash_len);
|
|
|
|
@ -595,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)
|
|
|
|
@ -616,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",
|
|
|
|
@ -649,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)
|
|
|
|
@ -675,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)
|
|
|
|
@ -700,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 (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(oids, 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;
|
|
|
|
@ -845,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,
|
|
|
|
@ -858,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;
|
|
|
|
|
struct strbuf packname = STRBUF_INIT;
|
|
|
|
|
int dirlen;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
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 (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;
|
|
|
|
|
if (open_pack_index(p)) {
|
|
|
|
|
error(_("error opening index for %s"), packname.buf);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
for_each_object_in_pack(p, add_packed_commits, ctx,
|
|
|
|
|
FOR_EACH_OBJECT_PACK_ORDER);
|
|
|
|
|
close_pack(p);
|
|
|
|
|
free(p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
stop_progress(&ctx->progress);
|
|
|
|
|
strbuf_reset(&progress_title);
|
|
|
|
|
strbuf_release(&packname);
|
|
|
|
|
|
|
|
|
|
display_progress(progress, i + 1);
|
|
|
|
|
if (commit_hex->items[i].string &&
|
|
|
|
|
parse_oid_hex(commit_hex->items[i].string, &oid, &end))
|
|
|
|
|
continue;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = lookup_commit_reference_gently(the_repository, &oid, 1);
|
|
|
|
|
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 (result) {
|
|
|
|
|
ALLOC_GROW(oids.list, oids.nr + 1, oids.alloc);
|
|
|
|
|
oidcpy(&oids.list[oids.nr], &(result->object.oid));
|
|
|
|
|
oids.nr++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
stop_progress(&progress);
|
|
|
|
|
strbuf_reset(&progress_title);
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
close_reachable(&oids, report_progress);
|
|
|
|
|
|
|
|
|
|
if (report_progress)
|
|
|
|
|
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]))
|
|
|
|
|
count_distinct++;
|
|
|
|
|
}
|
|
|
|
|
stop_progress(&progress);
|
|
|
|
|
|
|
|
|
|
if (count_distinct >= GRAPH_EDGE_LAST_MASK)
|
|
|
|
|
die(_("the commit graph format cannot write %d commits"), count_distinct);
|
|
|
|
|
|
|
|
|
|
commits.nr = 0;
|
|
|
|
|
commits.alloc = count_distinct;
|
|
|
|
|
ALLOC_ARRAY(commits.list, commits.alloc);
|
|
|
|
|
|
|
|
|
|
num_extra_edges = 0;
|
|
|
|
|
if (report_progress)
|
|
|
|
|
progress = start_delayed_progress(
|
|
|
|
|
_("Finding extra edges in commit graph"),
|
|
|
|
|
oids.nr);
|
|
|
|
|
for (i = 0; i < 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 (commit_hex->items[i].string &&
|
|
|
|
|
parse_oid_hex(commit_hex->items[i].string, &oid, &end))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
commits.list[commits.nr] = lookup_commit(the_repository, &oids.list[i]);
|
|
|
|
|
parse_commit_no_graph(commits.list[commits.nr]);
|
|
|
|
|
result = lookup_commit_reference_gently(ctx->r, &oid, 1);
|
|
|
|
|
|
|
|
|
|
for (parent = commits.list[commits.nr]->parents;
|
|
|
|
|
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(&ctx->progress);
|
|
|
|
|
strbuf_release(&progress_title);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint32_t count_distinct_commits(struct write_commit_graph_context *ctx)
|
|
|
|
|
{
|
|
|
|
|
uint32_t i, count_distinct = 1;
|
|
|
|
|
|
|
|
|
|
if (ctx->report_progress)
|
|
|
|
|
ctx->progress = start_delayed_progress(
|
|
|
|
|
_("Counting distinct commits in commit graph"),
|
|
|
|
|
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(&ctx->progress);
|
|
|
|
|
|
|
|
|
|
return count_distinct;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void copy_oids_to_commits(struct write_commit_graph_context *ctx)
|
|
|
|
|
{
|
|
|
|
|
uint32_t i;
|
|
|
|
|
struct commit_list *parent;
|
|
|
|
|
|
|
|
|
|
ctx->num_extra_edges = 0;
|
|
|
|
|
if (ctx->report_progress)
|
|
|
|
|
ctx->progress = start_delayed_progress(
|
|
|
|
|
_("Finding extra edges in commit graph"),
|
|
|
|
|
ctx->oids.nr);
|
|
|
|
|
for (i = 0; i < ctx->oids.nr; i++) {
|
|
|
|
|
int num_parents = 0;
|
|
|
|
|
display_progress(ctx->progress, i + 1);
|
|
|
|
|
if (i > 0 && oideq(&ctx->oids.list[i - 1], &ctx->oids.list[i]))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
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 = 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);
|
|
|
|
|
stop_progress(&ctx->progress);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (commits.nr >= GRAPH_EDGE_LAST_MASK)
|
|
|
|
|
die(_("too many commits to write graph"));
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
compute_generation_numbers(&commits, report_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);
|
|
|
|
|
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);
|
|
|
|
@ -1071,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;
|
|
|
|
@ -1079,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];
|
|
|
|
@ -1092,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);
|
|
|
|
|
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
|
|
|
|
|