From: Junio C Hamano Date: Tue, 16 Oct 2018 07:15:59 +0000 (+0900) Subject: Merge branch 'ds/commit-graph-with-grafts' X-Git-Tag: v2.20.0-rc0~193 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/6d8f8ebb74d21b51cfbf427a436094134af36ee2?hp=-c Merge branch 'ds/commit-graph-with-grafts' 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 --- 6d8f8ebb74d21b51cfbf427a436094134af36ee2 diff --combined builtin/commit-graph.c index bc0fa9ba52,da737df321..22b974f4b4 --- a/builtin/commit-graph.c +++ b/builtin/commit-graph.c @@@ -120,6 -120,8 +120,8 @@@ static int graph_read(int argc, const c return 0; } + extern int read_replace_refs; + static int graph_write(int argc, const char **argv) { struct string_list *pack_indexes = NULL; @@@ -150,8 -152,10 +152,10 @@@ 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); + write_commit_graph_reachable(opts.obj_dir, opts.append, 1); return 0; } @@@ -171,8 -175,7 +175,8 @@@ write_commit_graph(opts.obj_dir, pack_indexes, commit_hex, - opts.append); + opts.append, + 1); string_list_clear(&lines, 0); return 0; diff --combined builtin/replace.c index 8e67e09819,b5861a0ee9..30a661ea0c --- a/builtin/replace.c +++ b/builtin/replace.c @@@ -39,7 -39,8 +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; @@@ -54,11 -55,10 +55,10 @@@ enum object_type obj_type, repl_type; if (get_oid(refname, &object)) - return error("Failed to resolve '%s' as a valid ref.", refname); + 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)); @@@ -83,8 -83,8 +83,8 @@@ static int list_replace_refs(const cha else if (!strcmp(format, "long")) data.format = REPLACE_FORMAT_LONG; else - return error("invalid replace format '%s'\n" - "valid formats are 'short', 'medium' and 'long'\n", + return error(_("invalid replace format '%s'\n" + "valid formats are 'short', 'medium' and 'long'"), format); for_each_replace_ref(the_repository, show_reference, (void *)&data); @@@ -108,7 -108,7 +108,7 @@@ static int for_each_replace_name(const for (p = argv; *p; p++) { if (get_oid(*p, &oid)) { - error("Failed to resolve '%s' as a valid ref.", *p); + error("failed to resolve '%s' as a valid ref", *p); had_error = 1; continue; } @@@ -118,7 -118,7 +118,7 @@@ full_hex = ref.buf + base_len; if (read_ref(ref.buf, &oid)) { - error("replace ref '%s' not found.", full_hex); + error(_("replace ref '%s' not found"), full_hex); had_error = 1; continue; } @@@ -134,7 -134,7 +134,7 @@@ static int delete_replace_ref(const cha { if (delete_ref(NULL, ref, oid, 0)) return 1; - printf("Deleted replace ref '%s'\n", name); + printf_ln(_("Deleted replace ref '%s'"), name); return 0; } @@@ -146,12 -146,12 +146,12 @@@ static int check_ref_valid(struct objec strbuf_reset(ref); strbuf_addf(ref, "%s%s", git_replace_ref_base, oid_to_hex(object)); if (check_refname_format(ref->buf, 0)) - return error("'%s' is not a valid ref name.", ref->buf); + return error(_("'%s' is not a valid ref name"), ref->buf); if (read_ref(ref->buf, prev)) oidclr(prev); else if (!force) - return error("replace ref '%s' already exists", ref->buf); + return error(_("replace ref '%s' already exists"), ref->buf); return 0; } @@@ -171,10 -171,10 +171,10 @@@ static int replace_object_oid(const cha obj_type = oid_object_info(the_repository, object, NULL); repl_type = oid_object_info(the_repository, repl, NULL); if (!force && obj_type != repl_type) - return error("Objects must be of the same type.\n" - "'%s' points to a replaced object of type '%s'\n" - "while '%s' points to a replacement object of " - "type '%s'.", + return error(_("Objects must be of the same type.\n" + "'%s' points to a replaced object of type '%s'\n" + "while '%s' points to a replacement object of " + "type '%s'."), object_ref, type_name(obj_type), replace_ref, type_name(repl_type)); @@@ -200,10 -200,10 +200,10 @@@ static int replace_object(const char *o struct object_id object, repl; if (get_oid(object_ref, &object)) - return error("Failed to resolve '%s' as a valid ref.", + return error(_("failed to resolve '%s' as a valid ref"), object_ref); if (get_oid(replace_ref, &repl)) - return error("Failed to resolve '%s' as a valid ref.", + return error(_("failed to resolve '%s' as a valid ref"), replace_ref); return replace_object_oid(object_ref, &object, replace_ref, &repl, force); @@@ -222,7 -222,7 +222,7 @@@ static int export_object(const struct o fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) - return error_errno("unable to open %s for writing", filename); + return error_errno(_("unable to open %s for writing"), filename); argv_array_push(&cmd.args, "--no-replace-objects"); argv_array_push(&cmd.args, "cat-file"); @@@ -235,7 -235,7 +235,7 @@@ cmd.out = fd; if (run_command(&cmd)) - return error("cat-file reported failure"); + return error(_("cat-file reported failure")); return 0; } @@@ -251,7 -251,7 +251,7 @@@ static int import_object(struct object_ fd = open(filename, O_RDONLY); if (fd < 0) - return error_errno("unable to open %s for reading", filename); + return error_errno(_("unable to open %s for reading"), filename); if (!raw && type == OBJ_TREE) { const char *argv[] = { "mktree", NULL }; @@@ -265,11 -265,11 +265,11 @@@ if (start_command(&cmd)) { close(fd); - return error("unable to spawn mktree"); + return error(_("unable to spawn mktree")); } if (strbuf_read(&result, cmd.out, 41) < 0) { - error_errno("unable to read from mktree"); + error_errno(_("unable to read from mktree")); close(fd); close(cmd.out); return -1; @@@ -278,11 -278,11 +278,11 @@@ if (finish_command(&cmd)) { strbuf_release(&result); - return error("mktree reported failure"); + return error(_("mktree reported failure")); } if (get_oid_hex(result.buf, oid) < 0) { strbuf_release(&result); - return error("mktree did not return an object name"); + return error(_("mktree did not return an object name")); } strbuf_release(&result); @@@ -291,12 -291,12 +291,12 @@@ int flags = HASH_FORMAT_CHECK | HASH_WRITE_OBJECT; if (fstat(fd, &st) < 0) { - error_errno("unable to fstat %s", filename); + error_errno(_("unable to fstat %s"), filename); close(fd); return -1; } if (index_fd(oid, fd, &st, type, NULL, flags) < 0) - return error("unable to write object to database"); + return error(_("unable to write object to database")); /* index_fd close()s fd for us */ } @@@ -315,11 -315,11 +315,11 @@@ static int edit_and_replace(const char struct strbuf ref = STRBUF_INIT; if (get_oid(object_ref, &old_oid) < 0) - return error("Not a valid object name: '%s'", object_ref); + return error(_("not a valid object name: '%s'"), object_ref); type = oid_object_info(the_repository, &old_oid, NULL); if (type < 0) - return error("unable to get object type for %s", + return error(_("unable to get object type for %s"), oid_to_hex(&old_oid)); if (check_ref_valid(&old_oid, &prev, &ref, force)) { @@@ -335,7 -335,7 +335,7 @@@ } if (launch_editor(tmpfile, NULL, NULL) < 0) { free(tmpfile); - return error("editing object file failed"); + return error(_("editing object file failed")); } if (import_object(&new_oid, type, raw, tmpfile)) { free(tmpfile); @@@ -343,8 -343,8 +343,8 @@@ } free(tmpfile); - if (!oidcmp(&old_oid, &new_oid)) - return error("new object is the same as the old one: '%s'", oid_to_hex(&old_oid)); + if (oideq(&old_oid, &new_oid)) + return error(_("new object is the same as the old one: '%s'"), oid_to_hex(&old_oid)); return replace_object_oid(object_ref, &old_oid, "replacement", &new_oid, force); } @@@ -368,7 -368,7 +368,7 @@@ static int replace_parents(struct strbu struct object_id oid; if (get_oid(argv[i], &oid) < 0) { strbuf_release(&new_parents); - return error(_("Not a valid object name: '%s'"), + return error(_("not a valid object name: '%s'"), argv[i]); } if (!lookup_commit_reference(the_repository, &oid)) { @@@ -412,9 -412,9 +412,9 @@@ static int check_one_mergetag(struct co for (i = 1; i < mergetag_data->argc; i++) { struct object_id oid; if (get_oid(mergetag_data->argv[i], &oid) < 0) - return error(_("Not a valid object name: '%s'"), + return error(_("not a valid object name: '%s'"), mergetag_data->argv[i]); - if (!oidcmp(&tag->tagged->oid, &oid)) + if (oideq(&tag->tagged->oid, &oid)) return 0; /* found */ } @@@ -442,7 -442,7 +442,7 @@@ static int create_graft(int argc, cons unsigned long size; if (get_oid(old_ref, &old_oid) < 0) - return error(_("Not a valid object name: '%s'"), old_ref); + return error(_("not a valid object name: '%s'"), old_ref); commit = lookup_commit_reference(the_repository, &old_oid); if (!commit) return error(_("could not parse %s"), old_ref); @@@ -457,7 -457,7 +457,7 @@@ } if (remove_signature(&buf)) { - warning(_("the original commit '%s' has a gpg signature."), old_ref); + warning(_("the original commit '%s' has a gpg signature"), old_ref); warning(_("the signature will be removed in the replacement commit!")); } @@@ -474,12 -474,12 +474,12 @@@ strbuf_release(&buf); - if (!oidcmp(&old_oid, &new_oid)) { + if (oideq(&old_oid, &new_oid)) { if (gentle) { - warning("graft for '%s' unnecessary", oid_to_hex(&old_oid)); + warning(_("graft for '%s' unnecessary"), oid_to_hex(&old_oid)); return 0; } - return error("new commit is the same as the old one: '%s'", oid_to_hex(&old_oid)); + return error(_("new commit is the same as the old one: '%s'"), oid_to_hex(&old_oid)); } return replace_object_oid(old_ref, &old_oid, "replacement", &new_oid, force); @@@ -553,7 -553,7 +553,7 @@@ int cmd_replace(int argc, const char ** cmdmode = argc ? MODE_REPLACE : MODE_LIST; if (format && cmdmode != MODE_LIST) - usage_msg_opt("--format cannot be used when not listing", + usage_msg_opt(_("--format cannot be used when not listing"), git_replace_usage, options); if (force && @@@ -561,47 -561,47 +561,47 @@@ cmdmode != MODE_EDIT && cmdmode != MODE_GRAFT && cmdmode != MODE_CONVERT_GRAFT_FILE) - usage_msg_opt("-f only makes sense when writing a replacement", + usage_msg_opt(_("-f only makes sense when writing a replacement"), git_replace_usage, options); if (raw && cmdmode != MODE_EDIT) - usage_msg_opt("--raw only makes sense with --edit", + usage_msg_opt(_("--raw only makes sense with --edit"), git_replace_usage, options); switch (cmdmode) { case MODE_DELETE: if (argc < 1) - usage_msg_opt("-d needs at least one argument", + usage_msg_opt(_("-d needs at least one argument"), git_replace_usage, options); return for_each_replace_name(argv, delete_replace_ref); case MODE_REPLACE: if (argc != 2) - usage_msg_opt("bad number of arguments", + usage_msg_opt(_("bad number of arguments"), git_replace_usage, options); return replace_object(argv[0], argv[1], force); case MODE_EDIT: if (argc != 1) - usage_msg_opt("-e needs exactly one argument", + usage_msg_opt(_("-e needs exactly one argument"), git_replace_usage, options); return edit_and_replace(argv[0], force, raw); case MODE_GRAFT: if (argc < 1) - usage_msg_opt("-g needs at least one argument", + usage_msg_opt(_("-g needs at least one argument"), git_replace_usage, options); return create_graft(argc, argv, force, 0); case MODE_CONVERT_GRAFT_FILE: if (argc != 0) - usage_msg_opt("--convert-graft-file takes no argument", + usage_msg_opt(_("--convert-graft-file takes no argument"), git_replace_usage, options); return !!convert_graft_file(force); case MODE_LIST: if (argc > 1) - usage_msg_opt("only one pattern can be given with -l", + usage_msg_opt(_("only one pattern can be given with -l"), git_replace_usage, options); return list_replace_refs(argv[0], format); diff --combined commit-graph.c index 5908bd4e34,4bd1a4abbf..a686758603 --- a/commit-graph.c +++ b/commit-graph.c @@@ -13,7 -13,8 +13,9 @@@ #include "commit-graph.h" #include "object-store.h" #include "alloc.h" + #include "hashmap.h" + #include "replace-object.h" +#include "progress.h" #define GRAPH_SIGNATURE 0x43475048 /* "CGPH" */ #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */ @@@ -57,6 -58,28 +59,28 @@@ static struct commit_graph *alloc_commi 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; @@@ -81,28 -104,28 +105,28 @@@ if (graph_size < GRAPH_MIN_SIZE) { close(fd); - die("graph file %s is too small", graph_file); + die(_("graph file %s is too small"), graph_file); } graph_map = xmmap(NULL, graph_size, PROT_READ, MAP_PRIVATE, fd, 0); data = (const unsigned char *)graph_map; graph_signature = get_be32(data); if (graph_signature != GRAPH_SIGNATURE) { - error("graph signature %X does not match signature %X", + error(_("graph signature %X does not match signature %X"), graph_signature, GRAPH_SIGNATURE); goto cleanup_fail; } graph_version = *(unsigned char*)(data + 4); if (graph_version != GRAPH_VERSION) { - error("graph version %X does not match version %X", + error(_("graph version %X does not match version %X"), graph_version, GRAPH_VERSION); goto cleanup_fail; } hash_version = *(unsigned char*)(data + 5); if (hash_version != GRAPH_OID_VERSION) { - error("hash version %X does not match version %X", + error(_("hash version %X does not match version %X"), hash_version, GRAPH_OID_VERSION); goto cleanup_fail; } @@@ -126,7 -149,7 +150,7 @@@ chunk_lookup += GRAPH_CHUNKLOOKUP_WIDTH; if (chunk_offset > graph_size - GIT_MAX_RAWSZ) { - error("improper chunk offset %08x%08x", (uint32_t)(chunk_offset >> 32), + error(_("improper chunk offset %08x%08x"), (uint32_t)(chunk_offset >> 32), (uint32_t)chunk_offset); goto cleanup_fail; } @@@ -162,7 -185,7 +186,7 @@@ } if (chunk_repeated) { - error("chunk id %08x appears multiple times", chunk_id); + error(_("chunk id %08x appears multiple times"), chunk_id); goto cleanup_fail; } @@@ -214,9 -237,8 +238,9 @@@ static int prepare_commit_graph(struct return !!r->objects->commit_graph; r->objects->commit_graph_attempted = 1; - if (repo_config_get_bool(r, "core.commitgraph", &config_value) || - !config_value) + if (!git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) && + (repo_config_get_bool(r, "core.commitgraph", &config_value) || + !config_value)) /* * This repository is not configured to use commit graphs, so * do not load one. (But report commit_graph_attempted anyway @@@ -225,6 -247,9 +249,9 @@@ */ 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); @@@ -235,28 -260,10 +262,28 @@@ return !!r->objects->commit_graph; } +int generation_numbers_enabled(struct repository *r) +{ + uint32_t first_generation; + struct commit_graph *g; + if (!prepare_commit_graph(r)) + return 0; + + g = r->objects->commit_graph; + + if (!g->num_commits) + return 0; + + first_generation = get_be32(g->chunk_commit_data + + g->hash_len + 8) >> 2; + + return !!first_generation; +} + - 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) @@@ -278,7 -285,7 +305,7 @@@ static struct commit_list **insert_pare hashcpy(oid.hash, g->chunk_oid_lookup + g->hash_len * pos); c = lookup_commit(the_repository, &oid); if (!c) - die("could not find commit %s", oid_to_hex(&oid)); + die(_("could not find commit %s"), oid_to_hex(&oid)); c->graph_pos = pos; return &commit_list_insert(c, pptr)->next; } @@@ -568,8 -575,6 +595,8 @@@ 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, @@@ -582,12 -587,9 +609,12 @@@ 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); + oi.typep = &type; if (packed_object_info(the_repository, pack, offset, &oi) < 0) - die("unable to get type of object %s", oid_to_hex(oid)); + die(_("unable to get type of object %s"), oid_to_hex(oid)); if (type != OBJ_COMMIT) return 0; @@@ -612,18 -614,12 +639,18 @@@ static void add_missing_parents(struct } } -static void close_reachable(struct packed_oid_list *oids) +static void close_reachable(struct packed_oid_list *oids, int report_progress) { int i; struct commit *commit; + struct progress *progress = NULL; + int j = 0; + if (report_progress) + progress = start_delayed_progress( + _("Annotating commits in commit graph"), 0); for (i = 0; i < oids->nr; i++) { + display_progress(progress, ++j); commit = lookup_commit(the_repository, &oids->list[i]); if (commit) commit->object.flags |= UNINTERESTING; @@@ -635,7 -631,6 +662,7 @@@ * closure. */ for (i = 0; i < oids->nr; i++) { + display_progress(progress, ++j); commit = lookup_commit(the_repository, &oids->list[i]); if (commit && !parse_commit(commit)) @@@ -643,28 -638,19 +670,28 @@@ } for (i = 0; i < oids->nr; i++) { + display_progress(progress, ++j); commit = lookup_commit(the_repository, &oids->list[i]); if (commit) commit->object.flags &= ~UNINTERESTING; } + stop_progress(&progress); } -static void compute_generation_numbers(struct packed_commit_list* commits) +static void compute_generation_numbers(struct packed_commit_list* commits, + int report_progress) { 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) continue; @@@ -696,7 -682,6 +723,7 @@@ } } } + stop_progress(&progress); } static int add_ref_to_list(const char *refname, @@@ -709,20 -694,19 +736,20 @@@ return 0; } -void write_commit_graph_reachable(const char *obj_dir, int append) +void write_commit_graph_reachable(const char *obj_dir, int append, + int report_progress) { struct string_list list; string_list_init(&list, 1); for_each_ref(add_ref_to_list, &list); - write_commit_graph(obj_dir, NULL, &list, append); + write_commit_graph(obj_dir, NULL, &list, append, report_progress); } void write_commit_graph(const char *obj_dir, struct string_list *pack_indexes, struct string_list *commit_hex, - int append) + int append, int report_progress) { struct packed_oid_list oids; struct packed_commit_list commits; @@@ -735,12 -719,12 +762,15 @@@ int num_chunks; int num_extra_edges; struct commit_list *parent; + struct progress *progress = NULL; + if (!commit_graph_compatible(the_repository)) + return; + oids.nr = 0; oids.alloc = approximate_object_count() / 4; + oids.progress = NULL; + oids.progress_done = 0; if (append) { prepare_commit_graph_one(the_repository, obj_dir); @@@ -767,38 -751,27 +797,38 @@@ int dirlen; strbuf_addf(&packname, "%s/pack/", obj_dir); dirlen = packname.len; + if (report_progress) { + oids.progress = start_delayed_progress( + _("Finding commits for commit graph"), 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); + 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); + die(_("error opening index for %s"), packname.buf); + for_each_object_in_pack(p, add_packed_commits, &oids, 0); close_pack(p); } + stop_progress(&oids.progress); strbuf_release(&packname); } if (commit_hex) { + if (report_progress) + progress = start_delayed_progress( + _("Finding commits for commit graph"), + 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; @@@ -811,24 -784,18 +841,24 @@@ oids.nr++; } } + stop_progress(&progress); } - if (!pack_indexes && !commit_hex) + if (!pack_indexes && !commit_hex) { + if (report_progress) + oids.progress = start_delayed_progress( + _("Finding commits for commit graph"), 0); for_each_packed_object(add_packed_commits, &oids, 0); + stop_progress(&oids.progress); + } - close_reachable(&oids); + close_reachable(&oids, report_progress); QSORT(oids.list, oids.nr, commit_compare); count_distinct = 1; for (i = 1; i < oids.nr; i++) { - if (oidcmp(&oids.list[i-1], &oids.list[i])) + if (!oideq(&oids.list[i - 1], &oids.list[i])) count_distinct++; } @@@ -842,7 -809,7 +872,7 @@@ num_extra_edges = 0; for (i = 0; i < oids.nr; i++) { int num_parents = 0; - if (i > 0 && !oidcmp(&oids.list[i-1], &oids.list[i])) + if (i > 0 && oideq(&oids.list[i - 1], &oids.list[i])) continue; commits.list[commits.nr] = lookup_commit(the_repository, &oids.list[i]); @@@ -862,7 -829,7 +892,7 @@@ if (commits.nr >= GRAPH_PARENT_MISSING) die(_("too many commits to write graph")); - compute_generation_numbers(&commits); + compute_generation_numbers(&commits, report_progress); graph_name = get_commit_graph_filename(obj_dir); if (safe_create_leading_directories(graph_name)) @@@ -908,7 -875,7 +938,7 @@@ 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); @@@ -941,7 -908,6 +971,7 @@@ int verify_commit_graph(struct reposito int generation_zero = 0; struct hashfile *f; int devnull; + struct progress *progress = NULL; if (!g) { graph_report("no commit-graph file loaded"); @@@ -964,7 -930,7 +994,7 @@@ f = hashfd(devnull, NULL); hashwrite(f, g->data, g->data_len - g->hash_len); finalize_hashfile(f, checksum.hash, CSUM_CLOSE); - if (hashcmp(checksum.hash, g->data + g->data_len - g->hash_len)) { + if (!hasheq(checksum.hash, g->data + g->data_len - g->hash_len)) { graph_report(_("the commit-graph file has incorrect checksum and is likely corrupt")); verify_commit_graph_error = VERIFY_COMMIT_GRAPH_ERROR_HASH; } @@@ -1009,14 -975,11 +1039,14 @@@ if (verify_commit_graph_error & ~VERIFY_COMMIT_GRAPH_ERROR_HASH) return verify_commit_graph_error; + progress = start_progress(_("Verifying commits in commit graph"), + g->num_commits); for (i = 0; i < g->num_commits; i++) { struct commit *graph_commit, *odb_commit; struct commit_list *graph_parents, *odb_parents; uint32_t max_generation = 0; + display_progress(progress, i + 1); hashcpy(cur_oid.hash, g->chunk_oid_lookup + g->hash_len * i); graph_commit = lookup_commit(r, &cur_oid); @@@ -1027,7 -990,7 +1057,7 @@@ continue; } - if (oidcmp(&get_commit_tree_in_graph_one(g, graph_commit)->object.oid, + if (!oideq(&get_commit_tree_in_graph_one(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", oid_to_hex(&cur_oid), @@@ -1044,7 -1007,7 +1074,7 @@@ break; } - if (oidcmp(&graph_parents->item->object.oid, &odb_parents->item->object.oid)) + if (!oideq(&graph_parents->item->object.oid, &odb_parents->item->object.oid)) graph_report("commit-graph parent for %s is %s != %s", oid_to_hex(&cur_oid), oid_to_hex(&graph_parents->item->object.oid), @@@ -1093,7 -1056,6 +1123,7 @@@ graph_commit->date, odb_commit->date); } + stop_progress(&progress); return verify_commit_graph_error; } diff --combined commit-graph.h index 5678a8f4ca,13d736cdde..9db40b4d3a --- a/commit-graph.h +++ b/commit-graph.h @@@ -4,9 -4,6 +4,9 @@@ #include "git-compat-util.h" #include "repository.h" #include "string-list.h" +#include "cache.h" + +#define GIT_TEST_COMMIT_GRAPH "GIT_TEST_COMMIT_GRAPH" struct commit; @@@ -54,21 -51,15 +54,22 @@@ struct commit_graph struct commit_graph *load_commit_graph_one(const char *graph_file); -void write_commit_graph_reachable(const char *obj_dir, int append); +/* + * Return 1 if and only if the repository has a commit-graph + * file and generation numbers are computed in that file. + */ +int generation_numbers_enabled(struct repository *r); + +void write_commit_graph_reachable(const char *obj_dir, int append, + int report_progress); void write_commit_graph(const char *obj_dir, struct string_list *pack_indexes, struct string_list *commit_hex, - int append); + int append, int report_progress); 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 diff --combined commit.c index d0f199e122,ef9a2cbb23..dc8a39d52a --- a/commit.c +++ b/commit.c @@@ -46,7 -46,7 +46,7 @@@ struct commit *lookup_commit_or_die(con struct commit *c = lookup_commit_reference(the_repository, oid); if (!c) die(_("could not parse %s"), ref_name); - if (oidcmp(oid, &c->object.oid)) { + if (!oideq(oid, &c->object.oid)) { warning(_("%s %s is not a commit!"), ref_name, oid_to_hex(oid)); } @@@ -209,7 -209,7 +209,7 @@@ static int read_graft_file(struct repos return 0; } - static void prepare_commit_graft(struct repository *r) + void prepare_commit_graft(struct repository *r) { char *graft_file; @@@ -656,7 -656,7 +656,7 @@@ struct commit *pop_commit(struct commit define_commit_slab(indegree_slab, int); /* record author-date for each commit object */ -define_commit_slab(author_date_slab, unsigned long); +define_commit_slab(author_date_slab, timestamp_t); static void record_author_date(struct author_date_slab *author_date, struct commit *commit) @@@ -843,6 -843,364 +843,6 @@@ void sort_in_topological_order(struct c clear_author_date_slab(&author_date); } -/* merge-base stuff */ - -/* Remember to update object flag allocation in object.h */ -#define PARENT1 (1u<<16) -#define PARENT2 (1u<<17) -#define STALE (1u<<18) -#define RESULT (1u<<19) - -static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT); - -static int queue_has_nonstale(struct prio_queue *queue) -{ - int i; - for (i = 0; i < queue->nr; i++) { - struct commit *commit = queue->array[i].data; - if (!(commit->object.flags & STALE)) - return 1; - } - return 0; -} - -/* all input commits in one and twos[] must have been parsed! */ -static struct commit_list *paint_down_to_common(struct commit *one, int n, - struct commit **twos, - int min_generation) -{ - struct prio_queue queue = { compare_commits_by_gen_then_commit_date }; - struct commit_list *result = NULL; - int i; - uint32_t last_gen = GENERATION_NUMBER_INFINITY; - - one->object.flags |= PARENT1; - if (!n) { - commit_list_append(one, &result); - return result; - } - prio_queue_put(&queue, one); - - for (i = 0; i < n; i++) { - twos[i]->object.flags |= PARENT2; - prio_queue_put(&queue, twos[i]); - } - - while (queue_has_nonstale(&queue)) { - struct commit *commit = prio_queue_get(&queue); - struct commit_list *parents; - int flags; - - if (commit->generation > last_gen) - BUG("bad generation skip %8x > %8x at %s", - commit->generation, last_gen, - oid_to_hex(&commit->object.oid)); - last_gen = commit->generation; - - if (commit->generation < min_generation) - break; - - flags = commit->object.flags & (PARENT1 | PARENT2 | STALE); - if (flags == (PARENT1 | PARENT2)) { - if (!(commit->object.flags & RESULT)) { - commit->object.flags |= RESULT; - commit_list_insert_by_date(commit, &result); - } - /* Mark parents of a found merge stale */ - flags |= STALE; - } - parents = commit->parents; - while (parents) { - struct commit *p = parents->item; - parents = parents->next; - if ((p->object.flags & flags) == flags) - continue; - if (parse_commit(p)) - return NULL; - p->object.flags |= flags; - prio_queue_put(&queue, p); - } - } - - clear_prio_queue(&queue); - return result; -} - -static struct commit_list *merge_bases_many(struct commit *one, int n, struct commit **twos) -{ - struct commit_list *list = NULL; - struct commit_list *result = NULL; - int i; - - for (i = 0; i < n; i++) { - if (one == twos[i]) - /* - * We do not mark this even with RESULT so we do not - * have to clean it up. - */ - return commit_list_insert(one, &result); - } - - if (parse_commit(one)) - return NULL; - for (i = 0; i < n; i++) { - if (parse_commit(twos[i])) - return NULL; - } - - list = paint_down_to_common(one, n, twos, 0); - - while (list) { - struct commit *commit = pop_commit(&list); - if (!(commit->object.flags & STALE)) - commit_list_insert_by_date(commit, &result); - } - return result; -} - -struct commit_list *get_octopus_merge_bases(struct commit_list *in) -{ - struct commit_list *i, *j, *k, *ret = NULL; - - if (!in) - return ret; - - commit_list_insert(in->item, &ret); - - for (i = in->next; i; i = i->next) { - struct commit_list *new_commits = NULL, *end = NULL; - - for (j = ret; j; j = j->next) { - struct commit_list *bases; - bases = get_merge_bases(i->item, j->item); - if (!new_commits) - new_commits = bases; - else - end->next = bases; - for (k = bases; k; k = k->next) - end = k; - } - ret = new_commits; - } - return ret; -} - -static int remove_redundant(struct commit **array, int cnt) -{ - /* - * Some commit in the array may be an ancestor of - * another commit. Move such commit to the end of - * the array, and return the number of commits that - * are independent from each other. - */ - struct commit **work; - unsigned char *redundant; - int *filled_index; - int i, j, filled; - - work = xcalloc(cnt, sizeof(*work)); - redundant = xcalloc(cnt, 1); - ALLOC_ARRAY(filled_index, cnt - 1); - - for (i = 0; i < cnt; i++) - parse_commit(array[i]); - for (i = 0; i < cnt; i++) { - struct commit_list *common; - uint32_t min_generation = array[i]->generation; - - if (redundant[i]) - continue; - for (j = filled = 0; j < cnt; j++) { - if (i == j || redundant[j]) - continue; - filled_index[filled] = j; - work[filled++] = array[j]; - - if (array[j]->generation < min_generation) - min_generation = array[j]->generation; - } - common = paint_down_to_common(array[i], filled, work, - min_generation); - if (array[i]->object.flags & PARENT2) - redundant[i] = 1; - for (j = 0; j < filled; j++) - if (work[j]->object.flags & PARENT1) - redundant[filled_index[j]] = 1; - clear_commit_marks(array[i], all_flags); - clear_commit_marks_many(filled, work, all_flags); - free_commit_list(common); - } - - /* Now collect the result */ - COPY_ARRAY(work, array, cnt); - for (i = filled = 0; i < cnt; i++) - if (!redundant[i]) - array[filled++] = work[i]; - for (j = filled, i = 0; i < cnt; i++) - if (redundant[i]) - array[j++] = work[i]; - free(work); - free(redundant); - free(filled_index); - return filled; -} - -static struct commit_list *get_merge_bases_many_0(struct commit *one, - int n, - struct commit **twos, - int cleanup) -{ - struct commit_list *list; - struct commit **rslt; - struct commit_list *result; - int cnt, i; - - result = merge_bases_many(one, n, twos); - for (i = 0; i < n; i++) { - if (one == twos[i]) - return result; - } - if (!result || !result->next) { - if (cleanup) { - clear_commit_marks(one, all_flags); - clear_commit_marks_many(n, twos, all_flags); - } - return result; - } - - /* There are more than one */ - cnt = commit_list_count(result); - rslt = xcalloc(cnt, sizeof(*rslt)); - for (list = result, i = 0; list; list = list->next) - rslt[i++] = list->item; - free_commit_list(result); - - clear_commit_marks(one, all_flags); - clear_commit_marks_many(n, twos, all_flags); - - cnt = remove_redundant(rslt, cnt); - result = NULL; - for (i = 0; i < cnt; i++) - commit_list_insert_by_date(rslt[i], &result); - free(rslt); - return result; -} - -struct commit_list *get_merge_bases_many(struct commit *one, - int n, - struct commit **twos) -{ - return get_merge_bases_many_0(one, n, twos, 1); -} - -struct commit_list *get_merge_bases_many_dirty(struct commit *one, - int n, - struct commit **twos) -{ - return get_merge_bases_many_0(one, n, twos, 0); -} - -struct commit_list *get_merge_bases(struct commit *one, struct commit *two) -{ - return get_merge_bases_many_0(one, 1, &two, 1); -} - -/* - * Is "commit" a descendant of one of the elements on the "with_commit" list? - */ -int is_descendant_of(struct commit *commit, struct commit_list *with_commit) -{ - if (!with_commit) - return 1; - while (with_commit) { - struct commit *other; - - other = with_commit->item; - with_commit = with_commit->next; - if (in_merge_bases(other, commit)) - return 1; - } - return 0; -} - -/* - * Is "commit" an ancestor of one of the "references"? - */ -int in_merge_bases_many(struct commit *commit, int nr_reference, struct commit **reference) -{ - struct commit_list *bases; - int ret = 0, i; - uint32_t min_generation = GENERATION_NUMBER_INFINITY; - - if (parse_commit(commit)) - return ret; - for (i = 0; i < nr_reference; i++) { - if (parse_commit(reference[i])) - return ret; - if (reference[i]->generation < min_generation) - min_generation = reference[i]->generation; - } - - if (commit->generation > min_generation) - return ret; - - bases = paint_down_to_common(commit, nr_reference, reference, commit->generation); - if (commit->object.flags & PARENT2) - ret = 1; - clear_commit_marks(commit, all_flags); - clear_commit_marks_many(nr_reference, reference, all_flags); - free_commit_list(bases); - return ret; -} - -/* - * Is "commit" an ancestor of (i.e. reachable from) the "reference"? - */ -int in_merge_bases(struct commit *commit, struct commit *reference) -{ - return in_merge_bases_many(commit, 1, &reference); -} - -struct commit_list *reduce_heads(struct commit_list *heads) -{ - struct commit_list *p; - struct commit_list *result = NULL, **tail = &result; - struct commit **array; - int num_head, i; - - if (!heads) - return NULL; - - /* Uniquify */ - for (p = heads; p; p = p->next) - p->item->object.flags &= ~STALE; - for (p = heads, num_head = 0; p; p = p->next) { - if (p->item->object.flags & STALE) - continue; - p->item->object.flags |= STALE; - num_head++; - } - array = xcalloc(num_head, sizeof(*array)); - for (p = heads, i = 0; p; p = p->next) { - if (p->item->object.flags & STALE) { - array[i++] = p->item; - p->item->object.flags &= ~STALE; - } - } - num_head = remove_redundant(array, num_head); - for (i = 0; i < num_head; i++) - tail = &commit_list_insert(array[i], tail)->next; - free(array); - return result; -} - -void reduce_heads_replace(struct commit_list **heads) -{ - struct commit_list *result = reduce_heads(*heads); - free_commit_list(*heads); - *heads = result; -} - static const char gpg_sig_header[] = "gpgsig"; static const int gpg_sig_header_len = sizeof(gpg_sig_header) - 1; @@@ -1426,10 -1784,10 +1426,10 @@@ const char *find_commit_header(const ch * Returns the number of bytes from the tail to ignore, to be fed as * the second parameter to append_signoff(). */ -int ignore_non_trailer(const char *buf, size_t len) +size_t ignore_non_trailer(const char *buf, size_t len) { - int boc = 0; - int bol = 0; + size_t boc = 0; + size_t bol = 0; int in_old_conflicts_block = 0; size_t cutoff = wt_status_locate_end(buf, len); diff --combined commit.h index 2b1a734388,5459e279fe..1d260d62f5 --- a/commit.h +++ b/commit.h @@@ -202,8 -202,16 +202,9 @@@ typedef int (*each_commit_graft_fn)(con 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); -extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos); -extern struct commit_list *get_octopus_merge_bases(struct commit_list *in); - -/* To be used only when object flags after this call no longer matter */ -extern struct commit_list *get_merge_bases_many_dirty(struct commit *one, int n, struct commit **twos); - /* largest positive number a signed 32-bit integer can contain */ #define INFINITE_DEPTH 0x7fffffff @@@ -251,10 -259,32 +252,10 @@@ extern int delayed_reachability_test(st extern void prune_shallow(int show_only); extern struct trace_key trace_shallow; -int is_descendant_of(struct commit *, struct commit_list *); -int in_merge_bases(struct commit *, struct commit *); -int in_merge_bases_many(struct commit *, int, struct commit **); - extern int interactive_add(int argc, const char **argv, const char *prefix, int patch); extern int run_add_interactive(const char *revision, const char *patch_mode, const struct pathspec *pathspec); -/* - * Takes a list of commits and returns a new list where those - * have been removed that can be reached from other commits in - * the list. It is useful for, e.g., reducing the commits - * randomly thrown at the git-merge command and removing - * redundant commits that the user shouldn't have given to it. - * - * This function destroys the STALE bit of the commit objects' - * flags. - */ -extern struct commit_list *reduce_heads(struct commit_list *heads); - -/* - * Like `reduce_heads()`, except it replaces the list. Use this - * instead of `foo = reduce_heads(foo);` to avoid memory leaks. - */ -extern void reduce_heads_replace(struct commit_list **heads); - struct commit_extra_header { struct commit_extra_header *next; char *key; @@@ -293,7 -323,7 +294,7 @@@ extern const char *find_commit_header(c size_t *out_len); /* Find the end of the log message, the right place for a new trailer. */ -extern int ignore_non_trailer(const char *buf, size_t len); +extern size_t ignore_non_trailer(const char *buf, size_t len); typedef int (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra, void *cb_data); diff --combined refs.c index a7a75b4cc0,c5a5f727e8..bbcac921b6 --- a/refs.c +++ b/refs.c @@@ -189,7 -189,7 +189,7 @@@ int ref_resolves_to_object(const char * if (flags & REF_ISBROKEN) return 0; if (!has_sha1_file(oid->hash)) { - error("%s does not point to a valid object!", refname); + error(_("%s does not point to a valid object!"), refname); return 0; } return 1; @@@ -490,24 -490,16 +490,24 @@@ static const char *ref_rev_parse_rules[ NULL }; +#define NUM_REV_PARSE_RULES (ARRAY_SIZE(ref_rev_parse_rules) - 1) + +/* + * Is it possible that the caller meant full_name with abbrev_name? + * If so return a non-zero value to signal "yes"; the magnitude of + * the returned value gives the precedence used for disambiguation. + * + * If abbrev_name cannot mean full_name, return 0. + */ int refname_match(const char *abbrev_name, const char *full_name) { const char **p; const int abbrev_name_len = strlen(abbrev_name); + const int num_rules = NUM_REV_PARSE_RULES; - for (p = ref_rev_parse_rules; *p; p++) { - if (!strcmp(full_name, mkpath(*p, abbrev_name_len, abbrev_name))) { - return 1; - } - } + for (p = ref_rev_parse_rules; *p; p++) + if (!strcmp(full_name, mkpath(*p, abbrev_name_len, abbrev_name))) + return &ref_rev_parse_rules[num_rules] - p; return 0; } @@@ -576,9 -568,9 +576,9 @@@ int expand_ref(const char *str, int len if (!warn_ambiguous_refs) break; } else if ((flag & REF_ISSYMREF) && strcmp(fullref.buf, "HEAD")) { - warning("ignoring dangling symref %s.", fullref.buf); + warning(_("ignoring dangling symref %s"), fullref.buf); } else if ((flag & REF_ISBROKEN) && strchr(fullref.buf, '/')) { - warning("ignoring broken ref %s.", fullref.buf); + warning(_("ignoring broken ref %s"), fullref.buf); } } strbuf_release(&fullref); @@@ -682,7 -674,7 +682,7 @@@ static int write_pseudoref(const char * fd = hold_lock_file_for_update_timeout(&lock, filename, 0, get_files_ref_lock_timeout_ms()); if (fd < 0) { - strbuf_addf(err, "could not open '%s' for writing: %s", + strbuf_addf(err, _("could not open '%s' for writing: %s"), filename, strerror(errno)); goto done; } @@@ -692,18 -684,18 +692,18 @@@ if (read_ref(pseudoref, &actual_old_oid)) { if (!is_null_oid(old_oid)) { - strbuf_addf(err, "could not read ref '%s'", + strbuf_addf(err, _("could not read ref '%s'"), pseudoref); rollback_lock_file(&lock); goto done; } } else if (is_null_oid(old_oid)) { - strbuf_addf(err, "ref '%s' already exists", + strbuf_addf(err, _("ref '%s' already exists"), pseudoref); rollback_lock_file(&lock); goto done; - } else if (oidcmp(&actual_old_oid, old_oid)) { - strbuf_addf(err, "unexpected object ID when writing '%s'", + } else if (!oideq(&actual_old_oid, old_oid)) { + strbuf_addf(err, _("unexpected object ID when writing '%s'"), pseudoref); rollback_lock_file(&lock); goto done; @@@ -711,7 -703,7 +711,7 @@@ } if (write_in_full(fd, buf.buf, buf.len) < 0) { - strbuf_addf(err, "could not write to '%s'", filename); + strbuf_addf(err, _("could not write to '%s'"), filename); rollback_lock_file(&lock); goto done; } @@@ -743,9 -735,9 +743,9 @@@ static int delete_pseudoref(const char return -1; } if (read_ref(pseudoref, &actual_old_oid)) - die("could not read ref '%s'", pseudoref); - if (oidcmp(&actual_old_oid, old_oid)) { - error("unexpected object ID when deleting '%s'", + die(_("could not read ref '%s'"), pseudoref); + if (!oideq(&actual_old_oid, old_oid)) { + error(_("unexpected object ID when deleting '%s'"), pseudoref); rollback_lock_file(&lock); return -1; @@@ -875,14 -867,14 +875,14 @@@ static int read_ref_at_ent(struct objec */ if (!is_null_oid(&cb->ooid)) { oidcpy(cb->oid, noid); - if (oidcmp(&cb->ooid, noid)) - warning("Log for ref %s has gap after %s.", + if (!oideq(&cb->ooid, noid)) + warning(_("log for ref %s has gap after %s"), cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822))); } else if (cb->date == cb->at_time) oidcpy(cb->oid, noid); - else if (oidcmp(noid, cb->oid)) - warning("Log for ref %s unexpectedly ended on %s.", + else if (!oideq(noid, cb->oid)) + warning(_("log for ref %s unexpectedly ended on %s"), cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822))); oidcpy(&cb->ooid, ooid); @@@ -940,7 -932,7 +940,7 @@@ int read_ref_at(const char *refname, un if (flags & GET_OID_QUIETLY) exit(128); else - die("Log for %s is empty.", refname); + die(_("log for %s is empty"), refname); } if (cb.found_it) return 0; @@@ -1032,7 -1024,7 +1032,7 @@@ int ref_transaction_update(struct ref_t if ((new_oid && !is_null_oid(new_oid)) ? check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) : !refname_is_safe(refname)) { - strbuf_addf(err, "refusing to update ref with bad name '%s'", + strbuf_addf(err, _("refusing to update ref with bad name '%s'"), refname); return -1; } @@@ -1108,7 -1100,7 +1108,7 @@@ int refs_update_ref(struct ref_store *r } } if (ret) { - const char *str = "update_ref failed for ref '%s': %s"; + const char *str = _("update_ref failed for ref '%s': %s"); switch (onerr) { case UPDATE_REFS_MSG_ON_ERR: @@@ -1394,17 -1386,50 +1394,50 @@@ struct ref_iterator *refs_ref_iterator_ * 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) @@@ -1449,12 -1474,11 +1482,11 @@@ int refs_for_each_fullref_in(struct ref 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) @@@ -1850,7 -1874,7 +1882,7 @@@ int ref_update_reject_duplicates(struc if (!cmp) { strbuf_addf(err, - "multiple updates for ref '%s' not allowed.", + _("multiple updates for ref '%s' not allowed"), refnames->items[i].string); return 1; } else if (cmp > 0) { @@@ -1978,13 -2002,13 +2010,13 @@@ int refs_verify_refname_available(struc continue; if (!refs_read_raw_ref(refs, dirname.buf, &oid, &referent, &type)) { - strbuf_addf(err, "'%s' exists; cannot create '%s'", + strbuf_addf(err, _("'%s' exists; cannot create '%s'"), dirname.buf, refname); goto cleanup; } if (extras && string_list_has_string(extras, dirname.buf)) { - strbuf_addf(err, "cannot process '%s' and '%s' at the same time", + strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"), refname, dirname.buf); goto cleanup; } @@@ -2008,7 -2032,7 +2040,7 @@@ string_list_has_string(skip, iter->refname)) continue; - strbuf_addf(err, "'%s' exists; cannot create '%s'", + strbuf_addf(err, _("'%s' exists; cannot create '%s'"), iter->refname, refname); ref_iterator_abort(iter); goto cleanup; @@@ -2019,7 -2043,7 +2051,7 @@@ extra_refname = find_descendant_ref(dirname.buf, extras, skip); if (extra_refname) - strbuf_addf(err, "cannot process '%s' and '%s' at the same time", + strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"), refname, extra_refname); else ret = 0; @@@ -2033,10 -2057,12 +2065,12 @@@ cleanup 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) diff --combined refs.h index bd52c1bbae,a0a18223a1..6cc0397679 --- a/refs.h +++ b/refs.h @@@ -3,10 -3,8 +3,10 @@@ struct object_id; struct ref_store; +struct repository; struct strbuf; struct string_list; +struct string_list_item; struct worktree; /* @@@ -276,6 -274,16 +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 @@@ -309,7 -317,7 +319,7 @@@ int for_each_fullref_in(const char *pre 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); diff --combined replace-object.c index 4ec77ce418,9821f1477e..e295e87943 --- a/replace-object.c +++ b/replace-object.c @@@ -6,7 -6,8 +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) { @@@ -17,7 -18,7 +18,7 @@@ if (get_oid_hex(hash, &repl_obj->original.oid)) { free(repl_obj); - warning("bad replace ref name: %s", refname); + warning(_("bad replace ref name: %s"), refname); return 0; } @@@ -25,13 -26,13 +26,13 @@@ 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); + 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; @@@ -69,5 -70,5 +70,5 @@@ const struct object_id *do_lookup_repla return cur; cur = &repl_obj->replacement; } - die("replace depth too high for object %s", oid_to_hex(oid)); + die(_("replace depth too high for object %s"), oid_to_hex(oid)); } diff --combined t/t5318-commit-graph.sh index 75fe09521f,6aee861f78..db8dcafca3 --- a/t/t5318-commit-graph.sh +++ b/t/t5318-commit-graph.sh @@@ -8,8 -8,7 +8,8 @@@ test_expect_success 'setup full repo' cd "$TRASH_DIRECTORY/full" && git init && git config core.commitGraph true && - objdir=".git/objects" + objdir=".git/objects" && + test_oid_init ' test_expect_success 'verify graph with no graph file' ' @@@ -135,7 -134,7 +135,7 @@@ test_expect_success 'Add one more commi git branch commits/8 && ls $objdir/pack | grep idx >existing-idx && git repack && - ls $objdir/pack| grep idx | grep -v --file=existing-idx >new-idx + ls $objdir/pack| grep idx | grep -v -f existing-idx >new-idx ' # Current graph structure: @@@ -255,11 -254,71 +255,71 @@@ test_expect_success 'check that gc comp git config gc.writeCommitGraph true && git gc && cp $objdir/info/commit-graph commit-graph-after-gc && - ! test_cmp commit-graph-before-gc commit-graph-after-gc && + ! test_cmp_bin commit-graph-before-gc commit-graph-after-gc && git commit-graph write --reachable && - test_cmp commit-graph-after-gc $objdir/info/commit-graph + test_cmp_bin commit-graph-after-gc $objdir/info/commit-graph ' + test_expect_success 'replace-objects invalidates commit-graph' ' + cd "$TRASH_DIRECTORY" && + test_when_finished rm -rf replace && + git clone full replace && + ( + cd replace && + git commit-graph write --reachable && + test_path_is_file .git/objects/info/commit-graph && + git replace HEAD~1 HEAD~2 && + git -c core.commitGraph=false log >expect && + git -c core.commitGraph=true log >actual && + test_cmp expect actual && + git commit-graph write --reachable && + git -c core.commitGraph=false --no-replace-objects log >expect && + git -c core.commitGraph=true --no-replace-objects log >actual && + test_cmp expect actual && + rm -rf .git/objects/info/commit-graph && + git commit-graph write --reachable && + test_path_is_file .git/objects/info/commit-graph + ) + ' + + test_expect_success 'commit grafts invalidate commit-graph' ' + cd "$TRASH_DIRECTORY" && + test_when_finished rm -rf graft && + git clone full graft && + ( + cd graft && + git commit-graph write --reachable && + test_path_is_file .git/objects/info/commit-graph && + H1=$(git rev-parse --verify HEAD~1) && + H3=$(git rev-parse --verify HEAD~3) && + echo "$H1 $H3" >.git/info/grafts && + git -c core.commitGraph=false log >expect && + git -c core.commitGraph=true log >actual && + test_cmp expect actual && + git commit-graph write --reachable && + git -c core.commitGraph=false --no-replace-objects log >expect && + git -c core.commitGraph=true --no-replace-objects log >actual && + test_cmp expect actual && + rm -rf .git/objects/info/commit-graph && + git commit-graph write --reachable && + test_path_is_missing .git/objects/info/commit-graph + ) + ' + + test_expect_success 'replace-objects invalidates commit-graph' ' + cd "$TRASH_DIRECTORY" && + test_when_finished rm -rf shallow && + git clone --depth 2 "file://$TRASH_DIRECTORY/full" shallow && + ( + cd shallow && + git commit-graph write --reachable && + test_path_is_missing .git/objects/info/commit-graph && + git fetch origin --unshallow && + git commit-graph write --reachable && + test_path_is_file .git/objects/info/commit-graph + ) + ' + # the verify tests below expect the commit-graph to contain # exactly the commits reachable from the commits/8 branch. # If the file changes the set of commits in the list, then the @@@ -274,7 -333,7 +334,7 @@@ test_expect_success 'git commit-graph v NUM_COMMITS=9 NUM_OCTOPUS_EDGES=2 -HASH_LEN=20 +HASH_LEN="$(test_oid rawsz)" GRAPH_BYTE_VERSION=4 GRAPH_BYTE_HASH=5 GRAPH_BYTE_CHUNK_COUNT=6 @@@ -445,27 -504,25 +505,27 @@@ test_expect_success 'setup non-the_repo 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 && + { + git -C repo log --pretty=format:"%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 && + 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 && + 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 && + git -C repo rev-parse one^{tree} >expect && test_cmp expect actual ' diff --combined upload-pack.c index 62a1000f44,2ae9d9bb47..540778d1dd --- a/upload-pack.c +++ b/upload-pack.c @@@ -24,13 -24,14 +24,14 @@@ #include "quote.h" #include "upload-pack.h" #include "serve.h" + #include "commit-graph.h" +#include "commit-reach.h" /* Remember to update object flag allocation in object.h */ #define THEY_HAVE (1u << 11) #define OUR_REF (1u << 12) #define WANTED (1u << 13) #define COMMON_KNOWN (1u << 14) -#define REACHABLE (1u << 15) #define SHALLOW (1u << 16) #define NOT_SHALLOW (1u << 17) @@@ -337,16 -338,64 +338,16 @@@ static int got_oid(const char *hex, str return 0; } -static int reachable(struct commit *want) -{ - struct prio_queue work = { compare_commits_by_commit_date }; - - prio_queue_put(&work, want); - while (work.nr) { - struct commit_list *list; - struct commit *commit = prio_queue_get(&work); - - if (commit->object.flags & THEY_HAVE) { - want->object.flags |= COMMON_KNOWN; - break; - } - if (!commit->object.parsed) - parse_object(the_repository, &commit->object.oid); - if (commit->object.flags & REACHABLE) - continue; - commit->object.flags |= REACHABLE; - if (commit->date < oldest_have) - continue; - for (list = commit->parents; list; list = list->next) { - struct commit *parent = list->item; - if (!(parent->object.flags & REACHABLE)) - prio_queue_put(&work, parent); - } - } - want->object.flags |= REACHABLE; - clear_commit_marks(want, REACHABLE); - clear_prio_queue(&work); - return (want->object.flags & COMMON_KNOWN); -} - static int ok_to_give_up(void) { - int i; + uint32_t min_generation = GENERATION_NUMBER_ZERO; if (!have_obj.nr) return 0; - for (i = 0; i < want_obj.nr; i++) { - struct object *want = want_obj.objects[i].item; - - if (want->flags & COMMON_KNOWN) - continue; - want = deref_tag(the_repository, want, "a want line", 0); - if (!want || want->type != OBJ_COMMIT) { - /* no way to tell if this is reachable by - * looking at the ancestry chain alone, so - * leave a note to ourselves not to worry about - * this object anymore. - */ - want_obj.objects[i].item->flags |= COMMON_KNOWN; - continue; - } - if (!reachable((struct commit *)want)) - return 0; - } - return 1; + return can_all_from_reach_with_flag(&want_obj, THEY_HAVE, + COMMON_KNOWN, oldest_have, + min_generation); } static int get_common_commits(void) @@@ -692,6 -741,7 +693,7 @@@ static void deepen_by_rev_list(int ac, { struct commit_list *result; + close_commit_graph(the_repository); result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW); send_shallow(result); free_commit_list(result);