diff options
Diffstat (limited to 'builtin')
51 files changed, 1444 insertions, 518 deletions
diff --git a/builtin/add.c b/builtin/add.c index 0235854f80..32709794b3 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -14,13 +14,14 @@ #include "gettext.h" #include "pathspec.h" #include "run-command.h" +#include "object-file.h" +#include "odb.h" #include "parse-options.h" #include "path.h" #include "preload-index.h" #include "diff.h" #include "read-cache.h" #include "revision.h" -#include "bulk-checkin.h" #include "strvec.h" #include "submodule.h" #include "add-interactive.h" @@ -200,7 +201,7 @@ static int edit_patch(struct repository *repo, argc = setup_revisions(argc, argv, &rev, NULL); rev.diffopt.output_format = DIFF_FORMAT_PATCH; - rev.diffopt.use_color = 0; + rev.diffopt.use_color = GIT_COLOR_NEVER; rev.diffopt.flags.ignore_dirty_submodules = 1; out = xopen(file, O_CREAT | O_WRONLY | O_TRUNC, 0666); rev.diffopt.file = xfdopen(out, "w"); @@ -389,6 +390,7 @@ int cmd_add(int argc, char *seen = NULL; char *ps_matched = NULL; struct lock_file lock_file = LOCK_INIT; + struct odb_transaction *transaction; repo_config(repo, add_config, NULL); @@ -574,7 +576,7 @@ int cmd_add(int argc, string_list_clear(&only_match_skip_worktree, 0); } - begin_odb_transaction(); + transaction = odb_transaction_begin(repo->objects); ps_matched = xcalloc(pathspec.nr, 1); if (add_renormalize) @@ -593,7 +595,7 @@ int cmd_add(int argc, if (chmod_arg && pathspec.nr) exit_status |= chmod_pathspec(repo, &pathspec, chmod_arg[0], show_only); - end_odb_transaction(); + odb_transaction_commit(transaction); finish: if (write_locked_index(repo->index, &lock_file, diff --git a/builtin/am.c b/builtin/am.c index 6073d64ae9..277c2e7937 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1408,7 +1408,7 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm rev_info.no_commit_id = 1; rev_info.diffopt.flags.binary = 1; rev_info.diffopt.flags.full_index = 1; - rev_info.diffopt.use_color = 0; + rev_info.diffopt.use_color = GIT_COLOR_NEVER; rev_info.diffopt.file = fp; rev_info.diffopt.close_file = 1; add_pending_object(&rev_info, &commit->object, ""); @@ -1441,7 +1441,7 @@ static void write_index_patch(const struct am_state *state) rev_info.disable_stdin = 1; rev_info.no_commit_id = 1; rev_info.diffopt.output_format = DIFF_FORMAT_PATCH; - rev_info.diffopt.use_color = 0; + rev_info.diffopt.use_color = GIT_COLOR_NEVER; rev_info.diffopt.file = fp; rev_info.diffopt.close_file = 1; add_pending_object(&rev_info, &tree->object, ""); diff --git a/builtin/backfill.c b/builtin/backfill.c index 80056abe47..e80fc1b694 100644 --- a/builtin/backfill.c +++ b/builtin/backfill.c @@ -53,7 +53,7 @@ static void download_batch(struct backfill_context *ctx) * We likely have a new packfile. Add it to the packed list to * avoid possible duplicate downloads of the same objects. */ - reprepare_packed_git(ctx->repo); + odb_reprepare(ctx->repo->objects); } static int fill_missing_blobs(const char *path UNUSED, diff --git a/builtin/blame.c b/builtin/blame.c index 5b10e84b66..2703820258 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -413,7 +413,7 @@ static void parse_color_fields(const char *s) colorfield_nr = 0; /* Ideally this would be stripped and split at the same time? */ - string_list_split(&l, s, ',', -1); + string_list_split(&l, s, ",", -1); ALLOC_GROW(colorfield, colorfield_nr + 1, colorfield_alloc); for_each_string_list_item(item, &l) { diff --git a/builtin/branch.c b/builtin/branch.c index fa5ced452e..9fcf04bebb 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -46,7 +46,7 @@ static struct object_id head_oid; static int recurse_submodules = 0; static int submodule_propagate_branches = 0; -static int branch_use_color = -1; +static enum git_colorbool branch_use_color = GIT_COLOR_UNKNOWN; static char branch_colors[][COLOR_MAXLEN] = { GIT_COLOR_RESET, GIT_COLOR_NORMAL, /* PLAIN */ diff --git a/builtin/cat-file.c b/builtin/cat-file.c index fce0b06451..ee6715fa52 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -852,9 +852,10 @@ static void batch_each_object(struct batch_options *opt, if (bitmap && !for_each_bitmapped_object(bitmap, &opt->objects_filter, batch_one_object_bitmapped, &payload)) { + struct packfile_store *packs = the_repository->objects->packfiles; struct packed_git *pack; - for (pack = get_all_packs(the_repository); pack; pack = pack->next) { + for (pack = packfile_store_get_all_packs(packs); pack; pack = pack->next) { if (bitmap_index_contains_pack(bitmap, pack) || open_pack_index(pack)) continue; diff --git a/builtin/clean.c b/builtin/clean.c index a1977b92dc..1d5e7e5366 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -64,7 +64,7 @@ static const char *color_interactive_slots[] = { [CLEAN_COLOR_RESET] = "reset", }; -static int clean_use_color = -1; +static enum git_colorbool clean_use_color = GIT_COLOR_UNKNOWN; static char clean_colors[][COLOR_MAXLEN] = { [CLEAN_COLOR_ERROR] = GIT_COLOR_BOLD_RED, [CLEAN_COLOR_HEADER] = GIT_COLOR_BOLD, @@ -478,43 +478,39 @@ static int find_unique(const char *choice, struct menu_stuff *menu_stuff) */ static int parse_choice(struct menu_stuff *menu_stuff, int is_single, - struct strbuf input, + char *input, int **chosen) { - struct strbuf **choice_list, **ptr; + struct string_list choice = STRING_LIST_INIT_NODUP; + struct string_list_item *item; int nr = 0; int i; - if (is_single) { - choice_list = strbuf_split_max(&input, '\n', 0); - } else { - char *p = input.buf; - do { - if (*p == ',') - *p = ' '; - } while (*p++); - choice_list = strbuf_split_max(&input, ' ', 0); - } + string_list_split_in_place_f(&choice, input, + is_single ? "\n" : ", ", -1, + STRING_LIST_SPLIT_TRIM); - for (ptr = choice_list; *ptr; ptr++) { - char *p; - int choose = 1; + for_each_string_list_item(item, &choice) { + const char *string; + int choose; int bottom = 0, top = 0; int is_range, is_number; - strbuf_trim(*ptr); - if (!(*ptr)->len) + string = item->string; + if (!*string) continue; /* Input that begins with '-'; unchoose */ - if (*(*ptr)->buf == '-') { + if (string[0] == '-') { choose = 0; - strbuf_remove((*ptr), 0, 1); + string++; + } else { + choose = 1; } is_range = 0; is_number = 1; - for (p = (*ptr)->buf; *p; p++) { + for (const char *p = string; *p; p++) { if ('-' == *p) { if (!is_range) { is_range = 1; @@ -532,27 +528,27 @@ static int parse_choice(struct menu_stuff *menu_stuff, } if (is_number) { - bottom = atoi((*ptr)->buf); + bottom = atoi(string); top = bottom; } else if (is_range) { - bottom = atoi((*ptr)->buf); + bottom = atoi(string); /* a range can be specified like 5-7 or 5- */ - if (!*(strchr((*ptr)->buf, '-') + 1)) + if (!*(strchr(string, '-') + 1)) top = menu_stuff->nr; else - top = atoi(strchr((*ptr)->buf, '-') + 1); - } else if (!strcmp((*ptr)->buf, "*")) { + top = atoi(strchr(string, '-') + 1); + } else if (!strcmp(string, "*")) { bottom = 1; top = menu_stuff->nr; } else { - bottom = find_unique((*ptr)->buf, menu_stuff); + bottom = find_unique(string, menu_stuff); top = bottom; } if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > top || (is_single && bottom != top)) { clean_print_color(CLEAN_COLOR_ERROR); - printf(_("Huh (%s)?\n"), (*ptr)->buf); + printf(_("Huh (%s)?\n"), string); clean_print_color(CLEAN_COLOR_RESET); continue; } @@ -561,7 +557,7 @@ static int parse_choice(struct menu_stuff *menu_stuff, (*chosen)[i-1] = choose; } - strbuf_list_free(choice_list); + string_list_clear(&choice, 0); for (i = 0; i < menu_stuff->nr; i++) nr += (*chosen)[i]; @@ -631,7 +627,7 @@ static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff) nr = parse_choice(stuff, opts->flags & MENU_OPTS_SINGLETON, - choice, + choice.buf, &chosen); if (opts->flags & MENU_OPTS_SINGLETON) { @@ -679,12 +675,13 @@ static int filter_by_patterns_cmd(void) { struct dir_struct dir = DIR_INIT; struct strbuf confirm = STRBUF_INIT; - struct strbuf **ignore_list; - struct string_list_item *item; struct pattern_list *pl; int changed = -1, i; for (;;) { + struct string_list ignore_list = STRING_LIST_INIT_NODUP; + struct string_list_item *item; + if (!del_list.nr) break; @@ -702,14 +699,15 @@ static int filter_by_patterns_cmd(void) break; pl = add_pattern_list(&dir, EXC_CMDL, "manual exclude"); - ignore_list = strbuf_split_max(&confirm, ' ', 0); - for (i = 0; ignore_list[i]; i++) { - strbuf_trim(ignore_list[i]); - if (!ignore_list[i]->len) - continue; + string_list_split_in_place_f(&ignore_list, confirm.buf, " ", -1, + STRING_LIST_SPLIT_TRIM); - add_pattern(ignore_list[i]->buf, "", 0, pl, -(i+1)); + for (i = 0; i < ignore_list.nr; i++) { + item = &ignore_list.items[i]; + if (!*item->string) + continue; + add_pattern(item->string, "", 0, pl, -(i+1)); } changed = 0; @@ -730,7 +728,7 @@ static int filter_by_patterns_cmd(void) clean_print_color(CLEAN_COLOR_RESET); } - strbuf_list_free(ignore_list); + string_list_clear(&ignore_list, 0); dir_clear(&dir); } diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c index 4992ac146e..fe3ebaadad 100644 --- a/builtin/commit-graph.c +++ b/builtin/commit-graph.c @@ -102,14 +102,15 @@ static int graph_verify(int argc, const char **argv, const char *prefix, if (opts.progress) flags |= COMMIT_GRAPH_WRITE_PROGRESS; - source = odb_find_source(the_repository->objects, opts.obj_dir); + source = odb_find_source_or_die(the_repository->objects, opts.obj_dir); graph_name = get_commit_graph_filename(source); chain_name = get_commit_graph_chain_filename(source); if (open_commit_graph(graph_name, &fd, &st)) opened = OPENED_GRAPH; else if (errno != ENOENT) die_errno(_("Could not open commit-graph '%s'"), graph_name); - else if (open_commit_graph_chain(chain_name, &fd, &st)) + else if (open_commit_graph_chain(chain_name, &fd, &st, + the_repository->hash_algo)) opened = OPENED_CHAIN; else if (errno != ENOENT) die_errno(_("could not open commit-graph chain '%s'"), chain_name); @@ -121,15 +122,15 @@ static int graph_verify(int argc, const char **argv, const char *prefix, if (opened == OPENED_NONE) return 0; else if (opened == OPENED_GRAPH) - graph = load_commit_graph_one_fd_st(the_repository, fd, &st, source); + graph = load_commit_graph_one_fd_st(source, fd, &st); else - graph = load_commit_graph_chain_fd_st(the_repository, fd, &st, + graph = load_commit_graph_chain_fd_st(the_repository->objects, fd, &st, &incomplete_chain); if (!graph) return 1; - ret = verify_commit_graph(the_repository, graph, flags); + ret = verify_commit_graph(graph, flags); free_commit_graph(graph); if (incomplete_chain) { @@ -290,7 +291,7 @@ static int graph_write(int argc, const char **argv, const char *prefix, git_env_bool(GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS, 0)) flags |= COMMIT_GRAPH_WRITE_BLOOM_FILTERS; - source = odb_find_source(the_repository->objects, opts.obj_dir); + source = odb_find_source_or_die(the_repository->objects, opts.obj_dir); if (opts.reachable) { if (write_commit_graph_reachable(source, flags, &write_opts)) diff --git a/builtin/commit.c b/builtin/commit.c index b5b9608813..0243f17d53 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -695,6 +695,7 @@ static int author_date_is_interesting(void) return author_message || force_date; } +#ifndef WITH_BREAKING_CHANGES static void adjust_comment_line_char(const struct strbuf *sb) { char candidates[] = "#;@!$%^&|:"; @@ -732,6 +733,7 @@ static void adjust_comment_line_char(const struct strbuf *sb) free(comment_line_str_to_free); comment_line_str = comment_line_str_to_free = xstrfmt("%c", *p); } +#endif /* !WITH_BREAKING_CHANGES */ static void prepare_amend_commit(struct commit *commit, struct strbuf *sb, struct pretty_print_context *ctx) @@ -928,15 +930,17 @@ static int prepare_to_commit(const char *index_file, const char *prefix, if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len) die_errno(_("could not write commit template")); +#ifndef WITH_BREAKING_CHANGES if (auto_comment_line_char) adjust_comment_line_char(&sb); +#endif /* !WITH_BREAKING_CHANGES */ strbuf_release(&sb); /* This checks if committer ident is explicitly given */ strbuf_addstr(&committer_ident, git_committer_info(IDENT_STRICT)); if (use_editor && include_status) { int ident_shown = 0; - int saved_color_setting; + enum git_colorbool saved_color_setting; struct ident_split ci, ai; const char *hint_cleanup_all = allow_empty_message ? _("Please enter the commit message for your changes." @@ -1016,7 +1020,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, status_printf_ln(s, GIT_COLOR_NORMAL, "%s", ""); /* Add new line for clarity */ saved_color_setting = s->use_color; - s->use_color = 0; + s->use_color = GIT_COLOR_NEVER; committable = run_status(s->fp, index_file, prefix, 1, s); s->use_color = saved_color_setting; string_list_clear_func(&s->change, change_data_free); @@ -1793,6 +1797,9 @@ int cmd_commit(int argc, show_usage_with_options_if_asked(argc, argv, builtin_commit_usage, builtin_commit_options); +#ifndef WITH_BREAKING_CHANGES + warn_on_auto_comment_char = true; +#endif /* !WITH_BREAKING_CHANGES */ prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; @@ -1947,7 +1954,7 @@ int cmd_commit(int argc, "new index file. Check that disk is not full and quota is\n" "not exceeded, and then \"git restore --staged :/\" to recover.")); - git_test_write_commit_graph_or_die(); + git_test_write_commit_graph_or_die(the_repository->objects->sources); repo_rerere(the_repository, 0); run_auto_maintenance(quiet); diff --git a/builtin/config.c b/builtin/config.c index 59fb113b07..75852bd79d 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -547,30 +547,37 @@ static int git_get_color_config(const char *var, const char *value, return 0; } -static void get_color(const struct config_location_options *opts, +static int get_color(const struct config_location_options *opts, const char *var, const char *def_color) { struct get_color_config_data data = { .get_color_slot = var, .parsed_color[0] = '\0', }; + int ret; config_with_options(git_get_color_config, &data, &opts->source, the_repository, &opts->options); if (!data.get_color_found && def_color) { - if (color_parse(def_color, data.parsed_color) < 0) - die(_("unable to parse default color value")); + if (color_parse(def_color, data.parsed_color) < 0) { + ret = error(_("unable to parse default color value")); + goto out; + } } + ret = 0; + +out: fputs(data.parsed_color, stdout); + return ret; } struct get_colorbool_config_data { - int get_colorbool_found; - int get_diff_color_found; - int get_color_ui_found; + enum git_colorbool get_colorbool_found; + enum git_colorbool get_diff_color_found; + enum git_colorbool get_color_ui_found; const char *get_colorbool_slot; }; @@ -594,33 +601,34 @@ static int get_colorbool(const struct config_location_options *opts, { struct get_colorbool_config_data data = { .get_colorbool_slot = var, - .get_colorbool_found = -1, - .get_diff_color_found = -1, - .get_color_ui_found = -1, + .get_colorbool_found = GIT_COLOR_UNKNOWN, + .get_diff_color_found = GIT_COLOR_UNKNOWN, + .get_color_ui_found = GIT_COLOR_UNKNOWN, }; + bool result; config_with_options(git_get_colorbool_config, &data, &opts->source, the_repository, &opts->options); - if (data.get_colorbool_found < 0) { + if (data.get_colorbool_found == GIT_COLOR_UNKNOWN) { if (!strcmp(data.get_colorbool_slot, "color.diff")) data.get_colorbool_found = data.get_diff_color_found; - if (data.get_colorbool_found < 0) + if (data.get_colorbool_found == GIT_COLOR_UNKNOWN) data.get_colorbool_found = data.get_color_ui_found; } - if (data.get_colorbool_found < 0) + if (data.get_colorbool_found == GIT_COLOR_UNKNOWN) /* default value if none found in config */ data.get_colorbool_found = GIT_COLOR_AUTO; - data.get_colorbool_found = want_color(data.get_colorbool_found); + result = want_color(data.get_colorbool_found); if (print) { - printf("%s\n", data.get_colorbool_found ? "true" : "false"); + printf("%s\n", result ? "true" : "false"); return 0; } else - return data.get_colorbool_found ? 0 : 1; + return result ? 0 : 1; } static void check_write(const struct git_config_source *source) @@ -912,10 +920,13 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix, location_options_init(&location_opts, prefix); display_options_init(&display_opts); - setup_auto_pager("config", 1); + if (display_opts.type != TYPE_COLOR) + setup_auto_pager("config", 1); if (url) ret = get_urlmatch(&location_opts, &display_opts, argv[0], url); + else if (display_opts.type == TYPE_COLOR && !strlen(argv[0]) && display_opts.default_value) + ret = get_color(&location_opts, "", display_opts.default_value); else ret = get_value(&location_opts, &display_opts, argv[0], value_pattern, get_value_flags, flags); @@ -1390,7 +1401,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix) } else if (actions == ACTION_GET_COLOR) { check_argc(argc, 1, 2); - get_color(&location_opts, argv[0], argv[1]); + ret = get_color(&location_opts, argv[0], argv[1]); } else if (actions == ACTION_GET_COLORBOOL) { check_argc(argc, 1, 2); diff --git a/builtin/count-objects.c b/builtin/count-objects.c index a61d3b46aa..f2f407c2a7 100644 --- a/builtin/count-objects.c +++ b/builtin/count-objects.c @@ -122,6 +122,7 @@ int cmd_count_objects(int argc, count_loose, count_cruft, NULL, NULL); if (verbose) { + struct packfile_store *packs = the_repository->objects->packfiles; struct packed_git *p; unsigned long num_pack = 0; off_t size_pack = 0; @@ -129,7 +130,7 @@ int cmd_count_objects(int argc, struct strbuf pack_buf = STRBUF_INIT; struct strbuf garbage_buf = STRBUF_INIT; - for (p = get_all_packs(the_repository); p; p = p->next) { + for (p = packfile_store_get_all_packs(packs); p; p = p->next) { if (!p->pack_local) continue; if (open_pack_index(p)) diff --git a/builtin/describe.c b/builtin/describe.c index d7dd8139de..ffaf8d9f0a 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -23,6 +23,8 @@ #include "list-objects.h" #include "commit-slab.h" #include "wildmatch.h" +#include "prio-queue.h" +#include "oidset.h" #define MAX_TAGS (FLAG_BITS - 1) #define DEFAULT_CANDIDATES 10 @@ -249,36 +251,83 @@ static int compare_pt(const void *a_, const void *b_) return 0; } -static unsigned long finish_depth_computation( - struct commit_list **list, - struct possible_tag *best) +struct lazy_queue { + struct prio_queue queue; + bool get_pending; +}; + +#define LAZY_QUEUE_INIT { { compare_commits_by_commit_date }, false } + +static void *lazy_queue_get(struct lazy_queue *queue) +{ + if (queue->get_pending) + prio_queue_get(&queue->queue); + else + queue->get_pending = true; + return prio_queue_peek(&queue->queue); +} + +static void lazy_queue_put(struct lazy_queue *queue, void *thing) +{ + if (queue->get_pending) + prio_queue_replace(&queue->queue, thing); + else + prio_queue_put(&queue->queue, thing); + queue->get_pending = false; +} + +static bool lazy_queue_empty(const struct lazy_queue *queue) +{ + return queue->queue.nr == (queue->get_pending ? 1 : 0); +} + +static void lazy_queue_clear(struct lazy_queue *queue) +{ + clear_prio_queue(&queue->queue); + queue->get_pending = false; +} + +static unsigned long finish_depth_computation(struct lazy_queue *queue, + struct possible_tag *best) { unsigned long seen_commits = 0; - while (*list) { - struct commit *c = pop_commit(list); + struct oidset unflagged = OIDSET_INIT; + + for (size_t i = queue->get_pending ? 1 : 0; i < queue->queue.nr; i++) { + struct commit *commit = queue->queue.array[i].data; + if (!(commit->object.flags & best->flag_within)) + oidset_insert(&unflagged, &commit->object.oid); + } + + while (!lazy_queue_empty(queue)) { + struct commit *c = lazy_queue_get(queue); struct commit_list *parents = c->parents; seen_commits++; if (c->object.flags & best->flag_within) { - struct commit_list *a = *list; - while (a) { - struct commit *i = a->item; - if (!(i->object.flags & best->flag_within)) - break; - a = a->next; - } - if (!a) + if (!oidset_size(&unflagged)) break; - } else + } else { + oidset_remove(&unflagged, &c->object.oid); best->depth++; + } while (parents) { + unsigned seen, flag_before, flag_after; struct commit *p = parents->item; repo_parse_commit(the_repository, p); - if (!(p->object.flags & SEEN)) - commit_list_insert_by_date(p, list); + seen = p->object.flags & SEEN; + if (!seen) + lazy_queue_put(queue, p); + flag_before = p->object.flags & best->flag_within; p->object.flags |= c->object.flags; + flag_after = p->object.flags & best->flag_within; + if (!seen && !flag_after) + oidset_insert(&unflagged, &p->object.oid); + if (seen && !flag_before && flag_after) + oidset_remove(&unflagged, &p->object.oid); parents = parents->next; } } + oidset_clear(&unflagged); return seen_commits; } @@ -313,18 +362,16 @@ static void append_suffix(int depth, const struct object_id *oid, struct strbuf repo_find_unique_abbrev(the_repository, oid, abbrev)); } -static void describe_commit(struct object_id *oid, struct strbuf *dst) +static void describe_commit(struct commit *cmit, struct strbuf *dst) { - struct commit *cmit, *gave_up_on = NULL; - struct commit_list *list; + struct commit *gave_up_on = NULL; + struct lazy_queue queue = LAZY_QUEUE_INIT; struct commit_name *n; struct possible_tag all_matches[MAX_TAGS]; unsigned int match_cnt = 0, annotated_cnt = 0, cur_match; unsigned long seen_commits = 0; unsigned int unannotated_cnt = 0; - cmit = lookup_commit_reference(the_repository, oid); - n = find_commit_name(&cmit->object.oid); if (n && (tags || all || n->prio == 2)) { /* @@ -332,7 +379,7 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) */ append_name(n, dst); if (n->misnamed || longformat) - append_suffix(0, n->tag ? get_tagged_oid(n->tag) : oid, dst); + append_suffix(0, n->tag ? get_tagged_oid(n->tag) : &cmit->object.oid, dst); if (suffix) strbuf_addstr(dst, suffix); return; @@ -359,11 +406,10 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) have_util = 1; } - list = NULL; cmit->object.flags = SEEN; - commit_list_insert(cmit, &list); - while (list) { - struct commit *c = pop_commit(&list); + lazy_queue_put(&queue, cmit); + while (!lazy_queue_empty(&queue)) { + struct commit *c = lazy_queue_get(&queue); struct commit_list *parents = c->parents; struct commit_name **slot; @@ -397,7 +443,7 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) t->depth++; } /* Stop if last remaining path already covered by best candidate(s) */ - if (annotated_cnt && !list) { + if (annotated_cnt && lazy_queue_empty(&queue)) { int best_depth = INT_MAX; unsigned best_within = 0; for (cur_match = 0; cur_match < match_cnt; cur_match++) { @@ -420,7 +466,7 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) struct commit *p = parents->item; repo_parse_commit(the_repository, p); if (!(p->object.flags & SEEN)) - commit_list_insert_by_date(p, &list); + lazy_queue_put(&queue, p); p->object.flags |= c->object.flags; parents = parents->next; @@ -435,6 +481,7 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) strbuf_add_unique_abbrev(dst, cmit_oid, abbrev); if (suffix) strbuf_addstr(dst, suffix); + lazy_queue_clear(&queue); return; } if (unannotated_cnt) @@ -450,11 +497,11 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) QSORT(all_matches, match_cnt, compare_pt); if (gave_up_on) { - commit_list_insert_by_date(gave_up_on, &list); + lazy_queue_put(&queue, gave_up_on); seen_commits--; } - seen_commits += finish_depth_computation(&list, &all_matches[0]); - free_commit_list(list); + seen_commits += finish_depth_computation(&queue, &all_matches[0]); + lazy_queue_clear(&queue); if (debug) { static int label_width = -1; @@ -489,8 +536,8 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) } struct process_commit_data { - struct object_id current_commit; - struct object_id looking_for; + struct commit *current_commit; + const struct object_id *looking_for; struct strbuf *dst; struct rev_info *revs; }; @@ -498,34 +545,43 @@ struct process_commit_data { static void process_commit(struct commit *commit, void *data) { struct process_commit_data *pcd = data; - pcd->current_commit = commit->object.oid; + pcd->current_commit = commit; } static void process_object(struct object *obj, const char *path, void *data) { struct process_commit_data *pcd = data; - if (oideq(&pcd->looking_for, &obj->oid) && !pcd->dst->len) { + if (oideq(pcd->looking_for, &obj->oid) && !pcd->dst->len) { reset_revision_walk(); - describe_commit(&pcd->current_commit, pcd->dst); - strbuf_addf(pcd->dst, ":%s", path); + if (pcd->current_commit) { + describe_commit(pcd->current_commit, pcd->dst); + strbuf_addf(pcd->dst, ":%s", path); + } free_commit_list(pcd->revs->commits); pcd->revs->commits = NULL; } } -static void describe_blob(struct object_id oid, struct strbuf *dst) +static void describe_blob(const struct object_id *oid, struct strbuf *dst) { struct rev_info revs; struct strvec args = STRVEC_INIT; - struct process_commit_data pcd = { *null_oid(the_hash_algo), oid, dst, &revs}; + struct object_id head_oid; + struct process_commit_data pcd = { NULL, oid, dst, &revs}; + + if (repo_get_oid(the_repository, "HEAD", &head_oid)) + die(_("cannot search for blob '%s' on an unborn branch"), + oid_to_hex(oid)); strvec_pushl(&args, "internal: The first arg is not parsed", - "--objects", "--in-commit-order", "--reverse", "HEAD", + "--objects", "--in-commit-order", "--reverse", + oid_to_hex(&head_oid), NULL); repo_init_revisions(the_repository, &revs, NULL); - if (setup_revisions(args.nr, args.v, &revs, NULL) > 1) + setup_revisions_from_strvec(&args, &revs, NULL); + if (args.nr > 1) BUG("setup_revisions could not handle all args?"); if (prepare_revision_walk(&revs)) @@ -535,6 +591,9 @@ static void describe_blob(struct object_id oid, struct strbuf *dst) reset_revision_walk(); release_revisions(&revs); strvec_clear(&args); + + if (!dst->len) + die(_("blob '%s' not reachable from HEAD"), oid_to_hex(oid)); } static void describe(const char *arg, int last_one) @@ -551,10 +610,10 @@ static void describe(const char *arg, int last_one) cmit = lookup_commit_reference_gently(the_repository, &oid, 1); if (cmit) - describe_commit(&oid, &sb); + describe_commit(cmit, &sb); else if (odb_read_object_info(the_repository->objects, &oid, NULL) == OBJ_BLOB) - describe_blob(oid, &sb); + describe_blob(&oid, &sb); else die(_("%s is neither a commit nor blob"), arg); diff --git a/builtin/diff.c b/builtin/diff.c index 9a89e25a98..0b23c41456 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -487,6 +487,21 @@ int cmd_diff(int argc, init_diff_ui_defaults(); repo_config(the_repository, git_diff_ui_config, NULL); + + /* + * If we are ignoring the fact that our current directory may + * be part of a working tree controlled by a Git repository to + * pretend to be a "better GNU diff", we should undo the + * effect of the setup code that did a chdir() to the top of + * the working tree. Where we came from is recorded in the + * prefix. + */ + if (no_index && prefix) { + if (chdir(prefix)) + die(_("cannot come back to cwd")); + prefix = NULL; + } + prefix = precompose_argv_prefix(argc, argv, prefix); repo_init_revisions(the_repository, &rev, prefix); diff --git a/builtin/fast-export.c b/builtin/fast-export.c index c06ee0b213..dc2486f9a8 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -37,8 +37,6 @@ static const char *const fast_export_usage[] = { NULL }; -enum sign_mode { SIGN_ABORT, SIGN_VERBATIM, SIGN_STRIP, SIGN_WARN_VERBATIM, SIGN_WARN_STRIP }; - static int progress; static enum sign_mode signed_tag_mode = SIGN_ABORT; static enum sign_mode signed_commit_mode = SIGN_STRIP; @@ -59,23 +57,16 @@ static struct hashmap anonymized_seeds; static struct revision_sources revision_sources; static int parse_opt_sign_mode(const struct option *opt, - const char *arg, int unset) + const char *arg, int unset) { enum sign_mode *val = opt->value; + if (unset) return 0; - else if (!strcmp(arg, "abort")) - *val = SIGN_ABORT; - else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore")) - *val = SIGN_VERBATIM; - else if (!strcmp(arg, "warn-verbatim") || !strcmp(arg, "warn")) - *val = SIGN_WARN_VERBATIM; - else if (!strcmp(arg, "warn-strip")) - *val = SIGN_WARN_STRIP; - else if (!strcmp(arg, "strip")) - *val = SIGN_STRIP; - else + + if (parse_sign_mode(arg, val)) return error("Unknown %s mode: %s", opt->long_name, arg); + return 0; } diff --git a/builtin/fast-import.c b/builtin/fast-import.c index 2c35f9345d..606c6aea82 100644 --- a/builtin/fast-import.c +++ b/builtin/fast-import.c @@ -188,6 +188,8 @@ static int global_argc; static const char **global_argv; static const char *global_prefix; +static enum sign_mode signed_commit_mode = SIGN_VERBATIM; + /* Memory pools */ static struct mem_pool fi_mem_pool = { .block_alloc = 2*1024*1024 - sizeof(struct mp_block), @@ -897,11 +899,11 @@ static void end_packfile(void) idx_name = keep_pack(create_index()); /* Register the packfile with core git's machinery. */ - new_p = add_packed_git(pack_data->repo, idx_name, strlen(idx_name), 1); + new_p = packfile_store_load_pack(pack_data->repo->objects->packfiles, + idx_name, 1); if (!new_p) die("core git rejected index %s", idx_name); all_packs[pack_id] = new_p; - install_packed_git(the_repository, new_p); free(idx_name); /* Print the boundary */ @@ -952,6 +954,7 @@ static int store_object( struct object_id *oidout, uintmax_t mark) { + struct packfile_store *packs = the_repository->objects->packfiles; void *out, *delta; struct object_entry *e; unsigned char hdr[96]; @@ -975,7 +978,7 @@ static int store_object( if (e->idx.offset) { duplicate_count_by_type[type]++; return 1; - } else if (find_oid_pack(&oid, get_all_packs(the_repository))) { + } else if (find_oid_pack(&oid, packfile_store_get_all_packs(packs))) { e->type = type; e->pack_id = MAX_PACK_ID; e->idx.offset = 1; /* just not zero! */ @@ -1092,6 +1095,7 @@ static void truncate_pack(struct hashfile_checkpoint *checkpoint) static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark) { + struct packfile_store *packs = the_repository->objects->packfiles; size_t in_sz = 64 * 1024, out_sz = 64 * 1024; unsigned char *in_buf = xmalloc(in_sz); unsigned char *out_buf = xmalloc(out_sz); @@ -1175,7 +1179,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark) duplicate_count_by_type[OBJ_BLOB]++; truncate_pack(&checkpoint); - } else if (find_oid_pack(&oid, get_all_packs(the_repository))) { + } else if (find_oid_pack(&oid, packfile_store_get_all_packs(packs))) { e->type = OBJ_BLOB; e->pack_id = MAX_PACK_ID; e->idx.offset = 1; /* just not zero! */ @@ -2752,6 +2756,15 @@ static void parse_one_signature(struct signature_data *sig, const char *v) parse_data(&sig->data, 0, NULL); } +static void discard_one_signature(void) +{ + struct strbuf data = STRBUF_INIT; + + read_next_command(); + parse_data(&data, 0, NULL); + strbuf_release(&data); +} + static void add_gpgsig_to_commit(struct strbuf *commit_data, const char *header, struct signature_data *sig) @@ -2785,6 +2798,22 @@ static void store_signature(struct signature_data *stored_sig, } } +static void import_one_signature(struct signature_data *sig_sha1, + struct signature_data *sig_sha256, + const char *v) +{ + struct signature_data sig = { NULL, NULL, STRBUF_INIT }; + + parse_one_signature(&sig, v); + + if (!strcmp(sig.hash_algo, "sha1")) + store_signature(sig_sha1, &sig, "SHA-1"); + else if (!strcmp(sig.hash_algo, "sha256")) + store_signature(sig_sha256, &sig, "SHA-256"); + else + die(_("parse_one_signature() returned unknown hash algo")); +} + static void parse_new_commit(const char *arg) { static struct strbuf msg = STRBUF_INIT; @@ -2817,19 +2846,32 @@ static void parse_new_commit(const char *arg) if (!committer) die("Expected committer but didn't get one"); - /* Process signatures (up to 2: one "sha1" and one "sha256") */ while (skip_prefix(command_buf.buf, "gpgsig ", &v)) { - struct signature_data sig = { NULL, NULL, STRBUF_INIT }; - - parse_one_signature(&sig, v); + switch (signed_commit_mode) { + + /* First, modes that don't need the signature to be parsed */ + case SIGN_ABORT: + die("encountered signed commit; use " + "--signed-commits=<mode> to handle it"); + case SIGN_WARN_STRIP: + warning(_("stripping a commit signature")); + /* fallthru */ + case SIGN_STRIP: + discard_one_signature(); + break; - if (!strcmp(sig.hash_algo, "sha1")) - store_signature(&sig_sha1, &sig, "SHA-1"); - else if (!strcmp(sig.hash_algo, "sha256")) - store_signature(&sig_sha256, &sig, "SHA-256"); - else - BUG("parse_one_signature() returned unknown hash algo"); + /* Second, modes that parse the signature */ + case SIGN_WARN_VERBATIM: + warning(_("importing a commit signature verbatim")); + /* fallthru */ + case SIGN_VERBATIM: + import_one_signature(&sig_sha1, &sig_sha256, v); + break; + /* Third, BUG */ + default: + BUG("invalid signed_commit_mode value %d", signed_commit_mode); + } read_next_command(); } @@ -3501,6 +3543,9 @@ static int parse_one_option(const char *option) option_active_branches(option); } else if (skip_prefix(option, "export-pack-edges=", &option)) { option_export_pack_edges(option); + } else if (skip_prefix(option, "signed-commits=", &option)) { + if (parse_sign_mode(option, &signed_commit_mode)) + usagef(_("unknown --signed-commits mode '%s'"), option); } else if (!strcmp(option, "quiet")) { show_stats = 0; quiet = 1; diff --git a/builtin/fetch.c b/builtin/fetch.c index 24645c4653..c7ff3480fb 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -1643,7 +1643,8 @@ cleanup: struct ref_rejection_data { int *retcode; - int conflict_msg_shown; + bool conflict_msg_shown; + bool case_sensitive_msg_shown; const char *remote_name; }; @@ -1657,11 +1658,25 @@ static void ref_transaction_rejection_handler(const char *refname, { struct ref_rejection_data *data = cb_data; - if (err == REF_TRANSACTION_ERROR_NAME_CONFLICT && !data->conflict_msg_shown) { + if (err == REF_TRANSACTION_ERROR_CASE_CONFLICT && ignore_case && + !data->case_sensitive_msg_shown) { + error(_("You're on a case-insensitive filesystem, and the remote you are\n" + "trying to fetch from has references that only differ in casing. It\n" + "is impossible to store such references with the 'files' backend. You\n" + "can either accept this as-is, in which case you won't be able to\n" + "store all remote references on disk. Or you can alternatively\n" + "migrate your repository to use the 'reftable' backend with the\n" + "following command:\n\n git refs migrate --ref-format=reftable\n\n" + "Please keep in mind that not all implementations of Git support this\n" + "new format yet. So if you use tools other than Git to access this\n" + "repository it may not be an option to migrate to reftables.\n")); + data->case_sensitive_msg_shown = true; + } else if (err == REF_TRANSACTION_ERROR_NAME_CONFLICT && + !data->conflict_msg_shown) { error(_("some local refs could not be updated; try running\n" " 'git remote prune %s' to remove any old, conflicting " "branches"), data->remote_name); - data->conflict_msg_shown = 1; + data->conflict_msg_shown = true; } else { const char *reason = ref_transaction_error_msg(err); diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index edb93c0b3a..cf4273a52c 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -1,4 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE #include "builtin.h" #include "config.h" #include "fmt-merge-msg.h" @@ -13,12 +12,13 @@ static const char * const fmt_merge_msg_usage[] = { int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { char *inpath = NULL; const char *message = NULL; char *into_name = NULL; int shortlog_len = -1; + int merge_log_config = -1; struct option options[] = { { .type = OPTION_INTEGER, @@ -53,7 +53,7 @@ int cmd_fmt_merge_msg(int argc, int ret; struct fmt_merge_msg_opts opts; - repo_config(the_repository, fmt_merge_msg_config, NULL); + repo_config(repo, fmt_merge_msg_config, &merge_log_config); argc = parse_options(argc, argv, prefix, options, fmt_merge_msg_usage, 0); if (argc > 0) diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 222637a2c0..4a2fc421db 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -2,6 +2,7 @@ #include "commit.h" #include "config.h" #include "environment.h" +#include "for-each-ref.h" #include "gettext.h" #include "object.h" #include "parse-options.h" @@ -9,19 +10,7 @@ #include "strbuf.h" #include "strvec.h" -static char const * const for_each_ref_usage[] = { - N_("git for-each-ref [<options>] [<pattern>]"), - N_("git for-each-ref [--points-at <object>]"), - N_("git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]"), - N_("git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"), - N_("git for-each-ref [--start-after <marker>]"), - NULL -}; - -int cmd_for_each_ref(int argc, - const char **argv, - const char *prefix, - struct repository *repo) +int for_each_ref_core(int argc, const char **argv, const char *prefix, struct repository *repo, const char *const *usage) { struct ref_sorting *sorting; struct string_list sorting_options = STRING_LIST_INIT_DUP; @@ -70,17 +59,17 @@ int cmd_for_each_ref(int argc, /* Set default (refname) sorting */ string_list_append(&sorting_options, "refname"); - parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0); + parse_options(argc, argv, prefix, opts, usage, 0); if (format.array_opts.max_count < 0) { error("invalid --count argument: `%d'", format.array_opts.max_count); - usage_with_options(for_each_ref_usage, opts); + usage_with_options(usage, opts); } if (HAS_MULTI_BITS(format.quote_style)) { error("more than one quoting style?"); - usage_with_options(for_each_ref_usage, opts); + usage_with_options(usage, opts); } if (verify_ref_format(&format)) - usage_with_options(for_each_ref_usage, opts); + usage_with_options(usage, opts); if (filter.start_after && sorting_options.nr > 1) die(_("cannot use --start-after with custom sort options")); @@ -120,3 +109,16 @@ int cmd_for_each_ref(int argc, strvec_clear(&vec); return 0; } + +int cmd_for_each_ref(int argc, + const char **argv, + const char *prefix, + struct repository *repo) +{ + static char const * const for_each_ref_usage[] = { + N_("git for-each-ref " COMMON_USAGE_FOR_EACH_REF), + NULL + }; + + return for_each_ref_core(argc, argv, prefix, repo, for_each_ref_usage); +} diff --git a/builtin/fsck.c b/builtin/fsck.c index 543a2cdb5c..8ee95e0d67 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -503,13 +503,12 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid, } } -static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid, +static int fsck_handle_reflog_ent(const char *refname, + struct object_id *ooid, struct object_id *noid, const char *email UNUSED, timestamp_t timestamp, int tz UNUSED, - const char *message UNUSED, void *cb_data) + const char *message UNUSED, void *cb_data UNUSED) { - const char *refname = cb_data; - if (verbose) fprintf_ln(stderr, _("Checking reflog %s->%s"), oid_to_hex(ooid), oid_to_hex(noid)); @@ -526,7 +525,7 @@ static int fsck_handle_reflog(const char *logname, void *cb_data) strbuf_worktree_ref(cb_data, &refname, logname); refs_for_each_reflog_ent(get_main_ref_store(the_repository), refname.buf, fsck_handle_reflog_ent, - refname.buf); + NULL); strbuf_release(&refname); return 0; } @@ -868,19 +867,20 @@ static int mark_packed_for_connectivity(const struct object_id *oid, static int check_pack_rev_indexes(struct repository *r, int show_progress) { + struct packfile_store *packs = r->objects->packfiles; struct progress *progress = NULL; uint32_t pack_count = 0; int res = 0; if (show_progress) { - for (struct packed_git *p = get_all_packs(r); p; p = p->next) + for (struct packed_git *p = packfile_store_get_all_packs(packs); p; p = p->next) pack_count++; progress = start_delayed_progress(the_repository, "Verifying reverse pack-indexes", pack_count); pack_count = 0; } - for (struct packed_git *p = get_all_packs(r); p; p = p->next) { + for (struct packed_git *p = packfile_store_get_all_packs(packs); p; p = p->next) { int load_error = load_pack_revindex_from_disk(p); if (load_error < 0) { @@ -1000,6 +1000,8 @@ int cmd_fsck(int argc, for_each_packed_object(the_repository, mark_packed_for_connectivity, NULL, 0); } else { + struct packfile_store *packs = the_repository->objects->packfiles; + odb_prepare_alternates(the_repository->objects); for (source = the_repository->objects->sources; source; source = source->next) fsck_source(source); @@ -1010,7 +1012,7 @@ int cmd_fsck(int argc, struct progress *progress = NULL; if (show_progress) { - for (p = get_all_packs(the_repository); p; + for (p = packfile_store_get_all_packs(packs); p; p = p->next) { if (open_pack_index(p)) continue; @@ -1020,7 +1022,7 @@ int cmd_fsck(int argc, progress = start_progress(the_repository, _("Checking objects"), total); } - for (p = get_all_packs(the_repository); p; + for (p = packfile_store_get_all_packs(packs); p; p = p->next) { /* verify gives error messages itself */ if (verify_pack(the_repository, diff --git a/builtin/gc.c b/builtin/gc.c index 0edd94a76f..e19e13d978 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -312,7 +312,8 @@ struct count_reflog_entries_data { size_t limit; }; -static int count_reflog_entries(struct object_id *old_oid, struct object_id *new_oid, +static int count_reflog_entries(const char *refname UNUSED, + struct object_id *old_oid, struct object_id *new_oid, const char *committer, timestamp_t timestamp, int tz, const char *msg, void *cb_data) { @@ -486,9 +487,10 @@ static int too_many_loose_objects(struct gc_config *cfg) static struct packed_git *find_base_packs(struct string_list *packs, unsigned long limit) { + struct packfile_store *packfiles = the_repository->objects->packfiles; struct packed_git *p, *base = NULL; - for (p = get_all_packs(the_repository); p; p = p->next) { + for (p = packfile_store_get_all_packs(packfiles); p; p = p->next) { if (!p->pack_local || p->is_cruft) continue; if (limit) { @@ -507,13 +509,14 @@ static struct packed_git *find_base_packs(struct string_list *packs, static int too_many_packs(struct gc_config *cfg) { + struct packfile_store *packs = the_repository->objects->packfiles; struct packed_git *p; int cnt; if (cfg->gc_auto_pack_limit <= 0) return 0; - for (cnt = 0, p = get_all_packs(the_repository); p; p = p->next) { + for (cnt = 0, p = packfile_store_get_all_packs(packs); p; p = p->next) { if (!p->pack_local) continue; if (p->pack_keep) @@ -1041,7 +1044,7 @@ int cmd_gc(int argc, die(FAILED_RUN, "rerere"); report_garbage = report_pack_garbage; - reprepare_packed_git(the_repository); + odb_reprepare(the_repository->objects); if (pack_garbage.nr > 0) { close_object_store(the_repository->objects); clean_pack_garbage(); @@ -1422,7 +1425,7 @@ static int incremental_repack_auto_condition(struct gc_config *cfg UNUSED) if (incremental_repack_auto_limit < 0) return 1; - for (p = get_packed_git(the_repository); + for (p = packfile_store_get_packs(the_repository->objects->packfiles); count < incremental_repack_auto_limit && p; p = p->next) { if (!p->multi_pack_index) @@ -1490,8 +1493,8 @@ static off_t get_auto_pack_size(void) struct packed_git *p; struct repository *r = the_repository; - reprepare_packed_git(r); - for (p = get_all_packs(r); p; p = p->next) { + odb_reprepare(r->objects); + for (p = packfile_store_get_all_packs(r->objects->packfiles); p; p = p->next) { if (p->pack_size > max_size) { second_largest_size = max_size; max_size = p->pack_size; diff --git a/builtin/grep.c b/builtin/grep.c index 5df6537333..13841fbf00 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -1091,7 +1091,7 @@ int cmd_grep(int argc, if (show_in_pager == default_pager) show_in_pager = git_pager(the_repository, 1); if (show_in_pager) { - opt.color = 0; + opt.color = GIT_COLOR_NEVER; opt.name_only = 1; opt.null_following_name = 1; opt.output_priv = &path_list; @@ -1214,7 +1214,7 @@ int cmd_grep(int argc, if (recurse_submodules) repo_read_gitmodules(the_repository, 1); if (startup_info->have_repository) - (void)get_packed_git(the_repository); + (void)packfile_store_get_packs(the_repository->objects->packfiles); start_threads(&opt); } else { diff --git a/builtin/index-pack.c b/builtin/index-pack.c index f91c301bba..2b78ba7fe4 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1640,13 +1640,9 @@ static void final(const char *final_pack_name, const char *curr_pack_name, rename_tmp_packfile(&final_index_name, curr_index_name, &index_name, hash, "idx", 1); - if (do_fsck_object) { - struct packed_git *p; - p = add_packed_git(the_repository, final_index_name, - strlen(final_index_name), 0); - if (p) - install_packed_git(the_repository, p); - } + if (do_fsck_object) + packfile_store_load_pack(the_repository->objects->packfiles, + final_index_name, 0); if (!from_stdin) { printf("%s\n", hash_to_hex(hash)); diff --git a/builtin/last-modified.c b/builtin/last-modified.c new file mode 100644 index 0000000000..ae8b36a2c3 --- /dev/null +++ b/builtin/last-modified.c @@ -0,0 +1,327 @@ +#include "git-compat-util.h" +#include "bloom.h" +#include "builtin.h" +#include "commit-graph.h" +#include "commit.h" +#include "config.h" +#include "environment.h" +#include "diff.h" +#include "diffcore.h" +#include "environment.h" +#include "hashmap.h" +#include "hex.h" +#include "log-tree.h" +#include "object-name.h" +#include "object.h" +#include "parse-options.h" +#include "quote.h" +#include "repository.h" +#include "revision.h" + +struct last_modified_entry { + struct hashmap_entry hashent; + struct object_id oid; + struct bloom_key key; + const char path[FLEX_ARRAY]; +}; + +static int last_modified_entry_hashcmp(const void *unused UNUSED, + const struct hashmap_entry *hent1, + const struct hashmap_entry *hent2, + const void *path) +{ + const struct last_modified_entry *ent1 = + container_of(hent1, const struct last_modified_entry, hashent); + const struct last_modified_entry *ent2 = + container_of(hent2, const struct last_modified_entry, hashent); + return strcmp(ent1->path, path ? path : ent2->path); +} + +struct last_modified { + struct hashmap paths; + struct rev_info rev; + bool recursive; + bool show_trees; +}; + +static void last_modified_release(struct last_modified *lm) +{ + struct hashmap_iter iter; + struct last_modified_entry *ent; + + hashmap_for_each_entry(&lm->paths, &iter, ent, hashent) + bloom_key_clear(&ent->key); + + hashmap_clear_and_free(&lm->paths, struct last_modified_entry, hashent); + release_revisions(&lm->rev); +} + +struct last_modified_callback_data { + struct last_modified *lm; + struct commit *commit; +}; + +static void add_path_from_diff(struct diff_queue_struct *q, + struct diff_options *opt UNUSED, void *data) +{ + struct last_modified *lm = data; + + for (int i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + struct last_modified_entry *ent; + const char *path = p->two->path; + + FLEX_ALLOC_STR(ent, path, path); + oidcpy(&ent->oid, &p->two->oid); + if (lm->rev.bloom_filter_settings) + bloom_key_fill(&ent->key, path, strlen(path), + lm->rev.bloom_filter_settings); + hashmap_entry_init(&ent->hashent, strhash(ent->path)); + hashmap_add(&lm->paths, &ent->hashent); + } +} + +static int populate_paths_from_revs(struct last_modified *lm) +{ + int num_interesting = 0; + struct diff_options diffopt; + + /* + * Create a copy of `struct diff_options`. In this copy a callback is + * set that when called adds entries to `paths` in `struct last_modified`. + * This copy is used to diff the tree of the target revision against an + * empty tree. This results in all paths in the target revision being + * listed. After `paths` is populated, we don't need this copy no more. + */ + memcpy(&diffopt, &lm->rev.diffopt, sizeof(diffopt)); + copy_pathspec(&diffopt.pathspec, &lm->rev.diffopt.pathspec); + diffopt.output_format = DIFF_FORMAT_CALLBACK; + diffopt.format_callback = add_path_from_diff; + diffopt.format_callback_data = lm; + + for (size_t i = 0; i < lm->rev.pending.nr; i++) { + struct object_array_entry *obj = lm->rev.pending.objects + i; + + if (obj->item->flags & UNINTERESTING) + continue; + + if (num_interesting++) + return error(_("last-modified can only operate on one tree at a time")); + + diff_tree_oid(lm->rev.repo->hash_algo->empty_tree, + &obj->item->oid, "", &diffopt); + diff_flush(&diffopt); + } + clear_pathspec(&diffopt.pathspec); + + return 0; +} + +static void last_modified_emit(struct last_modified *lm, + const char *path, const struct commit *commit) + +{ + if (commit->object.flags & BOUNDARY) + putchar('^'); + printf("%s\t", oid_to_hex(&commit->object.oid)); + + if (lm->rev.diffopt.line_termination) + write_name_quoted(path, stdout, '\n'); + else + printf("%s%c", path, '\0'); +} + +static void mark_path(const char *path, const struct object_id *oid, + struct last_modified_callback_data *data) +{ + struct last_modified_entry *ent; + + /* Is it even a path that we are interested in? */ + ent = hashmap_get_entry_from_hash(&data->lm->paths, strhash(path), path, + struct last_modified_entry, hashent); + if (!ent) + return; + + /* + * Is it arriving at a version of interest, or is it from a side branch + * which did not contribute to the final state? + */ + if (!oideq(oid, &ent->oid)) + return; + + last_modified_emit(data->lm, path, data->commit); + + hashmap_remove(&data->lm->paths, &ent->hashent, path); + bloom_key_clear(&ent->key); + free(ent); +} + +static void last_modified_diff(struct diff_queue_struct *q, + struct diff_options *opt UNUSED, void *cbdata) +{ + struct last_modified_callback_data *data = cbdata; + + for (int i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + switch (p->status) { + case DIFF_STATUS_DELETED: + /* + * There's no point in feeding a deletion, as it could + * not have resulted in our current state, which + * actually has the file. + */ + break; + + default: + /* + * Otherwise, we care only that we somehow arrived at + * a final oid state. Note that this covers some + * potentially controversial areas, including: + * + * 1. A rename or copy will be found, as it is the + * first time the content has arrived at the given + * path. + * + * 2. Even a non-content modification like a mode or + * type change will trigger it. + * + * We take the inclusive approach for now, and find + * anything which impacts the path. Options to tweak + * the behavior (e.g., to "--follow" the content across + * renames) can come later. + */ + mark_path(p->two->path, &p->two->oid, data); + break; + } + } +} + +static bool maybe_changed_path(struct last_modified *lm, struct commit *origin) +{ + struct bloom_filter *filter; + struct last_modified_entry *ent; + struct hashmap_iter iter; + + if (!lm->rev.bloom_filter_settings) + return true; + + if (commit_graph_generation(origin) == GENERATION_NUMBER_INFINITY) + return true; + + filter = get_bloom_filter(lm->rev.repo, origin); + if (!filter) + return true; + + hashmap_for_each_entry(&lm->paths, &iter, ent, hashent) { + if (bloom_filter_contains(filter, &ent->key, + lm->rev.bloom_filter_settings)) + return true; + } + return false; +} + +static int last_modified_run(struct last_modified *lm) +{ + struct last_modified_callback_data data = { .lm = lm }; + + lm->rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; + lm->rev.diffopt.format_callback = last_modified_diff; + lm->rev.diffopt.format_callback_data = &data; + + prepare_revision_walk(&lm->rev); + + while (hashmap_get_size(&lm->paths)) { + data.commit = get_revision(&lm->rev); + if (!data.commit) + BUG("paths remaining beyond boundary in last-modified"); + + if (data.commit->object.flags & BOUNDARY) { + diff_tree_oid(lm->rev.repo->hash_algo->empty_tree, + &data.commit->object.oid, "", + &lm->rev.diffopt); + diff_flush(&lm->rev.diffopt); + + break; + } + + if (!maybe_changed_path(lm, data.commit)) + continue; + + log_tree_commit(&lm->rev, data.commit); + } + + return 0; +} + +static int last_modified_init(struct last_modified *lm, struct repository *r, + const char *prefix, int argc, const char **argv) +{ + hashmap_init(&lm->paths, last_modified_entry_hashcmp, NULL, 0); + + repo_init_revisions(r, &lm->rev, prefix); + lm->rev.def = "HEAD"; + lm->rev.combine_merges = 1; + lm->rev.show_root_diff = 1; + lm->rev.boundary = 1; + lm->rev.no_commit_id = 1; + lm->rev.diff = 1; + lm->rev.diffopt.flags.no_recursive_diff_tree_combined = 1; + lm->rev.diffopt.flags.recursive = lm->recursive; + lm->rev.diffopt.flags.tree_in_recursive = lm->show_trees; + + argc = setup_revisions(argc, argv, &lm->rev, NULL); + if (argc > 1) { + error(_("unknown last-modified argument: %s"), argv[1]); + return argc; + } + + lm->rev.bloom_filter_settings = get_bloom_filter_settings(lm->rev.repo); + + if (populate_paths_from_revs(lm) < 0) + return error(_("unable to setup last-modified")); + + return 0; +} + +int cmd_last_modified(int argc, const char **argv, const char *prefix, + struct repository *repo) +{ + int ret; + struct last_modified lm = { 0 }; + + const char * const last_modified_usage[] = { + N_("git last-modified [--recursive] [--show-trees] " + "[<revision-range>] [[--] <path>...]"), + NULL + }; + + struct option last_modified_options[] = { + OPT_BOOL('r', "recursive", &lm.recursive, + N_("recurse into subtrees")), + OPT_BOOL('t', "show-trees", &lm.show_trees, + N_("show tree entries when recursing into subtrees")), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, last_modified_options, + last_modified_usage, + PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT); + + repo_config(repo, git_default_config, NULL); + + ret = last_modified_init(&lm, repo, prefix, argc, argv); + if (ret > 0) + usage_with_options(last_modified_usage, + last_modified_options); + if (ret) + goto out; + + ret = last_modified_run(&lm); + if (ret) + goto out; + +out: + last_modified_release(&lm); + + return ret; +} diff --git a/builtin/log.c b/builtin/log.c index c2f8bbf863..8aa1777940 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -543,7 +543,13 @@ int cmd_whatchanged(int argc, cmd_log_init(argc, argv, prefix, &rev, &opt, &cfg); if (!cfg.i_still_use_this) - you_still_use_that("git whatchanged"); + you_still_use_that("git whatchanged", + _("\n" + "hint: You can replace 'git whatchanged <opts>' with:\n" + "hint:\tgit log <opts> --raw --no-merges\n" + "hint: Or make an alias:\n" + "hint:\tgit config set --global alias.whatchanged 'log --raw --no-merges'\n" + "\n")); if (!rev.diffopt.output_format) rev.diffopt.output_format = DIFF_FORMAT_RAW; @@ -1404,6 +1410,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, struct range_diff_options range_diff_opts = { .creation_factor = rev->creation_factor, .dual_color = 1, + .max_memory = RANGE_DIFF_MAX_MEMORY_DEFAULT, .diffopt = &opts, .other_arg = &other_arg }; diff --git a/builtin/ls-files.c b/builtin/ls-files.c index c06a6f33e4..b148607f7a 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -414,14 +414,21 @@ static void show_files(struct repository *repo, struct dir_struct *dir) if (!(show_cached || show_stage || show_deleted || show_modified)) return; - if (!show_sparse_dirs) - ensure_full_index(repo->index); - for (i = 0; i < repo->index->cache_nr; i++) { const struct cache_entry *ce = repo->index->cache[i]; struct stat st; int stat_err; + if (S_ISSPARSEDIR(ce->ce_mode) && !show_sparse_dirs) { + /* + * This is the first time we've hit a sparse dir, + * so expansion will leave the first 'i' entries + * alone. + */ + ensure_full_index(repo->index); + ce = repo->index->cache[i]; + } + construct_fullname(&fullname, repo, ce); if ((dir->flags & DIR_SHOW_IGNORED) && diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index 5d55731ca3..ec6940fc7c 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -373,7 +373,6 @@ int cmd_ls_tree(int argc, OPT_END() }; struct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format; - struct object_context obj_context = {0}; int ret; repo_config(the_repository, git_default_config, NULL); @@ -405,9 +404,8 @@ int cmd_ls_tree(int argc, ls_tree_usage, ls_tree_options); if (argc < 1) usage_with_options(ls_tree_usage, ls_tree_options); - if (get_oid_with_context(the_repository, argv[0], - GET_OID_HASH_ANY, &oid, - &obj_context)) + if (repo_get_oid_with_flags(the_repository, argv[0], &oid, + GET_OID_HASH_ANY)) die("Not a valid object name %s", argv[0]); /* @@ -447,6 +445,5 @@ int cmd_ls_tree(int argc, ret = !!read_tree(the_repository, tree, &options.pathspec, fn, &options); clear_pathspec(&options.pathspec); - object_context_release(&obj_context); return ret; } diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c index 03b5100cfa..17aa4db37a 100644 --- a/builtin/merge-recursive.c +++ b/builtin/merge-recursive.c @@ -38,7 +38,8 @@ int cmd_merge_recursive(int argc, if (argv[0] && ends_with(argv[0], "-subtree")) o.subtree_shift = ""; - if (argc == 2 && !strcmp(argv[1], "-h")) { + if (argc == 2 && (!strcmp(argv[1], "-h") || + !strcmp(argv[1], "--help-all"))) { struct strbuf msg = STRBUF_INIT; strbuf_addf(&msg, builtin_merge_recursive_usage, argv[0]); show_usage_if_asked(argc, argv, msg.buf); diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 203f0e6456..1c063d9a41 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -619,32 +619,34 @@ int cmd_merge_tree(int argc, "--merge-base", "--stdin"); line_termination = '\0'; while (strbuf_getline_lf(&buf, stdin) != EOF) { - struct strbuf **split; + struct string_list split = STRING_LIST_INIT_NODUP; const char *input_merge_base = NULL; - split = strbuf_split(&buf, ' '); - if (!split[0] || !split[1]) + string_list_split_in_place_f(&split, buf.buf, " ", -1, + STRING_LIST_SPLIT_TRIM); + + if (split.nr < 2) die(_("malformed input line: '%s'."), buf.buf); - strbuf_rtrim(split[0]); - strbuf_rtrim(split[1]); /* parse the merge-base */ - if (!strcmp(split[1]->buf, "--")) { - input_merge_base = split[0]->buf; + if (!strcmp(split.items[1].string, "--")) { + input_merge_base = split.items[0].string; } - if (input_merge_base && split[2] && split[3] && !split[4]) { - strbuf_rtrim(split[2]); - strbuf_rtrim(split[3]); - real_merge(&o, input_merge_base, split[2]->buf, split[3]->buf, prefix); - } else if (!input_merge_base && !split[2]) { - real_merge(&o, NULL, split[0]->buf, split[1]->buf, prefix); + if (input_merge_base && split.nr == 4) { + real_merge(&o, input_merge_base, + split.items[2].string, split.items[3].string, + prefix); + } else if (!input_merge_base && split.nr == 2) { + real_merge(&o, NULL, + split.items[0].string, split.items[1].string, + prefix); } else { die(_("malformed input line: '%s'."), buf.buf); } maybe_flush_or_die(stdout, "stdout"); - strbuf_list_free(split); + string_list_clear(&split, 0); } strbuf_release(&buf); diff --git a/builtin/merge.c b/builtin/merge.c index ce880e6ccb..c421a11b0b 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -875,7 +875,7 @@ static void add_strategies(const char *string, unsigned attr) if (string) { struct string_list list = STRING_LIST_INIT_DUP; struct string_list_item *item; - string_list_split(&list, string, ' ', -1); + string_list_split(&list, string, " ", -1); for_each_string_list_item(item, &list) append_strategy(get_strategy(item->string)); string_list_clear(&list, 0); @@ -1374,10 +1374,14 @@ int cmd_merge(int argc, struct commit_list *remoteheads = NULL, *p; void *branch_to_free; int orig_argc = argc; + int merge_log_config = -1; show_usage_with_options_if_asked(argc, argv, builtin_merge_usage, builtin_merge_options); +#ifndef WITH_BREAKING_CHANGES + warn_on_auto_comment_char = true; +#endif /* !WITH_BREAKING_CHANGES */ prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; @@ -1392,7 +1396,7 @@ int cmd_merge(int argc, skip_prefix(branch, "refs/heads/", &branch); init_diff_ui_defaults(); - repo_config(the_repository, git_merge_config, NULL); + repo_config(the_repository, git_merge_config, &merge_log_config); if (!branch || is_null_oid(&head_oid)) head_commit = NULL; @@ -1862,7 +1866,7 @@ int cmd_merge(int argc, if (squash) { finish(head_commit, remoteheads, NULL, NULL); - git_test_write_commit_graph_or_die(); + git_test_write_commit_graph_or_die(the_repository->objects->sources); } else write_merge_state(remoteheads); diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c index d3b9e98be3..5f364aa816 100644 --- a/builtin/multi-pack-index.c +++ b/builtin/multi-pack-index.c @@ -65,12 +65,20 @@ static int parse_object_dir(const struct option *opt, const char *arg, char **value = opt->value; free(*value); if (unset) - *value = xstrdup(repo_get_object_directory(the_repository)); + *value = xstrdup(the_repository->objects->sources->path); else *value = real_pathdup(arg, 1); return 0; } +static struct odb_source *handle_object_dir_option(struct repository *repo) +{ + struct odb_source *source = odb_find_source(repo->objects, opts.object_dir); + if (!source) + source = odb_add_to_alternates_memory(repo->objects, opts.object_dir); + return source; +} + static struct option common_opts[] = { OPT_CALLBACK(0, "object-dir", &opts.object_dir, N_("directory"), @@ -140,6 +148,7 @@ static int cmd_multi_pack_index_write(int argc, const char **argv, N_("refs snapshot for selecting bitmap commits")), OPT_END(), }; + struct odb_source *source; int ret; opts.flags |= MIDX_WRITE_BITMAP_HASH_CACHE; @@ -158,6 +167,7 @@ static int cmd_multi_pack_index_write(int argc, const char **argv, if (argc) usage_with_options(builtin_multi_pack_index_write_usage, options); + source = handle_object_dir_option(repo); FREE_AND_NULL(options); @@ -166,7 +176,7 @@ static int cmd_multi_pack_index_write(int argc, const char **argv, read_packs_from_stdin(&packs); - ret = write_midx_file_only(repo, opts.object_dir, &packs, + ret = write_midx_file_only(source, &packs, opts.preferred_pack, opts.refs_snapshot, opts.flags); @@ -177,7 +187,7 @@ static int cmd_multi_pack_index_write(int argc, const char **argv, } - ret = write_midx_file(repo, opts.object_dir, opts.preferred_pack, + ret = write_midx_file(source, opts.preferred_pack, opts.refs_snapshot, opts.flags); free(opts.refs_snapshot); @@ -194,6 +204,8 @@ static int cmd_multi_pack_index_verify(int argc, const char **argv, N_("force progress reporting"), MIDX_PROGRESS), OPT_END(), }; + struct odb_source *source; + options = add_common_options(builtin_multi_pack_index_verify_options); trace2_cmd_mode(argv[0]); @@ -206,10 +218,11 @@ static int cmd_multi_pack_index_verify(int argc, const char **argv, if (argc) usage_with_options(builtin_multi_pack_index_verify_usage, options); + source = handle_object_dir_option(the_repository); FREE_AND_NULL(options); - return verify_midx_file(the_repository, opts.object_dir, opts.flags); + return verify_midx_file(source, opts.flags); } static int cmd_multi_pack_index_expire(int argc, const char **argv, @@ -222,6 +235,8 @@ static int cmd_multi_pack_index_expire(int argc, const char **argv, N_("force progress reporting"), MIDX_PROGRESS), OPT_END(), }; + struct odb_source *source; + options = add_common_options(builtin_multi_pack_index_expire_options); trace2_cmd_mode(argv[0]); @@ -234,10 +249,11 @@ static int cmd_multi_pack_index_expire(int argc, const char **argv, if (argc) usage_with_options(builtin_multi_pack_index_expire_usage, options); + source = handle_object_dir_option(the_repository); FREE_AND_NULL(options); - return expire_midx_packs(the_repository, opts.object_dir, opts.flags); + return expire_midx_packs(source, opts.flags); } static int cmd_multi_pack_index_repack(int argc, const char **argv, @@ -252,6 +268,7 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv, N_("force progress reporting"), MIDX_PROGRESS), OPT_END(), }; + struct odb_source *source; options = add_common_options(builtin_multi_pack_index_repack_options); @@ -266,11 +283,11 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv, if (argc) usage_with_options(builtin_multi_pack_index_repack_usage, options); + source = handle_object_dir_option(the_repository); FREE_AND_NULL(options); - return midx_repack(the_repository, opts.object_dir, - (size_t)opts.batch_size, opts.flags); + return midx_repack(source, (size_t)opts.batch_size, opts.flags); } int cmd_multi_pack_index(int argc, diff --git a/builtin/notes.c b/builtin/notes.c index 6fb4144da3..9af602bdd7 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -376,18 +376,19 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd) while (strbuf_getline_lf(&buf, stdin) != EOF) { struct object_id from_obj, to_obj; - struct strbuf **split; + struct string_list split = STRING_LIST_INIT_NODUP; int err; - split = strbuf_split(&buf, ' '); - if (!split[0] || !split[1]) + string_list_split_in_place_f(&split, buf.buf, " ", -1, + STRING_LIST_SPLIT_TRIM); + if (split.nr < 2) die(_("malformed input line: '%s'."), buf.buf); - strbuf_rtrim(split[0]); - strbuf_rtrim(split[1]); - if (repo_get_oid(the_repository, split[0]->buf, &from_obj)) - die(_("failed to resolve '%s' as a valid ref."), split[0]->buf); - if (repo_get_oid(the_repository, split[1]->buf, &to_obj)) - die(_("failed to resolve '%s' as a valid ref."), split[1]->buf); + if (repo_get_oid(the_repository, split.items[0].string, &from_obj)) + die(_("failed to resolve '%s' as a valid ref."), + split.items[0].string); + if (repo_get_oid(the_repository, split.items[1].string, &to_obj)) + die(_("failed to resolve '%s' as a valid ref."), + split.items[1].string); if (rewrite_cmd) err = copy_note_for_rewrite(c, &from_obj, &to_obj); @@ -397,11 +398,11 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd) if (err) { error(_("failed to copy notes from '%s' to '%s'"), - split[0]->buf, split[1]->buf); + split.items[0].string, split.items[1].string); ret = 1; } - strbuf_list_free(split); + string_list_clear(&split, 0); } if (!rewrite_cmd) { diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 53a2256250..5bdc44fb2d 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -1741,19 +1741,19 @@ static int want_object_in_pack_mtime(const struct object_id *oid, struct multi_pack_index *m = get_multi_pack_index(source); struct pack_entry e; - if (m && fill_midx_entry(the_repository, oid, &e, m)) { + if (m && fill_midx_entry(m, oid, &e)) { want = want_object_in_pack_one(e.p, oid, exclude, found_pack, found_offset, found_mtime); if (want != -1) return want; } } - list_for_each(pos, get_packed_git_mru(the_repository)) { + list_for_each(pos, packfile_store_get_packs_mru(the_repository->objects->packfiles)) { struct packed_git *p = list_entry(pos, struct packed_git, mru); want = want_object_in_pack_one(p, oid, exclude, found_pack, found_offset, found_mtime); if (!exclude && want > 0) list_move(&p->mru, - get_packed_git_mru(the_repository)); + packfile_store_get_packs_mru(the_repository->objects->packfiles)); if (want != -1) return want; } @@ -3774,7 +3774,7 @@ static void show_object_pack_hint(struct object *object, const char *name, enum stdin_packs_mode mode = *(enum stdin_packs_mode *)data; if (mode == STDIN_PACKS_MODE_FOLLOW) { if (object->type == OBJ_BLOB && - !has_object(the_repository, &object->oid, 0)) + !odb_has_object(the_repository->objects, &object->oid, 0)) return; add_object_entry(&object->oid, object->type, name, 0); } else { @@ -3831,6 +3831,7 @@ static int pack_mtime_cmp(const void *_a, const void *_b) static void read_packs_list_from_stdin(struct rev_info *revs) { + struct packfile_store *packs = the_repository->objects->packfiles; struct strbuf buf = STRBUF_INIT; struct string_list include_packs = STRING_LIST_INIT_DUP; struct string_list exclude_packs = STRING_LIST_INIT_DUP; @@ -3855,7 +3856,7 @@ static void read_packs_list_from_stdin(struct rev_info *revs) string_list_sort(&exclude_packs); string_list_remove_duplicates(&exclude_packs, 0); - for (p = get_all_packs(the_repository); p; p = p->next) { + for (p = packfile_store_get_all_packs(packs); p; p = p->next) { const char *pack_name = pack_basename(p); if ((item = string_list_lookup(&include_packs, pack_name))) @@ -4076,6 +4077,7 @@ static void enumerate_cruft_objects(void) static void enumerate_and_traverse_cruft_objects(struct string_list *fresh_packs) { + struct packfile_store *packs = the_repository->objects->packfiles; struct packed_git *p; struct rev_info revs; int ret; @@ -4105,7 +4107,7 @@ static void enumerate_and_traverse_cruft_objects(struct string_list *fresh_packs * Re-mark only the fresh packs as kept so that objects in * unknown packs do not halt the reachability traversal early. */ - for (p = get_all_packs(the_repository); p; p = p->next) + for (p = packfile_store_get_all_packs(packs); p; p = p->next) p->pack_keep_in_core = 0; mark_pack_kept_in_core(fresh_packs, 1); @@ -4122,6 +4124,7 @@ static void enumerate_and_traverse_cruft_objects(struct string_list *fresh_packs static void read_cruft_objects(void) { + struct packfile_store *packs = the_repository->objects->packfiles; struct strbuf buf = STRBUF_INIT; struct string_list discard_packs = STRING_LIST_INIT_DUP; struct string_list fresh_packs = STRING_LIST_INIT_DUP; @@ -4142,7 +4145,7 @@ static void read_cruft_objects(void) string_list_sort(&discard_packs); string_list_sort(&fresh_packs); - for (p = get_all_packs(the_repository); p; p = p->next) { + for (p = packfile_store_get_all_packs(packs); p; p = p->next) { const char *pack_name = pack_basename(p); struct string_list_item *item; @@ -4390,11 +4393,12 @@ static void add_unreachable_loose_objects(struct rev_info *revs) static int has_sha1_pack_kept_or_nonlocal(const struct object_id *oid) { + struct packfile_store *packs = the_repository->objects->packfiles; static struct packed_git *last_found = (void *)1; struct packed_git *p; p = (last_found != (void *)1) ? last_found : - get_all_packs(the_repository); + packfile_store_get_all_packs(packs); while (p) { if ((!p->pack_local || p->pack_keep || @@ -4404,7 +4408,7 @@ static int has_sha1_pack_kept_or_nonlocal(const struct object_id *oid) return 1; } if (p == last_found) - p = get_all_packs(the_repository); + p = packfile_store_get_all_packs(packs); else p = p->next; if (p == last_found) @@ -4436,12 +4440,13 @@ static int loosened_object_can_be_discarded(const struct object_id *oid, static void loosen_unused_packed_objects(void) { + struct packfile_store *packs = the_repository->objects->packfiles; struct packed_git *p; uint32_t i; uint32_t loosened_objects_nr = 0; struct object_id oid; - for (p = get_all_packs(the_repository); p; p = p->next) { + for (p = packfile_store_get_all_packs(packs); p; p = p->next) { if (!p->pack_local || p->pack_keep || p->pack_keep_in_core) continue; @@ -4591,8 +4596,8 @@ static int add_objects_by_path(const char *path, /* Skip objects that do not exist locally. */ if ((exclude_promisor_objects || arg_missing_action != MA_ERROR) && - oid_object_info_extended(the_repository, oid, &oi, - OBJECT_INFO_FOR_PREFETCH) < 0) + odb_read_object_info_extended(the_repository->objects, oid, &oi, + OBJECT_INFO_FOR_PREFETCH) < 0) continue; exclude = is_oid_uninteresting(the_repository, oid); @@ -4650,7 +4655,7 @@ static void get_object_list_path_walk(struct rev_info *revs) die(_("failed to pack objects via path-walk")); } -static void get_object_list(struct rev_info *revs, int ac, const char **av) +static void get_object_list(struct rev_info *revs, struct strvec *argv) { struct setup_revision_opt s_r_opt = { .allow_exclude_promisor_objects = 1, @@ -4660,7 +4665,7 @@ static void get_object_list(struct rev_info *revs, int ac, const char **av) int save_warning; save_commit_buffer = 0; - setup_revisions(ac, av, revs, &s_r_opt); + setup_revisions_from_strvec(argv, revs, &s_r_opt); /* make sure shallows are read */ is_repository_shallow(the_repository); @@ -4742,12 +4747,13 @@ static void get_object_list(struct rev_info *revs, int ac, const char **av) static void add_extra_kept_packs(const struct string_list *names) { + struct packfile_store *packs = the_repository->objects->packfiles; struct packed_git *p; if (!names->nr) return; - for (p = get_all_packs(the_repository); p; p = p->next) { + for (p = packfile_store_get_all_packs(packs); p; p = p->next) { const char *name = basename(p->pack_name); int i; @@ -5185,8 +5191,10 @@ int cmd_pack_objects(int argc, add_extra_kept_packs(&keep_pack_list); if (ignore_packed_keep_on_disk) { + struct packfile_store *packs = the_repository->objects->packfiles; struct packed_git *p; - for (p = get_all_packs(the_repository); p; p = p->next) + + for (p = packfile_store_get_all_packs(packs); p; p = p->next) if (p->pack_local && p->pack_keep) break; if (!p) /* no keep-able packs found */ @@ -5198,8 +5206,10 @@ int cmd_pack_objects(int argc, * want to unset "local" based on looking at packs, as * it also covers non-local objects */ + struct packfile_store *packs = the_repository->objects->packfiles; struct packed_git *p; - for (p = get_all_packs(the_repository); p; p = p->next) { + + for (p = packfile_store_get_all_packs(packs); p; p = p->next) { if (!p->pack_local) { have_non_local_packs = 1; break; @@ -5229,7 +5239,7 @@ int cmd_pack_objects(int argc, revs.include_check = is_not_in_promisor_pack; revs.include_check_obj = is_not_in_promisor_pack_obj; } - get_object_list(&revs, rp.nr, rp.v); + get_object_list(&revs, &rp); release_revisions(&revs); } cleanup_preferred_base(); diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c index fe81c293e3..80743d8806 100644 --- a/builtin/pack-redundant.c +++ b/builtin/pack-redundant.c @@ -566,7 +566,8 @@ static struct pack_list * add_pack(struct packed_git *p) static struct pack_list * add_pack_file(const char *filename) { - struct packed_git *p = get_all_packs(the_repository); + struct packfile_store *packs = the_repository->objects->packfiles; + struct packed_git *p = packfile_store_get_all_packs(packs); if (strlen(filename) < 40) die("Bad pack filename: %s", filename); @@ -581,7 +582,8 @@ static struct pack_list * add_pack_file(const char *filename) static void load_all(void) { - struct packed_git *p = get_all_packs(the_repository); + struct packfile_store *packs = the_repository->objects->packfiles; + struct packed_git *p = packfile_store_get_all_packs(packs); while (p) { add_pack(p); @@ -626,7 +628,7 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix UNUSED, s } if (!i_still_use_this) - you_still_use_that("git pack-redundant"); + you_still_use_that("git pack-redundant", NULL); if (load_all_packs) load_all(); diff --git a/builtin/pack-refs.c b/builtin/pack-refs.c index 5e28d0f9e8..3446b84cda 100644 --- a/builtin/pack-refs.c +++ b/builtin/pack-refs.c @@ -1,60 +1,16 @@ #include "builtin.h" -#include "config.h" -#include "environment.h" #include "gettext.h" -#include "parse-options.h" -#include "refs.h" -#include "revision.h" - -static char const * const pack_refs_usage[] = { - N_("git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude <pattern>]"), - NULL -}; +#include "pack-refs.h" int cmd_pack_refs(int argc, const char **argv, const char *prefix, struct repository *repo) { - struct ref_exclusions excludes = REF_EXCLUSIONS_INIT; - struct string_list included_refs = STRING_LIST_INIT_NODUP; - struct pack_refs_opts pack_refs_opts = { - .exclusions = &excludes, - .includes = &included_refs, - .flags = PACK_REFS_PRUNE, - }; - struct string_list option_excluded_refs = STRING_LIST_INIT_NODUP; - struct string_list_item *item; - int pack_all = 0; - int ret; - - struct option opts[] = { - OPT_BOOL(0, "all", &pack_all, N_("pack everything")), - OPT_BIT(0, "prune", &pack_refs_opts.flags, N_("prune loose refs (default)"), PACK_REFS_PRUNE), - OPT_BIT(0, "auto", &pack_refs_opts.flags, N_("auto-pack refs as needed"), PACK_REFS_AUTO), - OPT_STRING_LIST(0, "include", pack_refs_opts.includes, N_("pattern"), - N_("references to include")), - OPT_STRING_LIST(0, "exclude", &option_excluded_refs, N_("pattern"), - N_("references to exclude")), - OPT_END(), + static char const * const pack_refs_usage[] = { + N_("git pack-refs " PACK_REFS_OPTS), + NULL }; - repo_config(repo, git_default_config, NULL); - if (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0)) - usage_with_options(pack_refs_usage, opts); - - for_each_string_list_item(item, &option_excluded_refs) - add_ref_exclusion(pack_refs_opts.exclusions, item->string); - - if (pack_all) - string_list_append(pack_refs_opts.includes, "*"); - - if (!pack_refs_opts.includes->nr) - string_list_append(pack_refs_opts.includes, "refs/tags/*"); - - ret = refs_pack_refs(get_main_ref_store(repo), &pack_refs_opts); - clear_ref_exclusions(&excludes); - string_list_clear(&included_refs, 0); - string_list_clear(&option_excluded_refs, 0); - return ret; + return pack_refs_core(argc, argv, prefix, repo, pack_refs_usage); } diff --git a/builtin/push.c b/builtin/push.c index d0794b7b30..5b6cebbb85 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -27,7 +27,7 @@ static const char * const push_usage[] = { NULL, }; -static int push_use_color = -1; +static enum git_colorbool push_use_color = GIT_COLOR_UNKNOWN; static char push_colors[][COLOR_MAXLEN] = { GIT_COLOR_RESET, GIT_COLOR_RED, /* ERROR */ diff --git a/builtin/range-diff.c b/builtin/range-diff.c index a563abff5f..1bc082a869 100644 --- a/builtin/range-diff.c +++ b/builtin/range-diff.c @@ -6,6 +6,8 @@ #include "parse-options.h" #include "range-diff.h" #include "config.h" +#include "parse.h" +#include "color.h" static const char * const builtin_range_diff_usage[] = { @@ -15,6 +17,21 @@ N_("git range-diff [<options>] <base> <old-tip> <new-tip>"), NULL }; +static int parse_max_memory(const struct option *opt, const char *arg, int unset) +{ + size_t *max_memory = opt->value; + uintmax_t val; + + if (unset) + return 0; + + if (!git_parse_unsigned(arg, &val, SIZE_MAX)) + return error(_("invalid max-memory value: %s"), arg); + + *max_memory = (size_t)val; + return 0; +} + int cmd_range_diff(int argc, const char **argv, const char *prefix, @@ -25,6 +42,7 @@ int cmd_range_diff(int argc, struct strvec diff_merges_arg = STRVEC_INIT; struct range_diff_options range_diff_opts = { .creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT, + .max_memory = RANGE_DIFF_MAX_MEMORY_DEFAULT, .diffopt = &diffopt, .other_arg = &other_arg }; @@ -40,6 +58,10 @@ int cmd_range_diff(int argc, PARSE_OPT_OPTARG), OPT_PASSTHRU_ARGV(0, "diff-merges", &diff_merges_arg, N_("style"), N_("passed to 'git log'"), 0), + OPT_CALLBACK(0, "max-memory", &range_diff_opts.max_memory, + N_("size"), + N_("maximum memory for cost matrix (default 4G)"), + parse_max_memory), OPT_PASSTHRU_ARGV(0, "remerge-diff", &diff_merges_arg, NULL, N_("passed to 'git log'"), PARSE_OPT_NOARG), OPT_BOOL(0, "left-only", &left_only, @@ -66,7 +88,7 @@ int cmd_range_diff(int argc, /* force color when --dual-color was used */ if (!simple_color) - diffopt.use_color = 1; + diffopt.use_color = GIT_COLOR_ALWAYS; /* If `--diff-merges` was specified, imply `--merges` */ if (diff_merges_arg.nr) { diff --git a/builtin/rebase.c b/builtin/rebase.c index 3c85768d29..c468828189 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -299,8 +299,7 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags) oid_to_hex(&opts->restrict_revision->object.oid)); ret = sequencer_make_script(the_repository, &todo_list.buf, - make_script_args.nr, make_script_args.v, - flags); + &make_script_args, flags); if (ret) { error(_("could not generate todo list")); goto cleanup; @@ -1242,6 +1241,9 @@ int cmd_rebase(int argc, builtin_rebase_usage, builtin_rebase_options); +#ifndef WITH_BREAKING_CHANGES + warn_on_auto_comment_char = true; +#endif /* !WITH_BREAKING_CHANGES */ prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 1113137a6f..c9288a9c7e 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -2389,7 +2389,7 @@ static const char *unpack(int err_fd, struct shallow_info *si) status = finish_command(&child); if (status) return "index-pack abnormal exit"; - reprepare_packed_git(the_repository); + odb_reprepare(the_repository->objects); } return NULL; } diff --git a/builtin/reflog.c b/builtin/reflog.c index 1db26aa65f..dcbfe89339 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -3,6 +3,8 @@ #include "builtin.h" #include "config.h" #include "gettext.h" +#include "hex.h" +#include "odb.h" #include "revision.h" #include "reachable.h" #include "wildmatch.h" @@ -17,21 +19,24 @@ #define BUILTIN_REFLOG_LIST_USAGE \ N_("git reflog list") -#define BUILTIN_REFLOG_EXPIRE_USAGE \ - N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \ - " [--rewrite] [--updateref] [--stale-fix]\n" \ - " [--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]") +#define BUILTIN_REFLOG_EXISTS_USAGE \ + N_("git reflog exists <ref>") + +#define BUILTIN_REFLOG_WRITE_USAGE \ + N_("git reflog write <ref> <old-oid> <new-oid> <message>") #define BUILTIN_REFLOG_DELETE_USAGE \ N_("git reflog delete [--rewrite] [--updateref]\n" \ " [--dry-run | -n] [--verbose] <ref>@{<specifier>}...") -#define BUILTIN_REFLOG_EXISTS_USAGE \ - N_("git reflog exists <ref>") - #define BUILTIN_REFLOG_DROP_USAGE \ N_("git reflog drop [--all [--single-worktree] | <refs>...]") +#define BUILTIN_REFLOG_EXPIRE_USAGE \ + N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \ + " [--rewrite] [--updateref] [--stale-fix]\n" \ + " [--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]") + static const char *const reflog_show_usage[] = { BUILTIN_REFLOG_SHOW_USAGE, NULL, @@ -42,9 +47,14 @@ static const char *const reflog_list_usage[] = { NULL, }; -static const char *const reflog_expire_usage[] = { - BUILTIN_REFLOG_EXPIRE_USAGE, - NULL +static const char *const reflog_exists_usage[] = { + BUILTIN_REFLOG_EXISTS_USAGE, + NULL, +}; + +static const char *const reflog_write_usage[] = { + BUILTIN_REFLOG_WRITE_USAGE, + NULL, }; static const char *const reflog_delete_usage[] = { @@ -52,23 +62,24 @@ static const char *const reflog_delete_usage[] = { NULL }; -static const char *const reflog_exists_usage[] = { - BUILTIN_REFLOG_EXISTS_USAGE, - NULL, -}; - static const char *const reflog_drop_usage[] = { BUILTIN_REFLOG_DROP_USAGE, NULL, }; +static const char *const reflog_expire_usage[] = { + BUILTIN_REFLOG_EXPIRE_USAGE, + NULL +}; + static const char *const reflog_usage[] = { BUILTIN_REFLOG_SHOW_USAGE, BUILTIN_REFLOG_LIST_USAGE, - BUILTIN_REFLOG_EXPIRE_USAGE, + BUILTIN_REFLOG_EXISTS_USAGE, + BUILTIN_REFLOG_WRITE_USAGE, BUILTIN_REFLOG_DELETE_USAGE, BUILTIN_REFLOG_DROP_USAGE, - BUILTIN_REFLOG_EXISTS_USAGE, + BUILTIN_REFLOG_EXPIRE_USAGE, NULL }; @@ -395,6 +406,61 @@ static int cmd_reflog_drop(int argc, const char **argv, const char *prefix, return ret; } +static int cmd_reflog_write(int argc, const char **argv, const char *prefix, + struct repository *repo) +{ + const struct option options[] = { + OPT_END() + }; + struct object_id old_oid, new_oid; + struct strbuf err = STRBUF_INIT; + struct ref_transaction *tx; + const char *ref, *message; + int ret; + + repo_config(repo, git_ident_config, NULL); + + argc = parse_options(argc, argv, prefix, options, reflog_write_usage, 0); + if (argc != 4) + usage_with_options(reflog_write_usage, options); + + ref = argv[0]; + if (!is_root_ref(ref) && check_refname_format(ref, 0)) + die(_("invalid reference name: %s"), ref); + + ret = get_oid_hex_algop(argv[1], &old_oid, repo->hash_algo); + if (ret) + die(_("invalid old object ID: '%s'"), argv[1]); + if (!is_null_oid(&old_oid) && !odb_has_object(repo->objects, &old_oid, 0)) + die(_("old object '%s' does not exist"), argv[1]); + + ret = get_oid_hex_algop(argv[2], &new_oid, repo->hash_algo); + if (ret) + die(_("invalid new object ID: '%s'"), argv[2]); + if (!is_null_oid(&new_oid) && !odb_has_object(repo->objects, &new_oid, 0)) + die(_("new object '%s' does not exist"), argv[2]); + + message = argv[3]; + + tx = ref_store_transaction_begin(get_main_ref_store(repo), 0, &err); + if (!tx) + die(_("cannot start transaction: %s"), err.buf); + + ret = ref_transaction_update_reflog(tx, ref, &new_oid, &old_oid, + git_committer_info(0), + message, 0, &err); + if (ret) + die(_("cannot queue reflog update: %s"), err.buf); + + ret = ref_transaction_commit(tx, &err); + if (ret) + die(_("cannot commit reflog update: %s"), err.buf); + + ref_transaction_free(tx); + strbuf_release(&err); + return 0; +} + /* * main "reflog" */ @@ -407,10 +473,11 @@ int cmd_reflog(int argc, struct option options[] = { OPT_SUBCOMMAND("show", &fn, cmd_reflog_show), OPT_SUBCOMMAND("list", &fn, cmd_reflog_list), - OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire), - OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete), OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists), + OPT_SUBCOMMAND("write", &fn, cmd_reflog_write), + OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete), OPT_SUBCOMMAND("drop", &fn, cmd_reflog_drop), + OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire), OPT_END() }; diff --git a/builtin/refs.c b/builtin/refs.c index c7ad0a2963..3064f888b2 100644 --- a/builtin/refs.c +++ b/builtin/refs.c @@ -2,10 +2,13 @@ #include "builtin.h" #include "config.h" #include "fsck.h" +#include "pack-refs.h" #include "parse-options.h" #include "refs.h" #include "strbuf.h" #include "worktree.h" +#include "for-each-ref.h" +#include "refs/refs-internal.h" #define REFS_MIGRATE_USAGE \ N_("git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]") @@ -13,6 +16,12 @@ #define REFS_VERIFY_USAGE \ N_("git refs verify [--strict] [--verbose]") +#define REFS_EXISTS_USAGE \ + N_("git refs exists <ref>") + +#define REFS_OPTIMIZE_USAGE \ + N_("git refs optimize " PACK_REFS_OPTS) + static int cmd_refs_migrate(int argc, const char **argv, const char *prefix, struct repository *repo UNUSED) { @@ -101,6 +110,70 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix, return ret; } +static int cmd_refs_list(int argc, const char **argv, const char *prefix, + struct repository *repo) +{ + static char const * const refs_list_usage[] = { + N_("git refs list " COMMON_USAGE_FOR_EACH_REF), + NULL + }; + + return for_each_ref_core(argc, argv, prefix, repo, refs_list_usage); +} + +static int cmd_refs_exists(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) +{ + struct strbuf unused_referent = STRBUF_INIT; + struct object_id unused_oid; + unsigned int unused_type; + int failure_errno = 0; + const char *ref; + int ret = 0; + const char * const exists_usage[] = { + REFS_EXISTS_USAGE, + NULL, + }; + struct option options[] = { + OPT_END(), + }; + + argc = parse_options(argc, argv, prefix, options, exists_usage, 0); + if (argc != 1) + die(_("'git refs exists' requires a reference")); + + ref = *argv++; + if (refs_read_raw_ref(get_main_ref_store(the_repository), ref, + &unused_oid, &unused_referent, &unused_type, + &failure_errno)) { + if (failure_errno == ENOENT || failure_errno == EISDIR) { + error(_("reference does not exist")); + ret = 2; + } else { + errno = failure_errno; + error_errno(_("failed to look up reference")); + ret = 1; + } + + goto out; + } + +out: + strbuf_release(&unused_referent); + return ret; +} + +static int cmd_refs_optimize(int argc, const char **argv, const char *prefix, + struct repository *repo) +{ + static char const * const refs_optimize_usage[] = { + REFS_OPTIMIZE_USAGE, + NULL + }; + + return pack_refs_core(argc, argv, prefix, repo, refs_optimize_usage); +} + int cmd_refs(int argc, const char **argv, const char *prefix, @@ -109,12 +182,18 @@ int cmd_refs(int argc, const char * const refs_usage[] = { REFS_MIGRATE_USAGE, REFS_VERIFY_USAGE, + "git refs list " COMMON_USAGE_FOR_EACH_REF, + REFS_EXISTS_USAGE, + REFS_OPTIMIZE_USAGE, NULL, }; parse_opt_subcommand_fn *fn = NULL; struct option opts[] = { OPT_SUBCOMMAND("migrate", &fn, cmd_refs_migrate), OPT_SUBCOMMAND("verify", &fn, cmd_refs_verify), + OPT_SUBCOMMAND("list", &fn, cmd_refs_list), + OPT_SUBCOMMAND("exists", &fn, cmd_refs_exists), + OPT_SUBCOMMAND("optimize", &fn, cmd_refs_optimize), OPT_END(), }; diff --git a/builtin/remote.c b/builtin/remote.c index 8961ae6a89..8a7ed4299a 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -1,9 +1,11 @@ #define USE_THE_REPOSITORY_VARIABLE -#define DISABLE_SIGN_COMPARE_WARNINGS #include "builtin.h" +#include "advice.h" #include "config.h" +#include "date.h" #include "gettext.h" +#include "ident.h" #include "parse-options.h" #include "path.h" #include "transport.h" @@ -182,7 +184,6 @@ static int add(int argc, const char **argv, const char *prefix, struct remote *remote; struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT; const char *name, *url; - int i; int result = 0; struct option options[] = { @@ -233,7 +234,7 @@ static int add(int argc, const char **argv, const char *prefix, strbuf_addf(&buf, "remote.%s.fetch", name); if (track.nr == 0) string_list_append(&track, "*"); - for (i = 0; i < track.nr; i++) { + for (size_t i = 0; i < track.nr; i++) { add_branch(buf.buf, track.items[i].string, name, mirror, &buf2); } @@ -612,53 +613,169 @@ static int add_branch_for_removal(const char *refname, struct rename_info { const char *old_name; const char *new_name; - struct string_list *remote_branches; - uint32_t symrefs_nr; + struct ref_transaction *transaction; + struct progress *progress; + struct strbuf *err; + uint32_t progress_nr; + uint64_t index; }; -static int read_remote_branches(const char *refname, const char *referent UNUSED, - const struct object_id *oid UNUSED, - int flags UNUSED, void *cb_data) +static void compute_renamed_ref(struct rename_info *rename, + const char *refname, + struct strbuf *out) +{ + strbuf_reset(out); + strbuf_addstr(out, refname); + strbuf_splice(out, strlen("refs/remotes/"), strlen(rename->old_name), + rename->new_name, strlen(rename->new_name)); +} + +static int rename_one_reflog_entry(const char *old_refname, + struct object_id *old_oid, + struct object_id *new_oid, + const char *committer, + timestamp_t timestamp, int tz, + const char *msg, void *cb_data) { struct rename_info *rename = cb_data; - struct strbuf buf = STRBUF_INIT; - struct string_list_item *item; - int flag; - const char *symref; - - strbuf_addf(&buf, "refs/remotes/%s/", rename->old_name); - if (starts_with(refname, buf.buf)) { - item = string_list_append(rename->remote_branches, refname); - symref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), - refname, RESOLVE_REF_READING, - NULL, &flag); - if (symref && (flag & REF_ISSYMREF)) { - item->util = xstrdup(symref); - rename->symrefs_nr++; - } else { - item->util = NULL; - } + struct strbuf new_refname = STRBUF_INIT; + struct strbuf identity = STRBUF_INIT; + struct strbuf name = STRBUF_INIT; + struct strbuf mail = STRBUF_INIT; + struct ident_split ident; + const char *date; + int error; + + compute_renamed_ref(rename, old_refname, &new_refname); + + if (split_ident_line(&ident, committer, strlen(committer)) < 0) { + error = -1; + goto out; } - strbuf_release(&buf); - return 0; + strbuf_add(&name, ident.name_begin, ident.name_end - ident.name_begin); + strbuf_add(&mail, ident.mail_begin, ident.mail_end - ident.mail_begin); + + date = show_date(timestamp, tz, DATE_MODE(NORMAL)); + strbuf_addstr(&identity, fmt_ident(name.buf, mail.buf, + WANT_BLANK_IDENT, date, 0)); + + error = ref_transaction_update_reflog(rename->transaction, new_refname.buf, + new_oid, old_oid, identity.buf, msg, + rename->index++, rename->err); + +out: + strbuf_release(&new_refname); + strbuf_release(&identity); + strbuf_release(&name); + strbuf_release(&mail); + return error; +} + +static int rename_one_reflog(const char *old_refname, + const struct object_id *old_oid, + struct rename_info *rename) +{ + struct strbuf new_refname = STRBUF_INIT; + struct strbuf message = STRBUF_INIT; + int error; + + if (!refs_reflog_exists(get_main_ref_store(the_repository), old_refname)) + return 0; + + error = refs_for_each_reflog_ent(get_main_ref_store(the_repository), + old_refname, rename_one_reflog_entry, rename); + if (error < 0) + goto out; + + compute_renamed_ref(rename, old_refname, &new_refname); + + /* + * Manually write the reflog entry for the now-renamed ref. We cannot + * rely on `rename_one_ref()` to do this for us as that would screw + * over order in which reflog entries are being written. + * + * Furthermore, we only append the entry in case the reference + * resolves. Missing references shouldn't have reflogs anyway. + */ + strbuf_addf(&message, "remote: renamed %s to %s", old_refname, + new_refname.buf); + + error = ref_transaction_update_reflog(rename->transaction, new_refname.buf, + old_oid, old_oid, git_committer_info(0), + message.buf, rename->index++, rename->err); + if (error < 0) + return error; + +out: + strbuf_release(&new_refname); + strbuf_release(&message); + return error; +} + +static int rename_one_ref(const char *old_refname, const char *referent, + const struct object_id *oid, + int flags, void *cb_data) +{ + struct strbuf new_referent = STRBUF_INIT; + struct strbuf new_refname = STRBUF_INIT; + struct rename_info *rename = cb_data; + int error; + + compute_renamed_ref(rename, old_refname, &new_refname); + + if (flags & REF_ISSYMREF) { + /* + * Stupidly enough `referent` is not pointing to the immediate + * target of a symref, but it's the recursively resolved value. + * So symrefs pointing to symrefs would be misresolved, and + * unborn symrefs don't have any value for the `referent` at all. + */ + referent = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), + old_refname, RESOLVE_REF_NO_RECURSE, + NULL, NULL); + compute_renamed_ref(rename, referent, &new_referent); + oid = NULL; + } + + error = ref_transaction_delete(rename->transaction, old_refname, + oid, referent, REF_NO_DEREF, NULL, rename->err); + if (error < 0) + goto out; + + error = ref_transaction_update(rename->transaction, new_refname.buf, oid, null_oid(the_hash_algo), + (flags & REF_ISSYMREF) ? new_referent.buf : NULL, NULL, + REF_SKIP_CREATE_REFLOG | REF_NO_DEREF | REF_SKIP_OID_VERIFICATION, + NULL, rename->err); + if (error < 0) + goto out; + + error = rename_one_reflog(old_refname, oid, rename); + if (error < 0) + goto out; + + display_progress(rename->progress, ++rename->progress_nr); + +out: + strbuf_release(&new_referent); + strbuf_release(&new_refname); + return error; } static int migrate_file(struct remote *remote) { struct strbuf buf = STRBUF_INIT; - int i; strbuf_addf(&buf, "remote.%s.url", remote->name); - for (i = 0; i < remote->url.nr; i++) + for (size_t i = 0; i < remote->url.nr; i++) repo_config_set_multivar(the_repository, buf.buf, remote->url.v[i], "^$", 0); strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.push", remote->name); - for (i = 0; i < remote->push.nr; i++) + for (int i = 0; i < remote->push.nr; i++) repo_config_set_multivar(the_repository, buf.buf, remote->push.items[i].raw, "^$", 0); strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.fetch", remote->name); - for (i = 0; i < remote->fetch.nr; i++) + for (int i = 0; i < remote->fetch.nr; i++) repo_config_set_multivar(the_repository, buf.buf, remote->fetch.items[i].raw, "^$", 0); #ifndef WITH_BREAKING_CHANGES if (remote->origin == REMOTE_REMOTES) @@ -730,6 +847,14 @@ static void handle_push_default(const char* old_name, const char* new_name) strbuf_release(&push_default.origin); } +static const char conflicting_remote_refs_advice[] = N_( + "The remote you are trying to rename has conflicting references in the\n" + "new target refspec. This is most likely caused by you trying to nest\n" + "a remote into itself, e.g. by renaming 'parent' into 'parent/child'\n" + "or by unnesting a remote, e.g. the other way round.\n" + "\n" + "If that is the case, you can address this by first renaming the\n" + "remote to a different name.\n"); static int mv(int argc, const char **argv, const char *prefix, struct repository *repo UNUSED) @@ -741,11 +866,11 @@ static int mv(int argc, const char **argv, const char *prefix, }; struct remote *oldremote, *newremote; struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT, - old_remote_context = STRBUF_INIT; - struct string_list remote_branches = STRING_LIST_INIT_DUP; - struct rename_info rename; - int i, refs_renamed_nr = 0, refspec_updated = 0; - struct progress *progress = NULL; + old_remote_context = STRBUF_INIT, err = STRBUF_INIT; + struct rename_info rename = { + .err = &err, + }; + int refspecs_need_update = 0; int result = 0; argc = parse_options(argc, argv, prefix, options, @@ -756,8 +881,6 @@ static int mv(int argc, const char **argv, const char *prefix, rename.old_name = argv[0]; rename.new_name = argv[1]; - rename.remote_branches = &remote_branches; - rename.symrefs_nr = 0; oldremote = remote_get(rename.old_name); if (!remote_is_configured(oldremote, 1)) { @@ -785,19 +908,50 @@ static int mv(int argc, const char **argv, const char *prefix, goto out; } + strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name); + + for (int i = 0; i < oldremote->fetch.nr && !refspecs_need_update; i++) + refspecs_need_update = !!strstr(oldremote->fetch.items[i].raw, + old_remote_context.buf); + + if (refspecs_need_update) { + rename.transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), + 0, &err); + if (!rename.transaction) + goto out; + + if (show_progress) + rename.progress = start_delayed_progress(the_repository, + _("Renaming remote references"), 0); + + strbuf_reset(&buf); + strbuf_addf(&buf, "refs/remotes/%s/", rename.old_name); + + result = refs_for_each_rawref_in(get_main_ref_store(the_repository), buf.buf, + rename_one_ref, &rename); + if (result < 0) + die(_("queueing remote ref renames failed: %s"), rename.err->buf); + + result = ref_transaction_prepare(rename.transaction, &err); + if (result < 0) { + error("renaming remote references failed: %s", err.buf); + if (result == REF_TRANSACTION_ERROR_NAME_CONFLICT) + advise(conflicting_remote_refs_advice); + die(NULL); + } + } + if (oldremote->fetch.nr) { strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.fetch", rename.new_name); repo_config_set_multivar(the_repository, buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE); - strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name); - for (i = 0; i < oldremote->fetch.nr; i++) { + for (int i = 0; i < oldremote->fetch.nr; i++) { char *ptr; strbuf_reset(&buf2); strbuf_addstr(&buf2, oldremote->fetch.items[i].raw); ptr = strstr(buf2.buf, old_remote_context.buf); if (ptr) { - refspec_updated = 1; strbuf_splice(&buf2, ptr-buf2.buf + strlen(":refs/remotes/"), strlen(rename.old_name), rename.new_name, @@ -813,7 +967,7 @@ static int mv(int argc, const char **argv, const char *prefix, } read_branches(); - for (i = 0; i < branch_list.nr; i++) { + for (size_t i = 0; i < branch_list.nr; i++) { struct string_list_item *item = branch_list.items + i; struct branch_info *info = item->util; if (info->remote_name && !strcmp(info->remote_name, rename.old_name)) { @@ -828,83 +982,23 @@ static int mv(int argc, const char **argv, const char *prefix, } } - if (!refspec_updated) - goto out; - - /* - * First remove symrefs, then rename the rest, finally create - * the new symrefs. - */ - refs_for_each_ref(get_main_ref_store(the_repository), - read_remote_branches, &rename); - if (show_progress) { - /* - * Count symrefs twice, since "renaming" them is done by - * deleting and recreating them in two separate passes. - */ - progress = start_progress(the_repository, - _("Renaming remote references"), - rename.remote_branches->nr + rename.symrefs_nr); - } - for (i = 0; i < remote_branches.nr; i++) { - struct string_list_item *item = remote_branches.items + i; - struct strbuf referent = STRBUF_INIT; - - if (refs_read_symbolic_ref(get_main_ref_store(the_repository), item->string, - &referent)) - continue; - if (refs_delete_ref(get_main_ref_store(the_repository), NULL, item->string, NULL, REF_NO_DEREF)) - die(_("deleting '%s' failed"), item->string); - - strbuf_release(&referent); - display_progress(progress, ++refs_renamed_nr); - } - for (i = 0; i < remote_branches.nr; i++) { - struct string_list_item *item = remote_branches.items + i; + if (refspecs_need_update) { + result = ref_transaction_commit(rename.transaction, &err); + if (result < 0) + die(_("renaming remote refs failed: %s"), rename.err->buf); - if (item->util) - continue; - strbuf_reset(&buf); - strbuf_addstr(&buf, item->string); - strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name), - rename.new_name, strlen(rename.new_name)); - strbuf_reset(&buf2); - strbuf_addf(&buf2, "remote: renamed %s to %s", - item->string, buf.buf); - if (refs_rename_ref(get_main_ref_store(the_repository), item->string, buf.buf, buf2.buf)) - die(_("renaming '%s' failed"), item->string); - display_progress(progress, ++refs_renamed_nr); - } - for (i = 0; i < remote_branches.nr; i++) { - struct string_list_item *item = remote_branches.items + i; + stop_progress(&rename.progress); - if (!item->util) - continue; - strbuf_reset(&buf); - strbuf_addstr(&buf, item->string); - strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name), - rename.new_name, strlen(rename.new_name)); - strbuf_reset(&buf2); - strbuf_addstr(&buf2, item->util); - strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old_name), - rename.new_name, strlen(rename.new_name)); - strbuf_reset(&buf3); - strbuf_addf(&buf3, "remote: renamed %s to %s", - item->string, buf.buf); - if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, buf3.buf)) - die(_("creating '%s' failed"), buf.buf); - display_progress(progress, ++refs_renamed_nr); + handle_push_default(rename.old_name, rename.new_name); } - stop_progress(&progress); - - handle_push_default(rename.old_name, rename.new_name); out: - string_list_clear(&remote_branches, 1); + ref_transaction_free(rename.transaction); strbuf_release(&old_remote_context); strbuf_release(&buf); strbuf_release(&buf2); strbuf_release(&buf3); + strbuf_release(&err); return result; } @@ -920,7 +1014,7 @@ static int rm(int argc, const char **argv, const char *prefix, struct string_list branches = STRING_LIST_INIT_DUP; struct string_list skipped = STRING_LIST_INIT_DUP; struct branches_for_remote cb_data; - int i, result; + int result; memset(&cb_data, 0, sizeof(cb_data)); cb_data.branches = &branches; @@ -942,7 +1036,7 @@ static int rm(int argc, const char **argv, const char *prefix, for_each_remote(add_known_remote, &known_remotes); read_branches(); - for (i = 0; i < branch_list.nr; i++) { + for (size_t i = 0; i < branch_list.nr; i++) { struct string_list_item *item = branch_list.items + i; struct branch_info *info = item->util; if (info->remote_name && !strcmp(info->remote_name, remote->name)) { @@ -988,7 +1082,7 @@ static int rm(int argc, const char **argv, const char *prefix, "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n" "to delete them, use:", skipped.nr)); - for (i = 0; i < skipped.nr; i++) + for (size_t i = 0; i < skipped.nr; i++) fprintf(stderr, " git branch -d %s\n", skipped.items[i].string); } @@ -1166,7 +1260,6 @@ static int show_local_info_item(struct string_list_item *item, void *cb_data) struct branch_info *branch_info = item->util; struct string_list *merge = &branch_info->merge; int width = show_info->width + 4; - int i; if (branch_info->rebase >= REBASE_TRUE && branch_info->merge.nr > 1) { error(_("invalid branch.%s.merge; cannot rebase onto > 1 branch"), @@ -1192,7 +1285,7 @@ static int show_local_info_item(struct string_list_item *item, void *cb_data) } else { printf_ln(_("merges with remote %s"), merge->items[0].string); } - for (i = 1; i < merge->nr; i++) + for (size_t i = 1; i < merge->nr; i++) printf(_("%-*s and with remote %s\n"), width, "", merge->items[i].string); @@ -1277,7 +1370,6 @@ static int get_one_entry(struct remote *remote, void *priv) struct string_list *list = priv; struct strbuf remote_info_buf = STRBUF_INIT; struct strvec *url; - int i; if (remote->url.nr > 0) { struct strbuf promisor_config = STRBUF_INIT; @@ -1294,8 +1386,7 @@ static int get_one_entry(struct remote *remote, void *priv) } else string_list_append(list, remote->name)->util = NULL; url = push_url_of_remote(remote); - for (i = 0; i < url->nr; i++) - { + for (size_t i = 0; i < url->nr; i++) { strbuf_addf(&remote_info_buf, "%s (push)", url->v[i]); string_list_append(list, remote->name)->util = strbuf_detach(&remote_info_buf, NULL); @@ -1312,10 +1403,8 @@ static int show_all(void) result = for_each_remote(get_one_entry, &list); if (!result) { - int i; - string_list_sort(&list); - for (i = 0; i < list.nr; i++) { + for (size_t i = 0; i < list.nr; i++) { struct string_list_item *item = list.items + i; if (verbose) printf("%s\t%s\n", item->string, @@ -1352,7 +1441,7 @@ static int show(int argc, const char **argv, const char *prefix, query_flag = (GET_REF_STATES | GET_HEAD_NAMES | GET_PUSH_REF_STATES); for (; argc; argc--, argv++) { - int i; + size_t i; struct strvec *url; get_remote_ref_states(*argv, &info.states, query_flag); @@ -1458,7 +1547,7 @@ static void report_set_head_auto(const char *remote, const char *head_name, static int set_head(int argc, const char **argv, const char *prefix, struct repository *repo UNUSED) { - int i, opt_a = 0, opt_d = 0, result = 0, was_detached; + int opt_a = 0, opt_d = 0, result = 0, was_detached; struct strbuf b_head = STRBUF_INIT, b_remote_head = STRBUF_INIT, b_local_head = STRBUF_INIT; char *head_name = NULL; @@ -1492,7 +1581,7 @@ static int set_head(int argc, const char **argv, const char *prefix, else if (states.heads.nr > 1) { result |= error(_("Multiple remote HEAD branches. " "Please choose one explicitly with:")); - for (i = 0; i < states.heads.nr; i++) + for (size_t i = 0; i < states.heads.nr; i++) fprintf(stderr, " git remote set-head %s %s\n", argv[0], states.heads.items[i].string); } else @@ -1717,7 +1806,7 @@ static int set_branches(int argc, const char **argv, const char *prefix, static int get_url(int argc, const char **argv, const char *prefix, struct repository *repo UNUSED) { - int i, push_mode = 0, all_mode = 0; + int push_mode = 0, all_mode = 0; const char *remotename = NULL; struct remote *remote; struct strvec *url; @@ -1745,7 +1834,7 @@ static int get_url(int argc, const char **argv, const char *prefix, url = push_mode ? push_url_of_remote(remote) : &remote->url; if (all_mode) { - for (i = 0; i < url->nr; i++) + for (size_t i = 0; i < url->nr; i++) printf_ln("%s", url->v[i]); } else { printf_ln("%s", url->v[0]); @@ -1757,7 +1846,7 @@ static int get_url(int argc, const char **argv, const char *prefix, static int set_url(int argc, const char **argv, const char *prefix, struct repository *repo UNUSED) { - int i, push_mode = 0, add_mode = 0, delete_mode = 0; + int push_mode = 0, add_mode = 0, delete_mode = 0; int matches = 0, negative_matches = 0; const char *remotename = NULL; const char *newurl = NULL; @@ -1821,7 +1910,7 @@ static int set_url(int argc, const char **argv, const char *prefix, if (regcomp(&old_regex, oldurl, REG_EXTENDED)) die(_("Invalid old URL pattern: %s"), oldurl); - for (i = 0; i < urlset->nr; i++) + for (size_t i = 0; i < urlset->nr; i++) if (!regexec(&old_regex, urlset->v[i], 0, NULL, 0)) matches++; else diff --git a/builtin/repack.c b/builtin/repack.c index a4def39197..e8730808c5 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -223,9 +223,10 @@ static void mark_packs_for_deletion(struct existing_packs *existing, static void remove_redundant_pack(const char *dir_name, const char *base_name) { struct strbuf buf = STRBUF_INIT; - struct multi_pack_index *m = get_multi_pack_index(the_repository->objects->sources); + struct odb_source *source = the_repository->objects->sources; + struct multi_pack_index *m = get_multi_pack_index(source); strbuf_addf(&buf, "%s.pack", base_name); - if (m && m->local && midx_contains_pack(m, buf.buf)) + if (m && source->local && midx_contains_pack(m, buf.buf)) clear_midx_file(the_repository); strbuf_insertf(&buf, 0, "%s/", dir_name); unlink_pack_path(buf.buf, 1); @@ -264,10 +265,11 @@ static void existing_packs_release(struct existing_packs *existing) static void collect_pack_filenames(struct existing_packs *existing, const struct string_list *extra_keep) { + struct packfile_store *packs = the_repository->objects->packfiles; struct packed_git *p; struct strbuf buf = STRBUF_INIT; - for (p = get_all_packs(the_repository); p; p = p->next) { + for (p = packfile_store_get_all_packs(packs); p; p = p->next) { int i; const char *base; @@ -496,10 +498,11 @@ static void init_pack_geometry(struct pack_geometry *geometry, struct existing_packs *existing, const struct pack_objects_args *args) { + struct packfile_store *packs = the_repository->objects->packfiles; struct packed_git *p; struct strbuf buf = STRBUF_INIT; - for (p = get_all_packs(the_repository); p; p = p->next) { + for (p = packfile_store_get_all_packs(packs); p; p = p->next) { if (args->local && !p->pack_local) /* * When asked to only repack local packfiles we skip @@ -1136,11 +1139,12 @@ static int write_filtered_pack(const struct pack_objects_args *args, static void combine_small_cruft_packs(FILE *in, size_t combine_cruft_below_size, struct existing_packs *existing) { + struct packfile_store *packs = the_repository->objects->packfiles; struct packed_git *p; struct strbuf buf = STRBUF_INIT; size_t i; - for (p = get_all_packs(the_repository); p; p = p->next) { + for (p = packfile_store_get_all_packs(packs); p; p = p->next) { if (!(p->is_cruft && p->pack_local)) continue; @@ -1684,7 +1688,7 @@ int cmd_repack(int argc, goto cleanup; } - reprepare_packed_git(the_repository); + odb_reprepare(the_repository->objects); if (delete_redundant) { int opts = 0; @@ -1711,7 +1715,7 @@ int cmd_repack(int argc, unsigned flags = 0; if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX_WRITE_INCREMENTAL, 0)) flags |= MIDX_WRITE_INCREMENTAL; - write_midx_file(the_repository, repo_get_object_directory(the_repository), + write_midx_file(the_repository->objects->sources, NULL, NULL, flags); } diff --git a/builtin/repo.c b/builtin/repo.c new file mode 100644 index 0000000000..bbb0966f2d --- /dev/null +++ b/builtin/repo.c @@ -0,0 +1,171 @@ +#define USE_THE_REPOSITORY_VARIABLE + +#include "builtin.h" +#include "environment.h" +#include "parse-options.h" +#include "quote.h" +#include "refs.h" +#include "strbuf.h" +#include "shallow.h" + +static const char *const repo_usage[] = { + "git repo info [--format=(keyvalue|nul)] [-z] [<key>...]", + NULL +}; + +typedef int get_value_fn(struct repository *repo, struct strbuf *buf); + +enum output_format { + FORMAT_KEYVALUE, + FORMAT_NUL_TERMINATED, +}; + +struct field { + const char *key; + get_value_fn *get_value; +}; + +static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf) +{ + strbuf_addstr(buf, is_bare_repository() ? "true" : "false"); + return 0; +} + +static int get_layout_shallow(struct repository *repo, struct strbuf *buf) +{ + strbuf_addstr(buf, + is_repository_shallow(repo) ? "true" : "false"); + return 0; +} + +static int get_object_format(struct repository *repo, struct strbuf *buf) +{ + strbuf_addstr(buf, repo->hash_algo->name); + return 0; +} + +static int get_references_format(struct repository *repo, struct strbuf *buf) +{ + strbuf_addstr(buf, + ref_storage_format_to_name(repo->ref_storage_format)); + return 0; +} + +/* repo_info_fields keys must be in lexicographical order */ +static const struct field repo_info_fields[] = { + { "layout.bare", get_layout_bare }, + { "layout.shallow", get_layout_shallow }, + { "object.format", get_object_format }, + { "references.format", get_references_format }, +}; + +static int repo_info_fields_cmp(const void *va, const void *vb) +{ + const struct field *a = va; + const struct field *b = vb; + + return strcmp(a->key, b->key); +} + +static get_value_fn *get_value_fn_for_key(const char *key) +{ + const struct field search_key = { key, NULL }; + const struct field *found = bsearch(&search_key, repo_info_fields, + ARRAY_SIZE(repo_info_fields), + sizeof(*found), + repo_info_fields_cmp); + return found ? found->get_value : NULL; +} + +static int print_fields(int argc, const char **argv, + struct repository *repo, + enum output_format format) +{ + int ret = 0; + struct strbuf valbuf = STRBUF_INIT; + struct strbuf quotbuf = STRBUF_INIT; + + for (int i = 0; i < argc; i++) { + get_value_fn *get_value; + const char *key = argv[i]; + + get_value = get_value_fn_for_key(key); + + if (!get_value) { + ret = error(_("key '%s' not found"), key); + continue; + } + + strbuf_reset(&valbuf); + strbuf_reset("buf); + + get_value(repo, &valbuf); + + switch (format) { + case FORMAT_KEYVALUE: + quote_c_style(valbuf.buf, "buf, NULL, 0); + printf("%s=%s\n", key, quotbuf.buf); + break; + case FORMAT_NUL_TERMINATED: + printf("%s\n%s%c", key, valbuf.buf, '\0'); + break; + default: + BUG("not a valid output format: %d", format); + } + } + + strbuf_release(&valbuf); + strbuf_release("buf); + return ret; +} + +static int parse_format_cb(const struct option *opt, + const char *arg, int unset UNUSED) +{ + enum output_format *format = opt->value; + + if (opt->short_name == 'z') + *format = FORMAT_NUL_TERMINATED; + else if (!strcmp(arg, "nul")) + *format = FORMAT_NUL_TERMINATED; + else if (!strcmp(arg, "keyvalue")) + *format = FORMAT_KEYVALUE; + else + die(_("invalid format '%s'"), arg); + + return 0; +} + +static int repo_info(int argc, const char **argv, const char *prefix, + struct repository *repo) +{ + enum output_format format = FORMAT_KEYVALUE; + struct option options[] = { + OPT_CALLBACK_F(0, "format", &format, N_("format"), + N_("output format"), + PARSE_OPT_NONEG, parse_format_cb), + OPT_CALLBACK_F('z', NULL, &format, NULL, + N_("synonym for --format=nul"), + PARSE_OPT_NONEG | PARSE_OPT_NOARG, + parse_format_cb), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, repo_usage, 0); + + return print_fields(argc, argv, repo, format); +} + +int cmd_repo(int argc, const char **argv, const char *prefix, + struct repository *repo) +{ + parse_opt_subcommand_fn *fn = NULL; + struct option options[] = { + OPT_SUBCOMMAND("info", &fn, repo_info), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, repo_usage, 0); + + return fn(argc, argv, prefix, repo); +} diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 44ff1b8342..9da92b990d 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -708,7 +708,6 @@ int cmd_rev_parse(int argc, struct object_id oid; unsigned int flags = 0; const char *name = NULL; - struct object_context unused; struct strbuf buf = STRBUF_INIT; int seen_end_of_options = 0; enum format_type format = FORMAT_DEFAULT; @@ -1141,9 +1140,8 @@ int cmd_rev_parse(int argc, name++; type = REVERSED; } - if (!get_oid_with_context(the_repository, name, - flags, &oid, &unused)) { - object_context_release(&unused); + if (!repo_get_oid_with_flags(the_repository, name, &oid, + flags)) { if (output_algo) repo_oid_to_algop(the_repository, &oid, output_algo, &oid); @@ -1153,7 +1151,6 @@ int cmd_rev_parse(int argc, show_rev(type, &oid, name); continue; } - object_context_release(&unused); if (verify) die_no_single_rev(quiet); if (has_dashdash) diff --git a/builtin/revert.c b/builtin/revert.c index c3f92b585d..bedc40f368 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -4,6 +4,7 @@ #include "builtin.h" #include "parse-options.h" #include "diff.h" +#include "environment.h" #include "gettext.h" #include "revision.h" #include "rerere.h" @@ -285,6 +286,9 @@ int cmd_revert(int argc, struct replay_opts opts = REPLAY_OPTS_INIT; int res; +#ifndef WITH_BREAKING_CHANGES + warn_on_auto_comment_char = true; +#endif /* !WITH_BREAKING_CHANGES */ opts.action = REPLAY_REVERT; sequencer_init_config(&opts); res = run_sequencer(argc, argv, prefix, &opts); @@ -302,6 +306,9 @@ struct repository *repo UNUSED) struct replay_opts opts = REPLAY_OPTS_INIT; int res; +#ifndef WITH_BREAKING_CHANGES + warn_on_auto_comment_char = true; +#endif /* !WITH_BREAKING_CHANGES */ opts.action = REPLAY_PICK; sequencer_init_config(&opts); res = run_sequencer(argc, argv, prefix, &opts); diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 1ab7db9d2c..441babf2e3 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -29,7 +29,7 @@ static const char*const show_branch_usage[] = { NULL }; -static int showbranch_use_color = -1; +static enum git_colorbool showbranch_use_color = GIT_COLOR_UNKNOWN; static struct strvec default_args = STRVEC_INIT; diff --git a/builtin/stash.c b/builtin/stash.c index 1977e50df2..948eba06fb 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -146,6 +146,11 @@ static const char * const git_stash_import_usage[] = { static const char ref_stash[] = "refs/stash"; static struct strbuf stash_index_path = STRBUF_INIT; +static int show_stat = 1; +static int show_patch; +static int show_include_untracked; +static int use_index; + /* * w_commit is set to the commit containing the working tree * b_commit is set to the base commit @@ -377,7 +382,7 @@ static int diff_tree_binary(struct strbuf *out, struct object_id *w_commit) * however it should be done together with apply_cached. */ cp.git_cmd = 1; - strvec_pushl(&cp.args, "diff-tree", "--binary", NULL); + strvec_pushl(&cp.args, "diff-tree", "--binary", "--no-color", NULL); strvec_pushf(&cp.args, "%s^2^..%s^2", w_commit_hex, w_commit_hex); return pipe_command(&cp, NULL, 0, out, 0, NULL, 0); @@ -717,7 +722,7 @@ static int apply_stash(int argc, const char **argv, const char *prefix, { int ret = -1; int quiet = 0; - int index = 0; + int index = use_index; struct stash_info info = STASH_INFO_INIT; struct option options[] = { OPT__QUIET(&quiet, N_("be quiet, only report errors")), @@ -738,7 +743,8 @@ cleanup: return ret; } -static int reject_reflog_ent(struct object_id *ooid UNUSED, +static int reject_reflog_ent(const char *refname UNUSED, + struct object_id *ooid UNUSED, struct object_id *noid UNUSED, const char *email UNUSED, timestamp_t timestamp UNUSED, @@ -814,7 +820,7 @@ static int pop_stash(int argc, const char **argv, const char *prefix, struct repository *repo UNUSED) { int ret = -1; - int index = 0; + int index = use_index; int quiet = 0; struct stash_info info = STASH_INFO_INIT; struct option options[] = { @@ -904,10 +910,6 @@ static int list_stash(int argc, const char **argv, const char *prefix, return run_command(&cp); } -static int show_stat = 1; -static int show_patch; -static int show_include_untracked; - static int git_stash_config(const char *var, const char *value, const struct config_context *ctx, void *cb) { @@ -923,6 +925,10 @@ static int git_stash_config(const char *var, const char *value, show_include_untracked = git_config_bool(var, value); return 0; } + if (!strcmp(var, "stash.index")) { + use_index = git_config_bool(var, value); + return 0; + } return git_diff_basic_config(var, value, ctx, cb); } @@ -1014,8 +1020,8 @@ static int show_stash(int argc, const char **argv, const char *prefix, } } - argc = setup_revisions(revision_args.nr, revision_args.v, &rev, NULL); - if (argc > 1) + setup_revisions_from_strvec(&revision_args, &rev, NULL); + if (revision_args.nr > 1) goto usage; if (!rev.diffopt.output_format) { rev.diffopt.output_format = DIFF_FORMAT_PATCH; @@ -1088,7 +1094,6 @@ static int store_stash(int argc, const char **argv, const char *prefix, int quiet = 0; const char *stash_msg = NULL; struct object_id obj; - struct object_context dummy = {0}; struct option options[] = { OPT__QUIET(&quiet, N_("be quiet")), OPT_STRING('m', "message", &stash_msg, "message", @@ -1108,9 +1113,8 @@ static int store_stash(int argc, const char **argv, const char *prefix, return -1; } - if (get_oid_with_context(the_repository, - argv[0], quiet ? GET_OID_QUIETLY : 0, &obj, - &dummy)) { + if (repo_get_oid_with_flags(the_repository, argv[0], &obj, + quiet ? GET_OID_QUIETLY : 0)) { if (!quiet) fprintf_ln(stderr, _("Cannot update %s with %s"), ref_stash, argv[0]); @@ -1121,7 +1125,6 @@ static int store_stash(int argc, const char **argv, const char *prefix, ret = do_store_stash(&obj, stash_msg, quiet); out: - object_context_release(&dummy); return ret; } @@ -1283,6 +1286,7 @@ static int stash_staged(struct stash_info *info, struct strbuf *out_patch, cp_diff_tree.git_cmd = 1; strvec_pushl(&cp_diff_tree.args, "diff-tree", "-p", "--binary", + "--no-color", "-U1", "HEAD", oid_to_hex(&info->w_tree), "--", NULL); if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) { ret = -1; @@ -1345,6 +1349,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps, cp_diff_tree.git_cmd = 1; strvec_pushl(&cp_diff_tree.args, "diff-tree", "-p", "-U1", "HEAD", + "--no-color", oid_to_hex(&info->w_tree), "--", NULL); if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) { ret = -1; @@ -1719,6 +1724,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q cp_diff.git_cmd = 1; strvec_pushl(&cp_diff.args, "diff-index", "-p", + "--no-color", "--cached", "--binary", "HEAD", "--", NULL); add_pathspecs(&cp_diff.args, ps); @@ -2207,7 +2213,8 @@ struct stash_entry_data { size_t count; }; -static int collect_stash_entries(struct object_id *old_oid UNUSED, +static int collect_stash_entries(const char *refname UNUSED, + struct object_id *old_oid UNUSED, struct object_id *new_oid, const char *committer UNUSED, timestamp_t timestamp UNUSED, @@ -2233,7 +2240,6 @@ static int do_export_stash(struct repository *r, const char **argv) { struct object_id base; - struct object_context unused; struct commit *prev; struct commit_list *items = NULL, **iter = &items, *cur; int res = 0; @@ -2267,9 +2273,9 @@ static int do_export_stash(struct repository *r, struct commit *stash; if (parse_stash_revision(&revision, argv[i], 1) || - get_oid_with_context(r, revision.buf, - GET_OID_QUIETLY | GET_OID_GENTLY, - &oid, &unused)) { + repo_get_oid_with_flags(r, revision.buf, &oid, + GET_OID_QUIETLY | + GET_OID_GENTLY)) { res = error(_("unable to find stash entry %s"), argv[i]); goto out; } diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 07a1935cbe..fcd73abe53 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -616,9 +616,6 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, struct rev_info rev = REV_INFO_INIT; struct strbuf buf = STRBUF_INIT; const char *git_dir; - struct setup_revision_opt opt = { - .free_removed_argv_elements = 1, - }; if (validate_submodule_path(path) < 0) die(NULL); @@ -655,7 +652,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, repo_init_revisions(the_repository, &rev, NULL); rev.abbrev = 0; - setup_revisions(diff_files_args.nr, diff_files_args.v, &rev, &opt); + setup_revisions_from_strvec(&diff_files_args, &rev, NULL); run_diff_files(&rev, 0); if (!diff_result_code(&rev)) { @@ -1094,9 +1091,6 @@ static int compute_summary_module_list(struct object_id *head_oid, { struct strvec diff_args = STRVEC_INIT; struct rev_info rev; - struct setup_revision_opt opt = { - .free_removed_argv_elements = 1, - }; struct module_cb_list list = MODULE_CB_LIST_INIT; int ret = 0; @@ -1114,7 +1108,7 @@ static int compute_summary_module_list(struct object_id *head_oid, repo_init_revisions(the_repository, &rev, info->prefix); rev.abbrev = 0; precompose_argv_prefix(diff_args.nr, diff_args.v, NULL); - setup_revisions(diff_args.nr, diff_args.v, &rev, &opt); + setup_revisions_from_strvec(&diff_args, &rev, NULL); rev.diffopt.output_format = DIFF_FORMAT_NO_OUTPUT | DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = submodule_summary_callback; rev.diffopt.format_callback_data = &list; diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 7ae7c82b6c..ef79e43715 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -2,7 +2,6 @@ #define DISABLE_SIGN_COMPARE_WARNINGS #include "builtin.h" -#include "bulk-checkin.h" #include "config.h" #include "environment.h" #include "gettext.h" @@ -584,6 +583,7 @@ static void unpack_all(void) { int i; unsigned char *hdr = fill(sizeof(struct pack_header)); + struct odb_transaction *transaction; if (get_be32(hdr) != PACK_SIGNATURE) die("bad pack file"); @@ -599,12 +599,12 @@ static void unpack_all(void) progress = start_progress(the_repository, _("Unpacking objects"), nr_objects); CALLOC_ARRAY(obj_list, nr_objects); - begin_odb_transaction(); + transaction = odb_transaction_begin(the_repository->objects); for (i = 0; i < nr_objects; i++) { unpack_one(i); display_progress(progress, i + 1); } - end_odb_transaction(); + odb_transaction_commit(transaction); stop_progress(&progress); if (delta_list) diff --git a/builtin/update-index.c b/builtin/update-index.c index 2380f3ccd6..8a5907767b 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -8,7 +8,6 @@ #define DISABLE_SIGN_COMPARE_WARNINGS #include "builtin.h" -#include "bulk-checkin.h" #include "config.h" #include "environment.h" #include "gettext.h" @@ -19,6 +18,7 @@ #include "cache-tree.h" #include "tree-walk.h" #include "object-file.h" +#include "odb.h" #include "refs.h" #include "resolve-undo.h" #include "parse-options.h" @@ -70,14 +70,6 @@ static void report(const char *fmt, ...) if (!verbose) return; - /* - * It is possible, though unlikely, that a caller could use the verbose - * output to synchronize with addition of objects to the object - * database. The current implementation of ODB transactions leaves - * objects invisible while a transaction is active, so flush the - * transaction here before reporting a change made by update-index. - */ - flush_odb_transaction(); va_start(vp, fmt); vprintf(fmt, vp); putchar('\n'); @@ -940,6 +932,7 @@ int cmd_update_index(int argc, strbuf_getline_fn getline_fn; int parseopt_state = PARSE_OPT_UNKNOWN; struct repository *r = the_repository; + struct odb_transaction *transaction; struct option options[] = { OPT_BIT('q', NULL, &refresh_args.flags, N_("continue refresh even when index needs update"), @@ -1130,7 +1123,7 @@ int cmd_update_index(int argc, * Allow the object layer to optimize adding multiple objects in * a batch. */ - begin_odb_transaction(); + transaction = odb_transaction_begin(the_repository->objects); while (ctx.argc) { if (parseopt_state != PARSE_OPT_DONE) parseopt_state = parse_options_step(&ctx, options, @@ -1149,6 +1142,21 @@ int cmd_update_index(int argc, const char *path = ctx.argv[0]; char *p; + /* + * It is possible, though unlikely, that a caller could + * use the verbose output to synchronize with addition + * of objects to the object database. The current + * implementation of ODB transactions leaves objects + * invisible while a transaction is active, so end the + * transaction here early before processing the next + * update. All further updates are performed outside of + * a transaction. + */ + if (transaction && verbose) { + odb_transaction_commit(transaction); + transaction = NULL; + } + setup_work_tree(); p = prefix_path(prefix, prefix_length, path); update_one(p); @@ -1213,7 +1221,7 @@ int cmd_update_index(int argc, /* * By now we have added all of the new objects */ - end_odb_transaction(); + odb_transaction_commit(transaction); if (split_index > 0) { if (repo_config_get_split_index(the_repository) == 0) diff --git a/builtin/var.c b/builtin/var.c index a2d790d453..cc3a43cde2 100644 --- a/builtin/var.c +++ b/builtin/var.c @@ -182,7 +182,7 @@ static void list_vars(void) if (ptr->multivalued && *val) { struct string_list list = STRING_LIST_INIT_DUP; - string_list_split(&list, val, '\n', -1); + string_list_split(&list, val, "\n", -1); for (size_t i = 0; i < list.nr; i++) printf("%s=%s\n", ptr->name, list.items[i].string); string_list_clear(&list, 0); |
