diff options
Diffstat (limited to 'builtin')
122 files changed, 5632 insertions, 3639 deletions
diff --git a/builtin/add.c b/builtin/add.c index 61dd386d10..d46e4d10e9 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -4,18 +4,24 @@ * Copyright (C) 2006 Linus Torvalds */ #define USE_THE_INDEX_VARIABLE -#include "cache.h" -#include "config.h" #include "builtin.h" +#include "advice.h" +#include "config.h" #include "lockfile.h" +#include "editor.h" #include "dir.h" +#include "gettext.h" #include "pathspec.h" #include "exec-cmd.h" #include "cache-tree.h" #include "run-command.h" #include "parse-options.h" +#include "path.h" +#include "preload-index.h" #include "diff.h" #include "diffcore.h" +#include "read-cache.h" +#include "repository.h" #include "revision.h" #include "bulk-checkin.h" #include "strvec.h" @@ -33,11 +39,6 @@ static int pathspec_file_nul; static int include_sparse; static const char *pathspec_from_file; -struct update_callback_data { - int flags; - int add_errors; -}; - static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only) { int i, ret = 0; @@ -66,95 +67,6 @@ static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only) return ret; } -static int fix_unmerged_status(struct diff_filepair *p, - struct update_callback_data *data) -{ - if (p->status != DIFF_STATUS_UNMERGED) - return p->status; - if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL) && !p->two->mode) - /* - * This is not an explicit add request, and the - * path is missing from the working tree (deleted) - */ - return DIFF_STATUS_DELETED; - else - /* - * Either an explicit add request, or path exists - * in the working tree. An attempt to explicitly - * add a path that does not exist in the working tree - * will be caught as an error by the caller immediately. - */ - return DIFF_STATUS_MODIFIED; -} - -static void update_callback(struct diff_queue_struct *q, - struct diff_options *opt UNUSED, void *cbdata) -{ - int i; - struct update_callback_data *data = cbdata; - - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - const char *path = p->one->path; - - if (!include_sparse && !path_in_sparse_checkout(path, &the_index)) - continue; - - switch (fix_unmerged_status(p, data)) { - default: - die(_("unexpected diff status %c"), p->status); - case DIFF_STATUS_MODIFIED: - case DIFF_STATUS_TYPE_CHANGED: - if (add_file_to_index(&the_index, path, data->flags)) { - if (!(data->flags & ADD_CACHE_IGNORE_ERRORS)) - die(_("updating files failed")); - data->add_errors++; - } - break; - case DIFF_STATUS_DELETED: - if (data->flags & ADD_CACHE_IGNORE_REMOVAL) - break; - if (!(data->flags & ADD_CACHE_PRETEND)) - remove_file_from_index(&the_index, path); - if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE)) - printf(_("remove '%s'\n"), path); - break; - } - } -} - -int add_files_to_cache(const char *prefix, - const struct pathspec *pathspec, int flags) -{ - struct update_callback_data data; - struct rev_info rev; - - memset(&data, 0, sizeof(data)); - data.flags = flags; - - repo_init_revisions(the_repository, &rev, prefix); - setup_revisions(0, NULL, &rev, NULL); - if (pathspec) - copy_pathspec(&rev.prune_data, pathspec); - rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; - rev.diffopt.format_callback = update_callback; - rev.diffopt.format_callback_data = &data; - rev.diffopt.flags.override_submodule_config = 1; - rev.max_count = 0; /* do not compare unmerged paths with stage #2 */ - - /* - * Use an ODB transaction to optimize adding multiple objects. - * This function is invoked from commands other than 'add', which - * may not have their own transaction active. - */ - begin_odb_transaction(); - run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); - end_odb_transaction(); - - release_revisions(&rev); - return !!data.add_errors; -} - static int renormalize_tracked_files(const struct pathspec *pathspec, int flags) { int i, retval = 0; @@ -270,7 +182,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix) git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ if (repo_read_index(the_repository) < 0) - die(_("Could not read the index")); + die(_("could not read the index")); repo_init_revisions(the_repository, &rev, prefix); rev.diffopt.context = 7; @@ -282,22 +194,21 @@ static int edit_patch(int argc, const char **argv, const char *prefix) out = xopen(file, O_CREAT | O_WRONLY | O_TRUNC, 0666); rev.diffopt.file = xfdopen(out, "w"); rev.diffopt.close_file = 1; - if (run_diff_files(&rev, 0)) - die(_("Could not write patch")); + run_diff_files(&rev, 0); if (launch_editor(file, NULL, NULL)) die(_("editing patch failed")); if (stat(file, &st)) - die_errno(_("Could not stat '%s'"), file); + die_errno(_("could not stat '%s'"), file); if (!st.st_size) - die(_("Empty patch. Aborted.")); + die(_("empty patch. aborted")); child.git_cmd = 1; strvec_pushl(&child.args, "apply", "--recount", "--cached", file, NULL); if (run_command(&child)) - die(_("Could not apply '%s'"), file); + die(_("could not apply '%s'"), file); unlink(file); free(file); @@ -320,6 +231,8 @@ static char *chmod_arg; static int ignore_removal_cb(const struct option *opt, const char *arg, int unset) { + BUG_ON_OPT_ARG(arg); + /* if we are told to ignore, we are not adding removals */ *(int *)opt->value = !unset ? 0 : 1; return 0; @@ -354,7 +267,8 @@ static struct option builtin_add_options[] = { OPT_END(), }; -static int add_config(const char *var, const char *value, void *cb) +static int add_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { if (!strcmp(var, "add.ignoreerrors") || !strcmp(var, "add.ignore-errors")) { @@ -362,7 +276,10 @@ static int add_config(const char *var, const char *value, void *cb) return 0; } - return git_default_config(var, value, cb); + if (git_color_config(var, value, cb) < 0) + return -1; + + return git_default_config(var, value, ctx, cb); } static const char embedded_advice[] = N_( @@ -507,7 +424,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) * Check the "pathspec '%s' did not match any files" block * below before enabling new magic. */ - parse_pathspec(&pathspec, PATHSPEC_ATTR, + parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_FULL | PATHSPEC_SYMLINK_LEADING_PATH, prefix, argv); @@ -516,7 +433,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (pathspec.nr) die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file"); - parse_pathspec_file(&pathspec, PATHSPEC_ATTR, + parse_pathspec_file(&pathspec, 0, PATHSPEC_PREFER_FULL | PATHSPEC_SYMLINK_LEADING_PATH, prefix, pathspec_from_file, pathspec_file_nul); @@ -587,7 +504,8 @@ int cmd_add(int argc, const char **argv, const char *prefix) PATHSPEC_LITERAL | PATHSPEC_GLOB | PATHSPEC_ICASE | - PATHSPEC_EXCLUDE); + PATHSPEC_EXCLUDE | + PATHSPEC_ATTR); for (i = 0; i < pathspec.nr; i++) { const char *path = pathspec.items[i].match; @@ -637,7 +555,9 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (add_renormalize) exit_status |= renormalize_tracked_files(&pathspec, flags); else - exit_status |= add_files_to_cache(prefix, &pathspec, flags); + exit_status |= add_files_to_cache(the_repository, prefix, + &pathspec, include_sparse, + flags); if (add_new_files) exit_status |= add_files(&dir, flags); @@ -649,7 +569,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) finish: if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK | SKIP_IF_UNCHANGED)) - die(_("Unable to write new index file")); + die(_("unable to write new index file")); dir_clear(&dir); clear_pathspec(&pathspec); diff --git a/builtin/am.c b/builtin/am.c index e0848ddadf..9f084d58bc 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -4,10 +4,15 @@ * Based on git-am.sh by Junio C Hamano. */ #define USE_THE_INDEX_VARIABLE -#include "cache.h" -#include "config.h" #include "builtin.h" +#include "abspath.h" +#include "advice.h" +#include "config.h" +#include "editor.h" +#include "environment.h" #include "exec-cmd.h" +#include "gettext.h" +#include "hex.h" #include "parse-options.h" #include "dir.h" #include "run-command.h" @@ -22,6 +27,8 @@ #include "diffcore.h" #include "unpack-trees.h" #include "branch.h" +#include "object-name.h" +#include "preload-index.h" #include "sequencer.h" #include "revision.h" #include "merge-recursive.h" @@ -33,6 +40,8 @@ #include "apply.h" #include "string-list.h" #include "packfile.h" +#include "pager.h" +#include "path.h" #include "repository.h" #include "pretty.h" @@ -83,9 +92,16 @@ enum signoff_type { SIGNOFF_EXPLICIT /* --signoff was set on the command-line */ }; -enum show_patch_type { - SHOW_PATCH_RAW = 0, - SHOW_PATCH_DIFF = 1, +enum resume_type { + RESUME_FALSE = 0, + RESUME_APPLY, + RESUME_RESOLVED, + RESUME_SKIP, + RESUME_ABORT, + RESUME_QUIT, + RESUME_SHOW_PATCH_RAW, + RESUME_SHOW_PATCH_DIFF, + RESUME_ALLOW_EMPTY, }; enum empty_action { @@ -777,7 +793,7 @@ static int split_mail_conv(mail_conv_fn fn, struct am_state *state, * A split_mail_conv() callback that converts an StGit patch to an RFC2822 * message suitable for parsing with git-mailinfo. */ -static int stgit_patch_to_mail(FILE *out, FILE *in, int keep_cr) +static int stgit_patch_to_mail(FILE *out, FILE *in, int keep_cr UNUSED) { struct strbuf sb = STRBUF_INIT; int subject_printed = 0; @@ -860,7 +876,7 @@ static int split_mail_stgit_series(struct am_state *state, const char **paths, * A split_patches_conv() callback that converts a mercurial patch to a RFC2822 * message suitable for parsing with git-mailinfo. */ -static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr) +static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr UNUSED) { struct strbuf sb = STRBUF_INIT; int rc = 0; @@ -1066,7 +1082,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format, else write_state_text(state, "applying", ""); - if (!get_oid("HEAD", &curr_head)) { + if (!repo_get_oid(the_repository, "HEAD", &curr_head)) { write_state_text(state, "abort-safety", oid_to_hex(&curr_head)); if (!state->rebasing) update_ref("am", "ORIG_HEAD", &curr_head, NULL, 0, @@ -1109,7 +1125,7 @@ static void am_next(struct am_state *state) unlink(am_path(state, "original-commit")); delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF); - if (!get_oid("HEAD", &head)) + if (!repo_get_oid(the_repository, "HEAD", &head)) write_state_text(state, "abort-safety", oid_to_hex(&head)); else write_state_text(state, "abort-safety", ""); @@ -1274,7 +1290,7 @@ static int parse_mail(struct am_state *state, const char *mail) strbuf_addstr(&msg, "\n\n"); strbuf_addbuf(&msg, &mi.log_message); - strbuf_stripspace(&msg, 0); + strbuf_stripspace(&msg, '\0'); assert(!state->author_name); state->author_name = strbuf_detach(&author_name, NULL); @@ -1329,7 +1345,8 @@ static void get_commit_info(struct am_state *state, struct commit *commit) size_t ident_len; struct ident_split id; - buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding()); + buffer = repo_logmsg_reencode(the_repository, commit, NULL, + get_commit_output_encoding()); ident_line = find_commit_header(buffer, "author", &ident_len); if (!ident_line) @@ -1361,7 +1378,7 @@ static void get_commit_info(struct am_state *state, struct commit *commit) die(_("unable to parse commit %s"), oid_to_hex(&commit->object.oid)); state->msg = xstrdup(msg + 2); state->msg_len = strlen(state->msg); - unuse_commit_buffer(commit, buffer); + repo_unuse_commit_buffer(the_repository, commit, buffer); } /** @@ -1402,9 +1419,9 @@ static void write_index_patch(const struct am_state *state) struct rev_info rev_info; FILE *fp; - if (!get_oid("HEAD", &head)) { + if (!repo_get_oid(the_repository, "HEAD", &head)) { struct commit *commit = lookup_commit_or_die(&head, "HEAD"); - tree = get_commit_tree(commit); + tree = repo_get_commit_tree(the_repository, commit); } else tree = lookup_tree(the_repository, the_repository->hash_algo->empty_tree); @@ -1420,7 +1437,7 @@ static void write_index_patch(const struct am_state *state) rev_info.diffopt.close_file = 1; add_pending_object(&rev_info, &tree->object, ""); diff_setup_done(&rev_info.diffopt); - run_diff_index(&rev_info, 1); + run_diff_index(&rev_info, DIFF_INDEX_CACHED); release_revisions(&rev_info); } @@ -1556,7 +1573,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa struct commit *result; char *their_tree_name; - if (get_oid("HEAD", &our_tree) < 0) + if (repo_get_oid(the_repository, "HEAD", &our_tree) < 0) oidcpy(&our_tree, the_hash_algo->empty_tree); if (build_fake_ancestor(state, index_path)) @@ -1583,7 +1600,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa rev_info.diffopt.filter |= diff_filter_bit('M'); add_pending_oid(&rev_info, "HEAD", &our_tree, 0); diff_setup_done(&rev_info.diffopt); - run_diff_index(&rev_info, 1); + run_diff_index(&rev_info, DIFF_INDEX_CACHED); release_revisions(&rev_info); } @@ -1646,7 +1663,7 @@ static void do_commit(const struct am_state *state) if (write_index_as_tree(&tree, &the_index, get_index_file(), 0, NULL)) die(_("git write-tree failed to write a tree")); - if (!get_oid_commit("HEAD", &parent)) { + if (!repo_get_oid_commit(the_repository, "HEAD", &parent)) { old_oid = &parent; commit_list_insert(lookup_commit(the_repository, &parent), &parents); @@ -2088,7 +2105,7 @@ static void am_skip(struct am_state *state) am_rerere_clear(); - if (get_oid("HEAD", &head)) + if (repo_get_oid(the_repository, "HEAD", &head)) oidcpy(&head, the_hash_algo->empty_tree); if (clean_index(&head, &head)) @@ -2130,7 +2147,7 @@ static int safe_to_abort(const struct am_state *state) oidclr(&abort_safety); strbuf_release(&sb); - if (get_oid("HEAD", &head)) + if (repo_get_oid(the_repository, "HEAD", &head)) oidclr(&head); if (oideq(&head, &abort_safety)) @@ -2163,7 +2180,7 @@ static void am_abort(struct am_state *state) if (!has_curr_head) oidcpy(&curr_head, the_hash_algo->empty_tree); - has_orig_head = !get_oid("ORIG_HEAD", &orig_head); + has_orig_head = !repo_get_oid(the_repository, "ORIG_HEAD", &orig_head); if (!has_orig_head) oidcpy(&orig_head, the_hash_algo->empty_tree); @@ -2181,7 +2198,7 @@ static void am_abort(struct am_state *state) am_destroy(state); } -static int show_patch(struct am_state *state, enum show_patch_type sub_mode) +static int show_patch(struct am_state *state, enum resume_type resume_mode) { struct strbuf sb = STRBUF_INIT; const char *patch_path; @@ -2196,11 +2213,11 @@ static int show_patch(struct am_state *state, enum show_patch_type sub_mode) return run_command(&cmd); } - switch (sub_mode) { - case SHOW_PATCH_RAW: + switch (resume_mode) { + case RESUME_SHOW_PATCH_RAW: patch_path = am_path(state, msgnum(state)); break; - case SHOW_PATCH_DIFF: + case RESUME_SHOW_PATCH_DIFF: patch_path = am_path(state, "patch"); break; default: @@ -2247,77 +2264,35 @@ static int parse_opt_patchformat(const struct option *opt, const char *arg, int return 0; } -enum resume_type { - RESUME_FALSE = 0, - RESUME_APPLY, - RESUME_RESOLVED, - RESUME_SKIP, - RESUME_ABORT, - RESUME_QUIT, - RESUME_SHOW_PATCH, - RESUME_ALLOW_EMPTY, -}; - -struct resume_mode { - enum resume_type mode; - enum show_patch_type sub_mode; -}; - static int parse_opt_show_current_patch(const struct option *opt, const char *arg, int unset) { int *opt_value = opt->value; - struct resume_mode *resume = container_of(opt_value, struct resume_mode, mode); + BUG_ON_OPT_NEG(unset); + + if (!arg) + *opt_value = opt->defval; + else if (!strcmp(arg, "raw")) + *opt_value = RESUME_SHOW_PATCH_RAW; + else if (!strcmp(arg, "diff")) + *opt_value = RESUME_SHOW_PATCH_DIFF; /* * Please update $__git_showcurrentpatch in git-completion.bash * when you add new options */ - const char *valid_modes[] = { - [SHOW_PATCH_DIFF] = "diff", - [SHOW_PATCH_RAW] = "raw" - }; - int new_value = SHOW_PATCH_RAW; - - BUG_ON_OPT_NEG(unset); - - if (arg) { - for (new_value = 0; new_value < ARRAY_SIZE(valid_modes); new_value++) { - if (!strcmp(arg, valid_modes[new_value])) - break; - } - if (new_value >= ARRAY_SIZE(valid_modes)) - return error(_("invalid value for '%s': '%s'"), - "--show-current-patch", arg); - } - - if (resume->mode == RESUME_SHOW_PATCH && new_value != resume->sub_mode) - return error(_("options '%s=%s' and '%s=%s' " - "cannot be used together"), - "--show-current-patch", "--show-current-patch", arg, valid_modes[resume->sub_mode]); - - resume->mode = RESUME_SHOW_PATCH; - resume->sub_mode = new_value; + else + return error(_("invalid value for '%s': '%s'"), + "--show-current-patch", arg); return 0; } -static int git_am_config(const char *k, const char *v, void *cb UNUSED) -{ - int status; - - status = git_gpg_config(k, v, NULL); - if (status) - return status; - - return git_default_config(k, v, NULL); -} - int cmd_am(int argc, const char **argv, const char *prefix) { struct am_state state; int binary = -1; int keep_cr = -1; int patch_format = PATCH_FORMAT_UNKNOWN; - struct resume_mode resume = { .mode = RESUME_FALSE }; + enum resume_type resume_mode = RESUME_FALSE; int in_progress; int ret = 0; @@ -2348,12 +2323,9 @@ int cmd_am(int argc, const char **argv, const char *prefix) N_("pass -b flag to git-mailinfo"), KEEP_NON_PATCH), OPT_BOOL('m', "message-id", &state.message_id, N_("pass -m flag to git-mailinfo")), - OPT_SET_INT_F(0, "keep-cr", &keep_cr, - N_("pass --keep-cr flag to git-mailsplit for mbox format"), - 1, PARSE_OPT_NONEG), - OPT_SET_INT_F(0, "no-keep-cr", &keep_cr, - N_("do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"), - 0, PARSE_OPT_NONEG), + OPT_SET_INT(0, "keep-cr", &keep_cr, + N_("pass --keep-cr flag to git-mailsplit for mbox format"), + 1), OPT_BOOL('c', "scissors", &state.scissors, N_("strip everything before a scissors line")), OPT_CALLBACK_F(0, "quoted-cr", &state.quoted_cr, N_("action"), @@ -2391,27 +2363,27 @@ int cmd_am(int argc, const char **argv, const char *prefix) PARSE_OPT_NOARG), OPT_STRING(0, "resolvemsg", &state.resolvemsg, NULL, N_("override error message when patch failure occurs")), - OPT_CMDMODE(0, "continue", &resume.mode, + OPT_CMDMODE(0, "continue", &resume_mode, N_("continue applying patches after resolving a conflict"), RESUME_RESOLVED), - OPT_CMDMODE('r', "resolved", &resume.mode, + OPT_CMDMODE('r', "resolved", &resume_mode, N_("synonyms for --continue"), RESUME_RESOLVED), - OPT_CMDMODE(0, "skip", &resume.mode, + OPT_CMDMODE(0, "skip", &resume_mode, N_("skip the current patch"), RESUME_SKIP), - OPT_CMDMODE(0, "abort", &resume.mode, + OPT_CMDMODE(0, "abort", &resume_mode, N_("restore the original branch and abort the patching operation"), RESUME_ABORT), - OPT_CMDMODE(0, "quit", &resume.mode, + OPT_CMDMODE(0, "quit", &resume_mode, N_("abort the patching operation but keep HEAD where it is"), RESUME_QUIT), - { OPTION_CALLBACK, 0, "show-current-patch", &resume.mode, + { OPTION_CALLBACK, 0, "show-current-patch", &resume_mode, "(diff|raw)", N_("show the patch being applied"), PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP, - parse_opt_show_current_patch, RESUME_SHOW_PATCH }, - OPT_CMDMODE(0, "allow-empty", &resume.mode, + parse_opt_show_current_patch, RESUME_SHOW_PATCH_RAW }, + OPT_CMDMODE(0, "allow-empty", &resume_mode, N_("record the empty patch as an empty commit"), RESUME_ALLOW_EMPTY), OPT_BOOL(0, "committer-date-is-author-date", @@ -2423,7 +2395,7 @@ int cmd_am(int argc, const char **argv, const char *prefix) { OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"), N_("GPG-sign commits"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, - OPT_CALLBACK_F(STOP_ON_EMPTY_COMMIT, "empty", &state.empty_type, "{stop,drop,keep}", + OPT_CALLBACK_F(0, "empty", &state.empty_type, "(stop|drop|keep)", N_("how to handle empty patches"), PARSE_OPT_NONEG, am_option_parse_empty), OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing, @@ -2434,7 +2406,7 @@ int cmd_am(int argc, const char **argv, const char *prefix) if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(usage, options); - git_config(git_am_config, NULL); + git_config(git_default_config, NULL); am_state_init(&state); @@ -2466,12 +2438,12 @@ int cmd_am(int argc, const char **argv, const char *prefix) * intend to feed us a patch but wanted to continue * unattended. */ - if (argc || (resume.mode == RESUME_FALSE && !isatty(0))) + if (argc || (resume_mode == RESUME_FALSE && !isatty(0))) die(_("previous rebase directory %s still exists but mbox given."), state.dir); - if (resume.mode == RESUME_FALSE) - resume.mode = RESUME_APPLY; + if (resume_mode == RESUME_FALSE) + resume_mode = RESUME_APPLY; if (state.signoff == SIGNOFF_EXPLICIT) am_append_signoff(&state); @@ -2485,7 +2457,7 @@ int cmd_am(int argc, const char **argv, const char *prefix) * stray directories. */ if (file_exists(state.dir) && !state.rebasing) { - if (resume.mode == RESUME_ABORT || resume.mode == RESUME_QUIT) { + if (resume_mode == RESUME_ABORT || resume_mode == RESUME_QUIT) { am_destroy(&state); am_state_release(&state); return 0; @@ -2496,7 +2468,7 @@ int cmd_am(int argc, const char **argv, const char *prefix) state.dir); } - if (resume.mode) + if (resume_mode) die(_("Resolve operation not in progress, we are not resuming.")); for (i = 0; i < argc; i++) { @@ -2514,7 +2486,7 @@ int cmd_am(int argc, const char **argv, const char *prefix) strvec_clear(&paths); } - switch (resume.mode) { + switch (resume_mode) { case RESUME_FALSE: am_run(&state, 0); break; @@ -2523,7 +2495,7 @@ int cmd_am(int argc, const char **argv, const char *prefix) break; case RESUME_RESOLVED: case RESUME_ALLOW_EMPTY: - am_resolve(&state, resume.mode == RESUME_ALLOW_EMPTY ? 1 : 0); + am_resolve(&state, resume_mode == RESUME_ALLOW_EMPTY ? 1 : 0); break; case RESUME_SKIP: am_skip(&state); @@ -2535,8 +2507,9 @@ int cmd_am(int argc, const char **argv, const char *prefix) am_rerere_clear(); am_destroy(&state); break; - case RESUME_SHOW_PATCH: - ret = show_patch(&state, resume.sub_mode); + case RESUME_SHOW_PATCH_RAW: + case RESUME_SHOW_PATCH_DIFF: + ret = show_patch(&state, resume_mode); break; default: BUG("invalid resume value"); diff --git a/builtin/apply.c b/builtin/apply.c index 555219de40..c18b7ea5d3 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -1,6 +1,7 @@ -#include "cache.h" #include "builtin.h" +#include "gettext.h" #include "parse-options.h" +#include "repository.h" #include "apply.h" static const char * const apply_usage[] = { diff --git a/builtin/archive.c b/builtin/archive.c index f094390ee0..90761fdfee 100644 --- a/builtin/archive.c +++ b/builtin/archive.c @@ -2,12 +2,13 @@ * Copyright (c) 2006 Franck Bui-Huu * Copyright (c) 2006 Rene Scharfe */ -#include "cache.h" #include "builtin.h" #include "archive.h" +#include "gettext.h" #include "transport.h" #include "parse-options.h" #include "pkt-line.h" +#include "repository.h" #include "sideband.h" static void create_output_file(const char *output_file) @@ -81,7 +82,7 @@ static int run_remote_archiver(int argc, const char **argv, int cmd_archive(int argc, const char **argv, const char *prefix) { const char *exec = "git-upload-archive"; - const char *output = NULL; + char *output = NULL; const char *remote = NULL; struct option local_opts[] = { OPT_FILENAME('o', "output", &output, @@ -106,5 +107,6 @@ int cmd_archive(int argc, const char **argv, const char *prefix) setvbuf(stderr, NULL, _IOLBF, BUFSIZ); + UNLEAK(output); return write_archive(argc, argv, prefix, the_repository, output, 0); } diff --git a/builtin/bisect.c b/builtin/bisect.c index 7301740267..c5565686bf 100644 --- a/builtin/bisect.c +++ b/builtin/bisect.c @@ -1,11 +1,17 @@ #include "builtin.h" -#include "cache.h" +#include "copy.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" +#include "object-name.h" #include "parse-options.h" #include "bisect.h" #include "refs.h" #include "dir.h" #include "strvec.h" #include "run-command.h" +#include "oid-array.h" +#include "path.h" #include "prompt.h" #include "quote.h" #include "revision.h" @@ -20,7 +26,7 @@ static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT") static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN") #define BUILTIN_GIT_BISECT_START_USAGE \ - N_("git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]" \ + N_("git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]" \ " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--]" \ " [<pathspec>...]") #define BUILTIN_GIT_BISECT_STATE_USAGE \ @@ -40,7 +46,7 @@ static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN") #define BUILTIN_GIT_BISECT_LOG_USAGE \ "git bisect log" #define BUILTIN_GIT_BISECT_RUN_USAGE \ - N_("git bisect run <cmd>...") + N_("git bisect run <cmd> [<arg>...]") static const char * const git_bisect_usage[] = { BUILTIN_GIT_BISECT_START_USAGE, @@ -227,24 +233,24 @@ static int bisect_reset(const char *commit) struct strbuf branch = STRBUF_INIT; if (!commit) { - if (strbuf_read_file(&branch, git_path_bisect_start(), 0) < 1) { + if (!strbuf_read_file(&branch, git_path_bisect_start(), 0)) printf(_("We are not bisecting.\n")); - return 0; - } - strbuf_rtrim(&branch); + else + strbuf_rtrim(&branch); } else { struct object_id oid; - if (get_oid_commit(commit, &oid)) + if (repo_get_oid_commit(the_repository, commit, &oid)) return error(_("'%s' is not a valid commit"), commit); strbuf_addstr(&branch, commit); } - if (!ref_exists("BISECT_HEAD")) { + if (branch.len && !ref_exists("BISECT_HEAD")) { struct child_process cmd = CHILD_PROCESS_INIT; cmd.git_cmd = 1; - strvec_pushl(&cmd.args, "checkout", branch.buf, "--", NULL); + strvec_pushl(&cmd.args, "checkout", "--ignore-other-worktrees", + branch.buf, "--", NULL); if (run_command(&cmd)) { error(_("could not check out original" " HEAD '%s'. Try 'git bisect" @@ -265,7 +271,8 @@ static void log_commit(FILE *fp, char *fmt, const char *state, struct strbuf commit_msg = STRBUF_INIT; char *label = xstrfmt(fmt, state); - format_commit_message(commit, "%s", &commit_msg, &pp); + repo_format_commit_message(the_repository, commit, "%s", &commit_msg, + &pp); fprintf(fp, "# %s: [%s] %s\n", label, oid_to_hex(&commit->object.oid), commit_msg.buf); @@ -292,7 +299,7 @@ static int bisect_write(const char *state, const char *rev, goto finish; } - if (get_oid(rev, &oid)) { + if (repo_get_oid(the_repository, rev, &oid)) { res = error(_("couldn't get the oid of the rev '%s'"), rev); goto finish; } @@ -567,7 +574,7 @@ static int prepare_revs(struct bisect_terms *terms, struct rev_info *revs) * sets up a revision walk. */ reset_revision_walk(); - init_revisions(revs, NULL); + repo_init_revisions(the_repository, revs, NULL); setup_revisions(0, NULL, revs, NULL); for_each_glob_ref_in(add_bisect_ref, bad, "refs/bisect/", &cb); cb.object_flags = UNINTERESTING; @@ -603,8 +610,8 @@ static int bisect_skipped_commits(struct bisect_terms *terms) while ((commit = get_revision(&revs)) != NULL) { strbuf_reset(&commit_name); - format_commit_message(commit, "%s", - &commit_name, &pp); + repo_format_commit_message(the_repository, commit, "%s", + &commit_name, &pp); fprintf(fp, "# possible first %s commit: [%s] %s\n", terms->term_bad, oid_to_hex(&commit->object.oid), commit_name.buf); @@ -633,7 +640,8 @@ static int bisect_successful(struct bisect_terms *terms) read_ref(bad_ref, &oid); commit = lookup_commit_reference_by_name(bad_ref); - format_commit_message(commit, "%s", &commit_name, &pp); + repo_format_commit_message(the_repository, commit, "%s", &commit_name, + &pp); res = append_to_file(git_path_bisect_log(), "# first %s commit: [%s] %s\n", terms->term_bad, oid_to_hex(&commit->object.oid), @@ -775,7 +783,7 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, int argc, */ head = resolve_ref_unsafe("HEAD", 0, &head_oid, &flags); if (!head) - if (get_oid("HEAD", &head_oid)) + if (repo_get_oid(the_repository, "HEAD", &head_oid)) return error(_("bad HEAD - I need a HEAD")); /* @@ -801,11 +809,11 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, int argc, } } else { /* Get the rev from where we start. */ - if (!get_oid(head, &head_oid) && + if (!repo_get_oid(the_repository, head, &head_oid) && !starts_with(head, "refs/heads/")) { strbuf_reset(&start_head); strbuf_addstr(&start_head, oid_to_hex(&head_oid)); - } else if (!get_oid(head, &head_oid) && + } else if (!repo_get_oid(the_repository, head, &head_oid) && skip_prefix(head, "refs/heads/", &head)) { strbuf_addstr(&start_head, head); } else { @@ -828,7 +836,7 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, int argc, write_file(git_path_bisect_first_parent(), "\n"); if (no_checkout) { - if (get_oid(start_head.buf, &oid) < 0) { + if (repo_get_oid(the_repository, start_head.buf, &oid) < 0) { res = error(_("invalid ref: '%s'"), start_head.buf); goto finish; } @@ -933,11 +941,12 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc, if (argc == 0) { const char *head = "BISECT_HEAD"; - enum get_oid_result res_head = get_oid(head, &oid); + enum get_oid_result res_head = repo_get_oid(the_repository, + head, &oid); if (res_head == MISSING_OBJECT) { head = "HEAD"; - res_head = get_oid(head, &oid); + res_head = repo_get_oid(the_repository, head, &oid); } if (res_head) @@ -953,7 +962,7 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc, for (; argc; argc--, argv++) { struct commit *commit; - if (get_oid(*argv, &oid)){ + if (repo_get_oid(the_repository, *argv, &oid)){ error(_("Bad rev input: %s"), *argv); oid_array_clear(&revs); return BISECT_FAILED; @@ -1092,7 +1101,7 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, int argc, struct rev_info revs; struct commit *commit; - init_revisions(&revs, NULL); + repo_init_revisions(the_repository, &revs, NULL); setup_revisions(2, argv + i - 1, &revs, NULL); if (prepare_revision_walk(&revs)) diff --git a/builtin/blame.c b/builtin/blame.c index 71f925e456..2433b7da5c 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -5,10 +5,13 @@ * See COPYING for licensing conditions */ -#include "cache.h" +#include "git-compat-util.h" #include "config.h" #include "color.h" #include "builtin.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "repository.h" #include "commit.h" #include "diff.h" @@ -24,10 +27,14 @@ #include "line-log.h" #include "dir.h" #include "progress.h" -#include "object-store.h" +#include "object-name.h" +#include "object-store-ll.h" +#include "pager.h" #include "blame.h" #include "refs.h" +#include "setup.h" #include "tag.h" +#include "write-or-die.h" static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"); static char annotate_usage[] = N_("git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"); @@ -199,13 +206,13 @@ static void get_commit_info(struct commit *commit, const char *message; encoding = get_log_output_encoding(); - message = logmsg_reencode(commit, NULL, encoding); + message = repo_logmsg_reencode(the_repository, commit, NULL, encoding); get_ac_line(message, "\nauthor ", &ret->author, &ret->author_mail, &ret->author_time, &ret->author_tz); if (!detailed) { - unuse_commit_buffer(commit, message); + repo_unuse_commit_buffer(the_repository, commit, message); return; } @@ -219,7 +226,7 @@ static void get_commit_info(struct commit *commit, else strbuf_addf(&ret->summary, "(%s)", oid_to_hex(&commit->object.oid)); - unuse_commit_buffer(commit, message); + repo_unuse_commit_buffer(the_repository, commit, message); } /* @@ -601,8 +608,9 @@ static int read_ancestry(const char *graft_file) static int update_auto_abbrev(int auto_abbrev, struct blame_origin *suspect) { - const char *uniq = find_unique_abbrev(&suspect->commit->object.oid, - auto_abbrev); + const char *uniq = repo_find_unique_abbrev(the_repository, + &suspect->commit->object.oid, + auto_abbrev); int len = strlen(uniq); if (auto_abbrev < len) return len; @@ -685,7 +693,8 @@ static const char *add_prefix(const char *prefix, const char *path) return prefix_path(prefix, prefix ? strlen(prefix) : 0, path); } -static int git_blame_config(const char *var, const char *value, void *cb) +static int git_blame_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { if (!strcmp(var, "blame.showroot")) { show_root = git_config_bool(var, value); @@ -739,6 +748,8 @@ static int git_blame_config(const char *var, const char *value, void *cb) } if (!strcmp(var, "blame.coloring")) { + if (!value) + return config_error_nonbool(var); if (!strcmp(value, "repeatedLines")) { coloring_mode |= OUTPUT_COLOR_LINE; } else if (!strcmp(value, "highlightRecent")) { @@ -758,7 +769,7 @@ static int git_blame_config(const char *var, const char *value, void *cb) if (userdiff_config(var, value) < 0) return -1; - return git_default_config(var, value, cb); + return git_default_config(var, value, ctx, cb); } static int blame_copy_callback(const struct option *option, const char *arg, int unset) @@ -802,7 +813,7 @@ static int is_a_rev(const char *name) { struct object_id oid; - if (get_oid(name, &oid)) + if (repo_get_oid(the_repository, name, &oid)) return 0; return OBJ_NONE < oid_object_info(the_repository, &oid, NULL); } @@ -845,7 +856,7 @@ static void build_ignorelist(struct blame_scoreboard *sb, peel_to_commit_oid, sb); } for_each_string_list_item(i, ignore_rev_list) { - if (get_oid_committish(i->string, &oid) || + if (repo_get_oid_committish(the_repository, i->string, &oid) || peel_to_commit_oid(&oid, sb)) die(_("cannot find revision %s to ignore"), i->string); oidset_insert(&sb->ignore_list, &oid); diff --git a/builtin/branch.c b/builtin/branch.c index f63fd45edb..2ed59f16f1 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -5,16 +5,20 @@ * Based on git-branch.sh by Junio C Hamano. */ -#include "cache.h" +#include "builtin.h" #include "config.h" #include "color.h" +#include "editor.h" +#include "environment.h" #include "refs.h" #include "commit.h" -#include "builtin.h" +#include "gettext.h" +#include "object-name.h" #include "remote.h" #include "parse-options.h" #include "branch.h" #include "diff.h" +#include "path.h" #include "revision.h" #include "string-list.h" #include "column.h" @@ -77,7 +81,8 @@ static unsigned int colopts; define_list_config_array(color_branch_slots); -static int git_branch_config(const char *var, const char *value, void *cb) +static int git_branch_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { const char *slot_name; @@ -111,7 +116,10 @@ static int git_branch_config(const char *var, const char *value, void *cb) return 0; } - return git_color_default_config(var, value, cb); + if (git_color_config(var, value, cb) < 0) + return -1; + + return git_default_config(var, value, ctx, cb); } static const char *branch_get_color(enum color_branch ix) @@ -150,24 +158,25 @@ static int branch_merged(int kind, const char *name, if (!reference_rev) reference_rev = head_rev; - merged = reference_rev ? in_merge_bases(rev, reference_rev) : 0; + merged = reference_rev ? repo_in_merge_bases(the_repository, rev, + reference_rev) : 0; /* * After the safety valve is fully redefined to "check with * upstream, if any, otherwise with HEAD", we should just - * return the result of the in_merge_bases() above without + * return the result of the repo_in_merge_bases() above without * any of the following code, but during the transition period, * a gentle reminder is in order. */ if ((head_rev != reference_rev) && - (head_rev ? in_merge_bases(rev, head_rev) : 0) != merged) { + (head_rev ? repo_in_merge_bases(the_repository, rev, head_rev) : 0) != merged) { if (merged) warning(_("deleting branch '%s' that has been merged to\n" - " '%s', but not yet merged to HEAD."), + " '%s', but not yet merged to HEAD"), name, reference_name); else warning(_("not deleting branch '%s' that is not yet merged to\n" - " '%s', even though it is merged to HEAD."), + " '%s', even though it is merged to HEAD"), name, reference_name); } free(reference_name_to_free); @@ -180,13 +189,13 @@ static int check_branch_commit(const char *branchname, const char *refname, { struct commit *rev = lookup_commit_reference(the_repository, oid); if (!force && !rev) { - error(_("Couldn't look up commit object for '%s'"), refname); + error(_("couldn't look up commit object for '%s'"), refname); return -1; } if (!force && !branch_merged(kinds, branchname, rev, head_rev)) { - error(_("The branch '%s' is not fully merged.\n" + error(_("the branch '%s' is not fully merged.\n" "If you are sure you want to delete it, " - "run 'git branch -D %s'."), branchname, branchname); + "run 'git branch -D %s'"), branchname, branchname); return -1; } return 0; @@ -197,7 +206,7 @@ static void delete_branch_config(const char *branchname) struct strbuf buf = STRBUF_INIT; strbuf_addf(&buf, "branch.%s", branchname); if (git_config_rename_section(buf.buf, NULL) < 0) - warning(_("Update of config-file failed")); + warning(_("update of config-file failed")); strbuf_release(&buf); } @@ -216,10 +225,11 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, struct string_list refs_to_delete = STRING_LIST_INIT_DUP; struct string_list_item *item; int branch_name_pos; + const char *fmt_remotes = "refs/remotes/%s"; switch (kinds) { case FILTER_REFS_REMOTES: - fmt = "refs/remotes/%s"; + fmt = fmt_remotes; /* For subsequent UI messages */ remote_branch = 1; allowed_interpret = INTERPRET_BRANCH_REMOTE; @@ -249,8 +259,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, if (kinds == FILTER_REFS_BRANCHES) { const char *path; if ((path = branch_checked_out(name))) { - error(_("Cannot delete branch '%s' " - "checked out at '%s'"), + error(_("cannot delete branch '%s' " + "used by worktree at '%s'"), bname.buf, path); ret = 1; continue; @@ -263,9 +273,25 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, | RESOLVE_REF_ALLOW_BAD_NAME, &oid, &flags); if (!target) { - error(remote_branch - ? _("remote-tracking branch '%s' not found.") - : _("branch '%s' not found."), bname.buf); + if (remote_branch) { + error(_("remote-tracking branch '%s' not found"), bname.buf); + } else { + char *virtual_name = mkpathdup(fmt_remotes, bname.buf); + char *virtual_target = resolve_refdup(virtual_name, + RESOLVE_REF_READING + | RESOLVE_REF_NO_RECURSE + | RESOLVE_REF_ALLOW_BAD_NAME, + &oid, &flags); + FREE_AND_NULL(virtual_name); + + if (virtual_target) + error(_("branch '%s' not found.\n" + "Did you forget --remote?"), + bname.buf); + else + error(_("branch '%s' not found"), bname.buf); + FREE_AND_NULL(virtual_target); + } ret = 1; continue; } @@ -280,7 +306,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, item = string_list_append(&refs_to_delete, name); item->util = xstrdup((flags & REF_ISBROKEN) ? "broken" : (flags & REF_ISSYMREF) ? target - : find_unique_abbrev(&oid, DEFAULT_ABBREV)); + : repo_find_unique_abbrev(the_repository, &oid, DEFAULT_ABBREV)); next: free(target); @@ -342,17 +368,8 @@ static const char *quote_literal_for_format(const char *s) static struct strbuf buf = STRBUF_INIT; strbuf_reset(&buf); - while (*s) { - const char *ep = strchrnul(s, '%'); - if (s < ep) - strbuf_add(&buf, s, ep - s); - if (*ep == '%') { - strbuf_addstr(&buf, "%%"); - s = ep + 1; - } else { - s = ep; - } - } + while (strbuf_expand_step(&buf, &s)) + strbuf_addstr(&buf, "%%"); return buf.buf; } @@ -420,8 +437,6 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin { int i; struct ref_array array; - struct strbuf out = STRBUF_INIT; - struct strbuf err = STRBUF_INIT; int maxwidth = 0; const char *remote_prefix = ""; char *to_free = NULL; @@ -448,25 +463,30 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin if (verify_ref_format(format)) die(_("unable to parse format string")); + filter_ahead_behind(the_repository, format, &array); ref_array_sort(sorting, &array); - for (i = 0; i < array.nr; i++) { - strbuf_reset(&err); - strbuf_reset(&out); - if (format_ref_array_item(array.items[i], format, &out, &err)) - die("%s", err.buf); - if (column_active(colopts)) { - assert(!filter->verbose && "--column and --verbose are incompatible"); - /* format to a string_list to let print_columns() do its job */ + if (column_active(colopts)) { + struct strbuf out = STRBUF_INIT, err = STRBUF_INIT; + + assert(!filter->verbose && "--column and --verbose are incompatible"); + + for (i = 0; i < array.nr; i++) { + strbuf_reset(&err); + strbuf_reset(&out); + if (format_ref_array_item(array.items[i], format, &out, &err)) + die("%s", err.buf); + + /* format to a string_list to let print_columns() do its job */ string_list_append(output, out.buf); - } else { - fwrite(out.buf, 1, out.len, stdout); - putchar('\n'); } + + strbuf_release(&err); + strbuf_release(&out); + } else { + print_formatted_ref_array(&array, format); } - strbuf_release(&err); - strbuf_release(&out); ref_array_clear(&array); free(to_free); } @@ -486,9 +506,9 @@ static void print_current_branch_name(void) die(_("HEAD (%s) points outside of refs/heads/"), refname); } -static void reject_rebase_or_bisect_branch(const char *target) +static void reject_rebase_or_bisect_branch(struct worktree **worktrees, + const char *target) { - struct worktree **worktrees = get_worktrees(); int i; for (i = 0; worktrees[i]; i++) { @@ -498,24 +518,57 @@ static void reject_rebase_or_bisect_branch(const char *target) continue; if (is_worktree_being_rebased(wt, target)) - die(_("Branch %s is being rebased at %s"), + die(_("branch %s is being rebased at %s"), target, wt->path); if (is_worktree_being_bisected(wt, target)) - die(_("Branch %s is being bisected at %s"), + die(_("branch %s is being bisected at %s"), target, wt->path); } +} - free_worktrees(worktrees); +/* + * Update all per-worktree HEADs pointing at the old ref to point the new ref. + * This will be used when renaming a branch. Returns 0 if successful, non-zero + * otherwise. + */ +static int replace_each_worktree_head_symref(struct worktree **worktrees, + const char *oldref, const char *newref, + const char *logmsg) +{ + int ret = 0; + int i; + + for (i = 0; worktrees[i]; i++) { + struct ref_store *refs; + + if (worktrees[i]->is_detached) + continue; + if (!worktrees[i]->head_ref) + continue; + if (strcmp(oldref, worktrees[i]->head_ref)) + continue; + + refs = get_worktree_ref_store(worktrees[i]); + if (refs_create_symref(refs, "HEAD", newref, logmsg)) + ret = error(_("HEAD of working tree %s is not updated"), + worktrees[i]->path); + } + + return ret; } +#define IS_HEAD 1 +#define IS_ORPHAN 2 + static void copy_or_rename_branch(const char *oldname, const char *newname, int copy, int force) { struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT; struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT; const char *interpreted_oldname = NULL; const char *interpreted_newname = NULL; - int recovery = 0; + int recovery = 0, oldref_usage = 0; + struct worktree **worktrees = get_worktrees(); if (strbuf_check_branch_ref(&oldref, oldname)) { /* @@ -525,14 +578,25 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int if (ref_exists(oldref.buf)) recovery = 1; else - die(_("Invalid branch name: '%s'"), oldname); + die(_("invalid branch name: '%s'"), oldname); + } + + for (int i = 0; worktrees[i]; i++) { + struct worktree *wt = worktrees[i]; + + if (wt->head_ref && !strcmp(oldref.buf, wt->head_ref)) { + oldref_usage |= IS_HEAD; + if (is_null_oid(&wt->head_oid)) + oldref_usage |= IS_ORPHAN; + break; + } } - if ((copy || strcmp(head, oldname)) && !ref_exists(oldref.buf)) { - if (copy && !strcmp(head, oldname)) - die(_("No commit on branch '%s' yet."), oldname); + if ((copy || !(oldref_usage & IS_HEAD)) && !ref_exists(oldref.buf)) { + if (oldref_usage & IS_HEAD) + die(_("no commit on branch '%s' yet"), oldname); else - die(_("No branch named '%s'."), oldname); + die(_("no branch named '%s'"), oldname); } /* @@ -544,7 +608,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int else validate_new_branchname(newname, &newref, force); - reject_rebase_or_bisect_branch(oldref.buf); + reject_rebase_or_bisect_branch(worktrees, oldref.buf); if (!skip_prefix(oldref.buf, "refs/heads/", &interpreted_oldname) || !skip_prefix(newref.buf, "refs/heads/", &interpreted_newname)) { @@ -558,38 +622,39 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int strbuf_addf(&logmsg, "Branch: renamed %s to %s", oldref.buf, newref.buf); - if (!copy && - (!head || strcmp(oldname, head) || !is_null_oid(&head_oid)) && + if (!copy && !(oldref_usage & IS_ORPHAN) && rename_ref(oldref.buf, newref.buf, logmsg.buf)) - die(_("Branch rename failed")); + die(_("branch rename failed")); if (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf)) - die(_("Branch copy failed")); + die(_("branch copy failed")); if (recovery) { if (copy) - warning(_("Created a copy of a misnamed branch '%s'"), + warning(_("created a copy of a misnamed branch '%s'"), interpreted_oldname); else - warning(_("Renamed a misnamed branch '%s' away"), + warning(_("renamed a misnamed branch '%s' away"), interpreted_oldname); } - if (!copy && - replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf)) - die(_("Branch renamed to %s, but HEAD is not updated!"), newname); + if (!copy && (oldref_usage & IS_HEAD) && + replace_each_worktree_head_symref(worktrees, oldref.buf, newref.buf, + logmsg.buf)) + die(_("branch renamed to %s, but HEAD is not updated"), newname); strbuf_release(&logmsg); strbuf_addf(&oldsection, "branch.%s", interpreted_oldname); strbuf_addf(&newsection, "branch.%s", interpreted_newname); if (!copy && git_config_rename_section(oldsection.buf, newsection.buf) < 0) - die(_("Branch is renamed, but update of config-file failed")); + die(_("branch is renamed, but update of config-file failed")); if (copy && strcmp(interpreted_oldname, interpreted_newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0) - die(_("Branch is copied, but update of config-file failed")); + die(_("branch is copied, but update of config-file failed")); strbuf_release(&oldref); strbuf_release(&newref); strbuf_release(&oldsection); strbuf_release(&newsection); + free_worktrees(worktrees); } static GIT_PATH_FUNC(edit_description, "EDIT_DESCRIPTION") @@ -603,7 +668,7 @@ static int edit_branch_description(const char *branch_name) exists = !read_branch_desc(&buf, branch_name); if (!buf.len || buf.buf[buf.len-1] != '\n') strbuf_addch(&buf, '\n'); - strbuf_commented_addf(&buf, + strbuf_commented_addf(&buf, comment_line_char, _("Please edit the description for the branch\n" " %s\n" "Lines starting with '%c' will be stripped.\n"), @@ -614,7 +679,7 @@ static int edit_branch_description(const char *branch_name) strbuf_release(&buf); return -1; } - strbuf_stripspace(&buf, 1); + strbuf_stripspace(&buf, comment_line_char); strbuf_addf(&name, "branch.%s.description", branch_name); if (buf.len || exists) @@ -636,7 +701,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) int reflog = 0, quiet = 0, icase = 0, force = 0, recurse_submodules_explicit = 0; enum branch_track track; - struct ref_filter filter; + struct ref_filter filter = REF_FILTER_INIT; static struct ref_sorting *sorting; struct string_list sorting_options = STRING_LIST_INIT_DUP; struct ref_format format = REF_FORMAT_INIT; @@ -655,8 +720,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")), OPT_BOOL(0, "unset-upstream", &unset_upstream, N_("unset the upstream info")), OPT__COLOR(&branch_use_color, N_("use colored output")), - OPT_SET_INT('r', "remotes", &filter.kind, N_("act on remote-tracking branches"), - FILTER_REFS_REMOTES), + OPT_SET_INT_F('r', "remotes", &filter.kind, N_("act on remote-tracking branches"), + FILTER_REFS_REMOTES, + PARSE_OPT_NONEG), OPT_CONTAINS(&filter.with_commit, N_("print only branches that contain the commit")), OPT_NO_CONTAINS(&filter.no_commit, N_("print only branches that don't contain the commit")), OPT_WITH(&filter.with_commit, N_("print only branches that contain the commit")), @@ -664,12 +730,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT__ABBREV(&filter.abbrev), OPT_GROUP(N_("Specific git-branch actions:")), - OPT_SET_INT('a', "all", &filter.kind, N_("list both remote-tracking and local branches"), - FILTER_REFS_REMOTES | FILTER_REFS_BRANCHES), + OPT_SET_INT_F('a', "all", &filter.kind, N_("list both remote-tracking and local branches"), + FILTER_REFS_REMOTES | FILTER_REFS_BRANCHES, + PARSE_OPT_NONEG), OPT_BIT('d', "delete", &delete, N_("delete fully merged branch"), 1), OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2), OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1), OPT_BIT('M', NULL, &rename, N_("move/rename a branch, even if target exists"), 2), + OPT_BOOL(0, "omit-empty", &format.array_opts.omit_empty, + N_("do not output a newline after empty formatted refs")), OPT_BIT('c', "copy", ©, N_("copy a branch and its reflog"), 1), OPT_BIT('C', NULL, ©, N_("copy a branch, even if target exists"), 2), OPT_BOOL('l', "list", &list, N_("list branch names")), @@ -692,20 +761,25 @@ int cmd_branch(int argc, const char **argv, const char *prefix) setup_ref_filter_porcelain_msg(); - memset(&filter, 0, sizeof(filter)); filter.kind = FILTER_REFS_BRANCHES; filter.abbrev = -1; if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(builtin_branch_usage, options); + /* + * Try to set sort keys from config. If config does not set any, + * fall back on default (refname) sorting. + */ git_config(git_branch_config, &sorting_options); + if (!sorting_options.nr) + string_list_append(&sorting_options, "refname"); track = git_branch_track; head = resolve_refdup("HEAD", 0, &head_oid, NULL); if (!head) - die(_("Failed to resolve HEAD as a valid ref.")); + die(_("failed to resolve HEAD as a valid ref")); if (!strcmp(head, "HEAD")) filter.detached = 1; else if (!skip_prefix(head, "refs/heads/", &head)) @@ -759,6 +833,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (list) setup_auto_pager("branch", 1); + UNLEAK(sorting_options); + if (delete) { if (!argc) die(_("branch name required")); @@ -786,6 +862,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) print_columns(&output, colopts, NULL); string_list_clear(&output, 0); ref_sorting_release(sorting); + ref_filter_clear(&filter); return 0; } else if (edit_description) { const char *branch_name; @@ -795,7 +872,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (!argc) { if (filter.detached) - die(_("Cannot give description to detached HEAD")); + die(_("cannot give description to detached HEAD")); branch_name = head; } else if (argc == 1) { strbuf_branchname(&buf, argv[0], INTERPRET_BRANCH_LOCAL); @@ -806,9 +883,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix) strbuf_addf(&branch_ref, "refs/heads/%s", branch_name); if (!ref_exists(branch_ref.buf)) - error((!argc || !strcmp(head, branch_name)) - ? _("No commit on branch '%s' yet.") - : _("No branch named '%s'."), + error((!argc || branch_checked_out(branch_ref.buf)) + ? _("no commit on branch '%s' yet") + : _("no branch named '%s'"), branch_name); else if (!edit_branch_description(branch_name)) ret = 0; /* happy */ @@ -821,8 +898,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (!argc) die(_("branch name required")); else if ((argc == 1) && filter.detached) - die(copy? _("cannot copy the current branch while not on any.") - : _("cannot rename the current branch while not on any.")); + die(copy? _("cannot copy the current branch while not on any") + : _("cannot rename the current branch while not on any")); else if (argc == 1) copy_or_rename_branch(head, argv[0], copy, copy + rename > 1); else if (argc == 2) @@ -845,14 +922,14 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (!branch) { if (!argc || !strcmp(argv[0], "HEAD")) die(_("could not set upstream of HEAD to %s when " - "it does not point to any branch."), + "it does not point to any branch"), new_upstream); die(_("no such branch '%s'"), argv[0]); } if (!ref_exists(branch->refname)) { - if (!argc || !strcmp(head, branch->name)) - die(_("No commit on branch '%s' yet."), branch->name); + if (!argc || branch_checked_out(branch->refname)) + die(_("no commit on branch '%s' yet"), branch->name); die(_("branch '%s' does not exist"), branch->name); } @@ -875,12 +952,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (!branch) { if (!argc || !strcmp(argv[0], "HEAD")) die(_("could not unset upstream of HEAD when " - "it does not point to any branch.")); + "it does not point to any branch")); die(_("no such branch '%s'"), argv[0]); } if (!branch_has_merge_config(branch)) - die(_("Branch '%s' has no upstream information"), branch->name); + die(_("branch '%s' has no upstream information"), branch->name); strbuf_reset(&buf); strbuf_addf(&buf, "branch.%s.remote", branch->name); @@ -894,11 +971,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix) const char *start_name = argc == 2 ? argv[1] : head; if (filter.kind != FILTER_REFS_BRANCHES) - die(_("The -a, and -r, options to 'git branch' do not take a branch name.\n" + die(_("the -a, and -r, options to 'git branch' do not take a branch name.\n" "Did you mean to use: -a|-r --list <pattern>?")); if (track == BRANCH_TRACK_OVERRIDE) - die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead.")); + die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead")); if (recurse_submodules) { create_branches_recursively(the_repository, branch_name, diff --git a/builtin/bugreport.c b/builtin/bugreport.c index 5bc254be80..3106e56a13 100644 --- a/builtin/bugreport.c +++ b/builtin/bugreport.c @@ -1,4 +1,7 @@ #include "builtin.h" +#include "abspath.h" +#include "editor.h" +#include "gettext.h" #include "parse-options.h" #include "strbuf.h" #include "help.h" @@ -6,7 +9,8 @@ #include "hook.h" #include "hook-list.h" #include "diagnose.h" - +#include "object-file.h" +#include "setup.h" static void get_system_info(struct strbuf *sys_info) { @@ -122,6 +126,11 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, bugreport_options, bugreport_usage, 0); + if (argc) { + error(_("unknown argument `%s'"), argv[0]); + usage(bugreport_usage[0]); + } + /* Prepare the path to put the result */ prefixed_filename = prefix_filename(prefix, option_output ? option_output : ""); diff --git a/builtin/bundle.c b/builtin/bundle.c index acceef6200..3ad11dc5d0 100644 --- a/builtin/bundle.c +++ b/builtin/bundle.c @@ -1,7 +1,11 @@ #include "builtin.h" +#include "abspath.h" +#include "gettext.h" +#include "setup.h" #include "strvec.h" #include "parse-options.h" -#include "cache.h" +#include "pkt-line.h" +#include "repository.h" #include "bundle.h" /* @@ -12,7 +16,7 @@ */ #define BUILTIN_BUNDLE_CREATE_USAGE \ - N_("git bundle create [-q | --quiet | --progress | --all-progress] [--all-progress-implied]\n" \ + N_("git bundle create [-q | --quiet | --progress]\n" \ " [--version=<version>] <file> <git-rev-list-args>") #define BUILTIN_BUNDLE_VERIFY_USAGE \ N_("git bundle verify [-q | --quiet] <file>") @@ -59,46 +63,41 @@ static int parse_options_cmd_bundle(int argc, PARSE_OPT_STOP_AT_NON_OPTION); if (!argc) usage_msg_opt(_("need a <file> argument"), usagestr, options); - *bundle_file = prefix_filename(prefix, argv[0]); + *bundle_file = prefix_filename_except_for_dash(prefix, argv[0]); return argc; } static int cmd_bundle_create(int argc, const char **argv, const char *prefix) { - int all_progress_implied = 0; - int progress = isatty(STDERR_FILENO); - struct strvec pack_opts; + struct strvec pack_opts = STRVEC_INIT; int version = -1; int ret; struct option options[] = { - OPT_SET_INT('q', "quiet", &progress, - N_("do not show progress meter"), 0), - OPT_SET_INT(0, "progress", &progress, - N_("show progress meter"), 1), - OPT_SET_INT(0, "all-progress", &progress, - N_("show progress meter during object writing phase"), 2), - OPT_BOOL(0, "all-progress-implied", - &all_progress_implied, - N_("similar to --all-progress when progress meter is shown")), + OPT_PASSTHRU_ARGV('q', "quiet", &pack_opts, NULL, + N_("do not show progress meter"), + PARSE_OPT_NOARG), + OPT_PASSTHRU_ARGV(0, "progress", &pack_opts, NULL, + N_("show progress meter"), + PARSE_OPT_NOARG), + OPT_PASSTHRU_ARGV(0, "all-progress", &pack_opts, NULL, + N_("historical; same as --progress"), + PARSE_OPT_NOARG | PARSE_OPT_HIDDEN), + OPT_PASSTHRU_ARGV(0, "all-progress-implied", &pack_opts, NULL, + N_("historical; does nothing"), + PARSE_OPT_NOARG | PARSE_OPT_HIDDEN), OPT_INTEGER(0, "version", &version, N_("specify bundle format version")), OPT_END() }; char *bundle_file; + if (isatty(STDERR_FILENO)) + strvec_push(&pack_opts, "--progress"); + strvec_push(&pack_opts, "--all-progress-implied"); + argc = parse_options_cmd_bundle(argc, argv, prefix, builtin_bundle_create_usage, options, &bundle_file); /* bundle internals use argv[1] as further parameters */ - strvec_init(&pack_opts); - if (progress == 0) - strvec_push(&pack_opts, "--quiet"); - else if (progress == 1) - strvec_push(&pack_opts, "--progress"); - else if (progress == 2) - strvec_push(&pack_opts, "--all-progress"); - if (progress && all_progress_implied) - strvec_push(&pack_opts, "--all-progress-implied"); - if (!startup_info->have_repository) die(_("Need a repository to create a bundle.")); ret = !!create_bundle(the_repository, bundle_file, argc, argv, &pack_opts, version); @@ -107,6 +106,23 @@ static int cmd_bundle_create(int argc, const char **argv, const char *prefix) { return ret; } +/* + * Similar to read_bundle_header(), but handle "-" as stdin. + */ +static int open_bundle(const char *path, struct bundle_header *header, + const char **name) +{ + if (!strcmp(path, "-")) { + if (name) + *name = "<stdin>"; + return read_bundle_header_fd(0, header, "<stdin>"); + } + + if (name) + *name = path; + return read_bundle_header(path, header); +} + static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) { struct bundle_header header = BUNDLE_HEADER_INIT; int bundle_fd = -1; @@ -118,12 +134,13 @@ static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) { OPT_END() }; char *bundle_file; + const char *name; argc = parse_options_cmd_bundle(argc, argv, prefix, builtin_bundle_verify_usage, options, &bundle_file); /* bundle internals use argv[1] as further parameters */ - if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) { + if ((bundle_fd = open_bundle(bundle_file, &header, &name)) < 0) { ret = 1; goto cleanup; } @@ -134,7 +151,7 @@ static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) { goto cleanup; } - fprintf(stderr, _("%s is okay\n"), bundle_file); + fprintf(stderr, _("%s is okay\n"), name); ret = 0; cleanup: free(bundle_file); @@ -155,7 +172,7 @@ static int cmd_bundle_list_heads(int argc, const char **argv, const char *prefix builtin_bundle_list_heads_usage, options, &bundle_file); /* bundle internals use argv[1] as further parameters */ - if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) { + if ((bundle_fd = open_bundle(bundle_file, &header, NULL)) < 0) { ret = 1; goto cleanup; } @@ -185,7 +202,7 @@ static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix) builtin_bundle_unbundle_usage, options, &bundle_file); /* bundle internals use argv[1] as further parameters */ - if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) { + if ((bundle_fd = open_bundle(bundle_file, &header, NULL)) < 0) { ret = 1; goto cleanup; } diff --git a/builtin/cat-file.c b/builtin/cat-file.c index cc17635e76..ea8ad601ec 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -4,19 +4,27 @@ * Copyright (C) Linus Torvalds, 2005 */ #define USE_THE_INDEX_VARIABLE -#include "cache.h" -#include "config.h" #include "builtin.h" +#include "config.h" +#include "convert.h" #include "diff.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" +#include "ident.h" #include "parse-options.h" #include "userdiff.h" #include "streaming.h" #include "tree-walk.h" #include "oid-array.h" #include "packfile.h" -#include "object-store.h" +#include "object-file.h" +#include "object-name.h" +#include "object-store-ll.h" +#include "replace-object.h" #include "promisor-remote.h" #include "mailmap.h" +#include "write-or-die.h" enum batch_mode { BATCH_MODE_CONTENTS, @@ -32,7 +40,8 @@ struct batch_options { int all_objects; int unordered; int transform_mode; /* may be 'w' or 'c' for --filters or --textconv */ - int nul_terminated; + char input_delim; + char output_delim; const char *format; }; @@ -60,7 +69,7 @@ static int filter_object(const char *path, unsigned mode, { enum object_type type; - *buf = read_object_file(oid, &type, size); + *buf = repo_read_object_file(the_repository, oid, &type, size); if (!*buf) return error(_("cannot read object %s '%s'"), oid_to_hex(oid), path); @@ -152,7 +161,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, goto cleanup; case 'e': - return !has_object_file(&oid); + return !repo_has_object_file(the_repository, &oid); case 'w': @@ -187,7 +196,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, ret = stream_blob(&oid); goto cleanup; } - buf = read_object_file(&oid, &type, &size); + buf = repo_read_object_file(the_repository, &oid, &type, + &size); if (!buf) die("Cannot read object %s", obj_name); @@ -207,8 +217,10 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, if (exp_type_id == OBJ_BLOB) { struct object_id blob_oid; if (oid_object_info(the_repository, &oid, NULL) == OBJ_TAG) { - char *buffer = read_object_file(&oid, &type, - &size); + char *buffer = repo_read_object_file(the_repository, + &oid, + &type, + &size); const char *target; if (!skip_prefix(buffer, "object ", &target) || get_oid_hex(target, &blob_oid)) @@ -296,10 +308,8 @@ static int is_atom(const char *atom, const char *s, int slen) } static void expand_atom(struct strbuf *sb, const char *atom, int len, - void *vdata) + struct expand_data *data) { - struct expand_data *data = vdata; - if (is_atom("objectname", atom, len)) { if (!data->mark_query) strbuf_addstr(sb, oid_to_hex(&data->oid)); @@ -333,19 +343,21 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len, die("unknown format element: %.*s", len, atom); } -static size_t expand_format(struct strbuf *sb, const char *start, void *data) +static void expand_format(struct strbuf *sb, const char *start, + struct expand_data *data) { - const char *end; - - if (*start != '(') - return 0; - end = strchr(start + 1, ')'); - if (!end) - die("format element '%s' does not end in ')'", start); - - expand_atom(sb, start + 1, end - start - 1, data); - - return end - start + 1; + while (strbuf_expand_step(sb, &start)) { + const char *end; + + if (skip_prefix(start, "%", &start) || *start != '(') + strbuf_addch(sb, '%'); + else if (!(end = strchr(start + 1, ')'))) + die("format element '%s' does not end in ')'", start); + else { + expand_atom(sb, start + 1, end - start - 1, data); + start = end + 1; + } + } } static void batch_write(struct batch_options *opt, const void *data, int len) @@ -383,9 +395,10 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d if (!textconv_object(the_repository, data->rest, 0100644, oid, 1, &contents, &size)) - contents = read_object_file(oid, - &type, - &size); + contents = repo_read_object_file(the_repository, + oid, + &type, + &size); if (!contents) die("could not convert '%s' %s", oid_to_hex(oid), data->rest); @@ -402,7 +415,8 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d unsigned long size; void *contents; - contents = read_object_file(oid, &type, &size); + contents = repo_read_object_file(the_repository, oid, &type, + &size); if (use_mailmap) { size_t s = size; @@ -422,11 +436,12 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d } } -static void print_default_format(struct strbuf *scratch, struct expand_data *data) +static void print_default_format(struct strbuf *scratch, struct expand_data *data, + struct batch_options *opt) { - strbuf_addf(scratch, "%s %s %"PRIuMAX"\n", oid_to_hex(&data->oid), + strbuf_addf(scratch, "%s %s %"PRIuMAX"%c", oid_to_hex(&data->oid), type_name(data->type), - (uintmax_t)data->size); + (uintmax_t)data->size, opt->output_delim); } /* @@ -455,8 +470,8 @@ static void batch_object_write(const char *obj_name, &data->oid, &data->info, OBJECT_INFO_LOOKUP_REPLACE); if (ret < 0) { - printf("%s missing\n", - obj_name ? obj_name : oid_to_hex(&data->oid)); + printf("%s missing%c", + obj_name ? obj_name : oid_to_hex(&data->oid), opt->output_delim); fflush(stdout); return; } @@ -477,17 +492,17 @@ static void batch_object_write(const char *obj_name, strbuf_reset(scratch); if (!opt->format) { - print_default_format(scratch, data); + print_default_format(scratch, data, opt); } else { - strbuf_expand(scratch, opt->format, expand_format, data); - strbuf_addch(scratch, '\n'); + expand_format(scratch, opt->format, data); + strbuf_addch(scratch, opt->output_delim); } batch_write(opt, scratch->buf, scratch->len); if (opt->batch_mode == BATCH_MODE_CONTENTS) { print_object_or_die(opt, data); - batch_write(opt, "\n", 1); + batch_write(opt, &opt->output_delim, 1); } } @@ -505,22 +520,25 @@ static void batch_one_object(const char *obj_name, if (result != FOUND) { switch (result) { case MISSING_OBJECT: - printf("%s missing\n", obj_name); + printf("%s missing%c", obj_name, opt->output_delim); break; case SHORT_NAME_AMBIGUOUS: - printf("%s ambiguous\n", obj_name); + printf("%s ambiguous%c", obj_name, opt->output_delim); break; case DANGLING_SYMLINK: - printf("dangling %"PRIuMAX"\n%s\n", - (uintmax_t)strlen(obj_name), obj_name); + printf("dangling %"PRIuMAX"%c%s%c", + (uintmax_t)strlen(obj_name), + opt->output_delim, obj_name, opt->output_delim); break; case SYMLINK_LOOP: - printf("loop %"PRIuMAX"\n%s\n", - (uintmax_t)strlen(obj_name), obj_name); + printf("loop %"PRIuMAX"%c%s%c", + (uintmax_t)strlen(obj_name), + opt->output_delim, obj_name, opt->output_delim); break; case NOT_DIR: - printf("notdir %"PRIuMAX"\n%s\n", - (uintmax_t)strlen(obj_name), obj_name); + printf("notdir %"PRIuMAX"%c%s%c", + (uintmax_t)strlen(obj_name), + opt->output_delim, obj_name, opt->output_delim); break; default: BUG("unknown get_sha1_with_context result %d\n", @@ -532,9 +550,9 @@ static void batch_one_object(const char *obj_name, } if (ctx.mode == 0) { - printf("symlink %"PRIuMAX"\n%s\n", + printf("symlink %"PRIuMAX"%c%s%c", (uintmax_t)ctx.symlink_path.len, - ctx.symlink_path.buf); + opt->output_delim, ctx.symlink_path.buf, opt->output_delim); fflush(stdout); return; } @@ -559,7 +577,7 @@ static int batch_object_cb(const struct object_id *oid, void *vdata) } static int collect_loose_object(const struct object_id *oid, - const char *path, + const char *path UNUSED, void *data) { oid_array_append(data, oid); @@ -567,8 +585,8 @@ static int collect_loose_object(const struct object_id *oid, } static int collect_packed_object(const struct object_id *oid, - struct packed_git *pack, - uint32_t pos, + struct packed_git *pack UNUSED, + uint32_t pos UNUSED, void *data) { oid_array_append(data, oid); @@ -591,7 +609,7 @@ static int batch_unordered_object(const struct object_id *oid, } static int batch_unordered_loose(const struct object_id *oid, - const char *path, + const char *path UNUSED, void *data) { return batch_unordered_object(oid, NULL, 0, data); @@ -679,20 +697,12 @@ static void batch_objects_command(struct batch_options *opt, struct queued_cmd *queued_cmd = NULL; size_t alloc = 0, nr = 0; - while (1) { - int i, ret; + while (strbuf_getdelim_strip_crlf(&input, stdin, opt->input_delim) != EOF) { + int i; const struct parse_cmd *cmd = NULL; const char *p = NULL, *cmd_end; struct queued_cmd call = {0}; - if (opt->nul_terminated) - ret = strbuf_getline_nul(&input, stdin); - else - ret = strbuf_getline(&input, stdin); - - if (ret) - break; - if (!input.len) die(_("empty command in input")); if (isspace(*input.buf)) @@ -762,9 +772,8 @@ static int batch_objects(struct batch_options *opt) */ memset(&data, 0, sizeof(data)); data.mark_query = 1; - strbuf_expand(&output, + expand_format(&output, opt->format ? opt->format : DEFAULT_FORMAT, - expand_format, &data); data.mark_query = 0; strbuf_release(&output); @@ -787,10 +796,10 @@ static int batch_objects(struct batch_options *opt) if (!memcmp(&data.info, &empty, sizeof(empty))) data.skip_object_info = 1; - if (has_promisor_remote()) + if (repo_has_promisor_remote(the_repository)) warning("This repository uses promisor remotes. Some objects may not be loaded."); - read_replace_refs = 0; + disable_replace_refs(); cb.opt = opt; cb.expand = &data; @@ -836,16 +845,7 @@ static int batch_objects(struct batch_options *opt) goto cleanup; } - while (1) { - int ret; - if (opt->nul_terminated) - ret = strbuf_getline_nul(&input, stdin); - else - ret = strbuf_getline(&input, stdin); - - if (ret == EOF) - break; - + while (strbuf_getdelim_strip_crlf(&input, stdin, opt->input_delim) != EOF) { if (data.split_on_whitespace) { /* * Split at first whitespace, tying off the beginning @@ -870,12 +870,13 @@ static int batch_objects(struct batch_options *opt) return retval; } -static int git_cat_file_config(const char *var, const char *value, void *cb) +static int git_cat_file_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { if (userdiff_config(var, value) < 0) return -1; - return git_default_config(var, value, cb); + return git_default_config(var, value, ctx, cb); } static int batch_option_callback(const struct option *opt, @@ -914,16 +915,18 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) const char *exp_type = NULL, *obj_name = NULL; struct batch_options batch = {0}; int unknown_type = 0; + int input_nul_terminated = 0; + int nul_terminated = 0; const char * const usage[] = { N_("git cat-file <type> <object>"), N_("git cat-file (-e | -p) <object>"), N_("git cat-file (-t | -s) [--allow-unknown-type] <object>"), - N_("git cat-file (--batch | --batch-check | --batch-command) [--batch-all-objects]\n" - " [--buffer] [--follow-symlinks] [--unordered]\n" - " [--textconv | --filters] [-z]"), N_("git cat-file (--textconv | --filters)\n" " [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"), + N_("git cat-file (--batch | --batch-check | --batch-command) [--batch-all-objects]\n" + " [--buffer] [--follow-symlinks] [--unordered]\n" + " [--textconv | --filters] [-Z]"), NULL }; const struct option options[] = { @@ -950,7 +953,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) N_("like --batch, but don't emit <contents>"), PARSE_OPT_OPTARG | PARSE_OPT_NONEG, batch_option_callback), - OPT_BOOL('z', NULL, &batch.nul_terminated, N_("stdin is NUL-terminated")), + OPT_BOOL_F('z', NULL, &input_nul_terminated, N_("stdin is NUL-terminated"), + PARSE_OPT_HIDDEN), + OPT_BOOL('Z', NULL, &nul_terminated, N_("stdin and stdout is NUL-terminated")), OPT_CALLBACK_F(0, "batch-command", &batch, N_("format"), N_("read commands from stdin"), PARSE_OPT_OPTARG | PARSE_OPT_NONEG, @@ -1009,9 +1014,18 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) else if (batch.all_objects) usage_msg_optf(_("'%s' requires a batch mode"), usage, options, "--batch-all-objects"); - else if (batch.nul_terminated) + else if (input_nul_terminated) usage_msg_optf(_("'%s' requires a batch mode"), usage, options, "-z"); + else if (nul_terminated) + usage_msg_optf(_("'%s' requires a batch mode"), usage, options, + "-Z"); + + batch.input_delim = batch.output_delim = '\n'; + if (input_nul_terminated) + batch.input_delim = '\0'; + if (nul_terminated) + batch.input_delim = batch.output_delim = '\0'; /* Batch defaults */ if (batch.buffer_output < 0) diff --git a/builtin/check-attr.c b/builtin/check-attr.c index d7a40e674c..c1da1d184e 100644 --- a/builtin/check-attr.c +++ b/builtin/check-attr.c @@ -1,10 +1,15 @@ #define USE_THE_INDEX_VARIABLE #include "builtin.h" -#include "cache.h" #include "config.h" #include "attr.h" +#include "environment.h" +#include "gettext.h" +#include "object-name.h" #include "quote.h" +#include "repository.h" +#include "setup.h" #include "parse-options.h" +#include "write-or-die.h" static int all_attrs; static int cached_attrs; @@ -58,7 +63,7 @@ static void output_attr(struct attr_check *check, const char *file) } static void check_attr(const char *prefix, struct attr_check *check, - const struct object_id *tree_oid, int collect_all, + int collect_all, const char *file) { @@ -66,9 +71,9 @@ static void check_attr(const char *prefix, struct attr_check *check, prefix_path(prefix, prefix ? strlen(prefix) : 0, file); if (collect_all) { - git_all_attrs(&the_index, tree_oid, full_path, check); + git_all_attrs(&the_index, full_path, check); } else { - git_check_attr(&the_index, tree_oid, full_path, check); + git_check_attr(&the_index, full_path, check); } output_attr(check, file); @@ -76,7 +81,7 @@ static void check_attr(const char *prefix, struct attr_check *check, } static void check_attr_stdin_paths(const char *prefix, struct attr_check *check, - const struct object_id *tree_oid, int collect_all) + int collect_all) { struct strbuf buf = STRBUF_INIT; struct strbuf unquoted = STRBUF_INIT; @@ -90,7 +95,7 @@ static void check_attr_stdin_paths(const char *prefix, struct attr_check *check, die("line is badly quoted"); strbuf_swap(&buf, &unquoted); } - check_attr(prefix, check, tree_oid, collect_all, buf.buf); + check_attr(prefix, check, collect_all, buf.buf); maybe_flush_or_die(stdout, "attribute to stdout"); } strbuf_release(&buf); @@ -106,7 +111,6 @@ static NORETURN void error_with_usage(const char *msg) int cmd_check_attr(int argc, const char **argv, const char *prefix) { struct attr_check *check; - struct object_id *tree_oid = NULL; struct object_id initialized_oid; int cnt, i, doubledash, filei; @@ -118,6 +122,9 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, check_attr_options, check_attr_usage, PARSE_OPT_KEEP_DASHDASH); + prepare_repo_settings(the_repository); + the_repository->settings.command_requires_full_index = 0; + if (repo_read_index(the_repository) < 0) { die("invalid cache"); } @@ -182,14 +189,14 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix) if (source) { if (repo_get_oid_tree(the_repository, source, &initialized_oid)) die("%s: not a valid tree-ish source", source); - tree_oid = &initialized_oid; + set_git_attr_source(source); } if (stdin_paths) - check_attr_stdin_paths(prefix, check, tree_oid, all_attrs); + check_attr_stdin_paths(prefix, check, all_attrs); else { for (i = filei; i < argc; i++) - check_attr(prefix, check, tree_oid, all_attrs, argv[i]); + check_attr(prefix, check, all_attrs, argv[i]); maybe_flush_or_die(stdout, "attribute to stdout"); } diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index ab776061c7..906cd96753 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -1,12 +1,14 @@ #define USE_THE_INDEX_VARIABLE #include "builtin.h" -#include "cache.h" #include "config.h" #include "dir.h" +#include "gettext.h" #include "quote.h" #include "pathspec.h" #include "parse-options.h" +#include "repository.h" #include "submodule.h" +#include "write-or-die.h" static int quiet, verbose, stdin_paths, show_non_matching, no_index; static const char * const check_ignore_usage[] = { diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c index 7dc47e4793..b8a05b8e07 100644 --- a/builtin/check-mailmap.c +++ b/builtin/check-mailmap.c @@ -1,8 +1,12 @@ #include "builtin.h" #include "config.h" +#include "gettext.h" +#include "ident.h" #include "mailmap.h" #include "parse-options.h" +#include "strbuf.h" #include "string-list.h" +#include "write-or-die.h" static int use_stdin; static const char * const check_mailmap_usage[] = { diff --git a/builtin/check-ref-format.c b/builtin/check-ref-format.c index fd0e5f8683..5eb6bdc3f6 100644 --- a/builtin/check-ref-format.c +++ b/builtin/check-ref-format.c @@ -2,9 +2,9 @@ * GIT - The information manager from hell */ -#include "cache.h" -#include "refs.h" #include "builtin.h" +#include "refs.h" +#include "setup.h" #include "strbuf.h" static const char builtin_check_ref_format_usage[] = @@ -60,6 +60,8 @@ int cmd_check_ref_format(int argc, const char **argv, const char *prefix) char *to_free = NULL; int ret = 1; + BUG_ON_NON_EMPTY_PREFIX(prefix); + if (argc == 2 && !strcmp(argv[1], "-h")) usage(builtin_check_ref_format_usage); diff --git a/builtin/checkout--worker.c b/builtin/checkout--worker.c index ede7dc32a4..6b62b5375b 100644 --- a/builtin/checkout--worker.c +++ b/builtin/checkout--worker.c @@ -1,9 +1,11 @@ #include "builtin.h" #include "config.h" #include "entry.h" +#include "gettext.h" #include "parallel-checkout.h" #include "parse-options.h" #include "pkt-line.h" +#include "read-cache-ll.h" static void packet_to_pc_item(const char *buffer, int len, struct parallel_checkout_item *pc_item) diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c index cf6fba97ba..3b68b47615 100644 --- a/builtin/checkout-index.c +++ b/builtin/checkout-index.c @@ -8,18 +8,23 @@ #include "builtin.h" #include "config.h" #include "dir.h" +#include "gettext.h" #include "lockfile.h" #include "quote.h" +#include "repository.h" #include "cache-tree.h" #include "parse-options.h" #include "entry.h" #include "parallel-checkout.h" +#include "read-cache-ll.h" +#include "setup.h" +#include "sparse-index.h" #define CHECKOUT_ALL 4 static int nul_term_line; static int checkout_stage; /* default to checkout stage0 */ static int ignore_skip_worktree; /* default to 0 */ -static int to_tempfile; +static int to_tempfile = -1; static char topath[4][TEMPORARY_FILENAME_LENGTH + 1]; static struct checkout state = CHECKOUT_INIT; @@ -188,15 +193,16 @@ static const char * const builtin_checkout_index_usage[] = { static int option_parse_stage(const struct option *opt, const char *arg, int unset) { + int *stage = opt->value; + BUG_ON_OPT_NEG(unset); if (!strcmp(arg, "all")) { - to_tempfile = 1; - checkout_stage = CHECKOUT_ALL; + *stage = CHECKOUT_ALL; } else { int ch = arg[0]; if ('1' <= ch && ch <= '3') - checkout_stage = arg[0] - '0'; + *stage = arg[0] - '0'; else die(_("stage should be between 1 and 3 or all")); } @@ -234,7 +240,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) N_("write the content to temporary files")), OPT_STRING(0, "prefix", &state.base_dir, N_("string"), N_("when creating files, prepend <string>")), - OPT_CALLBACK_F(0, "stage", NULL, "(1|2|3|all)", + OPT_CALLBACK_F(0, "stage", &checkout_stage, "(1|2|3|all)", N_("copy out the files from named stage"), PARSE_OPT_NONEG, option_parse_stage), OPT_END() @@ -264,6 +270,12 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) state.base_dir = ""; state.base_dir_len = strlen(state.base_dir); + if (to_tempfile < 0) + to_tempfile = (checkout_stage == CHECKOUT_ALL); + if (!to_tempfile && checkout_stage == CHECKOUT_ALL) + die(_("options '%s' and '%s' cannot be used together"), + "--stage=all", "--no-temp"); + /* * when --prefix is specified we do not want to update cache. */ diff --git a/builtin/checkout.c b/builtin/checkout.c index a5155cf55c..59cca80dd4 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -9,19 +9,30 @@ #include "config.h" #include "diff.h" #include "dir.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "hook.h" -#include "ll-merge.h" +#include "merge-ll.h" #include "lockfile.h" +#include "mem-pool.h" #include "merge-recursive.h" -#include "object-store.h" +#include "object-name.h" +#include "object-store-ll.h" #include "parse-options.h" +#include "path.h" +#include "preload-index.h" +#include "read-cache.h" #include "refs.h" #include "remote.h" #include "resolve-undo.h" #include "revision.h" #include "run-command.h" +#include "setup.h" #include "submodule.h" #include "submodule-config.h" +#include "symlinks.h" +#include "trace2.h" #include "tree.h" #include "tree-walk.h" #include "unpack-trees.h" @@ -75,7 +86,7 @@ struct checkout_opts { const char *ignore_unmerged_opt; int ignore_unmerged; int pathspec_file_nul; - const char *pathspec_from_file; + char *pathspec_from_file; const char *new_branch; const char *new_branch_force; @@ -432,8 +443,8 @@ static int checkout_worktree(const struct checkout_opts *opts, "Updated %d paths from %s", nr_checkouts), nr_checkouts, - find_unique_abbrev(&opts->source_tree->object.oid, - DEFAULT_ABBREV)); + repo_find_unique_abbrev(the_repository, &opts->source_tree->object.oid, + DEFAULT_ABBREV)); else if (!nr_unmerged || nr_checkouts) fprintf_ln(stderr, Q_("Updated %d path from the index", "Updated %d paths from the index", @@ -489,15 +500,37 @@ static int checkout_paths(const struct checkout_opts *opts, die(_("'%s' must be used when '%s' is not specified"), "--worktree", "--source"); - if (opts->checkout_index && !opts->checkout_worktree && - opts->writeout_stage) - die(_("'%s' or '%s' cannot be used with %s"), - "--ours", "--theirs", "--staged"); + /* + * Reject --staged option to the restore command when combined with + * merge-related options. Use the accept_ref flag to distinguish it + * from the checkout command, which does not accept --staged anyway. + * + * `restore --ours|--theirs --worktree --staged` could mean resolving + * conflicted paths to one side in both the worktree and the index, + * but does not currently. + * + * `restore --merge|--conflict=<style>` already recreates conflicts + * in both the worktree and the index, so adding --staged would be + * meaningless. + */ + if (!opts->accept_ref && opts->checkout_index) { + if (opts->writeout_stage) + die(_("'%s' or '%s' cannot be used with %s"), + "--ours", "--theirs", "--staged"); + + if (opts->merge) + die(_("'%s' or '%s' cannot be used with %s"), + "--merge", "--conflict", "--staged"); + } - if (opts->checkout_index && !opts->checkout_worktree && - opts->merge) - die(_("'%s' or '%s' cannot be used with %s"), - "--merge", "--conflict", "--staged"); + /* + * recreating unmerged index entries and writing out data from + * unmerged index entries would make no sense when checking out + * of a tree-ish. + */ + if ((opts->merge || opts->writeout_stage) && opts->source_tree) + die(_("'%s', '%s', or '%s' cannot be used when checking out of a tree"), + "--merge", "--ours", "--theirs"); if (opts->patch_mode) { enum add_p_mode patch_mode; @@ -536,6 +569,8 @@ static int checkout_paths(const struct checkout_opts *opts, if (opts->source_tree) read_tree_some(opts->source_tree, &opts->pathspec); + if (opts->merge) + unmerge_index(&the_index, &opts->pathspec, CE_MATCHED); ps_matched = xcalloc(opts->pathspec.nr, 1); @@ -559,10 +594,6 @@ static int checkout_paths(const struct checkout_opts *opts, } free(ps_matched); - /* "checkout -m path" to recreate conflicted state */ - if (opts->merge) - unmerge_marked_index(&the_index); - /* Any unmerged paths? */ for (pos = 0; pos < the_index.cache_nr; pos++) { const struct cache_entry *ce = the_index.cache[pos]; @@ -640,14 +671,16 @@ static void describe_detached_head(const char *msg, struct commit *commit) { struct strbuf sb = STRBUF_INIT; - if (!parse_commit(commit)) + if (!repo_parse_commit(the_repository, commit)) pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb); if (print_sha1_ellipsis()) { fprintf(stderr, "%s %s... %s\n", msg, - find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf); + repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV), + sb.buf); } else { fprintf(stderr, "%s %s %s\n", msg, - find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf); + repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV), + sb.buf); } strbuf_release(&sb); } @@ -701,7 +734,8 @@ static void setup_branch_path(struct branch_info *branch) * If this is a ref, resolve it; otherwise, look up the OID for our * expression. Failure here is okay. */ - if (!dwim_ref(branch->name, strlen(branch->name), &branch->oid, &branch->refname, 0)) + if (!repo_dwim_ref(the_repository, branch->name, strlen(branch->name), + &branch->oid, &branch->refname, 0)) repo_get_oid_committish(the_repository, branch->name, &branch->oid); strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL); @@ -753,7 +787,8 @@ static int merge_working_tree(const struct checkout_opts *opts, BUG("'switch --orphan' should never accept a commit as starting point"); new_tree = parse_tree_indirect(the_hash_algo->empty_tree); } else - new_tree = get_commit_tree(new_branch_info->commit); + new_tree = repo_get_commit_tree(the_repository, + new_branch_info->commit); if (opts->discard_changes) { ret = reset_tree(new_tree, opts, 1, writeout_error, new_branch_info); if (ret) @@ -815,7 +850,8 @@ static int merge_working_tree(const struct checkout_opts *opts, */ if (!old_branch_info->commit) return 1; - old_tree = get_commit_tree(old_branch_info->commit); + old_tree = repo_get_commit_tree(the_repository, + old_branch_info->commit); if (repo_index_has_changes(the_repository, old_tree, &sb)) die(_("cannot continue with staged changes in " @@ -835,7 +871,7 @@ static int merge_working_tree(const struct checkout_opts *opts, * entries in the index. */ - add_files_to_cache(NULL, NULL, 0); + add_files_to_cache(the_repository, NULL, NULL, 0, 0); init_merge_options(&o, the_repository); o.verbosity = 0; work = write_in_core_index_as_tree(the_repository); @@ -887,7 +923,7 @@ static void report_tracking(struct branch_info *new_branch_info) struct strbuf sb = STRBUF_INIT; struct branch *branch = branch_get(new_branch_info->name); - if (!format_tracking_info(branch, &sb, AHEAD_BEHIND_FULL)) + if (!format_tracking_info(branch, &sb, AHEAD_BEHIND_FULL, 1)) return; fputs(sb.buf, stdout); strbuf_release(&sb); @@ -1004,7 +1040,7 @@ static void describe_one_orphan(struct strbuf *sb, struct commit *commit) strbuf_addstr(sb, " "); strbuf_add_unique_abbrev(sb, &commit->object.oid, DEFAULT_ABBREV); strbuf_addch(sb, ' '); - if (!parse_commit(commit)) + if (!repo_parse_commit(the_repository, commit)) pp_commit_easy(CMIT_FMT_ONELINE, commit, sb); strbuf_addch(sb, '\n'); } @@ -1060,7 +1096,7 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs) " git branch <new-branch-name> %s\n\n", /* Give ngettext() the count */ lost), - find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV)); + repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV)); } /* @@ -1160,11 +1196,14 @@ static int switch_branches(const struct checkout_opts *opts, return ret || writeout_error; } -static int git_checkout_config(const char *var, const char *value, void *cb) +static int git_checkout_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { struct checkout_opts *opts = cb; if (!strcmp(var, "diff.ignoresubmodules")) { + if (!value) + return config_error_nonbool(var); handle_ignore_submodules_arg(&opts->diff_options, value); return 0; } @@ -1176,7 +1215,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb) if (starts_with(var, "submodule.")) return git_default_submodule_config(var, value, NULL); - return git_xmerge_config(var, value, NULL); + return git_xmerge_config(var, value, ctx, NULL); } static void setup_new_branch_info_and_source_tree( @@ -1204,7 +1243,8 @@ static void setup_new_branch_info_and_source_tree( *source_tree = parse_tree_indirect(rev); } else { parse_commit_or_die(new_branch_info->commit); - *source_tree = get_commit_tree(new_branch_info->commit); + *source_tree = repo_get_commit_tree(the_repository, + new_branch_info->commit); } } @@ -1322,7 +1362,7 @@ static int parse_branchname_arg(int argc, const char **argv, if (!strcmp(arg, "-")) arg = "@{-1}"; - if (get_oid_mb(arg, rev)) { + if (repo_get_oid_mb(the_repository, arg, rev)) { /* * Either case (3) or (4), with <something> not being * a commit, or an attempt to use case (1) with an @@ -1419,7 +1459,8 @@ static void die_expecting_a_branch(const struct branch_info *branch_info) char *to_free; int code; - if (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free, 0) == 1) { + if (repo_dwim_ref(the_repository, branch_info->name, + strlen(branch_info->name), &oid, &to_free, 0) == 1) { const char *ref = to_free; if (skip_prefix(ref, "refs/tags/", &ref)) @@ -1477,6 +1518,26 @@ static void die_if_some_operation_in_progress(void) wt_status_state_free_buffers(&state); } +/* + * die if attempting to checkout an existing branch that is in use + * in another worktree, unless ignore-other-wortrees option is given. + * The check is bypassed when the branch is already the current one, + * as it will not make things any worse. + */ +static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts, + const char *full_ref) +{ + int flags; + char *head_ref; + + if (opts->ignore_other_worktrees) + return; + head_ref = resolve_refdup("HEAD", 0, NULL, &flags); + if (head_ref && (!(flags & REF_ISSYMREF) || strcmp(head_ref, full_ref))) + die_if_checked_out(full_ref, 1); + free(head_ref); +} + static int checkout_branch(struct checkout_opts *opts, struct branch_info *new_branch_info) { @@ -1537,14 +1598,15 @@ static int checkout_branch(struct checkout_opts *opts, if (!opts->can_switch_when_in_progress) die_if_some_operation_in_progress(); - if (new_branch_info->path && !opts->force_detach && !opts->new_branch && - !opts->ignore_other_worktrees) { - int flag; - char *head_ref = resolve_refdup("HEAD", 0, NULL, &flag); - if (head_ref && - (!(flag & REF_ISSYMREF) || strcmp(head_ref, new_branch_info->path))) - die_if_checked_out(new_branch_info->path, 1); - free(head_ref); + /* "git checkout <branch>" */ + if (new_branch_info->path && !opts->force_detach && !opts->new_branch) + die_if_switching_to_a_branch_in_use(opts, new_branch_info->path); + + /* "git checkout -B <branch>" */ + if (opts->new_branch_force) { + char *full_ref = xstrfmt("refs/heads/%s", opts->new_branch); + die_if_switching_to_a_branch_in_use(opts, full_ref); + free(full_ref); } if (!new_branch_info->commit && opts->new_branch) { @@ -1661,8 +1723,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix, } if (opts->conflict_style) { + struct key_value_info kvi = KVI_INIT; + struct config_context ctx = { + .kvi = &kvi, + }; opts->merge = 1; /* implied */ - git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL); + git_xmerge_config("merge.conflictstyle", opts->conflict_style, + &ctx, NULL); } if (opts->force) { opts->discard_changes = 1; @@ -1748,7 +1815,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix, } else if (!opts->accept_ref && opts->from_treeish) { struct object_id rev; - if (get_oid_mb(opts->from_treeish, &rev)) + if (repo_get_oid_mb(the_repository, opts->from_treeish, &rev)) die(_("could not resolve %s"), opts->from_treeish); setup_new_branch_info_and_source_tree(new_branch_info, @@ -1876,6 +1943,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) options, checkout_usage, &new_branch_info); branch_info_release(&new_branch_info); clear_pathspec(&opts.pathspec); + free(opts.pathspec_from_file); FREE_AND_NULL(options); return ret; } diff --git a/builtin/clean.c b/builtin/clean.c index 10aaa8c603..d90766cad3 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -8,10 +8,15 @@ #define USE_THE_INDEX_VARIABLE #include "builtin.h" -#include "cache.h" +#include "abspath.h" #include "config.h" #include "dir.h" +#include "gettext.h" #include "parse-options.h" +#include "path.h" +#include "read-cache-ll.h" +#include "repository.h" +#include "setup.h" #include "string-list.h" #include "quote.h" #include "column.h" @@ -99,7 +104,8 @@ struct menu_stuff { define_list_config_array(color_interactive_slots); -static int git_clean_config(const char *var, const char *value, void *cb) +static int git_clean_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { const char *slot_name; @@ -126,8 +132,10 @@ static int git_clean_config(const char *var, const char *value, void *cb) return 0; } - /* inspect the color.ui config variable and others */ - return git_color_default_config(var, value, cb); + if (git_color_config(var, value, cb) < 0) + return -1; + + return git_default_config(var, value, ctx, cb); } static const char *clean_get_color(enum color_clean ix) @@ -963,7 +971,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) dir.flags |= DIR_SHOW_OTHER_DIRECTORIES; if (ignored && ignored_only) - die(_("-x and -X cannot be used together")); + die(_("options '%s' and '%s' cannot be used together"), "-x", "-X"); if (!ignored) setup_standard_excludes(&dir); if (ignored_only) diff --git a/builtin/clone.c b/builtin/clone.c index 65b5b7db6d..d1801bbe1a 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -10,13 +10,20 @@ #define USE_THE_INDEX_VARIABLE #include "builtin.h" +#include "abspath.h" +#include "advice.h" #include "config.h" +#include "copy.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "lockfile.h" #include "parse-options.h" #include "fetch-pack.h" #include "refs.h" #include "refspec.h" -#include "object-store.h" +#include "object-file.h" +#include "object-store-ll.h" #include "tree.h" #include "tree-walk.h" #include "unpack-trees.h" @@ -29,8 +36,11 @@ #include "branch.h" #include "remote.h" #include "run-command.h" +#include "setup.h" #include "connected.h" #include "packfile.h" +#include "path.h" +#include "pkt-line.h" #include "list-objects-filter-options.h" #include "hook.h" #include "bundle.h" @@ -151,10 +161,7 @@ static struct option builtin_clone_options[] = { N_("set config inside the new repository")), OPT_STRING_LIST(0, "server-option", &server_options, N_("server-specific"), N_("option to transmit")), - OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"), - TRANSPORT_FAMILY_IPV4), - OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"), - TRANSPORT_FAMILY_IPV6), + OPT_IPVERSION(&family), OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), OPT_BOOL(0, "also-filter-submodules", &option_filter_submodules, N_("apply partial clone filters to submodules")), @@ -326,8 +333,18 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest, iter = dir_iterator_begin(src->buf, DIR_ITERATOR_PEDANTIC); - if (!iter) + if (!iter) { + if (errno == ENOTDIR) { + int saved_errno = errno; + struct stat st; + + if (!lstat(src->buf, &st) && S_ISLNK(st.st_mode)) + die(_("'%s' is a symlink, refusing to clone with --local"), + src->buf); + errno = saved_errno; + } die_errno(_("failed to start iterator over '%s'"), src->buf); + } strbuf_addch(src, '/'); src_len = src->len; @@ -547,9 +564,9 @@ static void write_followtags(const struct ref *refs, const char *msg) continue; if (ends_with(ref->name, "^{}")) continue; - if (!has_object_file_with_flags(&ref->old_oid, - OBJECT_INFO_QUICK | - OBJECT_INFO_SKIP_FETCH_OBJECT)) + if (!repo_has_object_file_with_flags(the_repository, &ref->old_oid, + OBJECT_INFO_QUICK | + OBJECT_INFO_SKIP_FETCH_OBJECT)) continue; update_ref(msg, ref->name, &ref->old_oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR); @@ -770,9 +787,12 @@ static int checkout(int submodule_progress, int filter_submodules) return err; } -static int git_clone_config(const char *k, const char *v, void *cb) +static int git_clone_config(const char *k, const char *v, + const struct config_context *ctx, void *cb) { if (!strcmp(k, "clone.defaultremotename")) { + if (!v) + return config_error_nonbool(k); free(remote_name); remote_name = xstrdup(v); } @@ -781,17 +801,19 @@ static int git_clone_config(const char *k, const char *v, void *cb) if (!strcmp(k, "clone.filtersubmodules")) config_filter_submodules = git_config_bool(k, v); - return git_default_config(k, v, cb); + return git_default_config(k, v, ctx, cb); } -static int write_one_config(const char *key, const char *value, void *data) +static int write_one_config(const char *key, const char *value, + const struct config_context *ctx, + void *data) { /* * give git_clone_config a chance to write config values back to the * environment, since git_config_set_multivar_gently only deals with * config-file writes */ - int apply_failed = git_clone_config(key, value, data); + int apply_failed = git_clone_config(key, value, ctx, data); if (apply_failed) return apply_failed; @@ -909,6 +931,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) int err = 0, complete_refs_before_fetch = 1; int submodule_progress; int filter_submodules = 0; + int hash_algo; + const int do_not_override_repo_unix_permissions = -1; struct transport_ls_refs_options transport_ls_refs_options = TRANSPORT_LS_REFS_OPTIONS_INIT; @@ -943,7 +967,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix) } if (bundle_uri && deepen) - die(_("--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-exclude")); + die(_("options '%s' and '%s' cannot be used together"), + "--bundle-uri", + "--depth/--shallow-since/--shallow-exclude"); repo_name = argv[0]; @@ -1076,7 +1102,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) } init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, NULL, - INIT_DB_QUIET); + do_not_override_repo_unix_permissions, INIT_DB_QUIET); if (real_git_dir) { free((char *)git_dir); @@ -1297,15 +1323,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix) } } - if (mapped_refs) { - int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport)); - /* * Now that we know what algorithm the remote side is using, * let's set ours to the same thing. */ - initialize_repository_version(hash_algo, 1); - repo_set_hash_algo(the_repository, hash_algo); + hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport)); + initialize_repository_version(hash_algo, 1); + repo_set_hash_algo(the_repository, hash_algo); + + if (mapped_refs) { /* * transport_get_remote_refs() may return refs with null sha-1 * in mapped_refs (see struct transport->get_refs_list diff --git a/builtin/column.c b/builtin/column.c index 158fdf53d9..e80218f81f 100644 --- a/builtin/column.c +++ b/builtin/column.c @@ -1,6 +1,6 @@ #include "builtin.h" -#include "cache.h" #include "config.h" +#include "gettext.h" #include "strbuf.h" #include "parse-options.h" #include "string-list.h" @@ -12,7 +12,8 @@ static const char * const builtin_column_usage[] = { }; static unsigned int colopts; -static int column_config(const char *var, const char *value, void *cb) +static int column_config(const char *var, const char *value, + const struct config_context *ctx UNUSED, void *cb) { return git_column_config(var, value, cb, &colopts); } @@ -55,5 +56,7 @@ int cmd_column(int argc, const char **argv, const char *prefix) string_list_append(&list, sb.buf); print_columns(&list, colopts, &copts); + strbuf_release(&sb); + string_list_clear(&list, 0); return 0; } diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c index 93704f95a9..45d035af60 100644 --- a/builtin/commit-graph.c +++ b/builtin/commit-graph.c @@ -1,13 +1,19 @@ #include "builtin.h" +#include "commit.h" #include "config.h" #include "dir.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "lockfile.h" #include "parse-options.h" #include "repository.h" #include "commit-graph.h" -#include "object-store.h" +#include "object-store-ll.h" #include "progress.h" +#include "replace-object.h" #include "tag.h" +#include "trace2.h" #define BUILTIN_COMMIT_GRAPH_VERIFY_USAGE \ N_("git commit-graph verify [--object-dir <dir>] [--shallow] [--[no-]progress]") @@ -63,10 +69,12 @@ static int graph_verify(int argc, const char **argv, const char *prefix) struct commit_graph *graph = NULL; struct object_directory *odb = NULL; char *graph_name; - int open_ok; + char *chain_name; + enum { OPENED_NONE, OPENED_GRAPH, OPENED_CHAIN } opened = OPENED_NONE; int fd; struct stat st; int flags = 0; + int incomplete_chain = 0; int ret; static struct option builtin_commit_graph_verify_options[] = { @@ -96,24 +104,39 @@ static int graph_verify(int argc, const char **argv, const char *prefix) odb = find_odb(the_repository, opts.obj_dir); graph_name = get_commit_graph_filename(odb); - open_ok = open_commit_graph(graph_name, &fd, &st); - if (!open_ok && errno != ENOENT) + chain_name = get_commit_graph_chain_filename(odb); + 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)) + opened = OPENED_CHAIN; + else if (errno != ENOENT) + die_errno(_("could not open commit-graph chain '%s'"), chain_name); FREE_AND_NULL(graph_name); + FREE_AND_NULL(chain_name); FREE_AND_NULL(options); - if (open_ok) + if (opened == OPENED_NONE) + return 0; + else if (opened == OPENED_GRAPH) graph = load_commit_graph_one_fd_st(the_repository, fd, &st, odb); else - graph = read_commit_graph_one(the_repository, odb); + graph = load_commit_graph_chain_fd_st(the_repository, fd, &st, + &incomplete_chain); - /* Return failure if open_ok predicted success */ if (!graph) - return !!open_ok; + return 1; ret = verify_commit_graph(the_repository, graph, flags); free_commit_graph(graph); + + if (incomplete_chain) { + error("one or more commit-graph chain files could not be loaded"); + ret |= 1; + } + return ret; } @@ -181,10 +204,11 @@ static int write_option_max_new_filters(const struct option *opt, } static int git_commit_graph_write_config(const char *var, const char *value, + const struct config_context *ctx, void *cb UNUSED) { if (!strcmp(var, "commitgraph.maxnewfilters")) - write_opts.max_new_filters = git_config_int(var, value); + write_opts.max_new_filters = git_config_int(var, value, ctx->kvi); /* * No need to fall-back to 'git_default_config', since this was already * called in 'cmd_commit_graph()'. @@ -304,6 +328,7 @@ cleanup: FREE_AND_NULL(options); string_list_clear(&pack_indexes, 0); strbuf_release(&buf); + oidset_clear(&commits); return result; } @@ -319,7 +344,7 @@ int cmd_commit_graph(int argc, const char **argv, const char *prefix) git_config(git_default_config, NULL); - read_replace_refs = 0; + disable_replace_refs(); save_commit_buffer = 0; argc = parse_options(argc, argv, prefix, options, diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c index cc8d584be2..02625e7176 100644 --- a/builtin/commit-tree.c +++ b/builtin/commit-tree.c @@ -3,13 +3,15 @@ * * Copyright (C) Linus Torvalds, 2005 */ -#include "cache.h" +#include "builtin.h" #include "config.h" -#include "object-store.h" +#include "gettext.h" +#include "hex.h" +#include "object-name.h" +#include "object-store-ll.h" #include "repository.h" #include "commit.h" #include "tree.h" -#include "builtin.h" #include "utf8.h" #include "gpg-interface.h" #include "parse-options.h" @@ -37,14 +39,6 @@ static void new_parent(struct commit *parent, struct commit_list **parents_p) commit_list_insert(parent, parents_p); } -static int commit_tree_config(const char *var, const char *value, void *cb) -{ - int status = git_gpg_config(var, value, NULL); - if (status) - return status; - return git_default_config(var, value, cb); -} - static int parse_parent_arg_callback(const struct option *opt, const char *arg, int unset) { @@ -53,7 +47,7 @@ static int parse_parent_arg_callback(const struct option *opt, BUG_ON_OPT_NEG_NOARG(unset, arg); - if (get_oid_commit(arg, &oid)) + if (repo_get_oid_commit(the_repository, arg, &oid)) die(_("not a valid object name %s"), arg); assert_oid_type(&oid, OBJ_COMMIT); @@ -121,7 +115,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) OPT_END() }; - git_config(commit_tree_config, NULL); + git_config(git_default_config, NULL); if (argc < 2 || !strcmp(argv[1], "-h")) usage_with_options(commit_tree_usage, options); @@ -131,7 +125,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) if (argc != 1) die(_("must give exactly one tree")); - if (get_oid_tree(argv[0], &tree_oid)) + if (repo_get_oid_tree(the_repository, argv[0], &tree_oid)) die(_("not a valid object name %s"), argv[0]); if (!buffer.len) { diff --git a/builtin/commit.c b/builtin/commit.c index 985a0445b7..781af2e206 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -6,16 +6,19 @@ */ #define USE_THE_INDEX_VARIABLE -#include "cache.h" +#include "builtin.h" +#include "advice.h" #include "config.h" #include "lockfile.h" #include "cache-tree.h" #include "color.h" #include "dir.h" -#include "builtin.h" +#include "editor.h" +#include "environment.h" #include "diff.h" #include "diffcore.h" #include "commit.h" +#include "gettext.h" #include "revision.h" #include "wt-status.h" #include "run-command.h" @@ -24,7 +27,11 @@ #include "log-tree.h" #include "strbuf.h" #include "utf8.h" +#include "object-name.h" #include "parse-options.h" +#include "path.h" +#include "preload-index.h" +#include "read-cache.h" #include "string-list.h" #include "rerere.h" #include "unpack-trees.h" @@ -33,6 +40,7 @@ #include "gpg-interface.h" #include "column.h" #include "sequencer.h" +#include "sparse-index.h" #include "mailmap.h" #include "help.h" #include "commit-reach.h" @@ -442,11 +450,12 @@ static const char *prepare_index(const char **argv, const char *prefix, if (all || (also && pathspec.nr)) { repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR); - add_files_to_cache(also ? prefix : NULL, &pathspec, 0); + add_files_to_cache(the_repository, also ? prefix : NULL, + &pathspec, 0, 0); refresh_cache_or_die(refresh_flags); cache_tree_update(&the_index, WRITE_TREE_SILENT); if (write_locked_index(&the_index, &index_lock, 0)) - die(_("unable to write new_index file")); + die(_("unable to write new index file")); commit_style = COMMIT_NORMAL; ret = get_lock_file_path(&index_lock); goto out; @@ -470,7 +479,7 @@ static const char *prepare_index(const char **argv, const char *prefix, cache_tree_update(&the_index, WRITE_TREE_SILENT); if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK | SKIP_IF_UNCHANGED)) - die(_("unable to write new_index file")); + die(_("unable to write new index file")); commit_style = COMMIT_AS_IS; ret = get_index_file(); goto out; @@ -518,7 +527,7 @@ static const char *prepare_index(const char **argv, const char *prefix, refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL); cache_tree_update(&the_index, WRITE_TREE_SILENT); if (write_locked_index(&the_index, &index_lock, 0)) - die(_("unable to write new_index file")); + die(_("unable to write new index file")); hold_lock_file_for_update(&false_lock, git_path("next-index-%"PRIuMAX, @@ -557,7 +566,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int s->index_file = index_file; s->fp = fp; s->nowarn = nowarn; - s->is_initial = get_oid(s->reference, &oid) ? 1 : 0; + s->is_initial = repo_get_oid(the_repository, s->reference, &oid) ? 1 : 0; if (!s->is_initial) oidcpy(&s->oid_commit, &oid); s->status_format = status_format; @@ -712,15 +721,15 @@ static void prepare_amend_commit(struct commit *commit, struct strbuf *sb, { const char *buffer, *subject, *fmt; - buffer = get_commit_buffer(commit, NULL); + buffer = repo_get_commit_buffer(the_repository, commit, NULL); find_commit_subject(buffer, &subject); /* * If we amend the 'amend!' commit then we don't want to * duplicate the subject line. */ fmt = starts_with(subject, "amend!") ? "%b" : "%B"; - format_commit_message(commit, fmt, sb, ctx); - unuse_commit_buffer(commit, buffer); + repo_format_commit_message(the_repository, commit, fmt, sb, ctx); + repo_unuse_commit_buffer(the_repository, commit, buffer); } static int prepare_to_commit(const char *index_file, const char *prefix, @@ -758,10 +767,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix, struct commit *c; c = lookup_commit_reference_by_name(squash_message); if (!c) - die(_("could not lookup commit %s"), squash_message); + die(_("could not lookup commit '%s'"), squash_message); ctx.output_encoding = get_commit_output_encoding(); - format_commit_message(c, "squash! %s\n\n", &sb, - &ctx); + repo_format_commit_message(the_repository, c, + "squash! %s\n\n", &sb, + &ctx); } } @@ -792,10 +802,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix, char *fmt; commit = lookup_commit_reference_by_name(fixup_commit); if (!commit) - die(_("could not lookup commit %s"), fixup_commit); + die(_("could not lookup commit '%s'"), fixup_commit); ctx.output_encoding = get_commit_output_encoding(); fmt = xstrfmt("%s! %%s\n\n", fixup_prefix); - format_commit_message(commit, fmt, &sb, &ctx); + repo_format_commit_message(the_repository, commit, fmt, &sb, + &ctx); free(fmt); hook_arg1 = "message"; @@ -886,7 +897,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, s->hints = 0; if (clean_message_contents) - strbuf_stripspace(&sb, 0); + strbuf_stripspace(&sb, '\0'); if (signoff) append_signoff(&sb, ignore_non_trailer(sb.buf, sb.len), 0); @@ -991,16 +1002,13 @@ static int prepare_to_commit(const char *index_file, const char *prefix, struct object_id oid; const char *parent = "HEAD"; - if (!the_index.cache_nr) { - discard_index(&the_index); - if (repo_read_index(the_repository) < 0) - die(_("Cannot read index")); - } + if (!the_index.initialized && repo_read_index(the_repository) < 0) + die(_("Cannot read index")); if (amend) parent = "HEAD^1"; - if (get_oid(parent, &oid)) { + if (repo_get_oid(the_repository, parent, &oid)) { int i, ita_nr = 0; /* TODO: audit for interaction with sparse-index. */ @@ -1036,7 +1044,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix, struct child_process run_trailer = CHILD_PROCESS_INIT; strvec_pushl(&run_trailer.args, "interpret-trailers", - "--in-place", git_path_commit_editmsg(), NULL); + "--in-place", "--no-divider", + git_path_commit_editmsg(), NULL); strvec_pushv(&run_trailer.args, trailer_args.v); run_trailer.git_cmd = 1; if (run_command(&run_trailer)) @@ -1135,7 +1144,8 @@ static const char *find_author_by_nickname(const char *name) struct pretty_print_context ctx = {0}; ctx.date_mode.type = DATE_NORMAL; strbuf_release(&buf); - format_commit_message(commit, "%aN <%aE>", &buf, &ctx); + repo_format_commit_message(the_repository, commit, + "%aN <%aE>", &buf, &ctx); release_revisions(&revs); return strbuf_detach(&buf, NULL); } @@ -1181,9 +1191,9 @@ static const char *read_commit_message(const char *name) commit = lookup_commit_reference_by_name(name); if (!commit) - die(_("could not lookup commit %s"), name); + die(_("could not lookup commit '%s'"), name); out_enc = get_commit_output_encoding(); - return logmsg_reencode(commit, NULL, out_enc); + return repo_logmsg_reencode(the_repository, commit, NULL, out_enc); } /* @@ -1397,7 +1407,8 @@ static int parse_status_slot(const char *slot) return LOOKUP_CONFIG(color_status_slots, slot); } -static int git_status_config(const char *k, const char *v, void *cb) +static int git_status_config(const char *k, const char *v, + const struct config_context *ctx, void *cb) { struct wt_status *s = cb; const char *slot_name; @@ -1406,7 +1417,8 @@ static int git_status_config(const char *k, const char *v, void *cb) return git_column_config(k, v, "status", &s->colopts); if (!strcmp(k, "status.submodulesummary")) { int is_bool; - s->submodule_summary = git_config_bool_or_int(k, v, &is_bool); + s->submodule_summary = git_config_bool_or_int(k, v, ctx->kvi, + &is_bool); if (is_bool && s->submodule_summary) s->submodule_summary = -1; return 0; @@ -1466,11 +1478,11 @@ static int git_status_config(const char *k, const char *v, void *cb) } if (!strcmp(k, "diff.renamelimit")) { if (s->rename_limit == -1) - s->rename_limit = git_config_int(k, v); + s->rename_limit = git_config_int(k, v, ctx->kvi); return 0; } if (!strcmp(k, "status.renamelimit")) { - s->rename_limit = git_config_int(k, v); + s->rename_limit = git_config_int(k, v, ctx->kvi); return 0; } if (!strcmp(k, "diff.renames")) { @@ -1482,7 +1494,7 @@ static int git_status_config(const char *k, const char *v, void *cb) s->detect_rename = git_config_rename(k, v); return 0; } - return git_diff_ui_config(k, v, NULL); + return git_diff_ui_config(k, v, ctx, NULL); } int cmd_status(int argc, const char **argv, const char *prefix) @@ -1567,7 +1579,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) else fd = -1; - s.is_initial = get_oid(s.reference, &oid) ? 1 : 0; + s.is_initial = repo_get_oid(the_repository, s.reference, &oid) ? 1 : 0; if (!s.is_initial) oidcpy(&s.oid_commit, &oid); @@ -1597,10 +1609,10 @@ int cmd_status(int argc, const char **argv, const char *prefix) return 0; } -static int git_commit_config(const char *k, const char *v, void *cb) +static int git_commit_config(const char *k, const char *v, + const struct config_context *ctx, void *cb) { struct wt_status *s = cb; - int status; if (!strcmp(k, "commit.template")) return git_config_pathname(&template_file, k, v); @@ -1616,14 +1628,12 @@ static int git_commit_config(const char *k, const char *v, void *cb) } if (!strcmp(k, "commit.verbose")) { int is_bool; - config_commit_verbose = git_config_bool_or_int(k, v, &is_bool); + config_commit_verbose = git_config_bool_or_int(k, v, ctx->kvi, + &is_bool); return 0; } - status = git_gpg_config(k, v, NULL); - if (status) - return status; - return git_status_config(k, v, s); + return git_status_config(k, v, ctx, s); } int cmd_commit(int argc, const char **argv, const char *prefix) @@ -1714,11 +1724,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix) status_format = STATUS_FORMAT_NONE; /* Ignore status.short */ s.colopts = 0; - if (get_oid("HEAD", &oid)) + if (repo_get_oid(the_repository, "HEAD", &oid)) current_head = NULL; else { current_head = lookup_commit_or_die(&oid, "HEAD"); - if (parse_commit(current_head)) + if (repo_parse_commit(the_repository, current_head)) die(_("could not parse HEAD commit")); } verbose = -1; /* unspecified */ @@ -1852,7 +1862,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) if (commit_index_files()) die(_("repository has been updated, but unable to write\n" - "new_index file. Check that disk is not full and quota is\n" + "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(); diff --git a/builtin/config.c b/builtin/config.c index 060cf9f3e0..11a4d4ef14 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -1,10 +1,18 @@ #include "builtin.h" -#include "cache.h" +#include "abspath.h" #include "config.h" #include "color.h" +#include "editor.h" +#include "environment.h" +#include "repository.h" +#include "gettext.h" +#include "ident.h" #include "parse-options.h" #include "urlmatch.h" +#include "path.h" #include "quote.h" +#include "setup.h" +#include "strbuf.h" #include "worktree.h" static const char *const builtin_config_usage[] = { @@ -185,37 +193,42 @@ static void check_argc(int argc, int min, int max) usage_builtin_config(); } -static void show_config_origin(struct strbuf *buf) +static void show_config_origin(const struct key_value_info *kvi, + struct strbuf *buf) { const char term = end_nul ? '\0' : '\t'; - strbuf_addstr(buf, current_config_origin_type()); + strbuf_addstr(buf, config_origin_type_name(kvi->origin_type)); strbuf_addch(buf, ':'); if (end_nul) - strbuf_addstr(buf, current_config_name()); + strbuf_addstr(buf, kvi->filename ? kvi->filename : ""); else - quote_c_style(current_config_name(), buf, NULL, 0); + quote_c_style(kvi->filename ? kvi->filename : "", buf, NULL, 0); strbuf_addch(buf, term); } -static void show_config_scope(struct strbuf *buf) +static void show_config_scope(const struct key_value_info *kvi, + struct strbuf *buf) { const char term = end_nul ? '\0' : '\t'; - const char *scope = config_scope_name(current_config_scope()); + const char *scope = config_scope_name(kvi->scope); strbuf_addstr(buf, N_(scope)); strbuf_addch(buf, term); } static int show_all_config(const char *key_, const char *value_, + const struct config_context *ctx, void *cb UNUSED) { + const struct key_value_info *kvi = ctx->kvi; + if (show_origin || show_scope) { struct strbuf buf = STRBUF_INIT; if (show_scope) - show_config_scope(&buf); + show_config_scope(kvi, &buf); if (show_origin) - show_config_origin(&buf); + show_config_origin(kvi, &buf); /* Use fwrite as "buf" can contain \0's if "end_null" is set. */ fwrite(buf.buf, 1, buf.len, stdout); strbuf_release(&buf); @@ -233,12 +246,13 @@ struct strbuf_list { int alloc; }; -static int format_config(struct strbuf *buf, const char *key_, const char *value_) +static int format_config(struct strbuf *buf, const char *key_, + const char *value_, const struct key_value_info *kvi) { if (show_scope) - show_config_scope(buf); + show_config_scope(kvi, buf); if (show_origin) - show_config_origin(buf); + show_config_origin(kvi, buf); if (show_keys) strbuf_addstr(buf, key_); if (!omit_values) { @@ -247,13 +261,14 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value if (type == TYPE_INT) strbuf_addf(buf, "%"PRId64, - git_config_int64(key_, value_ ? value_ : "")); + git_config_int64(key_, value_ ? value_ : "", kvi)); else if (type == TYPE_BOOL) strbuf_addstr(buf, git_config_bool(key_, value_) ? "true" : "false"); else if (type == TYPE_BOOL_OR_INT) { int is_bool, v; - v = git_config_bool_or_int(key_, value_, &is_bool); + v = git_config_bool_or_int(key_, value_, kvi, + &is_bool); if (is_bool) strbuf_addstr(buf, v ? "true" : "false"); else @@ -292,9 +307,11 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value return 0; } -static int collect_config(const char *key_, const char *value_, void *cb) +static int collect_config(const char *key_, const char *value_, + const struct config_context *ctx, void *cb) { struct strbuf_list *values = cb; + const struct key_value_info *kvi = ctx->kvi; if (!use_key_regexp && strcmp(key_, key)) return 0; @@ -309,7 +326,7 @@ static int collect_config(const char *key_, const char *value_, void *cb) ALLOC_GROW(values->items, values->nr + 1, values->alloc); strbuf_init(&values->items[values->nr], 0); - return format_config(&values->items[values->nr++], key_, value_); + return format_config(&values->items[values->nr++], key_, value_, kvi); } static int get_value(const char *key_, const char *regex_, unsigned flags) @@ -367,14 +384,18 @@ static int get_value(const char *key_, const char *regex_, unsigned flags) } config_with_options(collect_config, &values, - &given_config_source, &config_options); + &given_config_source, the_repository, + &config_options); if (!values.nr && default_value) { + struct key_value_info kvi = KVI_INIT; struct strbuf *item; + + kvi_from_param(&kvi); ALLOC_GROW(values.items, values.nr + 1, values.alloc); item = &values.items[values.nr++]; strbuf_init(item, 0); - if (format_config(item, key_, default_value) < 0) + if (format_config(item, key_, default_value, &kvi) < 0) die(_("failed to format default config value: %s"), default_value); } @@ -403,7 +424,8 @@ free_strings: return ret; } -static char *normalize_value(const char *key, const char *value) +static char *normalize_value(const char *key, const char *value, + struct key_value_info *kvi) { if (!value) return NULL; @@ -418,12 +440,12 @@ static char *normalize_value(const char *key, const char *value) */ return xstrdup(value); if (type == TYPE_INT) - return xstrfmt("%"PRId64, git_config_int64(key, value)); + return xstrfmt("%"PRId64, git_config_int64(key, value, kvi)); if (type == TYPE_BOOL) return xstrdup(git_config_bool(key, value) ? "true" : "false"); if (type == TYPE_BOOL_OR_INT) { int is_bool, v; - v = git_config_bool_or_int(key, value, &is_bool); + v = git_config_bool_or_int(key, value, kvi, &is_bool); if (!is_bool) return xstrfmt("%d", v); else @@ -460,6 +482,7 @@ static const char *get_colorbool_slot; static char parsed_color[COLOR_MAXLEN]; static int git_get_color_config(const char *var, const char *value, + const struct config_context *ctx UNUSED, void *cb UNUSED) { if (!strcmp(var, get_color_slot)) { @@ -478,7 +501,8 @@ static void get_color(const char *var, const char *def_color) get_color_found = 0; parsed_color[0] = '\0'; config_with_options(git_get_color_config, NULL, - &given_config_source, &config_options); + &given_config_source, the_repository, + &config_options); if (!get_color_found && def_color) { if (color_parse(def_color, parsed_color) < 0) @@ -492,6 +516,7 @@ static int get_colorbool_found; static int get_diff_color_found; static int get_color_ui_found; static int git_get_colorbool_config(const char *var, const char *value, + const struct config_context *ctx UNUSED, void *data UNUSED) { if (!strcmp(var, get_colorbool_slot)) @@ -510,7 +535,8 @@ static int get_colorbool(const char *var, int print) get_diff_color_found = -1; get_color_ui_found = -1; config_with_options(git_get_colorbool_config, NULL, - &given_config_source, &config_options); + &given_config_source, the_repository, + &config_options); if (get_colorbool_found < 0) { if (!strcmp(get_colorbool_slot, "color.diff")) @@ -547,13 +573,17 @@ static void check_write(void) struct urlmatch_current_candidate_value { char value_is_null; struct strbuf value; + struct key_value_info kvi; }; -static int urlmatch_collect_fn(const char *var, const char *value, void *cb) +static int urlmatch_collect_fn(const char *var, const char *value, + const struct config_context *ctx, + void *cb) { struct string_list *values = cb; struct string_list_item *item = string_list_insert(values, var); struct urlmatch_current_candidate_value *matched = item->util; + const struct key_value_info *kvi = ctx->kvi; if (!matched) { matched = xmalloc(sizeof(*matched)); @@ -562,6 +592,7 @@ static int urlmatch_collect_fn(const char *var, const char *value, void *cb) } else { strbuf_reset(&matched->value); } + matched->kvi = *kvi; if (value) { strbuf_addstr(&matched->value, value); @@ -599,7 +630,8 @@ static int get_urlmatch(const char *var, const char *url) } config_with_options(urlmatch_config_entry, &config, - &given_config_source, &config_options); + &given_config_source, the_repository, + &config_options); ret = !values.nr; @@ -608,7 +640,8 @@ static int get_urlmatch(const char *var, const char *url) struct strbuf buf = STRBUF_INIT; format_config(&buf, item->string, - matched->value_is_null ? NULL : matched->value.buf); + matched->value_is_null ? NULL : matched->value.buf, + &matched->kvi); fwrite(buf.buf, 1, buf.len, stdout); strbuf_release(&buf); @@ -642,6 +675,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) char *value = NULL; int flags = 0; int ret = 0; + struct key_value_info default_kvi = KVI_INIT; given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT)); @@ -705,7 +739,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) given_config_source.scope = CONFIG_SCOPE_LOCAL; } else if (use_worktree_config) { struct worktree **worktrees = get_worktrees(); - if (repository_format_worktree_config) + if (the_repository->repository_format_worktree_config) given_config_source.file = git_pathdup("config.worktree"); else if (worktrees[0] && worktrees[1]) die(_("--worktree cannot be used with multiple " @@ -819,7 +853,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) if (actions == ACTION_LIST) { check_argc(argc, 0, 0); if (config_with_options(show_all_config, NULL, - &given_config_source, + &given_config_source, the_repository, &config_options) < 0) { if (given_config_source.file) die_errno(_("unable to read config file '%s'"), @@ -859,7 +893,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) else if (actions == ACTION_SET) { check_write(); check_argc(argc, 2, 2); - value = normalize_value(argv[0], argv[1]); + value = normalize_value(argv[0], argv[1], &default_kvi); ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value); if (ret == CONFIG_NOTHING_SET) error(_("cannot overwrite multiple values with a single value\n" @@ -868,7 +902,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) else if (actions == ACTION_SET_ALL) { check_write(); check_argc(argc, 2, 3); - value = normalize_value(argv[0], argv[1]); + value = normalize_value(argv[0], argv[1], &default_kvi); ret = git_config_set_multivar_in_file_gently(given_config_source.file, argv[0], value, argv[2], flags); @@ -876,7 +910,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) else if (actions == ACTION_ADD) { check_write(); check_argc(argc, 2, 2); - value = normalize_value(argv[0], argv[1]); + value = normalize_value(argv[0], argv[1], &default_kvi); ret = git_config_set_multivar_in_file_gently(given_config_source.file, argv[0], value, CONFIG_REGEX_NONE, @@ -885,7 +919,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) else if (actions == ACTION_REPLACE_ALL) { check_write(); check_argc(argc, 2, 3); - value = normalize_value(argv[0], argv[1]); + value = normalize_value(argv[0], argv[1], &default_kvi); ret = git_config_set_multivar_in_file_gently(given_config_source.file, argv[0], value, argv[2], flags | CONFIG_FLAGS_MULTI_REPLACE); diff --git a/builtin/count-objects.c b/builtin/count-objects.c index 07b9419596..2d4bb5e8d0 100644 --- a/builtin/count-objects.c +++ b/builtin/count-objects.c @@ -4,15 +4,17 @@ * Copyright (c) 2006 Junio C Hamano */ -#include "cache.h" +#include "builtin.h" #include "config.h" #include "dir.h" +#include "environment.h" +#include "gettext.h" +#include "path.h" #include "repository.h" -#include "builtin.h" #include "parse-options.h" #include "quote.h" #include "packfile.h" -#include "object-store.h" +#include "object-store-ll.h" static unsigned long garbage; static off_t size_garbage; @@ -57,7 +59,8 @@ static void loose_garbage(const char *path) report_garbage(PACKDIR_FILE_GARBAGE, path); } -static int count_loose(const struct object_id *oid, const char *path, void *data) +static int count_loose(const struct object_id *oid, const char *path, + void *data UNUSED) { struct stat st; @@ -72,13 +75,14 @@ static int count_loose(const struct object_id *oid, const char *path, void *data return 0; } -static int count_cruft(const char *basename, const char *path, void *data) +static int count_cruft(const char *basename UNUSED, const char *path, + void *data UNUSED) { loose_garbage(path); return 0; } -static int print_alternate(struct object_directory *odb, void *data) +static int print_alternate(struct object_directory *odb, void *data UNUSED) { printf("alternate: "); quote_c_style(odb->path, NULL, stdout, 0); diff --git a/builtin/credential-cache--daemon.c b/builtin/credential-cache--daemon.c index 338058be7f..3a6a750a8e 100644 --- a/builtin/credential-cache--daemon.c +++ b/builtin/credential-cache--daemon.c @@ -1,4 +1,7 @@ #include "builtin.h" +#include "abspath.h" +#include "gettext.h" +#include "object-file.h" #include "parse-options.h" #ifndef NO_UNIX_SOCKETS @@ -34,19 +37,22 @@ static struct credential_cache_entry *lookup_credential(const struct credential int i; for (i = 0; i < entries_nr; i++) { struct credential *e = &entries[i].item; - if (credential_match(c, e)) + if (credential_match(c, e, 0)) return &entries[i]; } return NULL; } -static void remove_credential(const struct credential *c) +static void remove_credential(const struct credential *c, int match_password) { struct credential_cache_entry *e; - e = lookup_credential(c); - if (e) - e->expiration = 0; + int i; + for (i = 0; i < entries_nr; i++) { + e = &entries[i]; + if (credential_match(c, &e->item, match_password)) + e->expiration = 0; + } } static timestamp_t check_expirations(void) @@ -130,6 +136,9 @@ static void serve_one_client(FILE *in, FILE *out) if (e->item.password_expiry_utc != TIME_MAX) fprintf(out, "password_expiry_utc=%"PRItime"\n", e->item.password_expiry_utc); + if (e->item.oauth_refresh_token) + fprintf(out, "oauth_refresh_token=%s\n", + e->item.oauth_refresh_token); } } else if (!strcmp(action.buf, "exit")) { @@ -144,14 +153,14 @@ static void serve_one_client(FILE *in, FILE *out) exit(0); } else if (!strcmp(action.buf, "erase")) - remove_credential(&c); + remove_credential(&c, 1); else if (!strcmp(action.buf, "store")) { if (timeout < 0) warning("cache client didn't specify a timeout"); else if (!c.username || !c.password) warning("cache client gave us a partial credential"); else { - remove_credential(&c); + remove_credential(&c, 0); cache_credential(&c, timeout); } } diff --git a/builtin/credential-cache.c b/builtin/credential-cache.c index 78c02ad531..43b9d0e5b1 100644 --- a/builtin/credential-cache.c +++ b/builtin/credential-cache.c @@ -1,5 +1,9 @@ #include "builtin.h" +#include "gettext.h" #include "parse-options.h" +#include "path.h" +#include "strbuf.h" +#include "write-or-die.h" #ifndef NO_UNIX_SOCKETS diff --git a/builtin/credential-store.c b/builtin/credential-store.c index 62a4f3c265..4a492411bb 100644 --- a/builtin/credential-store.c +++ b/builtin/credential-store.c @@ -1,16 +1,20 @@ #include "builtin.h" #include "config.h" +#include "gettext.h" #include "lockfile.h" #include "credential.h" +#include "path.h" #include "string-list.h" #include "parse-options.h" +#include "write-or-die.h" static struct lock_file credential_lock; static int parse_credential_file(const char *fn, struct credential *c, void (*match_cb)(struct credential *), - void (*other_cb)(struct strbuf *)) + void (*other_cb)(struct strbuf *), + int match_password) { FILE *fh; struct strbuf line = STRBUF_INIT; @@ -27,7 +31,7 @@ static int parse_credential_file(const char *fn, while (strbuf_getline_lf(&line, fh) != EOF) { if (!credential_from_url_gently(&entry, line.buf, 1) && entry.username && entry.password && - credential_match(c, &entry)) { + credential_match(c, &entry, match_password)) { found_credential = 1; if (match_cb) { match_cb(&entry); @@ -57,7 +61,7 @@ static void print_line(struct strbuf *buf) } static void rewrite_credential_file(const char *fn, struct credential *c, - struct strbuf *extra) + struct strbuf *extra, int match_password) { int timeout_ms = 1000; @@ -66,11 +70,30 @@ static void rewrite_credential_file(const char *fn, struct credential *c, die_errno(_("unable to get credential storage lock in %d ms"), timeout_ms); if (extra) print_line(extra); - parse_credential_file(fn, c, NULL, print_line); + parse_credential_file(fn, c, NULL, print_line, match_password); if (commit_lock_file(&credential_lock) < 0) die_errno("unable to write credential store"); } +static int is_rfc3986_unreserved(char ch) +{ + return isalnum(ch) || + ch == '-' || ch == '_' || ch == '.' || ch == '~'; +} + +static int is_rfc3986_reserved_or_unreserved(char ch) +{ + if (is_rfc3986_unreserved(ch)) + return 1; + switch (ch) { + case '!': case '*': case '\'': case '(': case ')': case ';': + case ':': case '@': case '&': case '=': case '+': case '$': + case ',': case '/': case '?': case '#': case '[': case ']': + return 1; + } + return 0; +} + static void store_credential_file(const char *fn, struct credential *c) { struct strbuf buf = STRBUF_INIT; @@ -88,7 +111,7 @@ static void store_credential_file(const char *fn, struct credential *c) is_rfc3986_reserved_or_unreserved); } - rewrite_credential_file(fn, c, &buf); + rewrite_credential_file(fn, c, &buf, 0); strbuf_release(&buf); } @@ -135,7 +158,7 @@ static void remove_credential(const struct string_list *fns, struct credential * return; for_each_string_list_item(fn, fns) if (!access(fn->string, F_OK)) - rewrite_credential_file(fn->string, c, NULL); + rewrite_credential_file(fn->string, c, NULL, 1); } static void lookup_credential(const struct string_list *fns, struct credential *c) @@ -143,7 +166,7 @@ static void lookup_credential(const struct string_list *fns, struct credential * struct string_list_item *fn; for_each_string_list_item(fn, fns) - if (parse_credential_file(fn->string, c, print_entry, NULL)) + if (parse_credential_file(fn->string, c, print_entry, NULL, 0)) return; /* Found credential */ } diff --git a/builtin/credential.c b/builtin/credential.c index d7b304fa08..7010752987 100644 --- a/builtin/credential.c +++ b/builtin/credential.c @@ -6,7 +6,7 @@ static const char usage_msg[] = "git credential (fill|approve|reject)"; -int cmd_credential(int argc, const char **argv, const char *prefix) +int cmd_credential(int argc, const char **argv, const char *prefix UNUSED) { const char *op; struct credential c = CREDENTIAL_INIT; diff --git a/builtin/describe.c b/builtin/describe.c index eea1e330c0..fb6b0508f3 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -1,24 +1,31 @@ #define USE_THE_INDEX_VARIABLE -#include "cache.h" +#include "builtin.h" #include "config.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "lockfile.h" #include "commit.h" #include "tag.h" #include "blob.h" #include "refs.h" -#include "builtin.h" #include "exec-cmd.h" +#include "object-name.h" #include "parse-options.h" +#include "read-cache-ll.h" #include "revision.h" #include "diff.h" #include "hashmap.h" +#include "setup.h" #include "strvec.h" #include "run-command.h" -#include "object-store.h" +#include "object-store-ll.h" #include "list-objects.h" #include "commit-slab.h" +#include "wildmatch.h" #define MAX_TAGS (FLAG_BITS - 1) +#define DEFAULT_CANDIDATES 10 define_commit_slab(commit_names, struct commit_name *); @@ -35,7 +42,7 @@ static int tags; /* Allow lightweight tags */ static int longformat; static int first_parent; static int abbrev = -1; /* unspecified */ -static int max_candidates = 10; +static int max_candidates = DEFAULT_CANDIDATES; static struct hashmap names; static int have_util; static struct string_list patterns = STRING_LIST_INIT_NODUP; @@ -261,7 +268,7 @@ static unsigned long finish_depth_computation( best->depth++; while (parents) { struct commit *p = parents->item; - parse_commit(p); + repo_parse_commit(the_repository, p); if (!(p->object.flags & SEEN)) commit_list_insert_by_date(p, list); p->object.flags |= c->object.flags; @@ -298,7 +305,8 @@ static void append_name(struct commit_name *n, struct strbuf *dst) static void append_suffix(int depth, const struct object_id *oid, struct strbuf *dst) { - strbuf_addf(dst, "-%d-g%s", depth, find_unique_abbrev(oid, abbrev)); + strbuf_addf(dst, "-%d-g%s", depth, + repo_find_unique_abbrev(the_repository, oid, abbrev)); } static void describe_commit(struct object_id *oid, struct strbuf *dst) @@ -403,7 +411,7 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) } while (parents) { struct commit *p = parents->item; - parse_commit(p); + repo_parse_commit(the_repository, p); if (!(p->object.flags & SEEN)) commit_list_insert_by_date(p, &list); p->object.flags |= c->object.flags; @@ -531,7 +539,7 @@ static void describe(const char *arg, int last_one) if (debug) fprintf(stderr, _("describe %s\n"), arg); - if (get_oid(arg, &oid)) + if (repo_get_oid(the_repository, arg, &oid)) die(_("Not a valid object name %s"), arg); cmit = lookup_commit_reference_gently(the_repository, &oid, 1); @@ -550,6 +558,17 @@ static void describe(const char *arg, int last_one) strbuf_release(&sb); } +static int option_parse_exact_match(const struct option *opt, const char *arg, + int unset) +{ + int *val = opt->value; + + BUG_ON_OPT_ARG(arg); + + *val = unset ? DEFAULT_CANDIDATES : 0; + return 0; +} + int cmd_describe(int argc, const char **argv, const char *prefix) { int contains = 0; @@ -561,8 +580,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "long", &longformat, N_("always use long format")), OPT_BOOL(0, "first-parent", &first_parent, N_("only follow first parent")), OPT__ABBREV(&abbrev), - OPT_SET_INT(0, "exact-match", &max_candidates, - N_("only output exact matches"), 0), + OPT_CALLBACK_F(0, "exact-match", &max_candidates, NULL, + N_("only output exact matches"), + PARSE_OPT_NOARG, option_parse_exact_match), OPT_INTEGER(0, "candidates", &max_candidates, N_("consider <n> most recent tags (default: 10)")), OPT_STRING_LIST(0, "match", &patterns, N_("pattern"), @@ -650,9 +670,11 @@ int cmd_describe(int argc, const char **argv, const char *prefix) struct lock_file index_lock = LOCK_INIT; struct rev_info revs; struct strvec args = STRVEC_INIT; - int fd, result; + int fd; setup_work_tree(); + prepare_repo_settings(the_repository); + the_repository->settings.command_requires_full_index = 0; repo_read_index(the_repository); refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL); @@ -665,9 +687,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix) strvec_pushv(&args, diff_index_args); if (setup_revisions(args.nr, args.v, &revs, NULL) != 1) BUG("malformed internal diff-index command line"); - result = run_diff_index(&revs, 0); + run_diff_index(&revs, 0); - if (!diff_result_code(&revs.diffopt, result)) + if (!diff_result_code(&revs.diffopt)) suffix = NULL; else suffix = dirty; diff --git a/builtin/diagnose.c b/builtin/diagnose.c index d52015c67a..4f22eb2b55 100644 --- a/builtin/diagnose.c +++ b/builtin/diagnose.c @@ -1,4 +1,7 @@ #include "builtin.h" +#include "abspath.h" +#include "gettext.h" +#include "object-file.h" #include "parse-options.h" #include "diagnose.h" diff --git a/builtin/diff-files.c b/builtin/diff-files.c index dc991f753b..f38912cd40 100644 --- a/builtin/diff-files.c +++ b/builtin/diff-files.c @@ -3,13 +3,14 @@ * * Copyright (C) Linus Torvalds, 2005 */ -#include "cache.h" +#include "builtin.h" #include "config.h" #include "diff.h" #include "diff-merges.h" #include "commit.h" +#include "preload-index.h" +#include "repository.h" #include "revision.h" -#include "builtin.h" #include "submodule.h" static const char diff_files_usage[] = @@ -27,6 +28,10 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix) usage(diff_files_usage); git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ + + prepare_repo_settings(the_repository); + the_repository->settings.command_requires_full_index = 0; + repo_init_revisions(the_repository, &rev, prefix); rev.abbrev = 0; @@ -75,14 +80,10 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix) (rev.diffopt.output_format & DIFF_FORMAT_PATCH)) diff_merges_set_dense_combined_if_unset(&rev); - if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) { - perror("repo_read_index_preload"); - result = -1; - goto cleanup; - } - result = run_diff_files(&rev, options); - result = diff_result_code(&rev.diffopt, result); -cleanup: + if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) + die_errno("repo_read_index_preload"); + run_diff_files(&rev, options); + result = diff_result_code(&rev.diffopt); release_revisions(&rev); return result; } diff --git a/builtin/diff-index.c b/builtin/diff-index.c index 35dc9b23ee..220f341ffa 100644 --- a/builtin/diff-index.c +++ b/builtin/diff-index.c @@ -1,10 +1,13 @@ -#include "cache.h" +#include "builtin.h" #include "config.h" #include "diff.h" #include "diff-merges.h" #include "commit.h" +#include "preload-index.h" +#include "repository.h" #include "revision.h" -#include "builtin.h" +#include "setup.h" +#include "sparse-index.h" #include "submodule.h" static const char diff_cache_usage[] = @@ -69,8 +72,8 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix) perror("repo_read_index"); return -1; } - result = run_diff_index(&rev, option); - result = diff_result_code(&rev.diffopt, result); + run_diff_index(&rev, option); + result = diff_result_code(&rev.diffopt); release_revisions(&rev); return result; } diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index 25b853b85c..86be634286 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -1,12 +1,16 @@ #define USE_THE_INDEX_VARIABLE -#include "cache.h" +#include "builtin.h" #include "config.h" #include "diff.h" #include "commit.h" +#include "gettext.h" +#include "hex.h" #include "log-tree.h" -#include "builtin.h" #include "submodule.h" +#include "read-cache-ll.h" #include "repository.h" +#include "revision.h" +#include "tree.h" static struct rev_info log_tree_opt; @@ -95,7 +99,7 @@ static const char diff_tree_usage[] = " --root include the initial commit as diff against /dev/null\n" COMMON_DIFF_OPTIONS_HELP; -static void diff_tree_tweak_rev(struct rev_info *rev, struct setup_revision_opt *opt) +static void diff_tree_tweak_rev(struct rev_info *rev) { if (!rev->diffopt.output_format) { if (rev->dense_combined_merges) @@ -119,6 +123,10 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) usage(diff_tree_usage); git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ + + prepare_repo_settings(the_repository); + the_repository->settings.command_requires_full_index = 0; + repo_init_revisions(the_repository, opt, prefix); if (repo_read_index(the_repository) < 0) die(_("index file corrupt")); @@ -224,5 +232,5 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) diff_free(&opt->diffopt); } - return diff_result_code(&opt->diffopt, 0); + return diff_result_code(&opt->diffopt); } diff --git a/builtin/diff.c b/builtin/diff.c index 26f1e532c6..55e7d21755 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -4,22 +4,26 @@ * Copyright (c) 2006 Junio C Hamano */ #define USE_THE_INDEX_VARIABLE -#include "cache.h" +#include "builtin.h" #include "config.h" #include "ewah/ewok.h" #include "lockfile.h" #include "color.h" #include "commit.h" #include "blob.h" +#include "gettext.h" #include "tag.h" #include "diff.h" #include "diff-merges.h" #include "diffcore.h" +#include "preload-index.h" +#include "read-cache-ll.h" #include "revision.h" #include "log-tree.h" -#include "builtin.h" +#include "setup.h" #include "submodule.h" #include "oid-array.h" +#include "tree.h" #define DIFF_NO_INDEX_EXPLICIT 1 #define DIFF_NO_INDEX_IMPLICIT 2 @@ -73,9 +77,9 @@ static void stuff_change(struct diff_options *opt, diff_queue(&diff_queued_diff, one, two); } -static int builtin_diff_b_f(struct rev_info *revs, - int argc, const char **argv, - struct object_array_entry **blob) +static void builtin_diff_b_f(struct rev_info *revs, + int argc, const char **argv UNUSED, + struct object_array_entry **blob) { /* Blob vs file in the working tree*/ struct stat st; @@ -105,12 +109,11 @@ static int builtin_diff_b_f(struct rev_info *revs, path); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); - return 0; } -static int builtin_diff_blobs(struct rev_info *revs, - int argc, const char **argv, - struct object_array_entry **blob) +static void builtin_diff_blobs(struct rev_info *revs, + int argc, const char **argv UNUSED, + struct object_array_entry **blob) { const unsigned mode = canon_mode(S_IFREG | 0644); @@ -130,11 +133,10 @@ static int builtin_diff_blobs(struct rev_info *revs, blob_path(blob[0]), blob_path(blob[1])); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); - return 0; } -static int builtin_diff_index(struct rev_info *revs, - int argc, const char **argv) +static void builtin_diff_index(struct rev_info *revs, + int argc, const char **argv) { unsigned int option = 0; while (1 < argc) { @@ -159,20 +161,18 @@ static int builtin_diff_index(struct rev_info *revs, setup_work_tree(); if (repo_read_index_preload(the_repository, &revs->diffopt.pathspec, 0) < 0) { - perror("repo_read_index_preload"); - return -1; + die_errno("repo_read_index_preload"); } } else if (repo_read_index(the_repository) < 0) { - perror("repo_read_cache"); - return -1; + die_errno("repo_read_cache"); } - return run_diff_index(revs, option); + run_diff_index(revs, option); } -static int builtin_diff_tree(struct rev_info *revs, - int argc, const char **argv, - struct object_array_entry *ent0, - struct object_array_entry *ent1) +static void builtin_diff_tree(struct rev_info *revs, + int argc, const char **argv, + struct object_array_entry *ent0, + struct object_array_entry *ent1) { const struct object_id *(oid[2]); struct object_id mb_oid; @@ -205,13 +205,12 @@ static int builtin_diff_tree(struct rev_info *revs, } diff_tree_oid(oid[0], oid[1], "", &revs->diffopt); log_tree_diff_flush(revs); - return 0; } -static int builtin_diff_combined(struct rev_info *revs, - int argc, const char **argv, - struct object_array_entry *ent, - int ents, int first_non_parent) +static void builtin_diff_combined(struct rev_info *revs, + int argc, const char **argv UNUSED, + struct object_array_entry *ent, + int ents, int first_non_parent) { struct oid_array parents = OID_ARRAY_INIT; int i; @@ -232,7 +231,6 @@ static int builtin_diff_combined(struct rev_info *revs, } diff_tree_combined(&ent[first_non_parent].item->oid, &parents, revs); oid_array_clear(&parents); - return 0; } static void refresh_index_quietly(void) @@ -250,7 +248,7 @@ static void refresh_index_quietly(void) repo_update_index_if_able(the_repository, &lock_file); } -static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv) +static void builtin_diff_files(struct rev_info *revs, int argc, const char **argv) { unsigned int options = 0; @@ -265,8 +263,10 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv options |= DIFF_SILENT_ON_REMOVED; else if (!strcmp(argv[1], "-h")) usage(builtin_diff_usage); - else - return error(_("invalid option: %s"), argv[1]); + else { + error(_("invalid option: %s"), argv[1]); + usage(builtin_diff_usage); + } argv++; argc--; } @@ -283,10 +283,9 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv setup_work_tree(); if (repo_read_index_preload(the_repository, &revs->diffopt.pathspec, 0) < 0) { - perror("repo_read_index_preload"); - return -1; + die_errno("repo_read_index_preload"); } - return run_diff_files(revs, options); + run_diff_files(revs, options); } struct symdiff { @@ -400,7 +399,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) int blobs = 0, paths = 0; struct object_array_entry *blob[2]; int nongit = 0, no_index = 0; - int result = 0; + int result; struct symdiff sdiff; /* @@ -475,8 +474,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) repo_init_revisions(the_repository, &rev, prefix); /* Set up defaults that will apply to both no-index and regular diffs. */ - rev.diffopt.stat_width = -1; - rev.diffopt.stat_graph_width = -1; + init_diffstat_widths(&rev.diffopt); rev.diffopt.flags.allow_external = 1; rev.diffopt.flags.allow_textconv = 1; @@ -548,7 +546,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix) if (!obj) die(_("invalid object '%s' given."), name); if (obj->type == OBJ_COMMIT) - obj = &get_commit_tree(((struct commit *)obj))->object; + obj = &repo_get_commit_tree(the_repository, + ((struct commit *)obj))->object; if (obj->type == OBJ_TREE) { if (sdiff.skip && bitmap_get(sdiff.skip, i)) @@ -578,17 +577,17 @@ int cmd_diff(int argc, const char **argv, const char *prefix) if (!ent.nr) { switch (blobs) { case 0: - result = builtin_diff_files(&rev, argc, argv); + builtin_diff_files(&rev, argc, argv); break; case 1: if (paths != 1) usage(builtin_diff_usage); - result = builtin_diff_b_f(&rev, argc, argv, blob); + builtin_diff_b_f(&rev, argc, argv, blob); break; case 2: if (paths) usage(builtin_diff_usage); - result = builtin_diff_blobs(&rev, argc, argv, blob); + builtin_diff_blobs(&rev, argc, argv, blob); break; default: usage(builtin_diff_usage); @@ -597,18 +596,18 @@ int cmd_diff(int argc, const char **argv, const char *prefix) else if (blobs) usage(builtin_diff_usage); else if (ent.nr == 1) - result = builtin_diff_index(&rev, argc, argv); + builtin_diff_index(&rev, argc, argv); else if (ent.nr == 2) { if (sdiff.warn) warning(_("%s...%s: multiple merge bases, using %s"), sdiff.left, sdiff.right, sdiff.base); - result = builtin_diff_tree(&rev, argc, argv, - &ent.objects[0], &ent.objects[1]); + builtin_diff_tree(&rev, argc, argv, + &ent.objects[0], &ent.objects[1]); } else - result = builtin_diff_combined(&rev, argc, argv, - ent.objects, ent.nr, - first_non_parent); - result = diff_result_code(&rev.diffopt, result); + builtin_diff_combined(&rev, argc, argv, + ent.objects, ent.nr, + first_non_parent); + result = diff_result_code(&rev.diffopt); if (1 < rev.diffopt.skip_stat_unmatch) refresh_index_quietly(); release_revisions(&rev); diff --git a/builtin/difftool.c b/builtin/difftool.c index dbbfb19f19..0f5eae9cd4 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -12,18 +12,26 @@ * Copyright (C) 2016 Johannes Schindelin */ #define USE_THE_INDEX_VARIABLE -#include "cache.h" -#include "config.h" #include "builtin.h" +#include "abspath.h" +#include "config.h" +#include "copy.h" #include "run-command.h" +#include "environment.h" #include "exec-cmd.h" +#include "gettext.h" +#include "hex.h" #include "parse-options.h" +#include "read-cache-ll.h" +#include "sparse-index.h" #include "strvec.h" #include "strbuf.h" #include "lockfile.h" -#include "object-store.h" +#include "object-file.h" +#include "object-store-ll.h" #include "dir.h" #include "entry.h" +#include "setup.h" static int trust_exit_code; @@ -32,14 +40,15 @@ static const char *const builtin_difftool_usage[] = { NULL }; -static int difftool_config(const char *var, const char *value, void *cb) +static int difftool_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { if (!strcmp(var, "difftool.trustexitcode")) { trust_exit_code = git_config_bool(var, value); return 0; } - return git_default_config(var, value, cb); + return git_default_config(var, value, ctx, cb); } static int print_tool_help(void) @@ -295,7 +304,8 @@ static char *get_symlink(const struct object_id *oid, const char *path) } else { enum object_type type; unsigned long size; - data = read_object_file(oid, &type, &size); + data = repo_read_object_file(the_repository, oid, &type, + &size); if (!data) die(_("could not read object %s for symlink %s"), oid_to_hex(oid), path); @@ -684,7 +694,7 @@ static int run_file_diff(int prompt, const char *prefix, int cmd_difftool(int argc, const char **argv, const char *prefix) { - int use_gui_tool = 0, dir_diff = 0, prompt = -1, symlinks = 0, + int use_gui_tool = -1, dir_diff = 0, prompt = -1, symlinks = 0, tool_help = 0, no_index = 0; static char *difftool_cmd = NULL, *extcmd = NULL; struct option builtin_difftool_options[] = { @@ -734,13 +744,21 @@ int cmd_difftool(int argc, const char **argv, const char *prefix) } else if (dir_diff) die(_("options '%s' and '%s' cannot be used together"), "--dir-diff", "--no-index"); - die_for_incompatible_opt3(use_gui_tool, "--gui", + die_for_incompatible_opt3(use_gui_tool == 1, "--gui", !!difftool_cmd, "--tool", !!extcmd, "--extcmd"); - if (use_gui_tool) + /* + * Explicitly specified GUI option is forwarded to git-mergetool--lib.sh; + * empty or unset means "use the difftool.guiDefault config or default to + * false". + */ + if (use_gui_tool == 1) setenv("GIT_MERGETOOL_GUI", "true", 1); - else if (difftool_cmd) { + else if (use_gui_tool == 0) + setenv("GIT_MERGETOOL_GUI", "false", 1); + + if (difftool_cmd) { if (*difftool_cmd) setenv("GIT_DIFF_TOOL", difftool_cmd, 1); else diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 39a890fc00..70aff515ac 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -4,11 +4,13 @@ * Copyright (C) 2007 Johannes E. Schindelin */ #include "builtin.h" -#include "cache.h" #include "config.h" +#include "gettext.h" +#include "hex.h" #include "refs.h" #include "refspec.h" -#include "object-store.h" +#include "object-file.h" +#include "object-store-ll.h" #include "commit.h" #include "object.h" #include "tag.h" @@ -31,9 +33,9 @@ static const char *fast_export_usage[] = { }; static int progress; -static enum { SIGNED_TAG_ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = SIGNED_TAG_ABORT; -static enum { TAG_FILTERING_ABORT, DROP, REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT; -static enum { REENCODE_ABORT, REENCODE_YES, REENCODE_NO } reencode_mode = REENCODE_ABORT; +static enum signed_tag_mode { SIGNED_TAG_ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = SIGNED_TAG_ABORT; +static enum tag_of_filtered_mode { TAG_FILTERING_ABORT, DROP, REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT; +static enum reencode_mode { REENCODE_ABORT, REENCODE_YES, REENCODE_NO } reencode_mode = REENCODE_ABORT; static int fake_missing_tagger; static int use_done_feature; static int no_data; @@ -51,16 +53,18 @@ static struct revision_sources revision_sources; static int parse_opt_signed_tag_mode(const struct option *opt, const char *arg, int unset) { + enum signed_tag_mode *val = opt->value; + if (unset || !strcmp(arg, "abort")) - signed_tag_mode = SIGNED_TAG_ABORT; + *val = SIGNED_TAG_ABORT; else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore")) - signed_tag_mode = VERBATIM; + *val = VERBATIM; else if (!strcmp(arg, "warn")) - signed_tag_mode = WARN; + *val = WARN; else if (!strcmp(arg, "warn-strip")) - signed_tag_mode = WARN_STRIP; + *val = WARN_STRIP; else if (!strcmp(arg, "strip")) - signed_tag_mode = STRIP; + *val = STRIP; else return error("Unknown signed-tags mode: %s", arg); return 0; @@ -69,12 +73,14 @@ static int parse_opt_signed_tag_mode(const struct option *opt, static int parse_opt_tag_of_filtered_mode(const struct option *opt, const char *arg, int unset) { + enum tag_of_filtered_mode *val = opt->value; + if (unset || !strcmp(arg, "abort")) - tag_of_filtered_mode = TAG_FILTERING_ABORT; + *val = TAG_FILTERING_ABORT; else if (!strcmp(arg, "drop")) - tag_of_filtered_mode = DROP; + *val = DROP; else if (!strcmp(arg, "rewrite")) - tag_of_filtered_mode = REWRITE; + *val = REWRITE; else return error("Unknown tag-of-filtered mode: %s", arg); return 0; @@ -83,21 +89,23 @@ static int parse_opt_tag_of_filtered_mode(const struct option *opt, static int parse_opt_reencode_mode(const struct option *opt, const char *arg, int unset) { + enum reencode_mode *val = opt->value; + if (unset) { - reencode_mode = REENCODE_ABORT; + *val = REENCODE_ABORT; return 0; } switch (git_parse_maybe_bool(arg)) { case 0: - reencode_mode = REENCODE_NO; + *val = REENCODE_NO; break; case 1: - reencode_mode = REENCODE_YES; + *val = REENCODE_YES; break; default: if (!strcasecmp(arg, "abort")) - reencode_mode = REENCODE_ABORT; + *val = REENCODE_ABORT; else return error("Unknown reencoding mode: %s", arg); } @@ -109,7 +117,7 @@ static struct decoration idnums; static uint32_t last_idnum; struct anonymized_entry { struct hashmap_entry hash; - const char *anon; + char *anon; const char orig[FLEX_ARRAY]; }; @@ -138,43 +146,56 @@ static int anonymized_entry_cmp(const void *cmp_data UNUSED, return strcmp(a->orig, b->orig); } +static struct anonymized_entry *add_anonymized_entry(struct hashmap *map, + unsigned hash, + const char *orig, size_t len, + char *anon) +{ + struct anonymized_entry *ret, *old; + + if (!map->cmpfn) + hashmap_init(map, anonymized_entry_cmp, NULL, 0); + + FLEX_ALLOC_MEM(ret, orig, orig, len); + hashmap_entry_init(&ret->hash, hash); + ret->anon = anon; + old = hashmap_put_entry(map, ret, hash); + + if (old) { + free(old->anon); + free(old); + } + + return ret; +} + /* * Basically keep a cache of X->Y so that we can repeatedly replace * the same anonymized string with another. The actual generation * is farmed out to the generate function. */ static const char *anonymize_str(struct hashmap *map, - char *(*generate)(void *), - const char *orig, size_t len, - void *data) + char *(*generate)(void), + const char *orig, size_t len) { struct anonymized_entry_key key; struct anonymized_entry *ret; - if (!map->cmpfn) - hashmap_init(map, anonymized_entry_cmp, NULL, 0); - hashmap_entry_init(&key.hash, memhash(orig, len)); key.orig = orig; key.orig_len = len; /* First check if it's a token the user configured manually... */ - if (anonymized_seeds.cmpfn) - ret = hashmap_get_entry(&anonymized_seeds, &key, hash, &key); - else - ret = NULL; + ret = hashmap_get_entry(&anonymized_seeds, &key, hash, &key); /* ...otherwise check if we've already seen it in this context... */ if (!ret) ret = hashmap_get_entry(map, &key, hash, &key); /* ...and finally generate a new mapping if necessary */ - if (!ret) { - FLEX_ALLOC_MEM(ret, orig, orig, len); - hashmap_entry_init(&ret->hash, key.hash.hash); - ret->anon = generate(data); - hashmap_put(map, &ret->hash); - } + if (!ret) + ret = add_anonymized_entry(map, key.hash.hash, + orig, len, generate()); return ret->anon; } @@ -187,12 +208,12 @@ static const char *anonymize_str(struct hashmap *map, */ static void anonymize_path(struct strbuf *out, const char *path, struct hashmap *map, - char *(*generate)(void *)) + char *(*generate)(void)) { while (*path) { const char *end_of_component = strchrnul(path, '/'); size_t len = end_of_component - path; - const char *c = anonymize_str(map, generate, path, len, NULL); + const char *c = anonymize_str(map, generate, path, len); strbuf_addstr(out, c); path = end_of_component; if (*path) @@ -296,7 +317,7 @@ static void export_blob(const struct object_id *oid) object = (struct object *)lookup_blob(the_repository, oid); eaten = 0; } else { - buf = read_object_file(oid, &type, &size); + buf = repo_read_object_file(the_repository, oid, &type, &size); if (!buf) die("could not read blob %s", oid_to_hex(oid)); if (check_object_signature(the_repository, oid, buf, size, @@ -367,7 +388,7 @@ static void print_path_1(const char *path) printf("%s", path); } -static char *anonymize_path_component(void *data) +static char *anonymize_path_component(void) { static int counter; struct strbuf out = STRBUF_INIT; @@ -389,7 +410,7 @@ static void print_path(const char *path) } } -static char *generate_fake_oid(void *data) +static char *generate_fake_oid(void) { static uint32_t counter = 1; /* avoid null oid */ const unsigned hashsz = the_hash_algo->rawsz; @@ -405,7 +426,7 @@ static const char *anonymize_oid(const char *oid_hex) { static struct hashmap objs; size_t len = strlen(oid_hex); - return anonymize_str(&objs, generate_fake_oid, oid_hex, len, NULL); + return anonymize_str(&objs, generate_fake_oid, oid_hex, len); } static void show_filemodify(struct diff_queue_struct *q, @@ -502,7 +523,7 @@ static const char *find_encoding(const char *begin, const char *end) return bol; } -static char *anonymize_ref_component(void *data) +static char *anonymize_ref_component(void) { static int counter; struct strbuf out = STRBUF_INIT; @@ -542,13 +563,13 @@ static const char *anonymize_refname(const char *refname) * We do not even bother to cache commit messages, as they are unlikely * to be repeated verbatim, and it is not that interesting when they are. */ -static char *anonymize_commit_message(const char *old) +static char *anonymize_commit_message(void) { static int counter; return xstrfmt("subject %d\n\nbody\n", counter++); } -static char *anonymize_ident(void *data) +static char *anonymize_ident(void) { static int counter; struct strbuf out = STRBUF_INIT; @@ -591,7 +612,7 @@ static void anonymize_ident_line(const char **beg, const char **end) len = split.mail_end - split.name_begin; ident = anonymize_str(&idents, anonymize_ident, - split.name_begin, len, NULL); + split.name_begin, len); strbuf_addstr(out, ident); strbuf_addch(out, ' '); strbuf_add(out, split.date_begin, split.tz_end - split.date_begin); @@ -618,7 +639,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev, rev->diffopt.output_format = DIFF_FORMAT_CALLBACK; parse_commit_or_die(commit); - commit_buffer = get_commit_buffer(commit, NULL); + commit_buffer = repo_get_commit_buffer(the_repository, commit, NULL); author = strstr(commit_buffer, "\nauthor "); if (!author) die("could not find author in commit %s", @@ -669,7 +690,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev, mark_next_object(&commit->object); if (anonymize) { - reencoded = anonymize_commit_message(message); + reencoded = anonymize_commit_message(); } else if (encoding) { switch(reencode_mode) { case REENCODE_YES: @@ -699,7 +720,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev, ? strlen(message) : 0), reencoded ? reencoded : message ? message : ""); free(reencoded); - unuse_commit_buffer(commit, commit_buffer); + repo_unuse_commit_buffer(the_repository, commit, commit_buffer); for (i = 0, p = commit->parents; p; p = p->next) { struct object *obj = &p->item->object; @@ -732,7 +753,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev, show_progress(); } -static char *anonymize_tag(void *data) +static char *anonymize_tag(void) { static int counter; struct strbuf out = STRBUF_INIT; @@ -766,7 +787,8 @@ static void handle_tag(const char *name, struct tag *tag) return; } - buf = read_object_file(&tag->object.oid, &type, &size); + buf = repo_read_object_file(the_repository, &tag->object.oid, &type, + &size); if (!buf) die("could not read tag %s", oid_to_hex(&tag->object.oid)); message = memmem(buf, size, "\n\n", 2); @@ -794,7 +816,7 @@ static void handle_tag(const char *name, struct tag *tag) if (message) { static struct hashmap tags; message = anonymize_str(&tags, anonymize_tag, - message, message_size, NULL); + message, message_size); message_size = strlen(message); } } @@ -917,7 +939,8 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info) if (e->flags & UNINTERESTING) continue; - if (dwim_ref(e->name, strlen(e->name), &oid, &full_name, 0) != 1) + if (repo_dwim_ref(the_repository, e->name, strlen(e->name), + &oid, &full_name, 0) != 1) continue; if (refspecs.nr) { @@ -1125,11 +1148,6 @@ static void handle_deletes(void) } } -static char *anonymize_seed(void *data) -{ - return xstrdup(data); -} - static int parse_opt_anonymize_map(const struct option *opt, const char *arg, int unset) { @@ -1151,7 +1169,8 @@ static int parse_opt_anonymize_map(const struct option *opt, if (!keylen || !*value) return error(_("--anonymize-map token cannot be empty")); - anonymize_str(map, anonymize_seed, arg, keylen, (void *)value); + add_anonymized_entry(map, memhash(arg, keylen), arg, keylen, + xstrdup(value)); return 0; } diff --git a/builtin/fast-import.c b/builtin/fast-import.c index 7134683ab9..444f41cf8c 100644 --- a/builtin/fast-import.c +++ b/builtin/fast-import.c @@ -1,5 +1,8 @@ #include "builtin.h" -#include "cache.h" +#include "abspath.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "repository.h" #include "config.h" #include "lockfile.h" @@ -9,13 +12,16 @@ #include "commit.h" #include "delta.h" #include "pack.h" +#include "path.h" #include "refs.h" #include "csum-file.h" #include "quote.h" #include "dir.h" #include "run-command.h" #include "packfile.h" -#include "object-store.h" +#include "object-file.h" +#include "object-name.h" +#include "object-store-ll.h" #include "mem-pool.h" #include "commit-reach.h" #include "khash.h" @@ -175,6 +181,7 @@ static FILE *pack_edges; static unsigned int show_stats = 1; static int global_argc; static const char **global_argv; +static const char *global_prefix; /* Memory pools */ static struct mem_pool fi_mem_pool = { @@ -436,7 +443,7 @@ static void set_checkpoint_signal(void) #else -static void checkpoint_signal(int signo) +static void checkpoint_signal(int signo UNUSED) { checkpoint_requested = 1; } @@ -1095,6 +1102,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark) || (pack_size + PACK_SIZE_THRESHOLD + len) < pack_size) cycle_packfile(); + the_hash_algo->init_fn(&checkpoint.ctx); hashfile_checkpoint(pack_file, &checkpoint); offset = checkpoint.offset; @@ -1265,7 +1273,7 @@ static void load_tree(struct tree_entry *root) die("Can't load tree %s", oid_to_hex(oid)); } else { enum object_type type; - buf = read_object_file(oid, &type, &size); + buf = repo_read_object_file(the_repository, oid, &type, &size); if (!buf || type != OBJ_TREE) die("Can't load tree %s", oid_to_hex(oid)); } @@ -1625,7 +1633,7 @@ static int update_branch(struct branch *b) if (!old_cmit || !new_cmit) return error("Branch %s is missing commits.", b->name); - if (!in_merge_bases(old_cmit, new_cmit)) { + if (!repo_in_merge_bases(the_repository, old_cmit, new_cmit)) { warning("Not updating %s" " (new tip %s does not contain %s)", b->name, oid_to_hex(&b->oid), @@ -2486,7 +2494,7 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa if (commit_oe->type != OBJ_COMMIT) die("Mark :%" PRIuMAX " not a commit", commit_mark); oidcpy(&commit_oid, &commit_oe->idx.oid); - } else if (!get_oid(p, &commit_oid)) { + } else if (!repo_get_oid(the_repository, p, &commit_oid)) { unsigned long size; char *buf = read_object_with_reference(the_repository, &commit_oid, @@ -2599,7 +2607,7 @@ static int parse_objectish(struct branch *b, const char *objectish) } else parse_from_existing(b); } - } else if (!get_oid(objectish, &b->oid)) { + } else if (!repo_get_oid(the_repository, objectish, &b->oid)) { parse_from_existing(b); if (is_null_oid(&b->oid)) b->delete = 1; @@ -2654,7 +2662,7 @@ static struct hash_list *parse_merge(unsigned int *count) if (oe->type != OBJ_COMMIT) die("Mark :%" PRIuMAX " not a commit", idnum); oidcpy(&n->oid, &oe->idx.oid); - } else if (!get_oid(from, &n->oid)) { + } else if (!repo_get_oid(the_repository, from, &n->oid)) { unsigned long size; char *buf = read_object_with_reference(the_repository, &n->oid, @@ -2827,7 +2835,7 @@ static void parse_new_tag(const char *arg) oe = find_mark(marks, from_mark); type = oe->type; oidcpy(&oid, &oe->idx.oid); - } else if (!get_oid(from, &oid)) { + } else if (!repo_get_oid(the_repository, from, &oid)) { struct object_entry *oe = find_object(&oid); if (!oe) { type = oid_object_info(the_repository, &oid, NULL); @@ -2936,7 +2944,7 @@ static void cat_blob(struct object_entry *oe, struct object_id *oid) char *buf; if (!oe || oe->pack_id == MAX_PACK_ID) { - buf = read_object_file(oid, &type, &size); + buf = repo_read_object_file(the_repository, oid, &type, &size); } else { type = oe->type; buf = gfi_unpack_entry(oe, &size); @@ -3044,7 +3052,8 @@ static struct object_entry *dereference(struct object_entry *oe, buf = gfi_unpack_entry(oe, &size); } else { enum object_type unused; - buf = read_object_file(oid, &unused, &size); + buf = repo_read_object_file(the_repository, oid, &unused, + &size); } if (!buf) die("Can't load object %s", oid_to_hex(oid)); @@ -3245,7 +3254,7 @@ static void parse_alias(void) static char* make_fast_import_path(const char *path) { if (!relative_marks_paths || is_absolute_path(path)) - return xstrdup(path); + return prefix_filename(global_prefix, path); return git_pathdup("info/fast-import/%s", path); } @@ -3316,9 +3325,11 @@ static void option_cat_blob_fd(const char *fd) static void option_export_pack_edges(const char *edges) { + char *fn = prefix_filename(global_prefix, edges); if (pack_edges) fclose(pack_edges); - pack_edges = xfopen(edges, "a"); + pack_edges = xfopen(fn, "a"); + free(fn); } static void option_rewrite_submodules(const char *arg, struct string_list *list) @@ -3333,11 +3344,13 @@ static void option_rewrite_submodules(const char *arg, struct string_list *list) f++; CALLOC_ARRAY(ms, 1); + f = prefix_filename(global_prefix, f); fp = fopen(f, "r"); if (!fp) die_errno("cannot read '%s'", f); read_mark_file(&ms, fp, insert_oid_entry); fclose(fp); + free(f); string_list_insert(list, s)->util = ms; } @@ -3551,6 +3564,7 @@ int cmd_fast_import(int argc, const char **argv, const char *prefix) global_argc = argc; global_argv = argv; + global_prefix = prefix; rc_free = mem_pool_alloc(&fi_mem_pool, cmd_save * sizeof(*rc_free)); for (i = 0; i < (cmd_save - 1); i++) diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index afe679368d..44c05ee86c 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -1,4 +1,7 @@ #include "builtin.h" +#include "gettext.h" +#include "hex.h" +#include "object-file.h" #include "pkt-line.h" #include "fetch-pack.h" #include "remote.h" @@ -40,7 +43,7 @@ static void add_sought_entry(struct ref ***sought, int *nr, int *alloc, (*sought)[*nr - 1] = ref; } -int cmd_fetch_pack(int argc, const char **argv, const char *prefix) +int cmd_fetch_pack(int argc, const char **argv, const char *prefix UNUSED) { int i, ret; struct ref *ref = NULL; @@ -211,8 +214,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) int flags = args.verbose ? CONNECT_VERBOSE : 0; if (args.diag_url) flags |= CONNECT_DIAG_URL; - conn = git_connect(fd, dest, args.uploadpack, - flags); + conn = git_connect(fd, dest, "git-upload-pack", + args.uploadpack, flags); if (!conn) return args.diag_url ? 0 : 1; } diff --git a/builtin/fetch.c b/builtin/fetch.c index a09606b472..fd134ba74d 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -1,15 +1,20 @@ /* * "git fetch" */ -#include "cache.h" +#include "builtin.h" +#include "advice.h" #include "config.h" +#include "gettext.h" +#include "environment.h" +#include "hex.h" #include "repository.h" #include "refs.h" #include "refspec.h" -#include "object-store.h" +#include "object-name.h" +#include "object-store-ll.h" #include "oidset.h" +#include "oid-array.h" #include "commit.h" -#include "builtin.h" #include "string-list.h" #include "remote.h" #include "transport.h" @@ -22,12 +27,17 @@ #include "strvec.h" #include "utf8.h" #include "packfile.h" +#include "pager.h" +#include "path.h" +#include "pkt-line.h" #include "list-objects-filter-options.h" #include "commit-reach.h" #include "branch.h" #include "promisor-remote.h" #include "commit-graph.h" #include "shallow.h" +#include "trace.h" +#include "trace2.h" #include "worktree.h" #include "bundle-uri.h" @@ -47,25 +57,35 @@ enum { TAGS_SET = 2 }; -static int fetch_prune_config = -1; /* unspecified */ -static int fetch_show_forced_updates = 1; +enum display_format { + DISPLAY_FORMAT_FULL, + DISPLAY_FORMAT_COMPACT, + DISPLAY_FORMAT_PORCELAIN, +}; + +struct display_state { + struct strbuf buf; + + int refcol_width; + enum display_format format; + + char *url; + int url_len, shown_url; +}; + static uint64_t forced_updates_ms = 0; static int prefetch = 0; static int prune = -1; /* unspecified */ #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */ -static int fetch_prune_tags_config = -1; /* unspecified */ static int prune_tags = -1; /* unspecified */ #define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */ -static int all, append, dry_run, force, keep, multiple, update_head_ok; +static int append, dry_run, force, keep, update_head_ok; static int write_fetch_head = 1; static int verbosity, deepen_relative, set_upstream, refetch; static int progress = -1; -static int enable_auto_gc = 1; -static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen; -static int max_jobs = -1, submodule_fetch_jobs_config = -1; -static int fetch_parallel_config = 1; +static int tags = TAGS_DEFAULT, update_shallow, deepen; static int atomic_fetch; static enum transport_family family; static const char *depth; @@ -75,60 +95,77 @@ static struct string_list deepen_not = STRING_LIST_INIT_NODUP; static struct strbuf default_rla = STRBUF_INIT; static struct transport *gtransport; static struct transport *gsecondary; -static const char *submodule_prefix = ""; -static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT; -static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT; -static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND; -static int shown_url = 0; static struct refspec refmap = REFSPEC_INIT_FETCH; static struct list_objects_filter_options filter_options = LIST_OBJECTS_FILTER_INIT; static struct string_list server_options = STRING_LIST_INIT_DUP; static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP; -static int fetch_write_commit_graph = -1; -static int stdin_refspecs = 0; -static int negotiate_only; -static int git_fetch_config(const char *k, const char *v, void *cb) +struct fetch_config { + enum display_format display_format; + int prune; + int prune_tags; + int show_forced_updates; + int recurse_submodules; + int parallel; + int submodule_fetch_jobs; +}; + +static int git_fetch_config(const char *k, const char *v, + const struct config_context *ctx, void *cb) { + struct fetch_config *fetch_config = cb; + if (!strcmp(k, "fetch.prune")) { - fetch_prune_config = git_config_bool(k, v); + fetch_config->prune = git_config_bool(k, v); return 0; } if (!strcmp(k, "fetch.prunetags")) { - fetch_prune_tags_config = git_config_bool(k, v); + fetch_config->prune_tags = git_config_bool(k, v); return 0; } if (!strcmp(k, "fetch.showforcedupdates")) { - fetch_show_forced_updates = git_config_bool(k, v); + fetch_config->show_forced_updates = git_config_bool(k, v); return 0; } if (!strcmp(k, "submodule.recurse")) { int r = git_config_bool(k, v) ? RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF; - recurse_submodules = r; + fetch_config->recurse_submodules = r; } if (!strcmp(k, "submodule.fetchjobs")) { - submodule_fetch_jobs_config = parse_submodule_fetchjobs(k, v); + fetch_config->submodule_fetch_jobs = parse_submodule_fetchjobs(k, v, ctx->kvi); return 0; } else if (!strcmp(k, "fetch.recursesubmodules")) { - recurse_submodules = parse_fetch_recurse_submodules_arg(k, v); + fetch_config->recurse_submodules = parse_fetch_recurse_submodules_arg(k, v); return 0; } if (!strcmp(k, "fetch.parallel")) { - fetch_parallel_config = git_config_int(k, v); - if (fetch_parallel_config < 0) + fetch_config->parallel = git_config_int(k, v, ctx->kvi); + if (fetch_config->parallel < 0) die(_("fetch.parallel cannot be negative")); - if (!fetch_parallel_config) - fetch_parallel_config = online_cpus(); + if (!fetch_config->parallel) + fetch_config->parallel = online_cpus(); return 0; } - return git_default_config(k, v, cb); + if (!strcmp(k, "fetch.output")) { + if (!v) + return config_error_nonbool(k); + else if (!strcasecmp(v, "full")) + fetch_config->display_format = DISPLAY_FORMAT_FULL; + else if (!strcasecmp(v, "compact")) + fetch_config->display_format = DISPLAY_FORMAT_COMPACT; + else + die(_("invalid value for '%s': '%s'"), + "fetch.output", v); + } + + return git_default_config(k, v, ctx, cb); } static int parse_refmap_arg(const struct option *opt, const char *arg, int unset) @@ -139,97 +176,11 @@ static int parse_refmap_arg(const struct option *opt, const char *arg, int unset * "git fetch --refmap='' origin foo" * can be used to tell the command not to store anywhere */ - refspec_append(&refmap, arg); + refspec_append(opt->value, arg); return 0; } -static struct option builtin_fetch_options[] = { - OPT__VERBOSITY(&verbosity), - OPT_BOOL(0, "all", &all, - N_("fetch from all remotes")), - OPT_BOOL(0, "set-upstream", &set_upstream, - N_("set upstream for git pull/fetch")), - OPT_BOOL('a', "append", &append, - N_("append to .git/FETCH_HEAD instead of overwriting")), - OPT_BOOL(0, "atomic", &atomic_fetch, - N_("use atomic transaction to update references")), - OPT_STRING(0, "upload-pack", &upload_pack, N_("path"), - N_("path to upload pack on remote end")), - OPT__FORCE(&force, N_("force overwrite of local reference"), 0), - OPT_BOOL('m', "multiple", &multiple, - N_("fetch from multiple remotes")), - OPT_SET_INT('t', "tags", &tags, - N_("fetch all tags and associated objects"), TAGS_SET), - OPT_SET_INT('n', NULL, &tags, - N_("do not fetch all tags (--no-tags)"), TAGS_UNSET), - OPT_INTEGER('j', "jobs", &max_jobs, - N_("number of submodules fetched in parallel")), - OPT_BOOL(0, "prefetch", &prefetch, - N_("modify the refspec to place all refs within refs/prefetch/")), - OPT_BOOL('p', "prune", &prune, - N_("prune remote-tracking branches no longer on remote")), - OPT_BOOL('P', "prune-tags", &prune_tags, - N_("prune local tags no longer on remote and clobber changed tags")), - OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules_cli, N_("on-demand"), - N_("control recursive fetching of submodules"), - PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules), - OPT_BOOL(0, "dry-run", &dry_run, - N_("dry run")), - OPT_BOOL(0, "write-fetch-head", &write_fetch_head, - N_("write fetched references to the FETCH_HEAD file")), - OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")), - OPT_BOOL('u', "update-head-ok", &update_head_ok, - N_("allow updating of HEAD ref")), - OPT_BOOL(0, "progress", &progress, N_("force progress reporting")), - OPT_STRING(0, "depth", &depth, N_("depth"), - N_("deepen history of shallow clone")), - OPT_STRING(0, "shallow-since", &deepen_since, N_("time"), - N_("deepen history of shallow repository based on time")), - OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"), - N_("deepen history of shallow clone, excluding rev")), - OPT_INTEGER(0, "deepen", &deepen_relative, - N_("deepen history of shallow clone")), - OPT_SET_INT_F(0, "unshallow", &unshallow, - N_("convert to a complete repository"), - 1, PARSE_OPT_NONEG), - OPT_SET_INT_F(0, "refetch", &refetch, - N_("re-fetch without negotiating common commits"), - 1, PARSE_OPT_NONEG), - { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"), - N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN }, - OPT_CALLBACK_F(0, "recurse-submodules-default", - &recurse_submodules_default, N_("on-demand"), - N_("default for recursive fetching of submodules " - "(lower priority than config files)"), - PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules), - OPT_BOOL(0, "update-shallow", &update_shallow, - N_("accept refs that update .git/shallow")), - OPT_CALLBACK_F(0, "refmap", NULL, N_("refmap"), - N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg), - OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")), - OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"), - TRANSPORT_FAMILY_IPV4), - OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"), - TRANSPORT_FAMILY_IPV6), - OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"), - N_("report that we have only objects reachable from this object")), - OPT_BOOL(0, "negotiate-only", &negotiate_only, - N_("do not fetch a packfile; instead, print ancestors of negotiation tips")), - OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), - OPT_BOOL(0, "auto-maintenance", &enable_auto_gc, - N_("run 'maintenance --auto' after fetching")), - OPT_BOOL(0, "auto-gc", &enable_auto_gc, - N_("run 'maintenance --auto' after fetching")), - OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates, - N_("check for forced-updates on all updated branches")), - OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph, - N_("write the commit-graph after fetching")), - OPT_BOOL(0, "stdin", &stdin_refspecs, - N_("accept refspecs from stdin")), - OPT_END() -}; - static void unlock_pack(unsigned int flags) { if (gtransport) @@ -357,7 +308,7 @@ static void clear_item(struct refname_hash_entry *item) static void add_already_queued_tags(const char *refname, - const struct object_id *old_oid, + const struct object_id *old_oid UNUSED, const struct object_id *new_oid, void *cb_data) { @@ -407,9 +358,9 @@ static void find_non_local_tags(const struct ref *refs, */ if (ends_with(ref->name, "^{}")) { if (item && - !has_object_file_with_flags(&ref->old_oid, quick_flags) && + !repo_has_object_file_with_flags(the_repository, &ref->old_oid, quick_flags) && !oidset_contains(&fetch_oids, &ref->old_oid) && - !has_object_file_with_flags(&item->oid, quick_flags) && + !repo_has_object_file_with_flags(the_repository, &item->oid, quick_flags) && !oidset_contains(&fetch_oids, &item->oid)) clear_item(item); item = NULL; @@ -423,7 +374,7 @@ static void find_non_local_tags(const struct ref *refs, * fetch. */ if (item && - !has_object_file_with_flags(&item->oid, quick_flags) && + !repo_has_object_file_with_flags(the_repository, &item->oid, quick_flags) && !oidset_contains(&fetch_oids, &item->oid)) clear_item(item); @@ -444,7 +395,7 @@ static void find_non_local_tags(const struct ref *refs, * checked to see if it needs fetching. */ if (item && - !has_object_file_with_flags(&item->oid, quick_flags) && + !repo_has_object_file_with_flags(the_repository, &item->oid, quick_flags) && !oidset_contains(&fetch_oids, &item->oid)) clear_item(item); @@ -741,76 +692,97 @@ out: return ret; } -static int refcol_width = 10; -static int compact_format; - -static void adjust_refcol_width(const struct ref *ref) +static int refcol_width(const struct ref *ref_map, int compact_format) { - int max, rlen, llen, len; + const struct ref *ref; + int max, width = 10; - /* uptodate lines are only shown on high verbosity level */ - if (verbosity <= 0 && oideq(&ref->peer_ref->old_oid, &ref->old_oid)) - return; + max = term_columns(); + if (compact_format) + max = max * 2 / 3; - max = term_columns(); - rlen = utf8_strwidth(prettify_refname(ref->name)); + for (ref = ref_map; ref; ref = ref->next) { + int rlen, llen = 0, len; - llen = utf8_strwidth(prettify_refname(ref->peer_ref->name)); + if (ref->status == REF_STATUS_REJECT_SHALLOW || + !ref->peer_ref || + !strcmp(ref->name, "HEAD")) + continue; - /* - * rough estimation to see if the output line is too long and - * should not be counted (we can't do precise calculation - * anyway because we don't know if the error explanation part - * will be printed in update_local_ref) - */ - if (compact_format) { - llen = 0; - max = max * 2 / 3; + /* uptodate lines are only shown on high verbosity level */ + if (verbosity <= 0 && oideq(&ref->peer_ref->old_oid, &ref->old_oid)) + continue; + + rlen = utf8_strwidth(prettify_refname(ref->name)); + if (!compact_format) + llen = utf8_strwidth(prettify_refname(ref->peer_ref->name)); + + /* + * rough estimation to see if the output line is too long and + * should not be counted (we can't do precise calculation + * anyway because we don't know if the error explanation part + * will be printed in update_local_ref) + */ + len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen; + if (len >= max) + continue; + + if (width < rlen) + width = rlen; } - len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen; - if (len >= max) - return; - /* - * Not precise calculation for compact mode because '*' can - * appear on the left hand side of '->' and shrink the column - * back. - */ - if (refcol_width < rlen) - refcol_width = rlen; + return width; } -static void prepare_format_display(struct ref *ref_map) +static void display_state_init(struct display_state *display_state, struct ref *ref_map, + const char *raw_url, enum display_format format) { - struct ref *rm; - const char *format = "full"; + int i; - if (verbosity < 0) - return; + memset(display_state, 0, sizeof(*display_state)); + strbuf_init(&display_state->buf, 0); + display_state->format = format; - git_config_get_string_tmp("fetch.output", &format); - if (!strcasecmp(format, "full")) - compact_format = 0; - else if (!strcasecmp(format, "compact")) - compact_format = 1; + if (raw_url) + display_state->url = transport_anonymize_url(raw_url); else - die(_("invalid value for '%s': '%s'"), - "fetch.output", format); + display_state->url = xstrdup("foreign"); - for (rm = ref_map; rm; rm = rm->next) { - if (rm->status == REF_STATUS_REJECT_SHALLOW || - !rm->peer_ref || - !strcmp(rm->name, "HEAD")) - continue; + display_state->url_len = strlen(display_state->url); + for (i = display_state->url_len - 1; display_state->url[i] == '/' && 0 <= i; i--) + ; + display_state->url_len = i + 1; + if (4 < i && !strncmp(".git", display_state->url + i - 3, 4)) + display_state->url_len = i - 3; - adjust_refcol_width(rm); + if (verbosity < 0) + return; + + switch (display_state->format) { + case DISPLAY_FORMAT_FULL: + case DISPLAY_FORMAT_COMPACT: + display_state->refcol_width = refcol_width(ref_map, + display_state->format == DISPLAY_FORMAT_COMPACT); + break; + case DISPLAY_FORMAT_PORCELAIN: + /* We don't need to precompute anything here. */ + break; + default: + BUG("unexpected display format %d", display_state->format); } } -static void print_remote_to_local(struct strbuf *display, +static void display_state_release(struct display_state *display_state) +{ + strbuf_release(&display_state->buf); + free(display_state->url); +} + +static void print_remote_to_local(struct display_state *display_state, const char *remote, const char *local) { - strbuf_addf(display, "%-*s -> %s", refcol_width, remote, local); + strbuf_addf(&display_state->buf, "%-*s -> %s", + display_state->refcol_width, remote, local); } static int find_and_replace(struct strbuf *haystack, @@ -840,14 +812,14 @@ static int find_and_replace(struct strbuf *haystack, return 1; } -static void print_compact(struct strbuf *display, +static void print_compact(struct display_state *display_state, const char *remote, const char *local) { struct strbuf r = STRBUF_INIT; struct strbuf l = STRBUF_INIT; if (!strcmp(remote, local)) { - strbuf_addf(display, "%-*s -> *", refcol_width, remote); + strbuf_addf(&display_state->buf, "%-*s -> *", display_state->refcol_width, remote); return; } @@ -856,40 +828,74 @@ static void print_compact(struct strbuf *display, if (!find_and_replace(&r, local, "*")) find_and_replace(&l, remote, "*"); - print_remote_to_local(display, r.buf, l.buf); + print_remote_to_local(display_state, r.buf, l.buf); strbuf_release(&r); strbuf_release(&l); } -static void format_display(struct strbuf *display, char code, - const char *summary, const char *error, - const char *remote, const char *local, - int summary_width) +static void display_ref_update(struct display_state *display_state, char code, + const char *summary, const char *error, + const char *remote, const char *local, + const struct object_id *old_oid, + const struct object_id *new_oid, + int summary_width) { - int width; + FILE *f = stderr; if (verbosity < 0) return; - width = (summary_width + strlen(summary) - gettext_width(summary)); + strbuf_reset(&display_state->buf); - strbuf_addf(display, "%c %-*s ", code, width, summary); - if (!compact_format) - print_remote_to_local(display, remote, local); - else - print_compact(display, remote, local); - if (error) - strbuf_addf(display, " (%s)", error); + switch (display_state->format) { + case DISPLAY_FORMAT_FULL: + case DISPLAY_FORMAT_COMPACT: { + int width; + + if (!display_state->shown_url) { + strbuf_addf(&display_state->buf, _("From %.*s\n"), + display_state->url_len, display_state->url); + display_state->shown_url = 1; + } + + width = (summary_width + strlen(summary) - gettext_width(summary)); + remote = prettify_refname(remote); + local = prettify_refname(local); + + strbuf_addf(&display_state->buf, " %c %-*s ", code, width, summary); + + if (display_state->format != DISPLAY_FORMAT_COMPACT) + print_remote_to_local(display_state, remote, local); + else + print_compact(display_state, remote, local); + + if (error) + strbuf_addf(&display_state->buf, " (%s)", error); + + break; + } + case DISPLAY_FORMAT_PORCELAIN: + strbuf_addf(&display_state->buf, "%c %s %s %s", code, + oid_to_hex(old_oid), oid_to_hex(new_oid), local); + f = stdout; + break; + default: + BUG("unexpected display format %d", display_state->format); + }; + strbuf_addch(&display_state->buf, '\n'); + + fputs(display_state->buf.buf, f); } static int update_local_ref(struct ref *ref, struct ref_transaction *transaction, - const char *remote, const struct ref *remote_ref, - struct strbuf *display, int summary_width) + struct display_state *display_state, + const struct ref *remote_ref, + int summary_width, + const struct fetch_config *config) { struct commit *current = NULL, *updated; - const char *pretty_ref = prettify_refname(ref->name); int fast_forward = 0; if (!repo_has_object_file(the_repository, &ref->new_oid)) @@ -897,8 +903,9 @@ static int update_local_ref(struct ref *ref, if (oideq(&ref->old_oid, &ref->new_oid)) { if (verbosity > 0) - format_display(display, '=', _("[up to date]"), NULL, - remote, pretty_ref, summary_width); + display_ref_update(display_state, '=', _("[up to date]"), NULL, + remote_ref->name, ref->name, + &ref->old_oid, &ref->new_oid, summary_width); return 0; } @@ -909,9 +916,10 @@ static int update_local_ref(struct ref *ref, * If this is the head, and it's not okay to update * the head, and the old value of the head isn't empty... */ - format_display(display, '!', _("[rejected]"), - _("can't fetch into checked-out branch"), - remote, pretty_ref, summary_width); + display_ref_update(display_state, '!', _("[rejected]"), + _("can't fetch into checked-out branch"), + remote_ref->name, ref->name, + &ref->old_oid, &ref->new_oid, summary_width); return 1; } @@ -920,13 +928,16 @@ static int update_local_ref(struct ref *ref, if (force || ref->force) { int r; r = s_update_ref("updating tag", ref, transaction, 0); - format_display(display, r ? '!' : 't', _("[tag update]"), - r ? _("unable to update local ref") : NULL, - remote, pretty_ref, summary_width); + display_ref_update(display_state, r ? '!' : 't', _("[tag update]"), + r ? _("unable to update local ref") : NULL, + remote_ref->name, ref->name, + &ref->old_oid, &ref->new_oid, summary_width); return r; } else { - format_display(display, '!', _("[rejected]"), _("would clobber existing tag"), - remote, pretty_ref, summary_width); + display_ref_update(display_state, '!', _("[rejected]"), + _("would clobber existing tag"), + remote_ref->name, ref->name, + &ref->old_oid, &ref->new_oid, summary_width); return 1; } } @@ -944,11 +955,10 @@ static int update_local_ref(struct ref *ref, * Base this on the remote's ref name, as it's * more likely to follow a standard layout. */ - const char *name = remote_ref ? remote_ref->name : ""; - if (starts_with(name, "refs/tags/")) { + if (starts_with(remote_ref->name, "refs/tags/")) { msg = "storing tag"; what = _("[new tag]"); - } else if (starts_with(name, "refs/heads/")) { + } else if (starts_with(remote_ref->name, "refs/heads/")) { msg = "storing head"; what = _("[new branch]"); } else { @@ -957,15 +967,17 @@ static int update_local_ref(struct ref *ref, } r = s_update_ref(msg, ref, transaction, 0); - format_display(display, r ? '!' : '*', what, - r ? _("unable to update local ref") : NULL, - remote, pretty_ref, summary_width); + display_ref_update(display_state, r ? '!' : '*', what, + r ? _("unable to update local ref") : NULL, + remote_ref->name, ref->name, + &ref->old_oid, &ref->new_oid, summary_width); return r; } - if (fetch_show_forced_updates) { + if (config->show_forced_updates) { uint64_t t_before = getnanotime(); - fast_forward = in_merge_bases(current, updated); + fast_forward = repo_in_merge_bases(the_repository, current, + updated); forced_updates_ms += (getnanotime() - t_before) / 1000000; } else { fast_forward = 1; @@ -979,9 +991,10 @@ static int update_local_ref(struct ref *ref, strbuf_addstr(&quickref, ".."); strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV); r = s_update_ref("fast-forward", ref, transaction, 1); - format_display(display, r ? '!' : ' ', quickref.buf, - r ? _("unable to update local ref") : NULL, - remote, pretty_ref, summary_width); + display_ref_update(display_state, r ? '!' : ' ', quickref.buf, + r ? _("unable to update local ref") : NULL, + remote_ref->name, ref->name, + &ref->old_oid, &ref->new_oid, summary_width); strbuf_release(&quickref); return r; } else if (force || ref->force) { @@ -991,14 +1004,16 @@ static int update_local_ref(struct ref *ref, strbuf_addstr(&quickref, "..."); strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV); r = s_update_ref("forced-update", ref, transaction, 1); - format_display(display, r ? '!' : '+', quickref.buf, - r ? _("unable to update local ref") : _("forced update"), - remote, pretty_ref, summary_width); + display_ref_update(display_state, r ? '!' : '+', quickref.buf, + r ? _("unable to update local ref") : _("forced update"), + remote_ref->name, ref->name, + &ref->old_oid, &ref->new_oid, summary_width); strbuf_release(&quickref); return r; } else { - format_display(display, '!', _("[rejected]"), _("non-fast-forward"), - remote, pretty_ref, summary_width); + display_ref_update(display_state, '!', _("[rejected]"), _("non-fast-forward"), + remote_ref->name, ref->name, + &ref->old_oid, &ref->new_oid, summary_width); return 1; } } @@ -1108,39 +1123,35 @@ N_("it took %.2f seconds to check forced updates; you can use\n" "'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates false'\n" "to avoid this check\n"); -static int store_updated_refs(const char *raw_url, const char *remote_name, +static int store_updated_refs(struct display_state *display_state, + const char *remote_name, int connectivity_checked, struct ref_transaction *transaction, struct ref *ref_map, - struct fetch_head *fetch_head) + struct fetch_head *fetch_head, + const struct fetch_config *config) { - int url_len, i, rc = 0; + int rc = 0; struct strbuf note = STRBUF_INIT; const char *what, *kind; struct ref *rm; - char *url; int want_status; int summary_width = 0; if (verbosity >= 0) summary_width = transport_summary_width(ref_map); - if (raw_url) - url = transport_anonymize_url(raw_url); - else - url = xstrdup("foreign"); - if (!connectivity_checked) { struct check_connected_options opt = CHECK_CONNECTED_INIT; + opt.exclude_hidden_refs_section = "fetch"; rm = ref_map; if (check_connected(iterate_ref_map, &rm, &opt)) { - rc = error(_("%s did not send all necessary objects\n"), url); + rc = error(_("%s did not send all necessary objects\n"), + display_state->url); goto abort; } } - prepare_format_display(ref_map); - /* * We do a pass for each fetch_head_status type in their enum order, so * merged entries are written before not-for-merge. That lets readers @@ -1200,7 +1211,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, ref->force = rm->peer_ref->force; } - if (recurse_submodules != RECURSE_SUBMODULES_OFF && + if (config->recurse_submodules != RECURSE_SUBMODULES_OFF && (!rm->peer_ref || !oideq(&ref->old_oid, &ref->new_oid))) { check_for_new_submodule_commits(&rm->old_oid); } @@ -1208,25 +1219,17 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, if (!strcmp(rm->name, "HEAD")) { kind = ""; what = ""; - } - else if (skip_prefix(rm->name, "refs/heads/", &what)) + } else if (skip_prefix(rm->name, "refs/heads/", &what)) { kind = "branch"; - else if (skip_prefix(rm->name, "refs/tags/", &what)) + } else if (skip_prefix(rm->name, "refs/tags/", &what)) { kind = "tag"; - else if (skip_prefix(rm->name, "refs/remotes/", &what)) + } else if (skip_prefix(rm->name, "refs/remotes/", &what)) { kind = "remote-tracking branch"; - else { + } else { kind = ""; what = rm->name; } - url_len = strlen(url); - for (i = url_len - 1; url[i] == '/' && 0 <= i; i--) - ; - url_len = i + 1; - if (4 < i && !strncmp(".git", url + i - 3, 4)) - url_len = i - 3; - strbuf_reset(¬e); if (*what) { if (*kind) @@ -1236,12 +1239,12 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, append_fetch_head(fetch_head, &rm->old_oid, rm->fetch_head_status, - note.buf, url, url_len); + note.buf, display_state->url, + display_state->url_len); - strbuf_reset(¬e); if (ref) { - rc |= update_local_ref(ref, transaction, what, - rm, ¬e, summary_width); + rc |= update_local_ref(ref, transaction, display_state, + rm, summary_width, config); free(ref); } else if (write_fetch_head || dry_run) { /* @@ -1249,18 +1252,12 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, * would be written to FETCH_HEAD, if --dry-run * is set). */ - format_display(¬e, '*', - *kind ? kind : "branch", NULL, - *what ? what : "HEAD", - "FETCH_HEAD", summary_width); - } - if (note.len) { - if (!shown_url) { - fprintf(stderr, _("From %.*s\n"), - url_len, url); - shown_url = 1; - } - fprintf(stderr, " %s\n", note.buf); + display_ref_update(display_state, '*', + *kind ? kind : "branch", NULL, + rm->name, + "FETCH_HEAD", + &rm->new_oid, &rm->old_oid, + summary_width); } } } @@ -1271,7 +1268,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, "branches"), remote_name); if (advice_enabled(ADVICE_FETCH_SHOW_FORCED_UPDATES)) { - if (!fetch_show_forced_updates) { + if (!config->show_forced_updates) { warning(_(warn_show_forced_updates)); } else if (forced_updates_ms > FORCED_UPDATES_DELAY_WARNING_IN_MS) { warning(_(warn_time_show_forced_updates), @@ -1281,7 +1278,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, abort: strbuf_release(¬e); - free(url); return rc; } @@ -1319,19 +1315,22 @@ static int check_exist_and_connected(struct ref *ref_map) * we need all direct targets to exist. */ for (r = rm; r; r = r->next) { - if (!has_object_file_with_flags(&r->old_oid, - OBJECT_INFO_SKIP_FETCH_OBJECT)) + if (!repo_has_object_file_with_flags(the_repository, &r->old_oid, + OBJECT_INFO_SKIP_FETCH_OBJECT)) return -1; } opt.quiet = 1; + opt.exclude_hidden_refs_section = "fetch"; return check_connected(iterate_ref_map, &rm, &opt); } -static int fetch_and_consume_refs(struct transport *transport, +static int fetch_and_consume_refs(struct display_state *display_state, + struct transport *transport, struct ref_transaction *transaction, struct ref *ref_map, - struct fetch_head *fetch_head) + struct fetch_head *fetch_head, + const struct fetch_config *config) { int connectivity_checked = 1; int ret; @@ -1352,9 +1351,9 @@ static int fetch_and_consume_refs(struct transport *transport, } trace2_region_enter("fetch", "consume_refs", the_repository); - ret = store_updated_refs(transport->url, transport->remote->name, + ret = store_updated_refs(display_state, transport->remote->name, connectivity_checked, transaction, ref_map, - fetch_head); + fetch_head, config); trace2_region_leave("fetch", "consume_refs", the_repository); out: @@ -1362,32 +1361,18 @@ out: return ret; } -static int prune_refs(struct refspec *rs, +static int prune_refs(struct display_state *display_state, + struct refspec *rs, struct ref_transaction *transaction, - struct ref *ref_map, - const char *raw_url) + struct ref *ref_map) { - int url_len, i, result = 0; + int result = 0; struct ref *ref, *stale_refs = get_stale_heads(rs, ref_map); struct strbuf err = STRBUF_INIT; - char *url; const char *dangling_msg = dry_run ? _(" (%s will become dangling)") : _(" (%s has become dangling)"); - if (raw_url) - url = transport_anonymize_url(raw_url); - else - url = xstrdup("foreign"); - - url_len = strlen(url); - for (i = url_len - 1; url[i] == '/' && 0 <= i; i--) - ; - - url_len = i + 1; - if (4 < i && !strncmp(".git", url + i - 3, 4)) - url_len = i - 3; - if (!dry_run) { if (transaction) { for (ref = stale_refs; ref; ref = ref->next) { @@ -1411,23 +1396,16 @@ static int prune_refs(struct refspec *rs, int summary_width = transport_summary_width(stale_refs); for (ref = stale_refs; ref; ref = ref->next) { - struct strbuf sb = STRBUF_INIT; - if (!shown_url) { - fprintf(stderr, _("From %.*s\n"), url_len, url); - shown_url = 1; - } - format_display(&sb, '-', _("[deleted]"), NULL, - _("(none)"), prettify_refname(ref->name), - summary_width); - fprintf(stderr, " %s\n",sb.buf); - strbuf_release(&sb); + display_ref_update(display_state, '-', _("[deleted]"), NULL, + _("(none)"), ref->name, + &ref->new_oid, &ref->old_oid, + summary_width); warn_dangling_symref(stderr, dangling_msg, ref->name); } } cleanup: strbuf_release(&err); - free(url); free_refs(stale_refs); return result; } @@ -1487,7 +1465,7 @@ static void add_negotiation_tips(struct git_transport_options *smart_options) int old_nr; if (!has_glob_specials(s)) { struct object_id oid; - if (get_oid(s, &oid)) + if (repo_get_oid(the_repository, s, &oid)) die(_("%s is not a valid object"), s); if (!has_object(the_repository, &oid, 0)) die(_("the object %s does not exist"), s); @@ -1542,10 +1520,12 @@ static struct transport *prepare_transport(struct remote *remote, int deepen) return transport; } -static int backfill_tags(struct transport *transport, +static int backfill_tags(struct display_state *display_state, + struct transport *transport, struct ref_transaction *transaction, struct ref *ref_map, - struct fetch_head *fetch_head) + struct fetch_head *fetch_head, + const struct fetch_config *config) { int retcode, cannot_reuse; @@ -1566,7 +1546,8 @@ static int backfill_tags(struct transport *transport, transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL); transport_set_option(transport, TRANS_OPT_DEPTH, "0"); transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL); - retcode = fetch_and_consume_refs(transport, transaction, ref_map, fetch_head); + retcode = fetch_and_consume_refs(display_state, transport, transaction, ref_map, + fetch_head, config); if (gsecondary) { transport_disconnect(gsecondary); @@ -1577,10 +1558,12 @@ static int backfill_tags(struct transport *transport, } static int do_fetch(struct transport *transport, - struct refspec *rs) + struct refspec *rs, + const struct fetch_config *config) { struct ref_transaction *transaction = NULL; struct ref *ref_map = NULL; + struct display_state display_state = { 0 }; int autotags = (transport->remote->fetch_tags == 1); int retcode = 0; const struct ref *remote_refs; @@ -1662,6 +1645,9 @@ static int do_fetch(struct transport *transport, if (retcode) goto cleanup; + display_state_init(&display_state, ref_map, transport->url, + config->display_format); + if (atomic_fetch) { transaction = ref_transaction_begin(&err); if (!transaction) { @@ -1679,17 +1665,17 @@ static int do_fetch(struct transport *transport, * don't care whether --tags was specified. */ if (rs->nr) { - retcode = prune_refs(rs, transaction, ref_map, transport->url); + retcode = prune_refs(&display_state, rs, transaction, ref_map); } else { - retcode = prune_refs(&transport->remote->fetch, - transaction, ref_map, - transport->url); + retcode = prune_refs(&display_state, &transport->remote->fetch, + transaction, ref_map); } if (retcode != 0) retcode = 1; } - if (fetch_and_consume_refs(transport, transaction, ref_map, &fetch_head)) { + if (fetch_and_consume_refs(&display_state, transport, transaction, ref_map, + &fetch_head, config)) { retcode = 1; goto cleanup; } @@ -1711,8 +1697,8 @@ static int do_fetch(struct transport *transport, * when `--atomic` is passed: in that case we'll abort * the transaction and don't commit anything. */ - if (backfill_tags(transport, transaction, tags_ref_map, - &fetch_head)) + if (backfill_tags(&display_state, transport, transaction, tags_ref_map, + &fetch_head, config)) retcode = 1; } @@ -1794,6 +1780,7 @@ cleanup: error("%s", err.buf); } + display_state_release(&display_state); close_fetch_head(&fetch_head); strbuf_release(&err); free_refs(ref_map); @@ -1813,7 +1800,9 @@ struct remote_group_data { struct string_list *list; }; -static int get_remote_group(const char *key, const char *value, void *priv) +static int get_remote_group(const char *key, const char *value, + const struct config_context *ctx UNUSED, + void *priv) { struct remote_group_data *g = priv; @@ -1848,7 +1837,8 @@ static int add_remote_or_group(const char *name, struct string_list *list) return 1; } -static void add_options_to_argv(struct strvec *argv) +static void add_options_to_argv(struct strvec *argv, + const struct fetch_config *config) { if (dry_run) strvec_push(argv, "--dry-run"); @@ -1862,9 +1852,11 @@ static void add_options_to_argv(struct strvec *argv) strvec_push(argv, "--force"); if (keep) strvec_push(argv, "--keep"); - if (recurse_submodules == RECURSE_SUBMODULES_ON) + if (config->recurse_submodules == RECURSE_SUBMODULES_ON) strvec_push(argv, "--recurse-submodules"); - else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) + else if (config->recurse_submodules == RECURSE_SUBMODULES_OFF) + strvec_push(argv, "--no-recurse-submodules"); + else if (config->recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) strvec_push(argv, "--recurse-submodules=on-demand"); if (tags == TAGS_SET) strvec_push(argv, "--tags"); @@ -1880,6 +1872,10 @@ static void add_options_to_argv(struct strvec *argv) strvec_push(argv, "--ipv4"); else if (family == TRANSPORT_FAMILY_IPV6) strvec_push(argv, "--ipv6"); + if (!write_fetch_head) + strvec_push(argv, "--no-write-fetch-head"); + if (config->display_format == DISPLAY_FORMAT_PORCELAIN) + strvec_pushf(argv, "--porcelain"); } /* Fetch multiple remotes in parallel */ @@ -1888,9 +1884,11 @@ struct parallel_fetch_state { const char **argv; struct string_list *remotes; int next, result; + const struct fetch_config *config; }; -static int fetch_next_remote(struct child_process *cp, struct strbuf *out, +static int fetch_next_remote(struct child_process *cp, + struct strbuf *out UNUSED, void *cb, void **task_cb) { struct parallel_fetch_state *state = cb; @@ -1906,13 +1904,14 @@ static int fetch_next_remote(struct child_process *cp, struct strbuf *out, strvec_push(&cp->args, remote); cp->git_cmd = 1; - if (verbosity >= 0) + if (verbosity >= 0 && state->config->display_format != DISPLAY_FORMAT_PORCELAIN) printf(_("Fetching %s\n"), remote); return 1; } -static int fetch_failed_to_start(struct strbuf *out, void *cb, void *task_cb) +static int fetch_failed_to_start(struct strbuf *out UNUSED, + void *cb, void *task_cb) { struct parallel_fetch_state *state = cb; const char *remote = task_cb; @@ -1937,7 +1936,8 @@ static int fetch_finished(int result, struct strbuf *out, return 0; } -static int fetch_multiple(struct string_list *list, int max_children) +static int fetch_multiple(struct string_list *list, int max_children, + const struct fetch_config *config) { int i, result = 0; struct strvec argv = STRVEC_INIT; @@ -1948,12 +1948,17 @@ static int fetch_multiple(struct string_list *list, int max_children) return errcode; } - strvec_pushl(&argv, "fetch", "--append", "--no-auto-gc", + /* + * Cancel out the fetch.bundleURI config when running subprocesses, + * to avoid fetching from the same bundle list multiple times. + */ + strvec_pushl(&argv, "-c", "fetch.bundleURI=", + "fetch", "--append", "--no-auto-gc", "--no-write-commit-graph", NULL); - add_options_to_argv(&argv); + add_options_to_argv(&argv, config); if (max_children != 1 && list->nr != 1) { - struct parallel_fetch_state state = { argv.v, list, 0, 0 }; + struct parallel_fetch_state state = { argv.v, list, 0, 0, config }; const struct run_process_parallel_opts opts = { .tr2_category = "fetch", .tr2_label = "parallel/fetch", @@ -1977,7 +1982,7 @@ static int fetch_multiple(struct string_list *list, int max_children) strvec_pushv(&cmd.args, argv.v); strvec_push(&cmd.args, name); - if (verbosity >= 0) + if (verbosity >= 0 && config->display_format != DISPLAY_FORMAT_PORCELAIN) printf(_("Fetching %s\n"), name); cmd.git_cmd = 1; if (run_command(&cmd)) { @@ -2007,7 +2012,7 @@ static inline void fetch_one_setup_partial(struct remote *remote) * If no prior partial clone/fetch and the current fetch DID NOT * request a partial-fetch, do a normal fetch. */ - if (!has_promisor_remote() && !filter_options.choice) + if (!repo_has_promisor_remote(the_repository) && !filter_options.choice) return; /* @@ -2032,7 +2037,8 @@ static inline void fetch_one_setup_partial(struct remote *remote) } static int fetch_one(struct remote *remote, int argc, const char **argv, - int prune_tags_ok, int use_stdin_refspecs) + int prune_tags_ok, int use_stdin_refspecs, + const struct fetch_config *config) { struct refspec rs = REFSPEC_INIT_FETCH; int i; @@ -2050,8 +2056,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, /* no command line request */ if (0 <= remote->prune) prune = remote->prune; - else if (0 <= fetch_prune_config) - prune = fetch_prune_config; + else if (0 <= config->prune) + prune = config->prune; else prune = PRUNE_BY_DEFAULT; } @@ -2060,8 +2066,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, /* no command line request */ if (0 <= remote->prune_tags) prune_tags = remote->prune_tags; - else if (0 <= fetch_prune_tags_config) - prune_tags = fetch_prune_tags_config; + else if (0 <= config->prune_tags) + prune_tags = config->prune_tags; else prune_tags = PRUNE_TAGS_BY_DEFAULT; } @@ -2099,7 +2105,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, sigchain_push_common(unlock_pack_on_signal); atexit(unlock_pack_atexit); sigchain_push(SIGPIPE, SIG_IGN); - exit_code = do_fetch(gtransport, &rs); + exit_code = do_fetch(gtransport, &rs, config); sigchain_pop(SIGPIPE); refspec_clear(&rs); transport_disconnect(gtransport); @@ -2109,12 +2115,116 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int cmd_fetch(int argc, const char **argv, const char *prefix) { - int i; + struct fetch_config config = { + .display_format = DISPLAY_FORMAT_FULL, + .prune = -1, + .prune_tags = -1, + .show_forced_updates = 1, + .recurse_submodules = RECURSE_SUBMODULES_DEFAULT, + .parallel = 1, + .submodule_fetch_jobs = -1, + }; + const char *submodule_prefix = ""; const char *bundle_uri; struct string_list list = STRING_LIST_INIT_DUP; struct remote *remote = NULL; + int all = 0, multiple = 0; int result = 0; int prune_tags_ok = 1; + int enable_auto_gc = 1; + int unshallow = 0; + int max_jobs = -1; + int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT; + int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND; + int fetch_write_commit_graph = -1; + int stdin_refspecs = 0; + int negotiate_only = 0; + int porcelain = 0; + int i; + + struct option builtin_fetch_options[] = { + OPT__VERBOSITY(&verbosity), + OPT_BOOL(0, "all", &all, + N_("fetch from all remotes")), + OPT_BOOL(0, "set-upstream", &set_upstream, + N_("set upstream for git pull/fetch")), + OPT_BOOL('a', "append", &append, + N_("append to .git/FETCH_HEAD instead of overwriting")), + OPT_BOOL(0, "atomic", &atomic_fetch, + N_("use atomic transaction to update references")), + OPT_STRING(0, "upload-pack", &upload_pack, N_("path"), + N_("path to upload pack on remote end")), + OPT__FORCE(&force, N_("force overwrite of local reference"), 0), + OPT_BOOL('m', "multiple", &multiple, + N_("fetch from multiple remotes")), + OPT_SET_INT('t', "tags", &tags, + N_("fetch all tags and associated objects"), TAGS_SET), + OPT_SET_INT('n', NULL, &tags, + N_("do not fetch all tags (--no-tags)"), TAGS_UNSET), + OPT_INTEGER('j', "jobs", &max_jobs, + N_("number of submodules fetched in parallel")), + OPT_BOOL(0, "prefetch", &prefetch, + N_("modify the refspec to place all refs within refs/prefetch/")), + OPT_BOOL('p', "prune", &prune, + N_("prune remote-tracking branches no longer on remote")), + OPT_BOOL('P', "prune-tags", &prune_tags, + N_("prune local tags no longer on remote and clobber changed tags")), + OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules_cli, N_("on-demand"), + N_("control recursive fetching of submodules"), + PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules), + OPT_BOOL(0, "dry-run", &dry_run, + N_("dry run")), + OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")), + OPT_BOOL(0, "write-fetch-head", &write_fetch_head, + N_("write fetched references to the FETCH_HEAD file")), + OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")), + OPT_BOOL('u', "update-head-ok", &update_head_ok, + N_("allow updating of HEAD ref")), + OPT_BOOL(0, "progress", &progress, N_("force progress reporting")), + OPT_STRING(0, "depth", &depth, N_("depth"), + N_("deepen history of shallow clone")), + OPT_STRING(0, "shallow-since", &deepen_since, N_("time"), + N_("deepen history of shallow repository based on time")), + OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"), + N_("deepen history of shallow clone, excluding rev")), + OPT_INTEGER(0, "deepen", &deepen_relative, + N_("deepen history of shallow clone")), + OPT_SET_INT_F(0, "unshallow", &unshallow, + N_("convert to a complete repository"), + 1, PARSE_OPT_NONEG), + OPT_SET_INT_F(0, "refetch", &refetch, + N_("re-fetch without negotiating common commits"), + 1, PARSE_OPT_NONEG), + { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"), + N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN }, + OPT_CALLBACK_F(0, "recurse-submodules-default", + &recurse_submodules_default, N_("on-demand"), + N_("default for recursive fetching of submodules " + "(lower priority than config files)"), + PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules), + OPT_BOOL(0, "update-shallow", &update_shallow, + N_("accept refs that update .git/shallow")), + OPT_CALLBACK_F(0, "refmap", &refmap, N_("refmap"), + N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg), + OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")), + OPT_IPVERSION(&family), + OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"), + N_("report that we have only objects reachable from this object")), + OPT_BOOL(0, "negotiate-only", &negotiate_only, + N_("do not fetch a packfile; instead, print ancestors of negotiation tips")), + OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), + OPT_BOOL(0, "auto-maintenance", &enable_auto_gc, + N_("run 'maintenance --auto' after fetching")), + OPT_BOOL(0, "auto-gc", &enable_auto_gc, + N_("run 'maintenance --auto' after fetching")), + OPT_BOOL(0, "show-forced-updates", &config.show_forced_updates, + N_("check for forced-updates on all updated branches")), + OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph, + N_("write the commit-graph after fetching")), + OPT_BOOL(0, "stdin", &stdin_refspecs, + N_("accept refspecs from stdin")), + OPT_END() + }; packet_trace_identity("fetch"); @@ -2128,7 +2238,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) free(anon); } - git_config(git_fetch_config, NULL); + git_config(git_fetch_config, &config); if (the_repository->gitdir) { prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; @@ -2138,7 +2248,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) builtin_fetch_options, builtin_fetch_usage, 0); if (recurse_submodules_cli != RECURSE_SUBMODULES_DEFAULT) - recurse_submodules = recurse_submodules_cli; + config.recurse_submodules = recurse_submodules_cli; if (negotiate_only) { switch (recurse_submodules_cli) { @@ -2149,7 +2259,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) * submodules. Skip it by setting recurse_submodules to * RECURSE_SUBMODULES_OFF. */ - recurse_submodules = RECURSE_SUBMODULES_OFF; + config.recurse_submodules = RECURSE_SUBMODULES_OFF; break; default: @@ -2158,15 +2268,35 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) } } - if (recurse_submodules != RECURSE_SUBMODULES_OFF) { - int *sfjc = submodule_fetch_jobs_config == -1 - ? &submodule_fetch_jobs_config : NULL; - int *rs = recurse_submodules == RECURSE_SUBMODULES_DEFAULT - ? &recurse_submodules : NULL; + if (config.recurse_submodules != RECURSE_SUBMODULES_OFF) { + int *sfjc = config.submodule_fetch_jobs == -1 + ? &config.submodule_fetch_jobs : NULL; + int *rs = config.recurse_submodules == RECURSE_SUBMODULES_DEFAULT + ? &config.recurse_submodules : NULL; fetch_config_from_gitmodules(sfjc, rs); } + + if (porcelain) { + switch (recurse_submodules_cli) { + case RECURSE_SUBMODULES_OFF: + case RECURSE_SUBMODULES_DEFAULT: + /* + * Reference updates in submodules would be ambiguous + * in porcelain mode, so we reject this combination. + */ + config.recurse_submodules = RECURSE_SUBMODULES_OFF; + break; + + default: + die(_("options '%s' and '%s' cannot be used together"), + "--porcelain", "--recurse-submodules"); + } + + config.display_format = DISPLAY_FORMAT_PORCELAIN; + } + if (negotiate_only && !negotiation_tip.nr) die(_("--negotiate-only needs one or more --negotiation-tip=*")); @@ -2263,9 +2393,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) printf("%s\n", oid_to_hex(oid)); oidset_clear(&acked_commits); } else if (remote) { - if (filter_options.choice || has_promisor_remote()) + if (filter_options.choice || repo_has_promisor_remote(the_repository)) fetch_one_setup_partial(remote); - result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs); + result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs, + &config); } else { int max_children = max_jobs; @@ -2282,13 +2413,12 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) "from one remote")); if (max_children < 0) - max_children = fetch_parallel_config; + max_children = config.parallel; /* TODO should this also die if we have a previous partial-clone? */ - result = fetch_multiple(&list, max_children); + result = fetch_multiple(&list, max_children, &config); } - /* * This is only needed after fetch_one(), which does not fetch * submodules by itself. @@ -2298,20 +2428,20 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) * the fetched history from each remote, so there is no need * to fetch submodules from here. */ - if (!result && remote && (recurse_submodules != RECURSE_SUBMODULES_OFF)) { + if (!result && remote && (config.recurse_submodules != RECURSE_SUBMODULES_OFF)) { struct strvec options = STRVEC_INIT; int max_children = max_jobs; if (max_children < 0) - max_children = submodule_fetch_jobs_config; + max_children = config.submodule_fetch_jobs; if (max_children < 0) - max_children = fetch_parallel_config; + max_children = config.parallel; - add_options_to_argv(&options); + add_options_to_argv(&options, &config); result = fetch_submodules(the_repository, &options, submodule_prefix, - recurse_submodules, + config.recurse_submodules, recurse_submodules_default, verbosity < 0, max_children); diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index 8d8fd393f8..0f9855b680 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -1,6 +1,7 @@ #include "builtin.h" #include "config.h" #include "fmt-merge-msg.h" +#include "gettext.h" #include "parse-options.h" static const char * const fmt_merge_msg_usage[] = { diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 6f62f40d12..1c19cd5bd3 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -1,10 +1,13 @@ #include "builtin.h" -#include "cache.h" #include "config.h" +#include "gettext.h" #include "refs.h" #include "object.h" #include "parse-options.h" #include "ref-filter.h" +#include "strbuf.h" +#include "strvec.h" +#include "commit-reach.h" static char const * const for_each_ref_usage[] = { N_("git for-each-ref [<options>] [<pattern>]"), @@ -16,15 +19,13 @@ static char const * const for_each_ref_usage[] = { int cmd_for_each_ref(int argc, const char **argv, const char *prefix) { - int i; struct ref_sorting *sorting; struct string_list sorting_options = STRING_LIST_INIT_DUP; - int maxcount = 0, icase = 0; - struct ref_array array; - struct ref_filter filter; + int icase = 0; + struct ref_filter filter = REF_FILTER_INIT; struct ref_format format = REF_FORMAT_INIT; - struct strbuf output = STRBUF_INIT; - struct strbuf err = STRBUF_INIT; + int from_stdin = 0; + struct strvec vec = STRVEC_INIT; struct option opts[] = { OPT_BIT('s', "shell", &format.quote_style, @@ -35,11 +36,14 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) N_("quote placeholders suitably for python"), QUOTE_PYTHON), OPT_BIT(0 , "tcl", &format.quote_style, N_("quote placeholders suitably for Tcl"), QUOTE_TCL), + OPT_BOOL(0, "omit-empty", &format.array_opts.omit_empty, + N_("do not output a newline after empty formatted refs")), OPT_GROUP(""), - OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")), + OPT_INTEGER( 0 , "count", &format.array_opts.max_count, N_("show only <n> matched refs")), OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")), OPT__COLOR(&format.use_color, N_("respect format colors")), + OPT_REF_FILTER_EXCLUDE(&filter), OPT_REF_SORT(&sorting_options), OPT_CALLBACK(0, "points-at", &filter.points_at, N_("object"), N_("print only refs which points at the given object"), @@ -49,19 +53,20 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) OPT_CONTAINS(&filter.with_commit, N_("print only refs which contain the commit")), OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")), OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")), + OPT_BOOL(0, "stdin", &from_stdin, N_("read reference patterns from stdin")), OPT_END(), }; - memset(&array, 0, sizeof(array)); - memset(&filter, 0, sizeof(filter)); - format.format = "%(objectname) %(objecttype)\t%(refname)"; git_config(git_default_config, NULL); + /* Set default (refname) sorting */ + string_list_append(&sorting_options, "refname"); + parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0); - if (maxcount < 0) { - error("invalid --count argument: `%d'", maxcount); + 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); } if (HAS_MULTI_BITS(format.quote_style)) { @@ -75,27 +80,28 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase); filter.ignore_case = icase; - filter.name_patterns = argv; - filter.match_as_path = 1; - filter_refs(&array, &filter, FILTER_REFS_ALL); - ref_array_sort(sorting, &array); - - if (!maxcount || array.nr < maxcount) - maxcount = array.nr; - for (i = 0; i < maxcount; i++) { - strbuf_reset(&err); - strbuf_reset(&output); - if (format_ref_array_item(array.items[i], &format, &output, &err)) - die("%s", err.buf); - fwrite(output.buf, 1, output.len, stdout); - putchar('\n'); + if (from_stdin) { + struct strbuf line = STRBUF_INIT; + + if (argv[0]) + die(_("unknown arguments supplied with --stdin")); + + while (strbuf_getline(&line, stdin) != EOF) + strvec_push(&vec, line.buf); + + strbuf_release(&line); + + /* vec.v is NULL-terminated, just like 'argv'. */ + filter.name_patterns = vec.v; + } else { + filter.name_patterns = argv; } - strbuf_release(&err); - strbuf_release(&output); - ref_array_clear(&array); - free_commit_list(filter.with_commit); - free_commit_list(filter.no_commit); + filter.match_as_path = 1; + filter_and_format_refs(&filter, FILTER_REFS_ALL, sorting, &format); + + ref_filter_clear(&filter); ref_sorting_release(sorting); + strvec_clear(&vec); return 0; } diff --git a/builtin/for-each-repo.c b/builtin/for-each-repo.c index 6aeac37148..28186b30f5 100644 --- a/builtin/for-each-repo.c +++ b/builtin/for-each-repo.c @@ -1,7 +1,9 @@ -#include "cache.h" -#include "config.h" #include "builtin.h" +#include "config.h" +#include "gettext.h" #include "parse-options.h" +#include "path.h" +#include "repository.h" #include "run-command.h" #include "string-list.h" @@ -32,6 +34,7 @@ int cmd_for_each_repo(int argc, const char **argv, const char *prefix) static const char *config_key = NULL; int i, result = 0; const struct string_list *values; + int err; const struct option options[] = { OPT_STRING(0, "config", &config_key, N_("config"), @@ -45,14 +48,11 @@ int cmd_for_each_repo(int argc, const char **argv, const char *prefix) if (!config_key) die(_("missing --config=<config>")); - values = repo_config_get_value_multi(the_repository, - config_key); - - /* - * Do nothing on an empty list, which is equivalent to the case - * where the config variable does not exist at all. - */ - if (!values) + err = repo_config_get_string_multi(the_repository, config_key, &values); + if (err < 0) + usage_msg_optf(_("got bad config --config=%s"), + for_each_repo_usage, options, config_key); + else if (err) return 0; for (i = 0; !result && i < values->nr; i++) diff --git a/builtin/fsck.c b/builtin/fsck.c index d207bd909b..611925905e 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -1,6 +1,6 @@ -#define USE_THE_INDEX_VARIABLE #include "builtin.h" -#include "cache.h" +#include "gettext.h" +#include "hex.h" #include "repository.h" #include "config.h" #include "commit.h" @@ -18,10 +18,18 @@ #include "streaming.h" #include "decorate.h" #include "packfile.h" -#include "object-store.h" +#include "object-file.h" +#include "object-name.h" +#include "object-store-ll.h" +#include "path.h" +#include "read-cache-ll.h" +#include "replace-object.h" #include "resolve-undo.h" #include "run-command.h" +#include "sparse-index.h" #include "worktree.h" +#include "pack-revindex.h" +#include "pack-bitmap.h" #define REACHABLE 0x0001 #define SEEN 0x0002 @@ -51,6 +59,8 @@ static int name_objects; #define ERROR_REFS 010 #define ERROR_COMMIT_GRAPH 020 #define ERROR_MULTI_PACK_INDEX 040 +#define ERROR_PACK_REV_INDEX 0100 +#define ERROR_BITMAP 0200 static const char *describe_object(const struct object_id *oid) { @@ -82,11 +92,11 @@ static int objerror(struct object *obj, const char *err) return -1; } -static int fsck_error_func(struct fsck_options *o, +static int fsck_error_func(struct fsck_options *o UNUSED, const struct object_id *oid, enum object_type object_type, enum fsck_msg_type msg_type, - enum fsck_msg_id msg_id, + enum fsck_msg_id msg_id UNUSED, const char *message) { switch (msg_type) { @@ -111,7 +121,7 @@ static int fsck_error_func(struct fsck_options *o, static struct object_array pending; static int mark_object(struct object *obj, enum object_type type, - void *data, struct fsck_options *options) + void *data, struct fsck_options *options UNUSED) { struct object *parent = data; @@ -196,8 +206,8 @@ static int traverse_reachable(void) return !!result; } -static int mark_used(struct object *obj, enum object_type object_type, - void *data, struct fsck_options *options) +static int mark_used(struct object *obj, enum object_type type UNUSED, + void *data UNUSED, struct fsck_options *options UNUSED) { if (!obj) return 1; @@ -233,17 +243,17 @@ static void mark_unreachable_referents(const struct object_id *oid) } static int mark_loose_unreachable_referents(const struct object_id *oid, - const char *path, - void *data) + const char *path UNUSED, + void *data UNUSED) { mark_unreachable_referents(oid); return 0; } static int mark_packed_unreachable_referents(const struct object_id *oid, - struct packed_git *pack, - uint32_t pos, - void *data) + struct packed_git *pack UNUSED, + uint32_t pos UNUSED, + void *data UNUSED) { mark_unreachable_referents(oid); return 0; @@ -661,14 +671,15 @@ static int fsck_loose(const struct object_id *oid, const char *path, void *data) return 0; /* keep checking other objects, even if we saw an error */ } -static int fsck_cruft(const char *basename, const char *path, void *data) +static int fsck_cruft(const char *basename, const char *path, + void *data UNUSED) { if (!starts_with(basename, "tmp_obj_")) fprintf_ln(stderr, _("bad sha1 file: %s"), path); return 0; } -static int fsck_subdir(unsigned int nr, const char *path, void *data) +static int fsck_subdir(unsigned int nr, const char *path UNUSED, void *data) { struct for_each_loose_cb *cb_data = data; struct progress *progress = cb_data->progress; @@ -732,19 +743,19 @@ static int fsck_head_link(const char *head_ref_name, return 0; } -static int fsck_cache_tree(struct cache_tree *it) +static int fsck_cache_tree(struct cache_tree *it, const char *index_path) { int i; int err = 0; if (verbose) - fprintf_ln(stderr, _("Checking cache tree")); + fprintf_ln(stderr, _("Checking cache tree of %s"), index_path); if (0 <= it->entry_count) { struct object *obj = parse_object(the_repository, &it->oid); if (!obj) { - error(_("%s: invalid sha1 pointer in cache-tree"), - oid_to_hex(&it->oid)); + error(_("%s: invalid sha1 pointer in cache-tree of %s"), + oid_to_hex(&it->oid), index_path); errors_found |= ERROR_REFS; return 1; } @@ -755,11 +766,12 @@ static int fsck_cache_tree(struct cache_tree *it) err |= objerror(obj, _("non-tree in cache-tree")); } for (i = 0; i < it->subtree_nr; i++) - err |= fsck_cache_tree(it->down[i]->cache_tree); + err |= fsck_cache_tree(it->down[i]->cache_tree, index_path); return err; } -static int fsck_resolve_undo(struct index_state *istate) +static int fsck_resolve_undo(struct index_state *istate, + const char *index_path) { struct string_list_item *item; struct string_list *resolve_undo = istate->resolve_undo; @@ -782,8 +794,9 @@ static int fsck_resolve_undo(struct index_state *istate) obj = parse_object(the_repository, &ru->oid[i]); if (!obj) { - error(_("%s: invalid sha1 pointer in resolve-undo"), - oid_to_hex(&ru->oid[i])); + error(_("%s: invalid sha1 pointer in resolve-undo of %s"), + oid_to_hex(&ru->oid[i]), + index_path); errors_found |= ERROR_REFS; continue; } @@ -796,6 +809,38 @@ static int fsck_resolve_undo(struct index_state *istate) return 0; } +static void fsck_index(struct index_state *istate, const char *index_path, + int is_current_worktree) +{ + unsigned int i; + + /* TODO: audit for interaction with sparse-index. */ + ensure_full_index(istate); + for (i = 0; i < istate->cache_nr; i++) { + unsigned int mode; + struct blob *blob; + struct object *obj; + + mode = istate->cache[i]->ce_mode; + if (S_ISGITLINK(mode)) + continue; + blob = lookup_blob(the_repository, + &istate->cache[i]->oid); + if (!blob) + continue; + obj = &blob->object; + obj->flags |= USED; + fsck_put_object_name(&fsck_walk_options, &obj->oid, + "%s:%s", + is_current_worktree ? "" : index_path, + istate->cache[i]->name); + mark_object_reachable(obj); + } + if (istate->cache_tree) + fsck_cache_tree(istate->cache_tree, index_path); + fsck_resolve_undo(istate, index_path); +} + static void mark_object_for_connectivity(const struct object_id *oid) { struct object *obj = lookup_unknown_object(the_repository, oid); @@ -803,22 +848,54 @@ static void mark_object_for_connectivity(const struct object_id *oid) } static int mark_loose_for_connectivity(const struct object_id *oid, - const char *path, - void *data) + const char *path UNUSED, + void *data UNUSED) { mark_object_for_connectivity(oid); return 0; } static int mark_packed_for_connectivity(const struct object_id *oid, - struct packed_git *pack, - uint32_t pos, - void *data) + struct packed_git *pack UNUSED, + uint32_t pos UNUSED, + void *data UNUSED) { mark_object_for_connectivity(oid); return 0; } +static int check_pack_rev_indexes(struct repository *r, int show_progress) +{ + 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) + pack_count++; + progress = start_delayed_progress("Verifying reverse pack-indexes", pack_count); + pack_count = 0; + } + + for (struct packed_git *p = get_all_packs(r); p; p = p->next) { + int load_error = load_pack_revindex_from_disk(p); + + if (load_error < 0) { + error(_("unable to load rev-index for pack '%s'"), p->pack_name); + res = ERROR_PACK_REV_INDEX; + } else if (!load_error && + !load_pack_revindex(r, p) && + verify_pack_revindex(p)) { + error(_("invalid rev-index for pack '%s'"), p->pack_name); + res = ERROR_PACK_REV_INDEX; + } + display_progress(progress, ++pack_count); + } + stop_progress(&progress); + + return res; +} + static char const * const fsck_usage[] = { N_("git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n" " [--[no-]full] [--strict] [--verbose] [--lost-found]\n" @@ -854,7 +931,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) fetch_if_missing = 0; errors_found = 0; - read_replace_refs = 0; + disable_replace_refs(); save_commit_buffer = 0; argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0); @@ -923,7 +1000,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) for (i = 0; i < argc; i++) { const char *arg = argv[i]; struct object_id oid; - if (!get_oid(arg, &oid)) { + if (!repo_get_oid(the_repository, arg, &oid)) { struct object *obj = lookup_object(the_repository, &oid); @@ -956,34 +1033,36 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) } if (keep_cache_objects) { + struct worktree **worktrees, **p; + verify_index_checksum = 1; verify_ce_order = 1; - repo_read_index(the_repository); - /* TODO: audit for interaction with sparse-index. */ - ensure_full_index(&the_index); - for (i = 0; i < the_index.cache_nr; i++) { - unsigned int mode; - struct blob *blob; - struct object *obj; - mode = the_index.cache[i]->ce_mode; - if (S_ISGITLINK(mode)) - continue; - blob = lookup_blob(the_repository, - &the_index.cache[i]->oid); - if (!blob) - continue; - obj = &blob->object; - obj->flags |= USED; - fsck_put_object_name(&fsck_walk_options, &obj->oid, - ":%s", the_index.cache[i]->name); - mark_object_reachable(obj); + worktrees = get_worktrees(); + for (p = worktrees; *p; p++) { + struct worktree *wt = *p; + struct index_state istate = + INDEX_STATE_INIT(the_repository); + char *path; + + /* + * Make a copy since the buffer is reusable + * and may get overwritten by other calls + * while we're examining the index. + */ + path = xstrdup(worktree_git_path(wt, "index")); + read_index_from(&istate, path, get_worktree_git_dir(wt)); + fsck_index(&istate, path, wt->is_current); + discard_index(&istate); + free(path); } - if (the_index.cache_tree) - fsck_cache_tree(the_index.cache_tree); - fsck_resolve_undo(&the_index); + free_worktrees(worktrees); } + errors_found |= check_pack_rev_indexes(the_repository, show_progress); + if (verify_bitmap_files(the_repository)) + errors_found |= ERROR_BITMAP; + check_connectivity(); if (the_repository->settings.core_commit_graph) { @@ -995,6 +1074,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) commit_graph_verify.git_cmd = 1; strvec_pushl(&commit_graph_verify.args, "commit-graph", "verify", "--object-dir", odb->path, NULL); + if (show_progress) + strvec_push(&commit_graph_verify.args, "--progress"); + else + strvec_push(&commit_graph_verify.args, "--no-progress"); if (run_command(&commit_graph_verify)) errors_found |= ERROR_COMMIT_GRAPH; } @@ -1009,6 +1092,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) midx_verify.git_cmd = 1; strvec_pushl(&midx_verify.args, "multi-pack-index", "verify", "--object-dir", odb->path, NULL); + if (show_progress) + strvec_push(&midx_verify.args, "--progress"); + else + strvec_push(&midx_verify.args, "--no-progress"); if (run_command(&midx_verify)) errors_found |= ERROR_MULTI_PACK_INDEX; } diff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c index 0feef8caf6..5d01db5c02 100644 --- a/builtin/fsmonitor--daemon.c +++ b/builtin/fsmonitor--daemon.c @@ -1,15 +1,21 @@ #include "builtin.h" +#include "abspath.h" #include "config.h" +#include "environment.h" +#include "gettext.h" #include "parse-options.h" -#include "fsmonitor.h" +#include "fsmonitor-ll.h" #include "fsmonitor-ipc.h" #include "fsmonitor-path-utils.h" +#include "fsmonitor-settings.h" #include "compat/fsmonitor/fsm-health.h" #include "compat/fsmonitor/fsm-listen.h" #include "fsmonitor--daemon.h" #include "simple-ipc.h" #include "khash.h" #include "pkt-line.h" +#include "trace.h" +#include "trace2.h" static const char * const builtin_fsmonitor__daemon_usage[] = { N_("git fsmonitor--daemon start [<options>]"), @@ -32,10 +38,11 @@ static int fsmonitor__start_timeout_sec = 60; #define FSMONITOR__ANNOUNCE_STARTUP "fsmonitor.announcestartup" static int fsmonitor__announce_startup = 0; -static int fsmonitor_config(const char *var, const char *value, void *cb) +static int fsmonitor_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { if (!strcmp(var, FSMONITOR__IPC_THREADS)) { - int i = git_config_int(var, value); + int i = git_config_int(var, value, ctx->kvi); if (i < 1) return error(_("value of '%s' out of range: %d"), FSMONITOR__IPC_THREADS, i); @@ -44,7 +51,7 @@ static int fsmonitor_config(const char *var, const char *value, void *cb) } if (!strcmp(var, FSMONITOR__START_TIMEOUT)) { - int i = git_config_int(var, value); + int i = git_config_int(var, value, ctx->kvi); if (i < 0) return error(_("value of '%s' out of range: %d"), FSMONITOR__START_TIMEOUT, i); @@ -54,7 +61,7 @@ static int fsmonitor_config(const char *var, const char *value, void *cb) if (!strcmp(var, FSMONITOR__ANNOUNCE_STARTUP)) { int is_bool; - int i = git_config_bool_or_int(var, value, &is_bool); + int i = git_config_bool_or_int(var, value, ctx->kvi, &is_bool); if (i < 0) return error(_("value of '%s' not bool or int: %d"), var, i); @@ -62,7 +69,7 @@ static int fsmonitor_config(const char *var, const char *value, void *cb) return 0; } - return git_default_config(var, value, cb); + return git_default_config(var, value, ctx, cb); } /* @@ -122,8 +129,9 @@ struct fsmonitor_cookie_item { enum fsmonitor_cookie_item_result result; }; -static int cookies_cmp(const void *data, const struct hashmap_entry *he1, - const struct hashmap_entry *he2, const void *keydata) +static int cookies_cmp(const void *data UNUSED, + const struct hashmap_entry *he1, + const struct hashmap_entry *he2, const void *keydata) { const struct fsmonitor_cookie_item *a = container_of(he1, const struct fsmonitor_cookie_item, entry); @@ -1405,7 +1413,7 @@ done: return err; } -static int try_to_run_foreground_daemon(int detach_console) +static int try_to_run_foreground_daemon(int detach_console MAYBE_UNUSED) { /* * Technically, we don't need to probe for an existing daemon @@ -1435,7 +1443,8 @@ static int try_to_run_foreground_daemon(int detach_console) static start_bg_wait_cb bg_wait_cb; -static int bg_wait_cb(const struct child_process *cp, void *cb_data) +static int bg_wait_cb(const struct child_process *cp UNUSED, + void *cb_data UNUSED) { enum ipc_active_state s = fsmonitor_ipc__get_state(); @@ -1574,7 +1583,7 @@ int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix) } #else -int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix) +int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix UNUSED) { struct option options[] = { OPT_END() diff --git a/builtin/gc.c b/builtin/gc.c index 02455fdcd7..7c11d5ebef 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -11,6 +11,10 @@ */ #include "builtin.h" +#include "abspath.h" +#include "date.h" +#include "environment.h" +#include "hex.h" #include "repository.h" #include "config.h" #include "tempfile.h" @@ -22,16 +26,21 @@ #include "commit.h" #include "commit-graph.h" #include "packfile.h" -#include "object-store.h" +#include "object-file.h" +#include "object-store-ll.h" #include "pack.h" #include "pack-objects.h" +#include "path.h" #include "blob.h" #include "tree.h" #include "promisor-remote.h" #include "refs.h" #include "remote.h" #include "exec-cmd.h" +#include "gettext.h" #include "hook.h" +#include "setup.h" +#include "trace2.h" #define FAILED_RUN "failed to run %s" @@ -42,7 +51,8 @@ static const char * const builtin_gc_usage[] = { static int pack_refs = 1; static int prune_reflogs = 1; -static int cruft_packs = -1; +static int cruft_packs = 1; +static unsigned long max_cruft_size; static int aggressive_depth = 50; static int aggressive_window = 250; static int gc_auto_threshold = 6700; @@ -52,6 +62,8 @@ static timestamp_t gc_log_expire_time; static const char *gc_log_expire = "1.day.ago"; static const char *prune_expire = "2.weeks.ago"; static const char *prune_worktrees_expire = "3.months.ago"; +static char *repack_filter; +static char *repack_filter_to; static unsigned long big_pack_threshold; static unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE; @@ -154,6 +166,7 @@ static void gc_config(void) git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit); git_config_get_bool("gc.autodetach", &detach_auto); git_config_get_bool("gc.cruftpacks", &cruft_packs); + git_config_get_ulong("gc.maxcruftsize", &max_cruft_size); git_config_get_expiry("gc.pruneexpire", &prune_expire); git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire); git_config_get_expiry("gc.logexpiry", &gc_log_expire); @@ -161,6 +174,9 @@ static void gc_config(void) git_config_get_ulong("gc.bigpackthreshold", &big_pack_threshold); git_config_get_ulong("pack.deltacachesize", &max_delta_cache_size); + git_config_get_string("gc.repackfilter", &repack_filter); + git_config_get_string("gc.repackfilterto", &repack_filter_to); + git_config(git_default_config, NULL); } @@ -213,7 +229,7 @@ static struct packed_git *find_base_packs(struct string_list *packs, struct packed_git *p, *base = NULL; for (p = get_all_packs(the_repository); p; p = p->next) { - if (!p->pack_local) + if (!p->pack_local || p->is_cruft) continue; if (limit) { if (p->pack_size >= limit) @@ -284,7 +300,7 @@ static uint64_t total_ram(void) static uint64_t estimate_repack_memory(struct packed_git *pack) { - unsigned long nr_objects = approximate_object_count(); + unsigned long nr_objects = repo_approximate_object_count(the_repository); size_t os_cache, heap; if (!pack || !nr_objects) @@ -338,6 +354,9 @@ static void add_repack_all_option(struct string_list *keep_pack) strvec_push(&repack, "--cruft"); if (prune_expire) strvec_pushf(&repack, "--cruft-expiration=%s", prune_expire); + if (max_cruft_size) + strvec_pushf(&repack, "--max-cruft-size=%lu", + max_cruft_size); } else { strvec_push(&repack, "-A"); if (prune_expire) @@ -346,6 +365,11 @@ static void add_repack_all_option(struct string_list *keep_pack) if (keep_pack) for_each_string_list(keep_pack, keep_one_pack, NULL); + + if (repack_filter && *repack_filter) + strvec_pushf(&repack, "--filter=%s", repack_filter); + if (repack_filter_to && *repack_filter_to) + strvec_pushf(&repack, "--filter-to=%s", repack_filter_to); } static void add_repack_incremental_option(void) @@ -566,6 +590,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix) N_("prune unreferenced objects"), PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire }, OPT_BOOL(0, "cruft", &cruft_packs, N_("pack unreferenced objects separately")), + OPT_MAGNITUDE(0, "max-cruft-size", &max_cruft_size, + N_("with --cruft, limit the size of new cruft packs")), OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")), OPT_BOOL_F(0, "auto", &auto_gc, N_("enable auto-gc mode"), PARSE_OPT_NOCOMPLETE), @@ -602,10 +628,6 @@ int cmd_gc(int argc, const char **argv, const char *prefix) if (prune_expire && parse_expiry_date(prune_expire, &dummy)) die(_("failed to parse prune expiry value %s"), prune_expire); - prepare_repo_settings(the_repository); - if (cruft_packs < 0) - cruft_packs = the_repository->settings.gc_cruft_packs; - if (aggressive) { strvec_push(&repack, "-f"); if (aggressive_depth > 0) @@ -699,7 +721,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) strvec_push(&prune, prune_expire); if (quiet) strvec_push(&prune, "--no-progress"); - if (has_promisor_remote()) + if (repo_has_promisor_remote(the_repository)) strvec_push(&prune, "--exclude-promisor-objects"); prune_cmd.git_cmd = 1; @@ -820,7 +842,7 @@ static int dfs_on_ref(const char *refname UNUSED, commit = lookup_commit(the_repository, oid); if (!commit) return 0; - if (parse_commit(commit) || + if (repo_parse_commit(the_repository, commit) || commit_graph_position(commit) != COMMIT_NOT_FROM_GRAPH) return 0; @@ -837,7 +859,7 @@ static int dfs_on_ref(const char *refname UNUSED, commit = pop_commit(&stack); for (parent = commit->parents; parent; parent = parent->next) { - if (parse_commit(parent->item) || + if (repo_parse_commit(the_repository, parent->item) || commit_graph_position(parent->item) != COMMIT_NOT_FROM_GRAPH || parent->item->object.flags & SEEN) continue; @@ -976,9 +998,9 @@ struct write_loose_object_data { static int loose_object_auto_limit = 100; -static int loose_object_count(const struct object_id *oid, - const char *path, - void *data) +static int loose_object_count(const struct object_id *oid UNUSED, + const char *path UNUSED, + void *data) { int *count = (int*)data; if (++(*count) >= loose_object_auto_limit) @@ -1003,15 +1025,15 @@ static int loose_object_auto_condition(void) NULL, NULL, &count); } -static int bail_on_loose(const struct object_id *oid, - const char *path, - void *data) +static int bail_on_loose(const struct object_id *oid UNUSED, + const char *path UNUSED, + void *data UNUSED) { return 1; } static int write_loose_object_to_stdin(const struct object_id *oid, - const char *path, + const char *path UNUSED, void *data) { struct write_loose_object_data *d = (struct write_loose_object_data *)data; @@ -1398,7 +1420,7 @@ static void initialize_task_config(int schedule) strbuf_release(&config_name); } -static int task_option_parse(const struct option *opt, +static int task_option_parse(const struct option *opt UNUSED, const char *arg, int unset) { int i, num_selected = 0; @@ -1493,7 +1515,6 @@ static int maintenance_register(int argc, const char **argv, const char *prefix) }; int found = 0; const char *key = "maintenance.repo"; - char *config_value; char *maintpath = get_maintpath(); struct string_list_item *item; const struct string_list *list; @@ -1508,13 +1529,10 @@ static int maintenance_register(int argc, const char **argv, const char *prefix) git_config_set("maintenance.auto", "false"); /* Set maintenance strategy, if unset */ - if (!git_config_get_string("maintenance.strategy", &config_value)) - free(config_value); - else + if (git_config_get("maintenance.strategy")) git_config_set("maintenance.strategy", "incremental"); - list = git_config_get_value_multi(key); - if (list) { + if (!git_config_get_string_multi(key, &list)) { for_each_string_list_item(item, list) { if (!strcmp(maintpath, item->string)) { found = 1; @@ -1580,11 +1598,10 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi if (config_file) { git_configset_init(&cs); git_configset_add_file(&cs, config_file); - list = git_configset_get_value_multi(&cs, key); - } else { - list = git_config_get_value_multi(key); } - if (list) { + if (!(config_file + ? git_configset_get_string_multi(&cs, key, &list) + : git_config_get_string_multi(key, &list))) { for_each_string_list_item(item, list) { if (!strcmp(maintpath, item->string)) { found = 1; @@ -1686,11 +1703,11 @@ static int get_schedule_cmd(const char **cmd, int *is_available) if (is_available) *is_available = 0; - string_list_split_in_place(&list, testing, ',', -1); + string_list_split_in_place(&list, testing, ",", -1); for_each_string_list_item(item, &list) { struct string_list pair = STRING_LIST_INIT_NODUP; - if (string_list_split_in_place(&pair, item->string, ':', 2) != 2) + if (string_list_split_in_place(&pair, item->string, ":", 2) != 2) continue; if (!strcmp(*cmd, pair.items[0].string)) { @@ -1708,6 +1725,15 @@ static int get_schedule_cmd(const char **cmd, int *is_available) return 1; } +static int get_random_minute(void) +{ + /* Use a static value when under tests. */ + if (getenv("GIT_TEST_MAINT_SCHEDULER")) + return 13; + + return git_rand() % 60; +} + static int is_launchctl_available(void) { const char *cmd = "launchctl"; @@ -1820,6 +1846,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit struct strbuf plist = STRBUF_INIT, plist2 = STRBUF_INIT; struct stat st; const char *cmd = "launchctl"; + int minute = get_random_minute(); get_schedule_cmd(&cmd, NULL); preamble = "<?xml version=\"1.0\"?>\n" @@ -1845,29 +1872,30 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit case SCHEDULE_HOURLY: repeat = "<dict>\n" "<key>Hour</key><integer>%d</integer>\n" - "<key>Minute</key><integer>0</integer>\n" + "<key>Minute</key><integer>%d</integer>\n" "</dict>\n"; for (i = 1; i <= 23; i++) - strbuf_addf(&plist, repeat, i); + strbuf_addf(&plist, repeat, i, minute); break; case SCHEDULE_DAILY: repeat = "<dict>\n" "<key>Day</key><integer>%d</integer>\n" "<key>Hour</key><integer>0</integer>\n" - "<key>Minute</key><integer>0</integer>\n" + "<key>Minute</key><integer>%d</integer>\n" "</dict>\n"; for (i = 1; i <= 6; i++) - strbuf_addf(&plist, repeat, i); + strbuf_addf(&plist, repeat, i, minute); break; case SCHEDULE_WEEKLY: - strbuf_addstr(&plist, - "<dict>\n" - "<key>Day</key><integer>0</integer>\n" - "<key>Hour</key><integer>0</integer>\n" - "<key>Minute</key><integer>0</integer>\n" - "</dict>\n"); + strbuf_addf(&plist, + "<dict>\n" + "<key>Day</key><integer>0</integer>\n" + "<key>Hour</key><integer>0</integer>\n" + "<key>Minute</key><integer>%d</integer>\n" + "</dict>\n", + minute); break; default: @@ -1923,7 +1951,7 @@ static int launchctl_add_plists(void) launchctl_schedule_plist(exec_path, SCHEDULE_WEEKLY); } -static int launchctl_update_schedule(int run_maintenance, int fd) +static int launchctl_update_schedule(int run_maintenance, int fd UNUSED) { if (run_maintenance) return launchctl_add_plists(); @@ -1984,6 +2012,7 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority const char *frequency = get_frequency(schedule); char *name = schtasks_task_name(frequency); struct strbuf tfilename = STRBUF_INIT; + int minute = get_random_minute(); get_schedule_cmd(&cmd, NULL); @@ -2004,7 +2033,7 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority switch (schedule) { case SCHEDULE_HOURLY: fprintf(tfile->fp, - "<StartBoundary>2020-01-01T01:00:00</StartBoundary>\n" + "<StartBoundary>2020-01-01T01:%02d:00</StartBoundary>\n" "<Enabled>true</Enabled>\n" "<ScheduleByDay>\n" "<DaysInterval>1</DaysInterval>\n" @@ -2013,12 +2042,13 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority "<Interval>PT1H</Interval>\n" "<Duration>PT23H</Duration>\n" "<StopAtDurationEnd>false</StopAtDurationEnd>\n" - "</Repetition>\n"); + "</Repetition>\n", + minute); break; case SCHEDULE_DAILY: fprintf(tfile->fp, - "<StartBoundary>2020-01-01T00:00:00</StartBoundary>\n" + "<StartBoundary>2020-01-01T00:%02d:00</StartBoundary>\n" "<Enabled>true</Enabled>\n" "<ScheduleByWeek>\n" "<DaysOfWeek>\n" @@ -2030,19 +2060,21 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority "<Saturday />\n" "</DaysOfWeek>\n" "<WeeksInterval>1</WeeksInterval>\n" - "</ScheduleByWeek>\n"); + "</ScheduleByWeek>\n", + minute); break; case SCHEDULE_WEEKLY: fprintf(tfile->fp, - "<StartBoundary>2020-01-01T00:00:00</StartBoundary>\n" + "<StartBoundary>2020-01-01T00:%02d:00</StartBoundary>\n" "<Enabled>true</Enabled>\n" "<ScheduleByWeek>\n" "<DaysOfWeek>\n" "<Sunday />\n" "</DaysOfWeek>\n" "<WeeksInterval>1</WeeksInterval>\n" - "</ScheduleByWeek>\n"); + "</ScheduleByWeek>\n", + minute); break; default: @@ -2068,7 +2100,7 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority "</Settings>\n" "<Actions Context=\"Author\">\n" "<Exec>\n" - "<Command>\"%s\\git.exe\"</Command>\n" + "<Command>\"%s\\headless-git.exe\"</Command>\n" "<Arguments>--exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%s</Arguments>\n" "</Exec>\n" "</Actions>\n" @@ -2100,7 +2132,7 @@ static int schtasks_schedule_tasks(void) schtasks_schedule_task(exec_path, SCHEDULE_WEEKLY); } -static int schtasks_update_schedule(int run_maintenance, int fd) +static int schtasks_update_schedule(int run_maintenance, int fd UNUSED) { if (run_maintenance) return schtasks_schedule_tasks(); @@ -2159,6 +2191,7 @@ static int crontab_update_schedule(int run_maintenance, int fd) FILE *cron_list, *cron_in; struct strbuf line = STRBUF_INIT; struct tempfile *tmpedit = NULL; + int minute = get_random_minute(); get_schedule_cmd(&cmd, NULL); strvec_split(&crontab_list.args, cmd); @@ -2213,11 +2246,11 @@ static int crontab_update_schedule(int run_maintenance, int fd) "# replaced in the future by a Git command.\n\n"); strbuf_addf(&line_format, - "%%s %%s * * %%s \"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%s\n", + "%%d %%s * * %%s \"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%s\n", exec_path, exec_path); - fprintf(cron_in, line_format.buf, "0", "1-23", "*", "hourly"); - fprintf(cron_in, line_format.buf, "0", "0", "1-6", "daily"); - fprintf(cron_in, line_format.buf, "0", "0", "0", "weekly"); + fprintf(cron_in, line_format.buf, minute, "1-23", "*", "hourly"); + fprintf(cron_in, line_format.buf, minute, "0", "1-6", "daily"); + fprintf(cron_in, line_format.buf, minute, "0", "0", "weekly"); strbuf_release(&line_format); fprintf(cron_in, "\n%s\n", END_LINE); @@ -2276,77 +2309,54 @@ static char *xdg_config_home_systemd(const char *filename) return xdg_config_home_for("systemd/user", filename); } -static int systemd_timer_enable_unit(int enable, - enum schedule_priority schedule) -{ - const char *cmd = "systemctl"; - struct child_process child = CHILD_PROCESS_INIT; - const char *frequency = get_frequency(schedule); - - /* - * Disabling the systemd unit while it is already disabled makes - * systemctl print an error. - * Let's ignore it since it means we already are in the expected state: - * the unit is disabled. - * - * On the other hand, enabling a systemd unit which is already enabled - * produces no error. - */ - if (!enable) - child.no_stderr = 1; - - get_schedule_cmd(&cmd, NULL); - strvec_split(&child.args, cmd); - strvec_pushl(&child.args, "--user", enable ? "enable" : "disable", - "--now", NULL); - strvec_pushf(&child.args, "git-maintenance@%s.timer", frequency); +#define SYSTEMD_UNIT_FORMAT "git-maintenance@%s.%s" - if (start_command(&child)) - return error(_("failed to start systemctl")); - if (finish_command(&child)) - /* - * Disabling an already disabled systemd unit makes - * systemctl fail. - * Let's ignore this failure. - * - * Enabling an enabled systemd unit doesn't fail. - */ - if (enable) - return error(_("failed to run systemctl")); - return 0; -} - -static int systemd_timer_delete_unit_templates(void) +static int systemd_timer_delete_timer_file(enum schedule_priority priority) { int ret = 0; - char *filename = xdg_config_home_systemd("git-maintenance@.timer"); - if (unlink(filename) && !is_missing_file_error(errno)) - ret = error_errno(_("failed to delete '%s'"), filename); - FREE_AND_NULL(filename); + const char *frequency = get_frequency(priority); + char *local_timer_name = xstrfmt(SYSTEMD_UNIT_FORMAT, frequency, "timer"); + char *filename = xdg_config_home_systemd(local_timer_name); - filename = xdg_config_home_systemd("git-maintenance@.service"); if (unlink(filename) && !is_missing_file_error(errno)) ret = error_errno(_("failed to delete '%s'"), filename); free(filename); + free(local_timer_name); return ret; } -static int systemd_timer_delete_units(void) +static int systemd_timer_delete_service_template(void) { - return systemd_timer_enable_unit(0, SCHEDULE_HOURLY) || - systemd_timer_enable_unit(0, SCHEDULE_DAILY) || - systemd_timer_enable_unit(0, SCHEDULE_WEEKLY) || - systemd_timer_delete_unit_templates(); + int ret = 0; + char *local_service_name = xstrfmt(SYSTEMD_UNIT_FORMAT, "", "service"); + char *filename = xdg_config_home_systemd(local_service_name); + if (unlink(filename) && !is_missing_file_error(errno)) + ret = error_errno(_("failed to delete '%s'"), filename); + + free(filename); + free(local_service_name); + return ret; } -static int systemd_timer_write_unit_templates(const char *exec_path) +/* + * Write the schedule information into a git-maintenance@<schedule>.timer + * file using a custom minute. This timer file cannot use the templating + * system, so we generate a specific file for each. + */ +static int systemd_timer_write_timer_file(enum schedule_priority schedule, + int minute) { + int res = -1; char *filename; FILE *file; const char *unit; + char *schedule_pattern = NULL; + const char *frequency = get_frequency(schedule); + char *local_timer_name = xstrfmt(SYSTEMD_UNIT_FORMAT, frequency, "timer"); + + filename = xdg_config_home_systemd(local_timer_name); - filename = xdg_config_home_systemd("git-maintenance@.timer"); if (safe_create_leading_directories(filename)) { error(_("failed to create directories for '%s'"), filename); goto error; @@ -2355,6 +2365,23 @@ static int systemd_timer_write_unit_templates(const char *exec_path) if (!file) goto error; + switch (schedule) { + case SCHEDULE_HOURLY: + schedule_pattern = xstrfmt("*-*-* 1..23:%02d:00", minute); + break; + + case SCHEDULE_DAILY: + schedule_pattern = xstrfmt("Tue..Sun *-*-* 0:%02d:00", minute); + break; + + case SCHEDULE_WEEKLY: + schedule_pattern = xstrfmt("Mon 0:%02d:00", minute); + break; + + default: + BUG("Unhandled schedule_priority"); + } + unit = "# This file was created and is maintained by Git.\n" "# Any edits made in this file might be replaced in the future\n" "# by a Git command.\n" @@ -2363,12 +2390,12 @@ static int systemd_timer_write_unit_templates(const char *exec_path) "Description=Optimize Git repositories data\n" "\n" "[Timer]\n" - "OnCalendar=%i\n" + "OnCalendar=%s\n" "Persistent=true\n" "\n" "[Install]\n" "WantedBy=timers.target\n"; - if (fputs(unit, file) == EOF) { + if (fprintf(file, unit, schedule_pattern) < 0) { error(_("failed to write to '%s'"), filename); fclose(file); goto error; @@ -2377,9 +2404,36 @@ static int systemd_timer_write_unit_templates(const char *exec_path) error_errno(_("failed to flush '%s'"), filename); goto error; } + + res = 0; + +error: + free(schedule_pattern); + free(local_timer_name); free(filename); + return res; +} + +/* + * No matter the schedule, we use the same service and can make use of the + * templating system. When installing git-maintenance@<schedule>.timer, + * systemd will notice that git-maintenance@.service exists as a template + * and will use this file and insert the <schedule> into the template at + * the position of "%i". + */ +static int systemd_timer_write_service_template(const char *exec_path) +{ + int res = -1; + char *filename; + FILE *file; + const char *unit; + char *local_service_name = xstrfmt(SYSTEMD_UNIT_FORMAT, "", "service"); - filename = xdg_config_home_systemd("git-maintenance@.service"); + filename = xdg_config_home_systemd(local_service_name); + if (safe_create_leading_directories(filename)) { + error(_("failed to create directories for '%s'"), filename); + goto error; + } file = fopen_or_warn(filename, "w"); if (!file) goto error; @@ -2397,7 +2451,7 @@ static int systemd_timer_write_unit_templates(const char *exec_path) "LockPersonality=yes\n" "MemoryDenyWriteExecute=yes\n" "NoNewPrivileges=yes\n" - "RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6\n" + "RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_VSOCK\n" "RestrictNamespaces=yes\n" "RestrictRealtime=yes\n" "RestrictSUIDSGID=yes\n" @@ -2412,29 +2466,114 @@ static int systemd_timer_write_unit_templates(const char *exec_path) error_errno(_("failed to flush '%s'"), filename); goto error; } + + res = 0; + +error: + free(local_service_name); free(filename); + return res; +} + +static int systemd_timer_enable_unit(int enable, + enum schedule_priority schedule, + int minute) +{ + const char *cmd = "systemctl"; + struct child_process child = CHILD_PROCESS_INIT; + const char *frequency = get_frequency(schedule); + + /* + * Disabling the systemd unit while it is already disabled makes + * systemctl print an error. + * Let's ignore it since it means we already are in the expected state: + * the unit is disabled. + * + * On the other hand, enabling a systemd unit which is already enabled + * produces no error. + */ + if (!enable) + child.no_stderr = 1; + else if (systemd_timer_write_timer_file(schedule, minute)) + return -1; + + get_schedule_cmd(&cmd, NULL); + strvec_split(&child.args, cmd); + strvec_pushl(&child.args, "--user", enable ? "enable" : "disable", + "--now", NULL); + strvec_pushf(&child.args, SYSTEMD_UNIT_FORMAT, frequency, "timer"); + + if (start_command(&child)) + return error(_("failed to start systemctl")); + if (finish_command(&child)) + /* + * Disabling an already disabled systemd unit makes + * systemctl fail. + * Let's ignore this failure. + * + * Enabling an enabled systemd unit doesn't fail. + */ + if (enable) + return error(_("failed to run systemctl")); return 0; +} + +/* + * A previous version of Git wrote the timer units as template files. + * Clean these up, if they exist. + */ +static void systemd_timer_delete_stale_timer_templates(void) +{ + char *timer_template_name = xstrfmt(SYSTEMD_UNIT_FORMAT, "", "timer"); + char *filename = xdg_config_home_systemd(timer_template_name); + + if (unlink(filename) && !is_missing_file_error(errno)) + warning(_("failed to delete '%s'"), filename); -error: free(filename); - systemd_timer_delete_unit_templates(); - return -1; + free(timer_template_name); +} + +static int systemd_timer_delete_unit_files(void) +{ + systemd_timer_delete_stale_timer_templates(); + + /* Purposefully not short-circuited to make sure all are called. */ + return systemd_timer_delete_timer_file(SCHEDULE_HOURLY) | + systemd_timer_delete_timer_file(SCHEDULE_DAILY) | + systemd_timer_delete_timer_file(SCHEDULE_WEEKLY) | + systemd_timer_delete_service_template(); +} + +static int systemd_timer_delete_units(void) +{ + int minute = get_random_minute(); + /* Purposefully not short-circuited to make sure all are called. */ + return systemd_timer_enable_unit(0, SCHEDULE_HOURLY, minute) | + systemd_timer_enable_unit(0, SCHEDULE_DAILY, minute) | + systemd_timer_enable_unit(0, SCHEDULE_WEEKLY, minute) | + systemd_timer_delete_unit_files(); } static int systemd_timer_setup_units(void) { + int minute = get_random_minute(); const char *exec_path = git_exec_path(); - int ret = systemd_timer_write_unit_templates(exec_path) || - systemd_timer_enable_unit(1, SCHEDULE_HOURLY) || - systemd_timer_enable_unit(1, SCHEDULE_DAILY) || - systemd_timer_enable_unit(1, SCHEDULE_WEEKLY); + int ret = systemd_timer_write_service_template(exec_path) || + systemd_timer_enable_unit(1, SCHEDULE_HOURLY, minute) || + systemd_timer_enable_unit(1, SCHEDULE_DAILY, minute) || + systemd_timer_enable_unit(1, SCHEDULE_WEEKLY, minute); + if (ret) systemd_timer_delete_units(); + else + systemd_timer_delete_stale_timer_templates(); + return ret; } -static int systemd_timer_update_schedule(int run_maintenance, int fd) +static int systemd_timer_update_schedule(int run_maintenance, int fd UNUSED) { if (run_maintenance) return systemd_timer_setup_units(); @@ -2606,9 +2745,12 @@ static int maintenance_start(int argc, const char **argv, const char *prefix) opts.scheduler = resolve_scheduler(opts.scheduler); validate_scheduler(opts.scheduler); + if (update_background_schedule(&opts, 1)) + die(_("failed to set up maintenance schedule")); + if (maintenance_register(ARRAY_SIZE(register_args)-1, register_args, NULL)) warning(_("failed to add repo to global config")); - return update_background_schedule(&opts, 1); + return 0; } static const char *const builtin_maintenance_stop_usage[] = { diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c index 491af9202d..20d0dfe9cf 100644 --- a/builtin/get-tar-commit-id.c +++ b/builtin/get-tar-commit-id.c @@ -1,10 +1,9 @@ /* * Copyright (c) 2005, 2006 Rene Scharfe */ -#include "cache.h" +#include "builtin.h" #include "commit.h" #include "tar.h" -#include "builtin.h" #include "quote.h" static const char builtin_get_tar_commit_id_usage[] = @@ -14,7 +13,7 @@ static const char builtin_get_tar_commit_id_usage[] = #define RECORDSIZE (512) #define HEADERSIZE (2 * RECORDSIZE) -int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix) +int cmd_get_tar_commit_id(int argc, const char **argv UNUSED, const char *prefix) { char buffer[HEADERSIZE]; struct ustar_header *header = (struct ustar_header *)buffer; @@ -24,6 +23,8 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix) long len; char *end; + BUG_ON_NON_EMPTY_PREFIX(prefix); + if (argc != 1) usage(builtin_get_tar_commit_id_usage); @@ -32,7 +33,7 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix) die_errno("git get-tar-commit-id: read error"); if (n != HEADERSIZE) die_errno("git get-tar-commit-id: EOF before reading tar header"); - if (header->typeflag[0] != 'g') + if (header->typeflag[0] != TYPEFLAG_GLOBAL_HEADER) return 1; len = strtol(content, &end, 10); diff --git a/builtin/grep.c b/builtin/grep.c index f7821c5fbb..fe78d4c98b 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -3,7 +3,10 @@ * * Copyright (c) 2006 Junio C Hamano */ -#include "cache.h" +#include "builtin.h" +#include "abspath.h" +#include "gettext.h" +#include "hex.h" #include "repository.h" #include "config.h" #include "blob.h" @@ -11,7 +14,6 @@ #include "commit.h" #include "tag.h" #include "tree-walk.h" -#include "builtin.h" #include "parse-options.h" #include "string-list.h" #include "run-command.h" @@ -20,10 +22,17 @@ #include "quote.h" #include "dir.h" #include "pathspec.h" +#include "setup.h" #include "submodule.h" #include "submodule-config.h" -#include "object-store.h" +#include "object-file.h" +#include "object-name.h" +#include "object-store-ll.h" #include "packfile.h" +#include "pager.h" +#include "path.h" +#include "read-cache-ll.h" +#include "write-or-die.h" static const char *grep_prefix; @@ -282,14 +291,18 @@ static int wait_all(void) return hit; } -static int grep_cmd_config(const char *var, const char *value, void *cb) +static int grep_cmd_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { - int st = grep_config(var, value, cb); - if (git_color_default_config(var, value, NULL) < 0) + int st = grep_config(var, value, ctx, cb); + + if (git_color_config(var, value, cb) < 0) + st = -1; + else if (git_default_config(var, value, ctx, cb) < 0) st = -1; if (!strcmp(var, "grep.threads")) { - num_threads = git_config_int(var, value); + num_threads = git_config_int(var, value, ctx->kvi); if (num_threads < 0) die(_("invalid number of threads specified (%d) for %s"), num_threads, var); @@ -560,7 +573,8 @@ static int grep_cache(struct grep_opt *opt, void *data; unsigned long size; - data = read_object_file(&ce->oid, &type, &size); + data = repo_read_object_file(the_repository, &ce->oid, + &type, &size); init_tree_desc(&tree, data, size); hit |= grep_tree(opt, pathspec, &tree, &name, 0, 0); @@ -630,7 +644,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, strbuf_addstr(&name, base->buf + tn_len); match = tree_entry_interesting(repo->index, &entry, &name, - 0, pathspec); + pathspec); strbuf_setlen(&name, name_base_len); if (match == all_entries_not_interesting) @@ -650,7 +664,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, void *data; unsigned long size; - data = read_object_file(&entry.oid, &type, &size); + data = repo_read_object_file(the_repository, + &entry.oid, &type, &size); if (!data) die(_("unable to read tree (%s)"), oid_to_hex(&entry.oid)); @@ -798,14 +813,20 @@ static int file_callback(const struct option *opt, const char *arg, int unset) { struct grep_opt *grep_opt = opt->value; int from_stdin; + const char *filename = arg; FILE *patterns; int lno = 0; struct strbuf sb = STRBUF_INIT; BUG_ON_OPT_NEG(unset); - from_stdin = !strcmp(arg, "-"); - patterns = from_stdin ? stdin : fopen(arg, "r"); + if (!*filename) + ; /* leave it as-is */ + else + filename = prefix_filename_except_for_dash(grep_prefix, filename); + + from_stdin = !strcmp(filename, "-"); + patterns = from_stdin ? stdin : fopen(filename, "r"); if (!patterns) die_errno(_("cannot open '%s'"), arg); while (strbuf_getline(&sb, patterns) == 0) { @@ -819,6 +840,8 @@ static int file_callback(const struct option *opt, const char *arg, int unset) if (!from_stdin) fclose(patterns); strbuf_release(&sb); + if (filename != arg) + free((void *)filename); return 0; } @@ -910,9 +933,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) N_("process binary files with textconv filters")), OPT_SET_INT('r', "recursive", &opt.max_depth, N_("search in subdirectories (default)"), -1), - { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, N_("depth"), - N_("descend at most <depth> levels"), PARSE_OPT_NONEG, - NULL, 1 }, + OPT_INTEGER_F(0, "max-depth", &opt.max_depth, + N_("descend at most <n> levels"), PARSE_OPT_NONEG), OPT_GROUP(""), OPT_SET_INT('E', "extended-regexp", &opt.pattern_type_option, N_("use extended POSIX regular expressions"), @@ -976,7 +998,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) OPT_CALLBACK_F(0, "and", &opt, NULL, N_("combine patterns specified with -e"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback), - OPT_BOOL(0, "or", &dummy, ""), + OPT_BOOL_F(0, "or", &dummy, "", PARSE_OPT_NONEG), OPT_CALLBACK_F(0, "not", &opt, NULL, "", PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback), OPT_CALLBACK_F('(', NULL, &opt, NULL, "", diff --git a/builtin/hash-object.c b/builtin/hash-object.c index 44db83f07f..5ffec99dce 100644 --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@ -5,12 +5,19 @@ * Copyright (C) Junio C Hamano, 2005 */ #include "builtin.h" +#include "abspath.h" #include "config.h" -#include "object-store.h" +#include "gettext.h" +#include "hex.h" +#include "object-file.h" +#include "object-store-ll.h" #include "blob.h" #include "quote.h" #include "parse-options.h" #include "exec-cmd.h" +#include "setup.h" +#include "strbuf.h" +#include "write-or-die.h" /* * This is to create corrupt objects for debugging and as such it diff --git a/builtin/help.c b/builtin/help.c index 53f2812dfb..dc1fbe2b98 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -1,15 +1,18 @@ /* * Builtin help command */ -#include "cache.h" -#include "config.h" #include "builtin.h" +#include "config.h" #include "exec-cmd.h" +#include "gettext.h" +#include "pager.h" #include "parse-options.h" +#include "path.h" #include "run-command.h" #include "config-list.h" #include "help.h" #include "alias.h" +#include "setup.h" #ifndef DEFAULT_HELP_FORMAT #define DEFAULT_HELP_FORMAT "man" @@ -394,7 +397,8 @@ static int add_man_viewer_info(const char *var, const char *value) return 0; } -static int git_help_config(const char *var, const char *value, void *cb) +static int git_help_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { if (!strcmp(var, "help.format")) { if (!value) @@ -417,7 +421,7 @@ static int git_help_config(const char *var, const char *value, void *cb) if (starts_with(var, "man.")) return add_man_viewer_info(var, value); - return git_default_config(var, value, cb); + return git_default_config(var, value, ctx, cb); } static struct cmdnames main_cmds, other_cmds; diff --git a/builtin/hook.c b/builtin/hook.c index f95b7965c5..09b51a6487 100644 --- a/builtin/hook.c +++ b/builtin/hook.c @@ -1,6 +1,6 @@ -#include "cache.h" #include "builtin.h" #include "config.h" +#include "gettext.h" #include "hook.h" #include "parse-options.h" #include "strbuf.h" diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 6648f2daef..dda94a9f46 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1,6 +1,9 @@ #include "builtin.h" #include "config.h" #include "delta.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "pack.h" #include "csum-file.h" #include "blob.h" @@ -10,11 +13,17 @@ #include "progress.h" #include "fsck.h" #include "exec-cmd.h" +#include "strbuf.h" #include "streaming.h" #include "thread-utils.h" #include "packfile.h" -#include "object-store.h" +#include "pack-revindex.h" +#include "object-file.h" +#include "object-store-ll.h" +#include "oid-array.h" +#include "replace-object.h" #include "promisor-remote.h" +#include "setup.h" static const char index_pack_usage[] = "git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--[no-]rev-index] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])"; @@ -212,7 +221,8 @@ static void cleanup_thread(void) } static int mark_link(struct object *obj, enum object_type type, - void *data, struct fsck_options *options) + void *data UNUSED, + struct fsck_options *options UNUSED) { if (!obj) return -1; @@ -801,7 +811,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, if (startup_info->have_repository) { read_lock(); collision_test_needed = - has_object_file_with_flags(oid, OBJECT_INFO_QUICK); + repo_has_object_file_with_flags(the_repository, oid, + OBJECT_INFO_QUICK); read_unlock(); } @@ -821,7 +832,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, die(_("cannot read existing object info %s"), oid_to_hex(oid)); if (has_type != type || has_size != size) die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid)); - has_data = read_object_file(oid, &has_type, &has_size); + has_data = repo_read_object_file(the_repository, oid, + &has_type, &has_size); read_unlock(); if (!data) data = new_data = get_data_from_pack(obj_entry); @@ -1154,6 +1166,7 @@ static void parse_pack_objects(unsigned char *hash) struct ofs_delta_entry *ofs_delta = ofs_deltas; struct object_id ref_delta_oid; struct stat st; + git_hash_ctx tmp_ctx; if (verbose) progress = start_progress( @@ -1190,7 +1203,9 @@ static void parse_pack_objects(unsigned char *hash) /* Check pack integrity */ flush(); - the_hash_algo->final_fn(hash, &input_ctx); + the_hash_algo->init_fn(&tmp_ctx); + the_hash_algo->clone_fn(&tmp_ctx, &input_ctx); + the_hash_algo->final_fn(hash, &tmp_ctx); if (!hasheq(fill(the_hash_algo->rawsz), hash)) die(_("pack is corrupted (SHA1 mismatch)")); use(the_hash_algo->rawsz); @@ -1388,7 +1403,7 @@ static void fix_unresolved_deltas(struct hashfile *f) sorted_by_pos[i] = &ref_deltas[i]; QSORT(sorted_by_pos, nr_ref_deltas, delta_pos_compare); - if (has_promisor_remote()) { + if (repo_has_promisor_remote(the_repository)) { /* * Prefetch the delta bases. */ @@ -1414,7 +1429,8 @@ static void fix_unresolved_deltas(struct hashfile *f) if (objects[d->obj_no].real_type != OBJ_REF_DELTA) continue; - data = read_object_file(&d->oid, &type, &size); + data = repo_read_object_file(the_repository, &d->oid, &type, + &size); if (!data) continue; @@ -1568,18 +1584,19 @@ static void final(const char *final_pack_name, const char *curr_pack_name, strbuf_release(&pack_name); } -static int git_index_pack_config(const char *k, const char *v, void *cb) +static int git_index_pack_config(const char *k, const char *v, + const struct config_context *ctx, void *cb) { struct pack_idx_option *opts = cb; if (!strcmp(k, "pack.indexversion")) { - opts->version = git_config_int(k, v); + opts->version = git_config_int(k, v, ctx->kvi); if (opts->version > 2) die(_("bad pack.indexVersion=%"PRIu32), opts->version); return 0; } if (!strcmp(k, "pack.threads")) { - nr_threads = git_config_int(k, v); + nr_threads = git_config_int(k, v, ctx->kvi); if (nr_threads < 0) die(_("invalid number of threads specified (%d)"), nr_threads); @@ -1595,7 +1612,7 @@ static int git_index_pack_config(const char *k, const char *v, void *cb) else opts->flags &= ~WRITE_REV; } - return git_default_config(k, v, cb); + return git_default_config(k, v, ctx, cb); } static int cmp_uint32(const void *a_, const void *b_) @@ -1739,16 +1756,17 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) if (argc == 2 && !strcmp(argv[1], "-h")) usage(index_pack_usage); - read_replace_refs = 0; + disable_replace_refs(); fsck_options.walk = mark_link; reset_pack_idx_option(&opts); + opts.flags |= WRITE_REV; git_config(git_index_pack_config, &opts); if (prefix && chdir(prefix)) die(_("Cannot come back to cwd")); - if (git_env_bool(GIT_TEST_WRITE_REV_INDEX, 0)) - rev_index = 1; + if (git_env_bool(GIT_TEST_NO_WRITE_REV_INDEX, 0)) + rev_index = 0; else rev_index = !!(opts.flags & (WRITE_REV_VERIFY | WRITE_REV)); diff --git a/builtin/init-db.c b/builtin/init-db.c index dcaaf102ea..cb727c826f 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -3,476 +3,16 @@ * * Copyright (C) Linus Torvalds, 2005 */ -#include "cache.h" -#include "config.h" -#include "refs.h" #include "builtin.h" -#include "exec-cmd.h" +#include "abspath.h" +#include "config.h" +#include "environment.h" +#include "gettext.h" +#include "object-file.h" #include "parse-options.h" -#include "worktree.h" - -#ifndef DEFAULT_GIT_TEMPLATE_DIR -#define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates" -#endif - -#ifdef NO_TRUSTABLE_FILEMODE -#define TEST_FILEMODE 0 -#else -#define TEST_FILEMODE 1 -#endif - -#define GIT_DEFAULT_HASH_ENVIRONMENT "GIT_DEFAULT_HASH" - -static int init_is_bare_repository = 0; -static int init_shared_repository = -1; - -static void copy_templates_1(struct strbuf *path, struct strbuf *template_path, - DIR *dir) -{ - size_t path_baselen = path->len; - size_t template_baselen = template_path->len; - struct dirent *de; - - /* Note: if ".git/hooks" file exists in the repository being - * re-initialized, /etc/core-git/templates/hooks/update would - * cause "git init" to fail here. I think this is sane but - * it means that the set of templates we ship by default, along - * with the way the namespace under .git/ is organized, should - * be really carefully chosen. - */ - safe_create_dir(path->buf, 1); - while ((de = readdir(dir)) != NULL) { - struct stat st_git, st_template; - int exists = 0; - - strbuf_setlen(path, path_baselen); - strbuf_setlen(template_path, template_baselen); - - if (de->d_name[0] == '.') - continue; - strbuf_addstr(path, de->d_name); - strbuf_addstr(template_path, de->d_name); - if (lstat(path->buf, &st_git)) { - if (errno != ENOENT) - die_errno(_("cannot stat '%s'"), path->buf); - } - else - exists = 1; - - if (lstat(template_path->buf, &st_template)) - die_errno(_("cannot stat template '%s'"), template_path->buf); - - if (S_ISDIR(st_template.st_mode)) { - DIR *subdir = opendir(template_path->buf); - if (!subdir) - die_errno(_("cannot opendir '%s'"), template_path->buf); - strbuf_addch(path, '/'); - strbuf_addch(template_path, '/'); - copy_templates_1(path, template_path, subdir); - closedir(subdir); - } - else if (exists) - continue; - else if (S_ISLNK(st_template.st_mode)) { - struct strbuf lnk = STRBUF_INIT; - if (strbuf_readlink(&lnk, template_path->buf, - st_template.st_size) < 0) - die_errno(_("cannot readlink '%s'"), template_path->buf); - if (symlink(lnk.buf, path->buf)) - die_errno(_("cannot symlink '%s' '%s'"), - lnk.buf, path->buf); - strbuf_release(&lnk); - } - else if (S_ISREG(st_template.st_mode)) { - if (copy_file(path->buf, template_path->buf, st_template.st_mode)) - die_errno(_("cannot copy '%s' to '%s'"), - template_path->buf, path->buf); - } - else - error(_("ignoring template %s"), template_path->buf); - } -} - -static void copy_templates(const char *template_dir, const char *init_template_dir) -{ - struct strbuf path = STRBUF_INIT; - struct strbuf template_path = STRBUF_INIT; - size_t template_len; - struct repository_format template_format = REPOSITORY_FORMAT_INIT; - struct strbuf err = STRBUF_INIT; - DIR *dir; - char *to_free = NULL; - - if (!template_dir) - template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT); - if (!template_dir) - template_dir = init_template_dir; - if (!template_dir) - template_dir = to_free = system_path(DEFAULT_GIT_TEMPLATE_DIR); - if (!template_dir[0]) { - free(to_free); - return; - } - - strbuf_addstr(&template_path, template_dir); - strbuf_complete(&template_path, '/'); - template_len = template_path.len; - - dir = opendir(template_path.buf); - if (!dir) { - warning(_("templates not found in %s"), template_dir); - goto free_return; - } - - /* Make sure that template is from the correct vintage */ - strbuf_addstr(&template_path, "config"); - read_repository_format(&template_format, template_path.buf); - strbuf_setlen(&template_path, template_len); - - /* - * No mention of version at all is OK, but anything else should be - * verified. - */ - if (template_format.version >= 0 && - verify_repository_format(&template_format, &err) < 0) { - warning(_("not copying templates from '%s': %s"), - template_dir, err.buf); - strbuf_release(&err); - goto close_free_return; - } - - strbuf_addstr(&path, get_git_common_dir()); - strbuf_complete(&path, '/'); - copy_templates_1(&path, &template_path, dir); -close_free_return: - closedir(dir); -free_return: - free(to_free); - strbuf_release(&path); - strbuf_release(&template_path); - clear_repository_format(&template_format); -} - -/* - * If the git_dir is not directly inside the working tree, then git will not - * find it by default, and we need to set the worktree explicitly. - */ -static int needs_work_tree_config(const char *git_dir, const char *work_tree) -{ - if (!strcmp(work_tree, "/") && !strcmp(git_dir, "/.git")) - return 0; - if (skip_prefix(git_dir, work_tree, &git_dir) && - !strcmp(git_dir, "/.git")) - return 0; - return 1; -} - -void initialize_repository_version(int hash_algo, int reinit) -{ - char repo_version_string[10]; - int repo_version = GIT_REPO_VERSION; - - if (hash_algo != GIT_HASH_SHA1) - repo_version = GIT_REPO_VERSION_READ; - - /* This forces creation of new config file */ - xsnprintf(repo_version_string, sizeof(repo_version_string), - "%d", repo_version); - git_config_set("core.repositoryformatversion", repo_version_string); - - if (hash_algo != GIT_HASH_SHA1) - git_config_set("extensions.objectformat", - hash_algos[hash_algo].name); - else if (reinit) - git_config_set_gently("extensions.objectformat", NULL); -} - -static int create_default_files(const char *template_path, - const char *original_git_dir, - const char *initial_branch, - const struct repository_format *fmt, - int quiet) -{ - struct stat st1; - struct strbuf buf = STRBUF_INIT; - char *path; - char junk[2]; - int reinit; - int filemode; - struct strbuf err = STRBUF_INIT; - const char *init_template_dir = NULL; - const char *work_tree = get_git_work_tree(); - - /* - * First copy the templates -- we might have the default - * config file there, in which case we would want to read - * from it after installing. - * - * Before reading that config, we also need to clear out any cached - * values (since we've just potentially changed what's available on - * disk). - */ - git_config_get_pathname("init.templatedir", &init_template_dir); - copy_templates(template_path, init_template_dir); - free((char *)init_template_dir); - git_config_clear(); - reset_shared_repository(); - git_config(git_default_config, NULL); - - /* - * We must make sure command-line options continue to override any - * values we might have just re-read from the config. - */ - is_bare_repository_cfg = init_is_bare_repository || !work_tree; - if (init_shared_repository != -1) - set_shared_repository(init_shared_repository); - - /* - * We would have created the above under user's umask -- under - * shared-repository settings, we would need to fix them up. - */ - if (get_shared_repository()) { - adjust_shared_perm(get_git_dir()); - } - - /* - * We need to create a "refs" dir in any case so that older - * versions of git can tell that this is a repository. - */ - safe_create_dir(git_path("refs"), 1); - adjust_shared_perm(git_path("refs")); - - if (refs_init_db(&err)) - die("failed to set up refs db: %s", err.buf); - - /* - * Point the HEAD symref to the initial branch with if HEAD does - * not yet exist. - */ - path = git_path_buf(&buf, "HEAD"); - reinit = (!access(path, R_OK) - || readlink(path, junk, sizeof(junk)-1) != -1); - if (!reinit) { - char *ref; - - if (!initial_branch) - initial_branch = git_default_branch_name(quiet); - - ref = xstrfmt("refs/heads/%s", initial_branch); - if (check_refname_format(ref, 0) < 0) - die(_("invalid initial branch name: '%s'"), - initial_branch); - - if (create_symref("HEAD", ref, NULL) < 0) - exit(1); - free(ref); - } - - initialize_repository_version(fmt->hash_algo, 0); - - /* Check filemode trustability */ - path = git_path_buf(&buf, "config"); - filemode = TEST_FILEMODE; - if (TEST_FILEMODE && !lstat(path, &st1)) { - struct stat st2; - filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) && - !lstat(path, &st2) && - st1.st_mode != st2.st_mode && - !chmod(path, st1.st_mode)); - if (filemode && !reinit && (st1.st_mode & S_IXUSR)) - filemode = 0; - } - git_config_set("core.filemode", filemode ? "true" : "false"); - - if (is_bare_repository()) - git_config_set("core.bare", "true"); - else { - git_config_set("core.bare", "false"); - /* allow template config file to override the default */ - if (log_all_ref_updates == LOG_REFS_UNSET) - git_config_set("core.logallrefupdates", "true"); - if (needs_work_tree_config(original_git_dir, work_tree)) - git_config_set("core.worktree", work_tree); - } - - if (!reinit) { - /* Check if symlink is supported in the work tree */ - path = git_path_buf(&buf, "tXXXXXX"); - if (!close(xmkstemp(path)) && - !unlink(path) && - !symlink("testing", path) && - !lstat(path, &st1) && - S_ISLNK(st1.st_mode)) - unlink(path); /* good */ - else - git_config_set("core.symlinks", "false"); - - /* Check if the filesystem is case-insensitive */ - path = git_path_buf(&buf, "CoNfIg"); - if (!access(path, F_OK)) - git_config_set("core.ignorecase", "true"); - probe_utf8_pathname_composition(); - } - - strbuf_release(&buf); - return reinit; -} - -static void create_object_directory(void) -{ - struct strbuf path = STRBUF_INIT; - size_t baselen; - - strbuf_addstr(&path, get_object_directory()); - baselen = path.len; - - safe_create_dir(path.buf, 1); - - strbuf_setlen(&path, baselen); - strbuf_addstr(&path, "/pack"); - safe_create_dir(path.buf, 1); - - strbuf_setlen(&path, baselen); - strbuf_addstr(&path, "/info"); - safe_create_dir(path.buf, 1); - - strbuf_release(&path); -} - -static void separate_git_dir(const char *git_dir, const char *git_link) -{ - struct stat st; - - if (!stat(git_link, &st)) { - const char *src; - - if (S_ISREG(st.st_mode)) - src = read_gitfile(git_link); - else if (S_ISDIR(st.st_mode)) - src = git_link; - else - die(_("unable to handle file type %d"), (int)st.st_mode); - - if (rename(src, git_dir)) - die_errno(_("unable to move %s to %s"), src, git_dir); - repair_worktrees(NULL, NULL); - } - - write_file(git_link, "gitdir: %s", git_dir); -} - -static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash) -{ - const char *env = getenv(GIT_DEFAULT_HASH_ENVIRONMENT); - /* - * If we already have an initialized repo, don't allow the user to - * specify a different algorithm, as that could cause corruption. - * Otherwise, if the user has specified one on the command line, use it. - */ - if (repo_fmt->version >= 0 && hash != GIT_HASH_UNKNOWN && hash != repo_fmt->hash_algo) - die(_("attempt to reinitialize repository with different hash")); - else if (hash != GIT_HASH_UNKNOWN) - repo_fmt->hash_algo = hash; - else if (env) { - int env_algo = hash_algo_by_name(env); - if (env_algo == GIT_HASH_UNKNOWN) - die(_("unknown hash algorithm '%s'"), env); - repo_fmt->hash_algo = env_algo; - } -} - -int init_db(const char *git_dir, const char *real_git_dir, - const char *template_dir, int hash, const char *initial_branch, - unsigned int flags) -{ - int reinit; - int exist_ok = flags & INIT_DB_EXIST_OK; - char *original_git_dir = real_pathdup(git_dir, 1); - struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT; - - if (real_git_dir) { - struct stat st; - - if (!exist_ok && !stat(git_dir, &st)) - die(_("%s already exists"), git_dir); - - if (!exist_ok && !stat(real_git_dir, &st)) - die(_("%s already exists"), real_git_dir); - - set_git_dir(real_git_dir, 1); - git_dir = get_git_dir(); - separate_git_dir(git_dir, original_git_dir); - } - else { - set_git_dir(git_dir, 1); - git_dir = get_git_dir(); - } - startup_info->have_repository = 1; - - /* Ensure `core.hidedotfiles` is processed */ - git_config(platform_core_config, NULL); - - safe_create_dir(git_dir, 0); - - init_is_bare_repository = is_bare_repository(); - - /* Check to see if the repository version is right. - * Note that a newly created repository does not have - * config file, so this will not fail. What we are catching - * is an attempt to reinitialize new repository with an old tool. - */ - check_repository_format(&repo_fmt); - - validate_hash_algorithm(&repo_fmt, hash); - - reinit = create_default_files(template_dir, original_git_dir, - initial_branch, &repo_fmt, - flags & INIT_DB_QUIET); - if (reinit && initial_branch) - warning(_("re-init: ignored --initial-branch=%s"), - initial_branch); - - create_object_directory(); - - if (get_shared_repository()) { - char buf[10]; - /* We do not spell "group" and such, so that - * the configuration can be read by older version - * of git. Note, we use octal numbers for new share modes, - * and compatibility values for PERM_GROUP and - * PERM_EVERYBODY. - */ - if (get_shared_repository() < 0) - /* force to the mode value */ - xsnprintf(buf, sizeof(buf), "0%o", -get_shared_repository()); - else if (get_shared_repository() == PERM_GROUP) - xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_GROUP); - else if (get_shared_repository() == PERM_EVERYBODY) - xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY); - else - BUG("invalid value for shared_repository"); - git_config_set("core.sharedrepository", buf); - git_config_set("receive.denyNonFastforwards", "true"); - } - - if (!(flags & INIT_DB_QUIET)) { - int len = strlen(git_dir); - - if (reinit) - printf(get_shared_repository() - ? _("Reinitialized existing shared Git repository in %s%s\n") - : _("Reinitialized existing Git repository in %s%s\n"), - git_dir, len && git_dir[len-1] != '/' ? "/" : ""); - else - printf(get_shared_repository() - ? _("Initialized empty shared Git repository in %s%s\n") - : _("Initialized empty Git repository in %s%s\n"), - git_dir, len && git_dir[len-1] != '/' ? "/" : ""); - } - - free(original_git_dir); - return 0; -} +#include "path.h" +#include "setup.h" +#include "strbuf.h" static int guess_repository_type(const char *git_dir) { @@ -538,6 +78,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) const char *object_format = NULL; const char *initial_branch = NULL; int hash_algo = GIT_HASH_UNKNOWN; + int init_shared_repository = -1; const struct option init_db_options[] = { OPT_STRING(0, "template", &template_dir, N_("template-directory"), N_("directory from which templates will be used")), @@ -695,5 +236,5 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) flags |= INIT_DB_EXIST_OK; return init_db(git_dir, real_git_dir, template_dir, hash_algo, - initial_branch, flags); + initial_branch, init_shared_repository, flags); } diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c index e58627c72a..033bd1556c 100644 --- a/builtin/interpret-trailers.c +++ b/builtin/interpret-trailers.c @@ -5,8 +5,8 @@ * */ -#include "cache.h" #include "builtin.h" +#include "gettext.h" #include "parse-options.h" #include "string-list.h" #include "trailer.h" @@ -14,7 +14,7 @@ static const char * const git_interpret_trailers_usage[] = { N_("git interpret-trailers [--in-place] [--trim-empty]\n" - " [(--trailer <token>[(=|:)<value>])...]\n" + " [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n" " [--parse] [<file>...]"), NULL }; @@ -24,21 +24,24 @@ static enum trailer_if_exists if_exists; static enum trailer_if_missing if_missing; static int option_parse_where(const struct option *opt, - const char *arg, int unset) + const char *arg, int unset UNUSED) { - return trailer_set_where(&where, arg); + /* unset implies NULL arg, which is handled in our helper */ + return trailer_set_where(opt->value, arg); } static int option_parse_if_exists(const struct option *opt, - const char *arg, int unset) + const char *arg, int unset UNUSED) { - return trailer_set_if_exists(&if_exists, arg); + /* unset implies NULL arg, which is handled in our helper */ + return trailer_set_if_exists(opt->value, arg); } static int option_parse_if_missing(const struct option *opt, - const char *arg, int unset) + const char *arg, int unset UNUSED) { - return trailer_set_if_missing(&if_missing, arg); + /* unset implies NULL arg, which is handled in our helper */ + return trailer_set_if_missing(opt->value, arg); } static void new_trailers_clear(struct list_head *trailers) @@ -97,19 +100,19 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "in-place", &opts.in_place, N_("edit files in place")), OPT_BOOL(0, "trim-empty", &opts.trim_empty, N_("trim empty trailers")), - OPT_CALLBACK(0, "where", NULL, N_("action"), + OPT_CALLBACK(0, "where", &where, N_("placement"), N_("where to place the new trailer"), option_parse_where), - OPT_CALLBACK(0, "if-exists", NULL, N_("action"), + OPT_CALLBACK(0, "if-exists", &if_exists, N_("action"), N_("action if trailer already exists"), option_parse_if_exists), - OPT_CALLBACK(0, "if-missing", NULL, N_("action"), + OPT_CALLBACK(0, "if-missing", &if_missing, N_("action"), N_("action if trailer is missing"), option_parse_if_missing), OPT_BOOL(0, "only-trailers", &opts.only_trailers, N_("output only the trailers")), - OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply config rules")), - OPT_BOOL(0, "unfold", &opts.unfold, N_("join whitespace-continued values")), - OPT_CALLBACK_F(0, "parse", &opts, NULL, N_("set parsing options"), + OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply trailer.* configuration variables")), + OPT_BOOL(0, "unfold", &opts.unfold, N_("reformat multiline trailer values as single-line values")), + OPT_CALLBACK_F(0, "parse", &opts, NULL, N_("alias for --only-trailers --only-input --unfold"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_parse), - OPT_BOOL(0, "no-divider", &opts.no_divider, N_("do not treat --- specially")), + OPT_BOOL(0, "no-divider", &opts.no_divider, N_("do not treat \"---\" as the end of input")), OPT_CALLBACK(0, "trailer", &trailers, N_("trailer"), N_("trailer(s) to add"), option_parse_trailer), OPT_END() diff --git a/builtin/log.c b/builtin/log.c index a70fba198f..1f61a388cf 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -4,10 +4,17 @@ * (C) Copyright 2006 Linus Torvalds * 2006 Junio Hamano */ -#include "cache.h" +#include "git-compat-util.h" +#include "abspath.h" #include "config.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "refs.h" -#include "object-store.h" +#include "object-file.h" +#include "object-name.h" +#include "object-store-ll.h" +#include "pager.h" #include "color.h" #include "commit.h" #include "diff.h" @@ -15,6 +22,7 @@ #include "revision.h" #include "log-tree.h" #include "builtin.h" +#include "oid-array.h" #include "tag.h" #include "reflog-walk.h" #include "patch-ids.h" @@ -35,6 +43,8 @@ #include "commit-reach.h" #include "range-diff.h" #include "tmp-objdir.h" +#include "tree.h" +#include "write-or-die.h" #define MAIL_DEFAULT_WRAP 72 #define COVER_FROM_AUTO_MAX_SUBJECT_LEN 100 @@ -56,6 +66,7 @@ static int stdout_mboxrd; static const char *fmt_patch_subject_prefix = "PATCH"; static int fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT; static const char *fmt_pretty; +static int format_no_prefix; static const char * const builtin_log_usage[] = { N_("git log [<options>] [<revision-range>] [[--] <path>...]"), @@ -107,16 +118,19 @@ static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP; static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP; static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP; -static int clear_decorations_callback(const struct option *opt, - const char *arg, int unset) +static int clear_decorations_callback(const struct option *opt UNUSED, + const char *arg, int unset) { + BUG_ON_OPT_NEG(unset); + BUG_ON_OPT_ARG(arg); string_list_clear(&decorate_refs_include, 0); string_list_clear(&decorate_refs_exclude, 0); use_default_decoration_filter = 0; return 0; } -static int decorate_callback(const struct option *opt, const char *arg, int unset) +static int decorate_callback(const struct option *opt UNUSED, const char *arg, + int unset) { if (unset) decoration_style = 0; @@ -162,16 +176,15 @@ static void cmd_log_init_defaults(struct rev_info *rev) if (default_follow) rev->diffopt.flags.default_follow_renames = 1; rev->verbose_header = 1; + init_diffstat_widths(&rev->diffopt); rev->diffopt.flags.recursive = 1; - rev->diffopt.stat_width = -1; /* use full terminal width */ - rev->diffopt.stat_graph_width = -1; /* respect statGraphWidth config */ + rev->diffopt.flags.allow_textconv = 1; rev->abbrev_commit = default_abbrev_commit; rev->show_root_diff = default_show_root; rev->subject_prefix = fmt_patch_subject_prefix; rev->patch_name_max = fmt_patch_name_max; rev->show_signature = default_show_signature; rev->encode_email_headers = default_encode_email_headers; - rev->diffopt.flags.allow_textconv = 1; if (default_date_mode) parse_date_format(default_date_mode, &rev->date_mode); @@ -182,10 +195,10 @@ static void set_default_decoration_filter(struct decoration_filter *decoration_f int i; char *value = NULL; struct string_list *include = decoration_filter->include_ref_pattern; - const struct string_list *config_exclude = - git_config_get_value_multi("log.excludeDecoration"); + const struct string_list *config_exclude; - if (config_exclude) { + if (!git_config_get_string_multi("log.excludeDecoration", + &config_exclude)) { struct string_list_item *item; for_each_string_list_item(item, config_exclude) string_list_append(decoration_filter->exclude_ref_config_pattern, @@ -436,7 +449,7 @@ static void log_show_early(struct rev_info *revs, struct commit_list *list) setitimer(ITIMER_REAL, &early_output_timer, NULL); } -static void early_output(int signal) +static void early_output(int signal UNUSED) { show_early_output = log_show_early; } @@ -538,7 +551,7 @@ static int cmd_log_walk_no_free(struct rev_info *rev) rev->diffopt.flags.check_failed) { return 02; } - return diff_result_code(&rev->diffopt, 0); + return diff_result_code(&rev->diffopt); } static int cmd_log_walk(struct rev_info *rev) @@ -552,7 +565,8 @@ static int cmd_log_walk(struct rev_info *rev) return retval; } -static int git_log_config(const char *var, const char *value, void *cb) +static int git_log_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { const char *slot_name; @@ -561,7 +575,7 @@ static int git_log_config(const char *var, const char *value, void *cb) if (!strcmp(var, "format.subjectprefix")) return git_config_string(&fmt_patch_subject_prefix, var, value); if (!strcmp(var, "format.filenamemaxlength")) { - fmt_patch_name_max = git_config_int(var, value); + fmt_patch_name_max = git_config_int(var, value, ctx->kvi); return 0; } if (!strcmp(var, "format.encodeemailheaders")) { @@ -580,8 +594,11 @@ static int git_log_config(const char *var, const char *value, void *cb) decoration_style = 0; /* maybe warn? */ return 0; } - if (!strcmp(var, "log.diffmerges")) + if (!strcmp(var, "log.diffmerges")) { + if (!value) + return config_error_nonbool(var); return diff_merges_config(value); + } if (!strcmp(var, "log.showroot")) { default_show_root = git_config_bool(var, value); return 0; @@ -601,9 +618,7 @@ static int git_log_config(const char *var, const char *value, void *cb) return 0; } - if (git_gpg_config(var, value, cb) < 0) - return -1; - return git_diff_ui_config(var, value, cb); + return git_diff_ui_config(var, value, ctx, cb); } int cmd_whatchanged(int argc, const char **argv, const char *prefix) @@ -675,7 +690,7 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev) { unsigned long size; enum object_type type; - char *buf = read_object_file(oid, &type, &size); + char *buf = repo_read_object_file(the_repository, oid, &type, &size); int offset = 0; if (!buf) @@ -708,8 +723,7 @@ static int show_tree_object(const struct object_id *oid UNUSED, return 0; } -static void show_setup_revisions_tweak(struct rev_info *rev, - struct setup_revision_opt *opt) +static void show_setup_revisions_tweak(struct rev_info *rev) { if (rev->first_parent_only) diff_merges_default_to_first_parent(rev); @@ -852,11 +866,10 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix) return cmd_log_deinit(cmd_log_walk(&rev), &rev); } -static void log_setup_revisions_tweak(struct rev_info *rev, - struct setup_revision_opt *opt) +static void log_setup_revisions_tweak(struct rev_info *rev) { if (rev->diffopt.flags.default_follow_renames && - rev->prune_data.nr == 1) + diff_check_follow_pathspec(&rev->prune_data, 0)) rev->diffopt.flags.follow_renames = 1; if (rev->first_parent_only) @@ -969,7 +982,8 @@ static enum cover_from_description parse_cover_from_description(const char *arg) die(_("%s: invalid cover from description mode"), arg); } -static int git_format_config(const char *var, const char *value, void *cb) +static int git_format_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { if (!strcmp(var, "format.headers")) { if (!value) @@ -1084,8 +1098,21 @@ static int git_format_config(const char *var, const char *value, void *cb) stdout_mboxrd = git_config_bool(var, value); return 0; } + if (!strcmp(var, "format.noprefix")) { + format_no_prefix = 1; + return 0; + } - return git_log_config(var, value, cb); + /* + * ignore some porcelain config which would otherwise be parsed by + * git_diff_ui_config(), via git_log_config(); we can't just avoid + * diff_ui_config completely, because we do care about some ui options + * like color. + */ + if (!strcmp(var, "diff.noprefix")) + return 0; + + return git_log_config(var, value, ctx, cb); } static const char *output_directory = NULL; @@ -1204,7 +1231,8 @@ static char *find_branch_name(struct rev_info *rev) return NULL; ref = rev->cmdline.rev[positive].name; tip_oid = &rev->cmdline.rev[positive].item->oid; - if (dwim_ref(ref, strlen(ref), &branch_oid, &full_ref, 0) && + if (repo_dwim_ref(the_repository, ref, strlen(ref), &branch_oid, + &full_ref, 0) && skip_prefix(full_ref, "refs/heads/", &v) && oideq(tip_oid, &branch_oid)) branch = xstrdup(v); @@ -1230,7 +1258,15 @@ static void show_diffstat(struct rev_info *rev, fprintf(rev->diffopt.file, "\n"); } +static void read_desc_file(struct strbuf *buf, const char *desc_file) +{ + if (strbuf_read_file(buf, desc_file, 0) < 0) + die_errno(_("unable to read branch description file '%s'"), + desc_file); +} + static void prepare_cover_text(struct pretty_print_context *pp, + const char *description_file, const char *branch_name, struct strbuf *sb, const char *encoding, @@ -1244,7 +1280,9 @@ static void prepare_cover_text(struct pretty_print_context *pp, if (cover_from_description_mode == COVER_FROM_NONE) goto do_pp; - if (branch_name && *branch_name) + if (description_file && *description_file) + read_desc_file(&description_sb, description_file); + else if (branch_name && *branch_name) read_branch_desc(&description_sb, branch_name); if (!description_sb.len) goto do_pp; @@ -1290,6 +1328,7 @@ static void get_notes_args(struct strvec *arg, struct rev_info *rev) static void make_cover_letter(struct rev_info *rev, int use_separate_file, struct commit *origin, int nr, struct commit **list, + const char *description_file, const char *branch_name, int quiet) { @@ -1314,10 +1353,11 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, log_write_email_headers(rev, head, &pp.after_subject, &need_8bit_cte, 0); for (i = 0; !need_8bit_cte && i < nr; i++) { - const char *buf = get_commit_buffer(list[i], NULL); + const char *buf = repo_get_commit_buffer(the_repository, + list[i], NULL); if (has_non_ascii(buf)) need_8bit_cte = 1; - unuse_commit_buffer(list[i], buf); + repo_unuse_commit_buffer(the_repository, list[i], buf); } if (!branch_name) @@ -1327,8 +1367,10 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, pp.date_mode.type = DATE_RFC2822; pp.rev = rev; pp.print_email_subject = 1; + pp.encode_email_headers = rev->encode_email_headers; pp_user_info(&pp, NULL, &sb, committer, encoding); - prepare_cover_text(&pp, branch_name, &sb, encoding, need_8bit_cte); + prepare_cover_text(&pp, description_file, branch_name, &sb, + encoding, need_8bit_cte); fprintf(rev->diffopt.file, "%s\n", sb.buf); strbuf_release(&sb); @@ -1370,7 +1412,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, .other_arg = &other_arg }; - diff_setup(&opts); + repo_diff_setup(the_repository, &opts); opts.file = rev->diffopt.file; opts.use_color = rev->diffopt.use_color; diff_setup_done(&opts); @@ -1381,7 +1423,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, } } -static const char *clean_message_id(const char *msg_id) +static char *clean_message_id(const char *msg_id) { char ch; const char *a, *z, *m; @@ -1399,7 +1441,7 @@ static const char *clean_message_id(const char *msg_id) if (!z) die(_("insane in-reply-to: %s"), msg_id); if (++z == m) - return a; + return xstrdup(a); return xmemdupz(a, z - a); } @@ -1444,19 +1486,16 @@ static int subject_prefix = 0; static int subject_prefix_callback(const struct option *opt, const char *arg, int unset) { + struct strbuf *sprefix; + BUG_ON_OPT_NEG(unset); + sprefix = opt->value; subject_prefix = 1; - ((struct rev_info *)opt->value)->subject_prefix = arg; + strbuf_reset(sprefix); + strbuf_addstr(sprefix, arg); return 0; } -static int rfc_callback(const struct option *opt, const char *arg, int unset) -{ - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - return subject_prefix_callback(opt, "RFC PATCH", unset); -} - static int numbered_cmdline_opt = 0; static int numbered_callback(const struct option *opt, const char *arg, @@ -1531,7 +1570,8 @@ static int inline_callback(const struct option *opt, const char *arg, int unset) return 0; } -static int header_callback(const struct option *opt, const char *arg, int unset) +static int header_callback(const struct option *opt UNUSED, const char *arg, + int unset) { if (unset) { string_list_clear(&extra_hdr, 0); @@ -1543,24 +1583,6 @@ static int header_callback(const struct option *opt, const char *arg, int unset) return 0; } -static int to_callback(const struct option *opt, const char *arg, int unset) -{ - if (unset) - string_list_clear(&extra_to, 0); - else - string_list_append(&extra_to, arg); - return 0; -} - -static int cc_callback(const struct option *opt, const char *arg, int unset) -{ - if (unset) - string_list_clear(&extra_cc, 0); - else - string_list_append(&extra_cc, arg); - return 0; -} - static int from_callback(const struct option *opt, const char *arg, int unset) { char **from = opt->value; @@ -1642,14 +1664,16 @@ static struct commit *get_base_commit(const char *base_commit, struct commit *commit; struct object_id oid; - if (get_oid(upstream, &oid)) { + if (repo_get_oid(the_repository, upstream, &oid)) { if (die_on_failure) die(_("failed to resolve '%s' as a valid ref"), upstream); else return NULL; } commit = lookup_commit_or_die(&oid, "upstream base"); - base_list = get_merge_bases_many(commit, total, list); + base_list = repo_get_merge_bases_many(the_repository, + commit, total, + list); /* There should be one and only one merge base. */ if (!base_list || base_list->next) { if (die_on_failure) { @@ -1683,7 +1707,9 @@ static struct commit *get_base_commit(const char *base_commit, while (rev_nr > 1) { for (i = 0; i < rev_nr / 2; i++) { struct commit_list *merge_base; - merge_base = get_merge_bases(rev[2 * i], rev[2 * i + 1]); + merge_base = repo_get_merge_bases(the_repository, + rev[2 * i], + rev[2 * i + 1]); if (!merge_base || merge_base->next) { if (die_on_failure) { die(_("failed to find exact merge base")); @@ -1701,7 +1727,7 @@ static struct commit *get_base_commit(const char *base_commit, rev_nr = DIV_ROUND_UP(rev_nr, 2); } - if (!in_merge_bases(base, rev[0])) { + if (!repo_in_merge_bases(the_repository, base, rev[0])) { if (die_on_failure) { die(_("base commit should be the ancestor of revision list")); } else { @@ -1865,6 +1891,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) int quiet = 0; const char *reroll_count = NULL; char *cover_from_description_arg = NULL; + char *description_file = NULL; char *branch_name = NULL; char *base_commit = NULL; struct base_tree_info bases; @@ -1879,6 +1906,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) struct strbuf rdiff_title = STRBUF_INIT; struct strbuf sprefix = STRBUF_INIT; int creation_factor = -1; + int rfc = 0; const struct option builtin_format_patch_options[] = { OPT_CALLBACK_F('n', "numbered", &numbered, NULL, @@ -1902,13 +1930,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) N_("mark the series as Nth re-roll")), OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max, N_("max length of output filename")), - OPT_CALLBACK_F(0, "rfc", &rev, NULL, - N_("use [RFC PATCH] instead of [PATCH]"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback), + OPT_BOOL(0, "rfc", &rfc, N_("use [RFC PATCH] instead of [PATCH]")), OPT_STRING(0, "cover-from-description", &cover_from_description_arg, N_("cover-from-description-mode"), N_("generate parts of a cover letter based on a branch's description")), - OPT_CALLBACK_F(0, "subject-prefix", &rev, N_("prefix"), + OPT_FILENAME(0, "description-file", &description_file, + N_("use branch description from file")), + OPT_CALLBACK_F(0, "subject-prefix", &sprefix, N_("prefix"), N_("use [<prefix>] instead of [PATCH]"), PARSE_OPT_NONEG, subject_prefix_callback), OPT_CALLBACK_F('o', "output-directory", &output_directory, @@ -1929,8 +1957,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) OPT_GROUP(N_("Messaging")), OPT_CALLBACK(0, "add-header", NULL, N_("header"), N_("add email header"), header_callback), - OPT_CALLBACK(0, "to", NULL, N_("email"), N_("add To: header"), to_callback), - OPT_CALLBACK(0, "cc", NULL, N_("email"), N_("add Cc: header"), cc_callback), + OPT_STRING_LIST(0, "to", &extra_to, N_("email"), N_("add To: header")), + OPT_STRING_LIST(0, "cc", &extra_cc, N_("email"), N_("add Cc: header")), OPT_CALLBACK_F(0, "from", &from, N_("ident"), N_("set From address to <ident> (or committer ident if absent)"), PARSE_OPT_OPTARG, from_callback), @@ -1988,11 +2016,14 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.max_parents = 1; rev.diffopt.flags.recursive = 1; rev.diffopt.no_free = 1; - rev.subject_prefix = fmt_patch_subject_prefix; memset(&s_r_opt, 0, sizeof(s_r_opt)); s_r_opt.def = "HEAD"; s_r_opt.revarg_opt = REVARG_COMMITTISH; + strbuf_addstr(&sprefix, fmt_patch_subject_prefix); + if (format_no_prefix) + diff_set_noprefix(&rev.diffopt); + if (default_attach) { rev.mime_boundary = default_attach; rev.no_inline = 1; @@ -2017,13 +2048,16 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (cover_from_description_arg) cover_from_description_mode = parse_cover_from_description(cover_from_description_arg); + if (rfc) + strbuf_insertstr(&sprefix, 0, "RFC "); + if (reroll_count) { - strbuf_addf(&sprefix, "%s v%s", - rev.subject_prefix, reroll_count); + strbuf_addf(&sprefix, " v%s", reroll_count); rev.reroll_count = reroll_count; - rev.subject_prefix = sprefix.buf; } + rev.subject_prefix = sprefix.buf; + for (i = 0; i < extra_hdr.nr; i++) { strbuf_addstr(&buf, extra_hdr.items[i].string); strbuf_addch(&buf, '\n'); @@ -2097,6 +2131,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) /* Always generate a patch */ rev.diffopt.output_format |= DIFF_FORMAT_PATCH; + rev.always_show_header = 1; rev.zero_commit = zero_commit; rev.patch_name_max = fmt_patch_name_max; @@ -2277,11 +2312,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (in_reply_to || thread || cover_letter) { rev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids)); - string_list_init_nodup(rev.ref_message_ids); + string_list_init_dup(rev.ref_message_ids); } if (in_reply_to) { - const char *msgid = clean_message_id(in_reply_to); - string_list_append(rev.ref_message_ids, msgid); + char *msgid = clean_message_id(in_reply_to); + string_list_append_nodup(rev.ref_message_ids, msgid); } rev.numbered_files = just_numbers; rev.patch_suffix = fmt_patch_suffix; @@ -2289,7 +2324,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (thread) gen_message_id(&rev, "cover"); make_cover_letter(&rev, !!output_directory, - origin, nr, list, branch_name, quiet); + origin, nr, list, description_file, branch_name, quiet); print_bases(&bases, rev.diffopt.file); print_signature(rev.diffopt.file); total++; @@ -2337,8 +2372,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) && (!cover_letter || rev.nr > 1)) free(rev.message_id); else - string_list_append(rev.ref_message_ids, - rev.message_id); + string_list_append_nodup(rev.ref_message_ids, + rev.message_id); } gen_message_id(&rev, oid_to_hex(&commit->object.oid)); } @@ -2387,6 +2422,7 @@ done: strbuf_release(&rdiff_title); strbuf_release(&sprefix); free(to_free); + free(rev.message_id); if (rev.ref_message_ids) string_list_clear(rev.ref_message_ids, 0); free(rev.ref_message_ids); @@ -2396,7 +2432,7 @@ done: static int add_pending_commit(const char *arg, struct rev_info *revs, int flags) { struct object_id oid; - if (get_oid(arg, &oid) == 0) { + if (repo_get_oid(the_repository, arg, &oid) == 0) { struct commit *commit = lookup_commit_reference(the_repository, &oid); if (commit) { @@ -2418,12 +2454,12 @@ static void print_commit(char sign, struct commit *commit, int verbose, { if (!verbose) { fprintf(file, "%c %s\n", sign, - find_unique_abbrev(&commit->object.oid, abbrev)); + repo_find_unique_abbrev(the_repository, &commit->object.oid, abbrev)); } else { struct strbuf buf = STRBUF_INIT; pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf); fprintf(file, "%c %s %s\n", sign, - find_unique_abbrev(&commit->object.oid, abbrev), + repo_find_unique_abbrev(the_repository, &commit->object.oid, abbrev), buf.buf); strbuf_release(&buf); } diff --git a/builtin/ls-files.c b/builtin/ls-files.c index a03b559eca..a0229c3277 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -5,22 +5,31 @@ * * Copyright (C) Linus Torvalds, 2005 */ -#include "cache.h" +#include "builtin.h" #include "repository.h" #include "config.h" +#include "convert.h" #include "quote.h" #include "dir.h" -#include "builtin.h" +#include "gettext.h" +#include "object-name.h" #include "strbuf.h" #include "tree.h" #include "cache-tree.h" #include "parse-options.h" #include "resolve-undo.h" #include "string-list.h" +#include "path.h" #include "pathspec.h" +#include "read-cache.h" #include "run-command.h" +#include "setup.h" +#include "sparse-index.h" #include "submodule.h" #include "submodule-config.h" +#include "object-store.h" +#include "hex.h" + static int abbrev; static int show_deleted; @@ -89,12 +98,15 @@ static void write_name(const char *name) static void write_name_to_buf(struct strbuf *sb, const char *name) { - const char *rel = relative_path(name, prefix_len ? prefix : NULL, sb); + struct strbuf buf = STRBUF_INIT; + const char *rel = relative_path(name, prefix_len ? prefix : NULL, &buf); if (line_terminator) quote_c_style(rel, sb, NULL, 0); else strbuf_addstr(sb, rel); + + strbuf_release(&buf); } static const char *get_tag(const struct cache_entry *ce, const char *tag) @@ -234,68 +246,75 @@ static void show_submodule(struct repository *superproject, repo_clear(&subrepo); } -struct show_index_data { - const char *pathname; - struct index_state *istate; - const struct cache_entry *ce; -}; - -static size_t expand_show_index(struct strbuf *sb, const char *start, - void *context) +static void expand_objectsize(struct strbuf *line, const struct object_id *oid, + const enum object_type type, unsigned int padded) { - struct show_index_data *data = context; - const char *end; - const char *p; - size_t len = strbuf_expand_literal_cb(sb, start, NULL); - struct stat st; - - if (len) - return len; - if (*start != '(') - die(_("bad ls-files format: element '%s' " - "does not start with '('"), start); - - end = strchr(start + 1, ')'); - if (!end) - die(_("bad ls-files format: element '%s' " - "does not end in ')'"), start); - - len = end - start + 1; - if (skip_prefix(start, "(objectmode)", &p)) - strbuf_addf(sb, "%06o", data->ce->ce_mode); - else if (skip_prefix(start, "(objectname)", &p)) - strbuf_add_unique_abbrev(sb, &data->ce->oid, abbrev); - else if (skip_prefix(start, "(stage)", &p)) - strbuf_addf(sb, "%d", ce_stage(data->ce)); - else if (skip_prefix(start, "(eolinfo:index)", &p)) - strbuf_addstr(sb, S_ISREG(data->ce->ce_mode) ? - get_cached_convert_stats_ascii(data->istate, - data->ce->name) : ""); - else if (skip_prefix(start, "(eolinfo:worktree)", &p)) - strbuf_addstr(sb, !lstat(data->pathname, &st) && - S_ISREG(st.st_mode) ? - get_wt_convert_stats_ascii(data->pathname) : ""); - else if (skip_prefix(start, "(eolattr)", &p)) - strbuf_addstr(sb, get_convert_attr_ascii(data->istate, - data->pathname)); - else if (skip_prefix(start, "(path)", &p)) - write_name_to_buf(sb, data->pathname); - else - die(_("bad ls-files format: %%%.*s"), (int)len, start); - - return len; + if (type == OBJ_BLOB) { + unsigned long size; + if (oid_object_info(the_repository, oid, &size) < 0) + die(_("could not get object info about '%s'"), + oid_to_hex(oid)); + if (padded) + strbuf_addf(line, "%7"PRIuMAX, (uintmax_t)size); + else + strbuf_addf(line, "%"PRIuMAX, (uintmax_t)size); + } else if (padded) { + strbuf_addf(line, "%7s", "-"); + } else { + strbuf_addstr(line, "-"); + } } static void show_ce_fmt(struct repository *repo, const struct cache_entry *ce, const char *format, const char *fullname) { - struct show_index_data data = { - .pathname = fullname, - .istate = repo->index, - .ce = ce, - }; struct strbuf sb = STRBUF_INIT; - strbuf_expand(&sb, format, expand_show_index, &data); + while (strbuf_expand_step(&sb, &format)) { + const char *end; + size_t len; + struct stat st; + + if (skip_prefix(format, "%", &format)) + strbuf_addch(&sb, '%'); + else if ((len = strbuf_expand_literal(&sb, format))) + format += len; + else if (*format != '(') + die(_("bad ls-files format: element '%s' " + "does not start with '('"), format); + else if (!(end = strchr(format + 1, ')'))) + die(_("bad ls-files format: element '%s' " + "does not end in ')'"), format); + else if (skip_prefix(format, "(objectmode)", &format)) + strbuf_addf(&sb, "%06o", ce->ce_mode); + else if (skip_prefix(format, "(objectname)", &format)) + strbuf_add_unique_abbrev(&sb, &ce->oid, abbrev); + else if (skip_prefix(format, "(objecttype)", &format)) + strbuf_addstr(&sb, type_name(object_type(ce->ce_mode))); + else if (skip_prefix(format, "(objectsize:padded)", &format)) + expand_objectsize(&sb, &ce->oid, + object_type(ce->ce_mode), 1); + else if (skip_prefix(format, "(objectsize)", &format)) + expand_objectsize(&sb, &ce->oid, + object_type(ce->ce_mode), 0); + else if (skip_prefix(format, "(stage)", &format)) + strbuf_addf(&sb, "%d", ce_stage(ce)); + else if (skip_prefix(format, "(eolinfo:index)", &format)) + strbuf_addstr(&sb, S_ISREG(ce->ce_mode) ? + get_cached_convert_stats_ascii(repo->index, + ce->name) : ""); + else if (skip_prefix(format, "(eolinfo:worktree)", &format)) + strbuf_addstr(&sb, !lstat(fullname, &st) && + S_ISREG(st.st_mode) ? + get_wt_convert_stats_ascii(fullname) : ""); + else if (skip_prefix(format, "(eolattr)", &format)) + strbuf_addstr(&sb, get_convert_attr_ascii(repo->index, + fullname)); + else if (skip_prefix(format, "(path)", &format)) + write_name_to_buf(&sb, fullname); + else + die(_("bad ls-files format: %%%.*s"), + (int)(end - format + 1), format); + } strbuf_addch(&sb, line_terminator); fwrite(sb.buf, sb.len, 1, stdout); strbuf_release(&sb); @@ -360,7 +379,7 @@ static void show_ru_info(struct index_state *istate) if (!ui->mode[i]) continue; printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i], - find_unique_abbrev(&ui->oid[i], abbrev), + repo_find_unique_abbrev(the_repository, &ui->oid[i], abbrev), i + 1); write_name(path); } @@ -509,143 +528,6 @@ static int get_common_prefix_len(const char *common_prefix) return common_prefix_len; } -static int read_one_entry_opt(struct index_state *istate, - const struct object_id *oid, - struct strbuf *base, - const char *pathname, - unsigned mode, int opt) -{ - int len; - struct cache_entry *ce; - - if (S_ISDIR(mode)) - return READ_TREE_RECURSIVE; - - len = strlen(pathname); - ce = make_empty_cache_entry(istate, base->len + len); - - ce->ce_mode = create_ce_mode(mode); - ce->ce_flags = create_ce_flags(1); - ce->ce_namelen = base->len + len; - memcpy(ce->name, base->buf, base->len); - memcpy(ce->name + base->len, pathname, len+1); - oidcpy(&ce->oid, oid); - return add_index_entry(istate, ce, opt); -} - -static int read_one_entry(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, - void *context) -{ - struct index_state *istate = context; - return read_one_entry_opt(istate, oid, base, pathname, - mode, - ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK); -} - -/* - * This is used when the caller knows there is no existing entries at - * the stage that will conflict with the entry being added. - */ -static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, - void *context) -{ - struct index_state *istate = context; - return read_one_entry_opt(istate, oid, base, pathname, - mode, ADD_CACHE_JUST_APPEND); -} - -/* - * Read the tree specified with --with-tree option - * (typically, HEAD) into stage #1 and then - * squash them down to stage #0. This is used for - * --error-unmatch to list and check the path patterns - * that were given from the command line. We are not - * going to write this index out. - */ -void overlay_tree_on_index(struct index_state *istate, - const char *tree_name, const char *prefix) -{ - struct tree *tree; - struct object_id oid; - struct pathspec pathspec; - struct cache_entry *last_stage0 = NULL; - int i; - read_tree_fn_t fn = NULL; - int err; - - if (get_oid(tree_name, &oid)) - die("tree-ish %s not found.", tree_name); - tree = parse_tree_indirect(&oid); - if (!tree) - die("bad tree-ish %s", tree_name); - - /* Hoist the unmerged entries up to stage #3 to make room */ - /* TODO: audit for interaction with sparse-index. */ - ensure_full_index(istate); - for (i = 0; i < istate->cache_nr; i++) { - struct cache_entry *ce = istate->cache[i]; - if (!ce_stage(ce)) - continue; - ce->ce_flags |= CE_STAGEMASK; - } - - if (prefix) { - static const char *(matchbuf[1]); - matchbuf[0] = NULL; - parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC, - PATHSPEC_PREFER_CWD, prefix, matchbuf); - } else - memset(&pathspec, 0, sizeof(pathspec)); - - /* - * See if we have cache entry at the stage. If so, - * do it the original slow way, otherwise, append and then - * sort at the end. - */ - for (i = 0; !fn && i < istate->cache_nr; i++) { - const struct cache_entry *ce = istate->cache[i]; - if (ce_stage(ce) == 1) - fn = read_one_entry; - } - - if (!fn) - fn = read_one_entry_quick; - err = read_tree(the_repository, tree, &pathspec, fn, istate); - clear_pathspec(&pathspec); - if (err) - die("unable to read tree entries %s", tree_name); - - /* - * Sort the cache entry -- we need to nuke the cache tree, though. - */ - if (fn == read_one_entry_quick) { - cache_tree_free(&istate->cache_tree); - QSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare); - } - - for (i = 0; i < istate->cache_nr; i++) { - struct cache_entry *ce = istate->cache[i]; - switch (ce_stage(ce)) { - case 0: - last_stage0 = ce; - /* fallthru */ - default: - continue; - case 1: - /* - * If there is stage #0 entry for this, we do not - * need to show it. We use CE_UPDATE bit to mark - * such an entry. - */ - if (last_stage0 && - !strcmp(last_stage0->name, ce->name)) - ce->ce_flags |= CE_UPDATE; - } - } -} - static const char * const ls_files_usage[] = { N_("git ls-files [<options>] [<file>...]"), NULL diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index 6516177348..b416602b4d 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -1,9 +1,13 @@ #include "builtin.h" -#include "cache.h" +#include "gettext.h" +#include "hex.h" #include "transport.h" +#include "pkt-line.h" #include "ref-filter.h" #include "remote.h" #include "refs.h" +#include "parse-options.h" +#include "wildmatch.h" static const char * const ls_remote_usage[] = { N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n" @@ -54,6 +58,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) struct transport *transport; const struct ref *ref; struct ref_array ref_array; + struct ref_sorting *sorting; struct string_list sorting_options = STRING_LIST_INIT_DUP; struct option options[] = { @@ -137,13 +142,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) item->symref = xstrdup_or_null(ref->symref); } - if (sorting_options.nr) { - struct ref_sorting *sorting; - - sorting = ref_sorting_options(&sorting_options); - ref_array_sort(sorting, &ref_array); - ref_sorting_release(sorting); - } + sorting = ref_sorting_options(&sorting_options); + ref_array_sort(sorting, &ref_array); for (i = 0; i < ref_array.nr; i++) { const struct ref_array_item *ref = ref_array.items[i]; @@ -153,6 +153,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) status = 0; /* we found something */ } + ref_sorting_release(sorting); ref_array_clear(&ref_array); if (transport_disconnect(transport)) status = 1; diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index 8cc8c995df..209d2dc0d5 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -3,14 +3,17 @@ * * Copyright (C) Linus Torvalds, 2005 */ -#include "cache.h" +#include "builtin.h" #include "config.h" -#include "object-store.h" +#include "gettext.h" +#include "hex.h" +#include "object-name.h" +#include "object-store-ll.h" #include "blob.h" #include "tree.h" #include "commit.h" +#include "path.h" #include "quote.h" -#include "builtin.h" #include "parse-options.h" #include "pathspec.h" @@ -47,68 +50,10 @@ struct ls_tree_options { LS_SHOW_TREES = 1 << 2, } ls_options; struct pathspec pathspec; - int chomp_prefix; - const char *ls_tree_prefix; + const char *prefix; const char *format; }; -struct show_tree_data { - struct ls_tree_options *options; - unsigned mode; - enum object_type type; - const struct object_id *oid; - const char *pathname; - struct strbuf *base; -}; - -static size_t expand_show_tree(struct strbuf *sb, const char *start, - void *context) -{ - struct show_tree_data *data = context; - struct ls_tree_options *options = data->options; - const char *end; - const char *p; - unsigned int errlen; - size_t len = strbuf_expand_literal_cb(sb, start, NULL); - - if (len) - return len; - if (*start != '(') - die(_("bad ls-tree format: element '%s' does not start with '('"), start); - - end = strchr(start + 1, ')'); - if (!end) - die(_("bad ls-tree format: element '%s' does not end in ')'"), start); - - len = end - start + 1; - if (skip_prefix(start, "(objectmode)", &p)) { - strbuf_addf(sb, "%06o", data->mode); - } else if (skip_prefix(start, "(objecttype)", &p)) { - strbuf_addstr(sb, type_name(data->type)); - } else if (skip_prefix(start, "(objectsize:padded)", &p)) { - expand_objectsize(sb, data->oid, data->type, 1); - } else if (skip_prefix(start, "(objectsize)", &p)) { - expand_objectsize(sb, data->oid, data->type, 0); - } else if (skip_prefix(start, "(objectname)", &p)) { - strbuf_add_unique_abbrev(sb, data->oid, options->abbrev); - } else if (skip_prefix(start, "(path)", &p)) { - const char *name = data->base->buf; - const char *prefix = options->chomp_prefix ? options->ls_tree_prefix : NULL; - struct strbuf sbuf = STRBUF_INIT; - size_t baselen = data->base->len; - - strbuf_addstr(data->base, data->pathname); - name = relative_path(data->base->buf, prefix, &sbuf); - quote_c_style(name, sb, NULL, 0); - strbuf_setlen(data->base, baselen); - strbuf_release(&sbuf); - } else { - errlen = (unsigned long)len; - die(_("bad ls-tree format: %%%.*s"), errlen, start); - } - return len; -} - static int show_recursive(struct ls_tree_options *options, const char *base, size_t baselen, const char *pathname) { @@ -147,14 +92,7 @@ static int show_tree_fmt(const struct object_id *oid, struct strbuf *base, int recurse = 0; struct strbuf sb = STRBUF_INIT; enum object_type type = object_type(mode); - struct show_tree_data cb_data = { - .options = options, - .mode = mode, - .type = type, - .oid = oid, - .pathname = pathname, - .base = base, - }; + const char *format = options->format; if (type == OBJ_TREE && show_recursive(options, base->buf, base->len, pathname)) recurse = READ_TREE_RECURSIVE; @@ -163,7 +101,45 @@ static int show_tree_fmt(const struct object_id *oid, struct strbuf *base, if (type == OBJ_BLOB && (options->ls_options & LS_TREE_ONLY)) return 0; - strbuf_expand(&sb, options->format, expand_show_tree, &cb_data); + while (strbuf_expand_step(&sb, &format)) { + const char *end; + size_t len; + + if (skip_prefix(format, "%", &format)) + strbuf_addch(&sb, '%'); + else if ((len = strbuf_expand_literal(&sb, format))) + format += len; + else if (*format != '(') + die(_("bad ls-tree format: element '%s' " + "does not start with '('"), format); + else if (!(end = strchr(format + 1, ')'))) + die(_("bad ls-tree format: element '%s' " + "does not end in ')'"), format); + else if (skip_prefix(format, "(objectmode)", &format)) + strbuf_addf(&sb, "%06o", mode); + else if (skip_prefix(format, "(objecttype)", &format)) + strbuf_addstr(&sb, type_name(type)); + else if (skip_prefix(format, "(objectsize:padded)", &format)) + expand_objectsize(&sb, oid, type, 1); + else if (skip_prefix(format, "(objectsize)", &format)) + expand_objectsize(&sb, oid, type, 0); + else if (skip_prefix(format, "(objectname)", &format)) + strbuf_add_unique_abbrev(&sb, oid, options->abbrev); + else if (skip_prefix(format, "(path)", &format)) { + const char *name; + const char *prefix = options->prefix; + struct strbuf sbuf = STRBUF_INIT; + size_t baselen = base->len; + + strbuf_addstr(base, pathname); + name = relative_path(base->buf, prefix, &sbuf); + quote_c_style(name, &sb, NULL, 0); + strbuf_setlen(base, baselen); + strbuf_release(&sbuf); + } else + die(_("bad ls-tree format: %%%.*s"), + (int)(end - format + 1), format); + } strbuf_addch(&sb, options->null_termination ? '\0' : '\n'); fwrite(sb.buf, sb.len, 1, stdout); strbuf_release(&sb); @@ -195,7 +171,7 @@ static void show_tree_common_default_long(struct ls_tree_options *options, const char *pathname, const size_t baselen) { - const char *prefix = options->chomp_prefix ? options->ls_tree_prefix : NULL; + const char *prefix = options->prefix; strbuf_addstr(base, pathname); @@ -228,7 +204,7 @@ static int show_tree_default(const struct object_id *oid, struct strbuf *base, return early; printf("%06o %s %s\t", mode, type_name(object_type(mode)), - find_unique_abbrev(oid, options->abbrev)); + repo_find_unique_abbrev(the_repository, oid, options->abbrev)); show_tree_common_default_long(options, base, pathname, base->len); return recurse; } @@ -259,12 +235,14 @@ static int show_tree_long(const struct object_id *oid, struct strbuf *base, } printf("%06o %s %s %7s\t", mode, type_name(type), - find_unique_abbrev(oid, options->abbrev), size_text); + repo_find_unique_abbrev(the_repository, oid, options->abbrev), + size_text); show_tree_common_default_long(options, base, pathname, base->len); return recurse; } -static int show_tree_name_only(const struct object_id *oid, struct strbuf *base, +static int show_tree_name_only(const struct object_id *oid UNUSED, + struct strbuf *base, const char *pathname, unsigned mode, void *context) { @@ -279,7 +257,7 @@ static int show_tree_name_only(const struct object_id *oid, struct strbuf *base, if (early >= 0) return early; - prefix = options->chomp_prefix ? options->ls_tree_prefix : NULL; + prefix = options->prefix; strbuf_addstr(base, pathname); if (options->null_termination) { struct strbuf sb = STRBUF_INIT; @@ -310,7 +288,7 @@ static int show_tree_object(const struct object_id *oid, struct strbuf *base, if (early >= 0) return early; - str = find_unique_abbrev(oid, options->abbrev); + str = repo_find_unique_abbrev(the_repository, oid, options->abbrev); if (options->null_termination) { fputs(str, stdout); fputc('\0', stdout); @@ -366,6 +344,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) struct object_id oid; struct tree *tree; int i, full_tree = 0; + int full_name = !prefix || !*prefix; read_tree_fn_t fn = NULL; enum ls_tree_cmdmode cmdmode = MODE_DEFAULT; int null_termination = 0; @@ -387,8 +366,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) MODE_NAME_STATUS), OPT_CMDMODE(0, "object-only", &cmdmode, N_("list only objects"), MODE_OBJECT_ONLY), - OPT_SET_INT(0, "full-name", &options.chomp_prefix, - N_("use full path names"), 0), + OPT_BOOL(0, "full-name", &full_name, N_("use full path names")), OPT_BOOL(0, "full-tree", &full_tree, N_("list entire tree; not just current directory " "(implies --full-name)")), @@ -402,18 +380,15 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) int ret; git_config(git_default_config, NULL); - options.ls_tree_prefix = prefix; - if (prefix) - options.chomp_prefix = strlen(prefix); argc = parse_options(argc, argv, prefix, ls_tree_options, ls_tree_usage, 0); options.null_termination = null_termination; - if (full_tree) { - options.ls_tree_prefix = prefix = NULL; - options.chomp_prefix = 0; - } + if (full_tree) + prefix = NULL; + options.prefix = full_name ? NULL : prefix; + /* * We wanted to detect conflicts between --name-only and * --name-status, but once we're done with that subsequent @@ -433,7 +408,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) ls_tree_usage, ls_tree_options); if (argc < 1) usage_with_options(ls_tree_usage, ls_tree_options); - if (get_oid(argv[0], &oid)) + if (repo_get_oid(the_repository, argv[0], &oid)) die("Not a valid object name %s", argv[0]); /* diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c index 01d16ef9e5..53b55dd71c 100644 --- a/builtin/mailinfo.c +++ b/builtin/mailinfo.c @@ -2,8 +2,10 @@ * Another stupid program, this one parsing the headers of an * email to figure out authorship and subject */ -#include "cache.h" #include "builtin.h" +#include "abspath.h" +#include "environment.h" +#include "gettext.h" #include "utf8.h" #include "strbuf.h" #include "mailinfo.h" diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c index 73509f651b..3af9ddb8ae 100644 --- a/builtin/mailsplit.c +++ b/builtin/mailsplit.c @@ -4,8 +4,8 @@ * It just splits a mbox into a list of files: "0001" "0002" .. * so you can process them further from there. */ -#include "cache.h" #include "builtin.h" +#include "gettext.h" #include "string-list.h" #include "strbuf.h" @@ -277,6 +277,8 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix) const char **argp; static const char *stdin_only[] = { "-", NULL }; + BUG_ON_NON_EMPTY_PREFIX(prefix); + for (argp = argv+1; *argp; argp++) { const char *arg = *argp; diff --git a/builtin/merge-base.c b/builtin/merge-base.c index 6f3941f2a4..e68b7fe45d 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -1,10 +1,12 @@ #include "builtin.h" -#include "cache.h" #include "config.h" #include "commit.h" +#include "gettext.h" +#include "hex.h" #include "refs.h" #include "diff.h" #include "revision.h" +#include "object-name.h" #include "parse-options.h" #include "repository.h" #include "commit-reach.h" @@ -13,7 +15,8 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all) { struct commit_list *result, *r; - result = get_merge_bases_many_dirty(rev[0], rev_nr - 1, rev + 1); + result = repo_get_merge_bases_many_dirty(the_repository, rev[0], + rev_nr - 1, rev + 1); if (!result) return 1; @@ -42,7 +45,7 @@ static struct commit *get_commit_reference(const char *arg) struct object_id revkey; struct commit *r; - if (get_oid(arg, &revkey)) + if (repo_get_oid(the_repository, arg, &revkey)) die("Not a valid object name %s", arg); r = lookup_commit_reference(the_repository, &revkey); if (!r) @@ -105,7 +108,7 @@ static int handle_is_ancestor(int argc, const char **argv) die("--is-ancestor takes exactly two commits"); one = get_commit_reference(argv[0]); two = get_commit_reference(argv[1]); - if (in_merge_bases(one, two)) + if (repo_in_merge_bases(the_repository, one, two)) return 0; else return 1; @@ -118,7 +121,7 @@ static int handle_fork_point(int argc, const char **argv) const char *commitname; commitname = (argc == 2) ? argv[1] : "HEAD"; - if (get_oid(commitname, &oid)) + if (repo_get_oid(the_repository, commitname, &oid)) die("Not a valid object name: '%s'", commitname); derived = lookup_commit_reference(the_repository, &oid); diff --git a/builtin/merge-file.c b/builtin/merge-file.c index c923bbf2ab..1f987334a3 100644 --- a/builtin/merge-file.c +++ b/builtin/merge-file.c @@ -1,6 +1,12 @@ #include "builtin.h" -#include "cache.h" +#include "abspath.h" +#include "diff.h" +#include "hex.h" +#include "object-name.h" +#include "object-store.h" #include "config.h" +#include "gettext.h" +#include "setup.h" #include "xdiff/xdiff.h" #include "xdiff-interface.h" #include "parse-options.h" @@ -23,16 +29,41 @@ static int label_cb(const struct option *opt, const char *arg, int unset) return 0; } +static int set_diff_algorithm(xpparam_t *xpp, + const char *alg) +{ + long diff_algorithm = parse_algorithm_value(alg); + if (diff_algorithm < 0) + return -1; + xpp->flags = (xpp->flags & ~XDF_DIFF_ALGORITHM_MASK) | diff_algorithm; + return 0; +} + +static int diff_algorithm_cb(const struct option *opt, + const char *arg, int unset) +{ + xpparam_t *xpp = opt->value; + + BUG_ON_OPT_NEG(unset); + + if (set_diff_algorithm(xpp, arg)) + return error(_("option diff-algorithm accepts \"myers\", " + "\"minimal\", \"patience\" and \"histogram\"")); + + return 0; +} + int cmd_merge_file(int argc, const char **argv, const char *prefix) { const char *names[3] = { 0 }; mmfile_t mmfs[3] = { 0 }; mmbuffer_t result = { 0 }; xmparam_t xmp = { 0 }; - int ret = 0, i = 0, to_stdout = 0; + int ret = 0, i = 0, to_stdout = 0, object_id = 0; int quiet = 0; struct option options[] = { OPT_BOOL('p', "stdout", &to_stdout, N_("send results to standard output")), + OPT_BOOL(0, "object-id", &object_id, N_("use object IDs instead of filenames")), OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3), OPT_SET_INT(0, "zdiff3", &xmp.style, N_("use a zealous diff3 based merge"), XDL_MERGE_ZEALOUS_DIFF3), @@ -42,6 +73,9 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) XDL_MERGE_FAVOR_THEIRS), OPT_SET_INT(0, "union", &xmp.favor, N_("for conflicts, use a union version"), XDL_MERGE_FAVOR_UNION), + OPT_CALLBACK_F(0, "diff-algorithm", &xmp.xpp, N_("<algorithm>"), + N_("choose a diff algorithm"), + PARSE_OPT_NONEG, diff_algorithm_cb), OPT_INTEGER(0, "marker-size", &xmp.marker_size, N_("for conflicts, use this marker size")), OPT__QUIET(&quiet, N_("do not warn about conflicts")), @@ -69,8 +103,12 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) return error_errno("failed to redirect stderr to /dev/null"); } + if (object_id) + setup_git_directory(); + for (i = 0; i < 3; i++) { char *fname; + struct object_id oid; mmfile_t *mmf = mmfs + i; if (!names[i]) @@ -78,12 +116,22 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) fname = prefix_filename(prefix, argv[i]); - if (read_mmfile(mmf, fname)) + if (object_id) { + if (repo_get_oid(the_repository, argv[i], &oid)) + ret = error(_("object '%s' does not exist"), + argv[i]); + else if (!oideq(&oid, the_hash_algo->empty_blob)) + read_mmblob(mmf, &oid); + else + read_mmfile(mmf, "/dev/null"); + } else if (read_mmfile(mmf, fname)) { ret = -1; - else if (mmf->size > MAX_XDIFF_SIZE || - buffer_is_binary(mmf->ptr, mmf->size)) + } + if (ret != -1 && (mmf->size > MAX_XDIFF_SIZE || + buffer_is_binary(mmf->ptr, mmf->size))) { ret = error("Cannot merge binary files: %s", argv[i]); + } free(fname); if (ret) @@ -97,20 +145,32 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) ret = xdl_merge(mmfs + 1, mmfs + 0, mmfs + 2, &xmp, &result); if (ret >= 0) { - const char *filename = argv[0]; - char *fpath = prefix_filename(prefix, argv[0]); - FILE *f = to_stdout ? stdout : fopen(fpath, "wb"); - - if (!f) - ret = error_errno("Could not open %s for writing", - filename); - else if (result.size && - fwrite(result.ptr, result.size, 1, f) != 1) - ret = error_errno("Could not write to %s", filename); - else if (fclose(f)) - ret = error_errno("Could not close %s", filename); + if (object_id && !to_stdout) { + struct object_id oid; + if (result.size) { + if (write_object_file(result.ptr, result.size, OBJ_BLOB, &oid) < 0) + ret = error(_("Could not write object file")); + } else { + oidcpy(&oid, the_hash_algo->empty_blob); + } + if (ret >= 0) + printf("%s\n", oid_to_hex(&oid)); + } else { + const char *filename = argv[0]; + char *fpath = prefix_filename(prefix, argv[0]); + FILE *f = to_stdout ? stdout : fopen(fpath, "wb"); + + if (!f) + ret = error_errno("Could not open %s for writing", + filename); + else if (result.size && + fwrite(result.ptr, result.size, 1, f) != 1) + ret = error_errno("Could not write to %s", filename); + else if (fclose(f)) + ret = error_errno("Could not close %s", filename); + free(fpath); + } free(result.ptr); - free(fpath); } if (ret > 127) diff --git a/builtin/merge-index.c b/builtin/merge-index.c index 452f833ac4..270d5f644a 100644 --- a/builtin/merge-index.c +++ b/builtin/merge-index.c @@ -1,6 +1,10 @@ #define USE_THE_INDEX_VARIABLE #include "builtin.h" +#include "hex.h" +#include "read-cache-ll.h" +#include "repository.h" #include "run-command.h" +#include "sparse-index.h" static const char *pgm; static int one_shot, quiet; @@ -70,7 +74,7 @@ static void merge_all(void) } } -int cmd_merge_index(int argc, const char **argv, const char *prefix) +int cmd_merge_index(int argc, const char **argv, const char *prefix UNUSED) { int i, force_file = 0; diff --git a/builtin/merge-ours.c b/builtin/merge-ours.c index 284eb48609..932924e5d0 100644 --- a/builtin/merge-ours.c +++ b/builtin/merge-ours.c @@ -10,11 +10,12 @@ #include "git-compat-util.h" #include "builtin.h" #include "diff.h" +#include "repository.h" static const char builtin_merge_ours_usage[] = "git merge-ours <base>... -- HEAD <remote>..."; -int cmd_merge_ours(int argc, const char **argv, const char *prefix) +int cmd_merge_ours(int argc, const char **argv, const char *prefix UNUSED) { if (argc == 2 && !strcmp(argv[1], "-h")) usage(builtin_merge_ours_usage); diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c index b9acbf5d34..3366699657 100644 --- a/builtin/merge-recursive.c +++ b/builtin/merge-recursive.c @@ -1,8 +1,12 @@ -#include "cache.h" #include "builtin.h" +#include "advice.h" #include "commit.h" +#include "gettext.h" +#include "hash.h" #include "tag.h" #include "merge-recursive.h" +#include "object-name.h" +#include "repository.h" #include "xdiff-interface.h" static const char builtin_merge_recursive_usage[] = @@ -20,7 +24,7 @@ static char *better_branch_name(const char *branch) return xstrdup(name ? name : branch); } -int cmd_merge_recursive(int argc, const char **argv, const char *prefix) +int cmd_merge_recursive(int argc, const char **argv, const char *prefix UNUSED) { const struct object_id *bases[21]; unsigned bases_count = 0; @@ -49,7 +53,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix) } if (bases_count < ARRAY_SIZE(bases)-1) { struct object_id *oid = xmalloc(sizeof(struct object_id)); - if (get_oid(argv[i], oid)) + if (repo_get_oid(the_repository, argv[i], oid)) die(_("could not parse object '%s'"), argv[i]); bases[bases_count++] = oid; } @@ -70,9 +74,9 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix) o.branch1 = argv[++i]; o.branch2 = argv[++i]; - if (get_oid(o.branch1, &h1)) + if (repo_get_oid(the_repository, o.branch1, &h1)) die(_("could not resolve ref '%s'"), o.branch1); - if (get_oid(o.branch2, &h2)) + if (repo_get_oid(the_repository, o.branch2, &h2)) die(_("could not resolve ref '%s'"), o.branch2); o.branch1 = better1 = better_branch_name(o.branch1); diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 828dc81c42..a4aa6013c5 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -3,16 +3,22 @@ #include "tree-walk.h" #include "xdiff-interface.h" #include "help.h" +#include "gettext.h" +#include "hex.h" #include "commit.h" #include "commit-reach.h" #include "merge-ort.h" -#include "object-store.h" +#include "object-name.h" +#include "object-store-ll.h" #include "parse-options.h" #include "repository.h" #include "blob.h" #include "exec-cmd.h" #include "merge-blobs.h" #include "quote.h" +#include "tree.h" +#include "config.h" +#include "strvec.h" static int line_termination = '\n'; @@ -69,7 +75,9 @@ static void *result(struct merge_list *entry, unsigned long *size) const char *path = entry->path; if (!entry->stage) - return read_object_file(&entry->blob->object.oid, &type, size); + return repo_read_object_file(the_repository, + &entry->blob->object.oid, &type, + size); base = NULL; if (entry->stage == 1) { base = entry->blob; @@ -92,8 +100,9 @@ static void *origin(struct merge_list *entry, unsigned long *size) enum object_type type; while (entry) { if (entry->stage == 2) - return read_object_file(&entry->blob->object.oid, - &type, size); + return repo_read_object_file(the_repository, + &entry->blob->object.oid, + &type, size); entry = entry->link; } return NULL; @@ -316,7 +325,9 @@ static void unresolved(const struct traverse_info *info, struct name_entry n[3]) * The successful merge rules are the same as for the three-way merge * in git-read-tree. */ -static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *info) +static int threeway_callback(int n UNUSED, unsigned long mask, + unsigned long dirmask UNUSED, + struct name_entry *entry, struct traverse_info *info) { /* Same in both? */ if (same_entry(entry+1, entry+2) || both_empty(entry+1, entry+2)) { @@ -404,6 +415,7 @@ struct merge_tree_options { int show_messages; int name_only; int use_stdin; + struct merge_options merge_options; }; static int real_merge(struct merge_tree_options *o, @@ -413,10 +425,11 @@ static int real_merge(struct merge_tree_options *o, { struct commit *parent1, *parent2; struct commit_list *merge_bases = NULL; - struct merge_options opt; struct merge_result result = { 0 }; int show_messages = o->show_messages; + struct merge_options opt; + copy_merge_options(&opt, &o->merge_options); parent1 = get_merge_parent(branch1); if (!parent1) help_unknown_ref(branch1, "merge-tree", @@ -427,8 +440,6 @@ static int real_merge(struct merge_tree_options *o, help_unknown_ref(branch2, "merge-tree", _("not something we can merge")); - init_merge_options(&opt, the_repository); - opt.show_rename_progress = 0; opt.branch1 = branch1; @@ -440,19 +451,20 @@ static int real_merge(struct merge_tree_options *o, base_commit = lookup_commit_reference_by_name(merge_base); if (!base_commit) - die(_("could not lookup commit %s"), merge_base); + die(_("could not lookup commit '%s'"), merge_base); opt.ancestor = merge_base; - base_tree = get_commit_tree(base_commit); - parent1_tree = get_commit_tree(parent1); - parent2_tree = get_commit_tree(parent2); + base_tree = repo_get_commit_tree(the_repository, base_commit); + parent1_tree = repo_get_commit_tree(the_repository, parent1); + parent2_tree = repo_get_commit_tree(the_repository, parent2); merge_incore_nonrecursive(&opt, base_tree, parent1_tree, parent2_tree, &result); } else { /* * Get the merge bases, in reverse order; see comment above * merge_incore_recursive in merge-ort.h */ - merge_bases = get_merge_bases(parent1, parent2); + merge_bases = repo_get_merge_bases(the_repository, parent1, + parent2); if (!merge_bases && !o->allow_unrelated_histories) die(_("refusing to merge unrelated histories")); merge_bases = reverse_commit_list(merge_bases); @@ -496,12 +508,14 @@ static int real_merge(struct merge_tree_options *o, if (o->use_stdin) putchar(line_termination); merge_finalize(&opt, &result); + clear_merge_options(&opt); return !result.clean; /* result.clean < 0 handled above */ } int cmd_merge_tree(int argc, const char **argv, const char *prefix) { struct merge_tree_options o = { .show_messages = -1 }; + struct strvec xopts = STRVEC_INIT; int expected_remaining_argc; int original_argc; const char *merge_base = NULL; @@ -537,14 +551,25 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix) &merge_base, N_("commit"), N_("specify a merge-base for the merge")), + OPT_STRVEC('X', "strategy-option", &xopts, N_("option=value"), + N_("option for selected merge strategy")), OPT_END() }; + /* Init merge options */ + init_merge_options(&o.merge_options, the_repository); + /* Parse arguments */ original_argc = argc - 1; /* ignoring argv[0] */ argc = parse_options(argc, argv, prefix, mt_options, merge_tree_usage, PARSE_OPT_STOP_AT_NON_OPTION); + if (xopts.nr && o.mode == MODE_TRIVIAL) + die(_("--trivial-merge is incompatible with all other options")); + for (int x = 0; x < xopts.nr; x++) + if (parse_merge_opt(&o.merge_options, xopts.v[x])) + die(_("unknown strategy option: -X%s"), xopts.v[x]); + /* Handle --stdin */ if (o.use_stdin) { struct strbuf buf = STRBUF_INIT; @@ -552,7 +577,8 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix) if (o.mode == MODE_TRIVIAL) die(_("--trivial-merge is incompatible with all other options")); if (merge_base) - die(_("--merge-base is incompatible with --stdin")); + die(_("options '%s' and '%s' cannot be used together"), + "--merge-base", "--stdin"); line_termination = '\0'; while (strbuf_getline_lf(&buf, stdin) != EOF) { struct strbuf **split; @@ -620,6 +646,8 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix) if (argc != expected_remaining_argc) usage_with_options(merge_tree_usage, mt_options); + git_config(git_default_config, NULL); + /* Do the relevant type of merge */ if (o.mode == MODE_REAL) return real_merge(&o, merge_base, argv[0], argv[1], prefix); diff --git a/builtin/merge.c b/builtin/merge.c index 0a3c10a096..d748d46e13 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -7,10 +7,16 @@ */ #define USE_THE_INDEX_VARIABLE -#include "cache.h" +#include "builtin.h" +#include "abspath.h" +#include "advice.h" #include "config.h" +#include "editor.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" +#include "object-name.h" #include "parse-options.h" -#include "builtin.h" #include "lockfile.h" #include "run-command.h" #include "hook.h" @@ -20,6 +26,7 @@ #include "refspec.h" #include "commit.h" #include "diffcore.h" +#include "path.h" #include "revision.h" #include "unpack-trees.h" #include "cache-tree.h" @@ -29,6 +36,7 @@ #include "color.h" #include "rerere.h" #include "help.h" +#include "merge.h" #include "merge-recursive.h" #include "merge-ort-wrappers.h" #include "resolve-undo.h" @@ -71,8 +79,7 @@ static int overwrite_ignore = 1; static struct strbuf merge_msg = STRBUF_INIT; static struct strategy **use_strategies; static size_t use_strategies_nr, use_strategies_alloc; -static const char **xopts; -static size_t xopts_nr, xopts_alloc; +static struct strvec xopts = STRVEC_INIT; static const char *branch; static char *branch_mergeoptions; static int verbosity; @@ -224,7 +231,7 @@ static void append_strategy(struct strategy *s) use_strategies[use_strategies_nr++] = s; } -static int option_parse_strategy(const struct option *opt, +static int option_parse_strategy(const struct option *opt UNUSED, const char *name, int unset) { if (unset) @@ -234,29 +241,9 @@ static int option_parse_strategy(const struct option *opt, return 0; } -static int option_parse_x(const struct option *opt, - const char *arg, int unset) -{ - if (unset) - return 0; - - ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc); - xopts[xopts_nr++] = xstrdup(arg); - return 0; -} - -static int option_parse_n(const struct option *opt, - const char *arg, int unset) -{ - BUG_ON_OPT_ARG(arg); - show_diffstat = unset; - return 0; -} - static struct option builtin_merge_options[] = { - OPT_CALLBACK_F('n', NULL, NULL, NULL, - N_("do not show a diffstat at the end of the merge"), - PARSE_OPT_NOARG, option_parse_n), + OPT_SET_INT('n', NULL, &show_diffstat, + N_("do not show a diffstat at the end of the merge"), 0), OPT_BOOL(0, "stat", &show_diffstat, N_("show a diffstat at the end of the merge")), OPT_BOOL(0, "summary", &show_diffstat, N_("(synonym to --stat)")), @@ -277,10 +264,10 @@ static struct option builtin_merge_options[] = { OPT_RERERE_AUTOUPDATE(&allow_rerere_auto), OPT_BOOL(0, "verify-signatures", &verify_signatures, N_("verify that the named commit has a valid GPG signature")), - OPT_CALLBACK('s', "strategy", &use_strategies, N_("strategy"), + OPT_CALLBACK('s', "strategy", NULL, N_("strategy"), N_("merge strategy to use"), option_parse_strategy), - OPT_CALLBACK('X', "strategy-option", &xopts, N_("option=value"), - N_("option for selected merge strategy"), option_parse_x), + OPT_STRVEC('X', "strategy-option", &xopts, N_("option=value"), + N_("option for selected merge strategy")), OPT_CALLBACK('m', "message", &merge_msg, N_("message"), N_("merge commit message (for a non-fast-forward merge)"), option_parse_message), @@ -337,7 +324,7 @@ static int save_state(struct object_id *stash) else if (!len) /* no changes */ goto out; strbuf_setlen(&buffer, buffer.len-1); - if (get_oid(buffer.buf, stash)) + if (repo_get_oid(the_repository, buffer.buf, stash)) die(_("not a valid object: %s"), buffer.buf); rc = 0; out: @@ -479,8 +466,7 @@ static void finish(struct commit *head_commit, if (new_head && show_diffstat) { struct diff_options opts; repo_diff_setup(the_repository, &opts); - opts.stat_width = -1; /* use full terminal width */ - opts.stat_graph_width = -1; /* respect statGraphWidth config */ + init_diffstat_widths(&opts); opts.output_format |= DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; opts.detect_rename = DIFF_DETECT_RENAME; @@ -517,7 +503,8 @@ static void merge_name(const char *remote, struct strbuf *msg) if (!remote_head) die(_("'%s' does not point to a commit"), remote); - if (dwim_ref(remote, strlen(remote), &branch_head, &found_ref, 0) > 0) { + if (repo_dwim_ref(the_repository, remote, strlen(remote), &branch_head, + &found_ref, 0) > 0) { if (starts_with(found_ref, "refs/heads/")) { strbuf_addf(msg, "%s\t\tbranch '%s' of .\n", oid_to_hex(&branch_head), remote); @@ -613,7 +600,8 @@ static void parse_branch_merge_options(char *bmo) free(argv); } -static int git_merge_config(const char *k, const char *v, void *cb) +static int git_merge_config(const char *k, const char *v, + const struct config_context *ctx, void *cb) { int status; const char *str; @@ -658,13 +646,10 @@ static int git_merge_config(const char *k, const char *v, void *cb) return 0; } - status = fmt_merge_msg_config(k, v, cb); - if (status) - return status; - status = git_gpg_config(k, v, NULL); + status = fmt_merge_msg_config(k, v, ctx, cb); if (status) return status; - return git_diff_ui_config(k, v, cb); + return git_diff_ui_config(k, v, ctx, cb); } static int read_tree_trivial(struct object_id *common, struct object_id *head, @@ -742,9 +727,9 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, o.show_rename_progress = show_progress == -1 ? isatty(2) : show_progress; - for (x = 0; x < xopts_nr; x++) - if (parse_merge_opt(&o, xopts[x])) - die(_("unknown strategy option: -X%s"), xopts[x]); + for (x = 0; x < xopts.nr; x++) + if (parse_merge_opt(&o, xopts.v[x])) + die(_("unknown strategy option: -X%s"), xopts.v[x]); o.branch1 = head_arg; o.branch2 = merge_remote_util(remoteheads->item)->name; @@ -770,7 +755,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, return clean ? 0 : 1; } else { return try_merge_command(the_repository, - strategy, xopts_nr, xopts, + strategy, xopts.nr, xopts.v, common, head_arg, remoteheads); } } @@ -872,13 +857,15 @@ static void prepare_to_commit(struct commit_list *remoteheads) strbuf_addch(&msg, '\n'); if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) { wt_status_append_cut_line(&msg); - strbuf_commented_addf(&msg, "\n"); + strbuf_commented_addf(&msg, comment_line_char, "\n"); } - strbuf_commented_addf(&msg, _(merge_editor_comment)); + strbuf_commented_addf(&msg, comment_line_char, + _(merge_editor_comment)); if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) - strbuf_commented_addf(&msg, _(scissors_editor_comment)); + strbuf_commented_addf(&msg, comment_line_char, + _(scissors_editor_comment)); else - strbuf_commented_addf(&msg, + strbuf_commented_addf(&msg, comment_line_char, _(no_scissors_editor_comment), comment_line_char); } if (signoff) @@ -1531,7 +1518,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (!remoteheads) ; /* already up-to-date */ else if (!remoteheads->next) - common = get_merge_bases(head_commit, remoteheads->item); + common = repo_get_merge_bases(the_repository, head_commit, + remoteheads->item); else { struct commit_list *list = remoteheads; commit_list_insert(head_commit, &list); @@ -1567,10 +1555,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (verbosity >= 0) { printf(_("Updating %s..%s\n"), - find_unique_abbrev(&head_commit->object.oid, - DEFAULT_ABBREV), - find_unique_abbrev(&remoteheads->item->object.oid, - DEFAULT_ABBREV)); + repo_find_unique_abbrev(the_repository, &head_commit->object.oid, + DEFAULT_ABBREV), + repo_find_unique_abbrev(the_repository, &remoteheads->item->object.oid, + DEFAULT_ABBREV)); } commit = remoteheads->item; if (!commit) { @@ -1610,7 +1598,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * Must first ensure that index matches HEAD before * attempting a trivial merge. */ - struct tree *head_tree = get_commit_tree(head_commit); + struct tree *head_tree = repo_get_commit_tree(the_repository, + head_commit); struct strbuf sb = STRBUF_INIT; if (repo_index_has_changes(the_repository, head_tree, @@ -1643,14 +1632,19 @@ int cmd_merge(int argc, const char **argv, const char *prefix) for (j = remoteheads; j; j = j->next) { struct commit_list *common_one; + struct commit *common_item; /* * Here we *have* to calculate the individual * merge_bases again, otherwise "git merge HEAD^ * HEAD^^" would be missed. */ - common_one = get_merge_bases(head_commit, j->item); - if (!oideq(&common_one->item->object.oid, &j->item->object.oid)) { + common_one = repo_get_merge_bases(the_repository, + head_commit, + j->item); + common_item = common_one->item; + free_commit_list(common_one); + if (!oideq(&common_item->object.oid, &j->item->object.oid)) { up_to_date = 0; break; } diff --git a/builtin/mktag.c b/builtin/mktag.c index 5d22909122..d8e0b5afc0 100644 --- a/builtin/mktag.c +++ b/builtin/mktag.c @@ -1,8 +1,12 @@ #include "builtin.h" +#include "gettext.h" +#include "hex.h" #include "parse-options.h" +#include "strbuf.h" #include "tag.h" #include "replace-object.h" -#include "object-store.h" +#include "object-file.h" +#include "object-store-ll.h" #include "fsck.h" #include "config.h" @@ -14,11 +18,11 @@ static int option_strict = 1; static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT; -static int mktag_fsck_error_func(struct fsck_options *o, - const struct object_id *oid, - enum object_type object_type, +static int mktag_fsck_error_func(struct fsck_options *o UNUSED, + const struct object_id *oid UNUSED, + enum object_type object_type UNUSED, enum fsck_msg_type msg_type, - enum fsck_msg_id msg_id, + enum fsck_msg_id msg_id UNUSED, const char *message) { switch (msg_type) { @@ -51,7 +55,8 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type) void *buffer; const struct object_id *repl; - buffer = read_object_file(tagged_oid, &type, &size); + buffer = repo_read_object_file(the_repository, tagged_oid, &type, + &size); if (!buffer) die(_("could not read tagged object '%s'"), oid_to_hex(tagged_oid)); @@ -80,7 +85,7 @@ int cmd_mktag(int argc, const char **argv, const char *prefix) int tagged_type; struct object_id result; - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, builtin_mktag_options, builtin_mktag_usage, 0); diff --git a/builtin/mktree.c b/builtin/mktree.c index 06d81400f5..9a22d4e277 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -4,10 +4,13 @@ * Copyright (c) Junio C Hamano, 2006, 2009 */ #include "builtin.h" +#include "gettext.h" +#include "hex.h" #include "quote.h" +#include "strbuf.h" #include "tree.h" #include "parse-options.h" -#include "object-store.h" +#include "object-store-ll.h" static struct treeent { unsigned mode; diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c index 9a18a82b05..a72aebecaa 100644 --- a/builtin/multi-pack-index.c +++ b/builtin/multi-pack-index.c @@ -1,10 +1,13 @@ #include "builtin.h" -#include "cache.h" +#include "abspath.h" #include "config.h" +#include "environment.h" +#include "gettext.h" #include "parse-options.h" #include "midx.h" +#include "strbuf.h" #include "trace2.h" -#include "object-store.h" +#include "object-store-ll.h" #define BUILTIN_MIDX_WRITE_USAGE \ N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]" \ @@ -79,6 +82,7 @@ static struct option *add_common_options(struct option *prev) } static int git_multi_pack_index_write_config(const char *var, const char *value, + const struct config_context *ctx UNUSED, void *cb UNUSED) { if (!strcmp(var, "pack.writebitmaphashcache")) { diff --git a/builtin/mv.c b/builtin/mv.c index edd7b931fd..c596515ad0 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -5,13 +5,22 @@ */ #define USE_THE_INDEX_VARIABLE #include "builtin.h" +#include "abspath.h" +#include "advice.h" #include "config.h" +#include "environment.h" +#include "gettext.h" +#include "name-hash.h" +#include "object-file.h" #include "pathspec.h" #include "lockfile.h" #include "dir.h" #include "cache-tree.h" #include "string-list.h" #include "parse-options.h" +#include "read-cache-ll.h" +#include "repository.h" +#include "setup.h" #include "submodule.h" #include "entry.h" @@ -175,7 +184,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) int src_dir_nr = 0, src_dir_alloc = 0; struct strbuf a_src_dir = STRBUF_INIT; enum update_mode *modes, dst_mode = 0; - struct stat st; + struct stat st, dest_st; struct string_list src_for_dst = STRING_LIST_INIT_NODUP; struct lock_file lock_file = LOCK_INIT; struct cache_entry *ce; @@ -295,8 +304,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix) goto act_on_entry; } if (S_ISDIR(st.st_mode) - && lstat(dst, &st) == 0) { - bad = _("cannot move directory over file"); + && lstat(dst, &dest_st) == 0) { + bad = _("destination already exists"); goto act_on_entry; } diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 97959bfaf9..2dd1807c4e 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -1,15 +1,20 @@ #include "builtin.h" -#include "cache.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "repository.h" #include "config.h" #include "commit.h" #include "tag.h" #include "refs.h" +#include "object-name.h" +#include "pager.h" #include "parse-options.h" #include "prio-queue.h" #include "hash-lookup.h" #include "commit-slab.h" #include "commit-graph.h" +#include "wildmatch.h" /* * One day. See the 'name a rev shortly after epoch' test in t6120 when @@ -181,7 +186,7 @@ static void name_rev(struct commit *start_commit, size_t parents_to_queue_nr, parents_to_queue_alloc = 0; struct rev_name *start_name; - parse_commit(start_commit); + repo_parse_commit(the_repository, start_commit); if (commit_is_before_cutoff(start_commit)) return; @@ -211,7 +216,7 @@ static void name_rev(struct commit *start_commit, struct rev_name *parent_name; int generation, distance; - parse_commit(parent); + repo_parse_commit(the_repository, parent); if (commit_is_before_cutoff(parent)) continue; @@ -493,7 +498,8 @@ static void show_name(const struct object *obj, else if (allow_undefined) printf("undefined\n"); else if (always) - printf("%s\n", find_unique_abbrev(oid, DEFAULT_ABBREV)); + printf("%s\n", + repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV)); else die("cannot describe '%s'", oid_to_hex(oid)); strbuf_release(&buf); @@ -527,7 +533,7 @@ static void name_rev_line(char *p, struct name_ref_data *data) counter = 0; *(p+1) = 0; - if (!get_oid(p - (hexsz - 1), &oid)) { + if (!repo_get_oid(the_repository, p - (hexsz - 1), &oid)) { struct object *o = lookup_object(the_repository, &oid); if (o) @@ -567,17 +573,17 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) N_("ignore refs matching <pattern>")), OPT_GROUP(""), OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")), - OPT_BOOL(0, "stdin", &transform_stdin, N_("deprecated: use --annotate-stdin instead")), + OPT_BOOL_F(0, + "stdin", + &transform_stdin, + N_("deprecated: use --annotate-stdin instead"), + PARSE_OPT_HIDDEN), OPT_BOOL(0, "annotate-stdin", &annotate_stdin, N_("annotate text from stdin")), OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")), OPT_BOOL(0, "always", &always, N_("show abbreviated commit object as fallback")), - { - /* A Hidden OPT_BOOL */ - OPTION_SET_INT, 0, "peel-tag", &peel_tag, NULL, - N_("dereference tags in the input (internal use)"), - PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1, - }, + OPT_HIDDEN_BOOL(0, "peel-tag", &peel_tag, + N_("dereference tags in the input (internal use)")), OPT_END(), }; @@ -604,7 +610,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) struct object *object; struct commit *commit; - if (get_oid(*argv, &oid)) { + if (repo_get_oid(the_repository, *argv, &oid)) { fprintf(stderr, "Could not get sha1 for %s. Skipping.\n", *argv); continue; diff --git a/builtin/notes.c b/builtin/notes.c index 80d9dfd25c..9f38863dd5 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -7,11 +7,17 @@ * and builtin/tag.c by Kristian Høgsberg and Carlos Rica. */ -#include "cache.h" -#include "config.h" #include "builtin.h" +#include "config.h" +#include "alloc.h" +#include "editor.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "notes.h" -#include "object-store.h" +#include "object-name.h" +#include "object-store-ll.h" +#include "path.h" #include "repository.h" #include "blob.h" #include "pretty.h" @@ -23,12 +29,14 @@ #include "notes-merge.h" #include "notes-utils.h" #include "worktree.h" +#include "write-or-die.h" +static const char *separator = "\n"; static const char * const git_notes_usage[] = { N_("git notes [--ref <notes-ref>] [list [<object>]]"), - N_("git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"), + N_("git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"), N_("git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"), - N_("git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"), + N_("git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"), N_("git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"), N_("git notes [--ref <notes-ref>] show [<object>]"), N_("git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>"), @@ -96,11 +104,26 @@ static const char * const git_notes_get_ref_usage[] = { static const char note_template[] = N_("Write/edit the notes for the following object:"); +enum notes_stripspace { + UNSPECIFIED = -1, + NO_STRIPSPACE = 0, + STRIPSPACE = 1, +}; + +struct note_msg { + enum notes_stripspace stripspace; + struct strbuf buf; +}; + struct note_data { int given; int use_editor; + int stripspace; char *edit_path; struct strbuf buf; + struct note_msg **messages; + size_t msg_nr; + size_t msg_alloc; }; static void free_note_data(struct note_data *d) @@ -110,11 +133,18 @@ static void free_note_data(struct note_data *d) free(d->edit_path); } strbuf_release(&d->buf); + + while (d->msg_nr--) { + strbuf_release(&d->messages[d->msg_nr]->buf); + free(d->messages[d->msg_nr]); + } + free(d->messages); } static int list_each_note(const struct object_id *object_oid, - const struct object_id *note_oid, char *note_path, - void *cb_data) + const struct object_id *note_oid, + char *note_path UNUSED, + void *cb_data UNUSED) { printf("%s %s\n", oid_to_hex(note_oid), oid_to_hex(object_oid)); return 0; @@ -124,7 +154,7 @@ static void copy_obj_to_fd(int fd, const struct object_id *oid) { unsigned long size; enum object_type type; - char *buf = read_object_file(oid, &type, &size); + char *buf = repo_read_object_file(the_repository, oid, &type, &size); if (buf) { if (size) write_or_die(fd, buf, size); @@ -151,7 +181,7 @@ static void write_commented_object(int fd, const struct object_id *object) if (strbuf_read(&buf, show.out, 0) < 0) die_errno(_("could not read 'show' output")); - strbuf_add_commented_lines(&cbuf, buf.buf, buf.len); + strbuf_add_commented_lines(&cbuf, buf.buf, buf.len, comment_line_char); write_or_die(fd, cbuf.buf, cbuf.len); strbuf_release(&cbuf); @@ -179,9 +209,10 @@ static void prepare_note_data(const struct object_id *object, struct note_data * copy_obj_to_fd(fd, old_note); strbuf_addch(&buf, '\n'); - strbuf_add_commented_lines(&buf, "\n", strlen("\n")); - strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template))); - strbuf_add_commented_lines(&buf, "\n", strlen("\n")); + strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_char); + strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template)), + comment_line_char); + strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_char); write_or_die(fd, buf.buf, buf.len); write_commented_object(fd, object); @@ -193,7 +224,8 @@ static void prepare_note_data(const struct object_id *object, struct note_data * if (launch_editor(d->edit_path, &d->buf, NULL)) { die(_("please supply the note contents using either -m or -F option")); } - strbuf_stripspace(&d->buf, 1); + if (d->stripspace) + strbuf_stripspace(&d->buf, comment_line_char); } } @@ -209,66 +241,102 @@ static void write_note_data(struct note_data *d, struct object_id *oid) } } +static void append_separator(struct strbuf *message) +{ + size_t sep_len = 0; + + if (!separator) + return; + else if ((sep_len = strlen(separator)) && separator[sep_len - 1] == '\n') + strbuf_addstr(message, separator); + else + strbuf_addf(message, "%s%s", separator, "\n"); +} + +static void concat_messages(struct note_data *d) +{ + struct strbuf msg = STRBUF_INIT; + size_t i; + + for (i = 0; i < d->msg_nr ; i++) { + if (d->buf.len) + append_separator(&d->buf); + strbuf_add(&msg, d->messages[i]->buf.buf, d->messages[i]->buf.len); + strbuf_addbuf(&d->buf, &msg); + if ((d->stripspace == UNSPECIFIED && + d->messages[i]->stripspace == STRIPSPACE) || + d->stripspace == STRIPSPACE) + strbuf_stripspace(&d->buf, 0); + strbuf_reset(&msg); + } + strbuf_release(&msg); +} + static int parse_msg_arg(const struct option *opt, const char *arg, int unset) { struct note_data *d = opt->value; + struct note_msg *msg = xmalloc(sizeof(*msg)); BUG_ON_OPT_NEG(unset); - strbuf_grow(&d->buf, strlen(arg) + 2); - if (d->buf.len) - strbuf_addch(&d->buf, '\n'); - strbuf_addstr(&d->buf, arg); - strbuf_stripspace(&d->buf, 0); - - d->given = 1; + strbuf_init(&msg->buf, strlen(arg)); + strbuf_addstr(&msg->buf, arg); + ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc); + d->messages[d->msg_nr - 1] = msg; + msg->stripspace = STRIPSPACE; return 0; } static int parse_file_arg(const struct option *opt, const char *arg, int unset) { struct note_data *d = opt->value; + struct note_msg *msg = xmalloc(sizeof(*msg)); BUG_ON_OPT_NEG(unset); - if (d->buf.len) - strbuf_addch(&d->buf, '\n'); + strbuf_init(&msg->buf , 0); if (!strcmp(arg, "-")) { - if (strbuf_read(&d->buf, 0, 1024) < 0) + if (strbuf_read(&msg->buf, 0, 1024) < 0) die_errno(_("cannot read '%s'"), arg); - } else if (strbuf_read_file(&d->buf, arg, 1024) < 0) + } else if (strbuf_read_file(&msg->buf, arg, 1024) < 0) die_errno(_("could not open or read '%s'"), arg); - strbuf_stripspace(&d->buf, 0); - d->given = 1; + ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc); + d->messages[d->msg_nr - 1] = msg; + msg->stripspace = STRIPSPACE; return 0; } static int parse_reuse_arg(const struct option *opt, const char *arg, int unset) { struct note_data *d = opt->value; - char *buf; + struct note_msg *msg = xmalloc(sizeof(*msg)); + char *value; struct object_id object; enum object_type type; unsigned long len; BUG_ON_OPT_NEG(unset); - if (d->buf.len) - strbuf_addch(&d->buf, '\n'); - - if (get_oid(arg, &object)) + strbuf_init(&msg->buf, 0); + if (repo_get_oid(the_repository, arg, &object)) die(_("failed to resolve '%s' as a valid ref."), arg); - if (!(buf = read_object_file(&object, &type, &len))) + if (!(value = repo_read_object_file(the_repository, &object, &type, &len))) die(_("failed to read object '%s'."), arg); if (type != OBJ_BLOB) { - free(buf); + strbuf_release(&msg->buf); + free(value); + free(msg); die(_("cannot read note data from non-blob object '%s'."), arg); } - strbuf_add(&d->buf, buf, len); - free(buf); - d->given = 1; + strbuf_add(&msg->buf, value, len); + free(value); + + msg->buf.len = len; + ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc); + d->messages[d->msg_nr - 1] = msg; + msg->stripspace = NO_STRIPSPACE; return 0; } @@ -280,6 +348,16 @@ static int parse_reedit_arg(const struct option *opt, const char *arg, int unset return parse_reuse_arg(opt, arg, unset); } +static int parse_separator_arg(const struct option *opt, const char *arg, + int unset) +{ + if (unset) + *(const char **)opt->value = NULL; + else + *(const char **)opt->value = arg ? arg : "\n"; + return 0; +} + static int notes_copy_from_stdin(int force, const char *rewrite_cmd) { struct strbuf buf = STRBUF_INIT; @@ -307,9 +385,9 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd) die(_("malformed input line: '%s'."), buf.buf); strbuf_rtrim(split[0]); strbuf_rtrim(split[1]); - if (get_oid(split[0]->buf, &from_obj)) + if (repo_get_oid(the_repository, split[0]->buf, &from_obj)) die(_("failed to resolve '%s' as a valid ref."), split[0]->buf); - if (get_oid(split[1]->buf, &to_obj)) + if (repo_get_oid(the_repository, split[1]->buf, &to_obj)) die(_("failed to resolve '%s' as a valid ref."), split[1]->buf); if (rewrite_cmd) @@ -377,7 +455,7 @@ static int list(int argc, const char **argv, const char *prefix) t = init_notes_check("list", 0); if (argc) { - if (get_oid(argv[0], &object)) + if (repo_get_oid(the_repository, argv[0], &object)) die(_("failed to resolve '%s' as a valid ref."), argv[0]); note = get_note(t, &object); if (note) { @@ -402,7 +480,8 @@ static int add(int argc, const char **argv, const char *prefix) struct notes_tree *t; struct object_id object, new_note; const struct object_id *note; - struct note_data d = { 0, 0, NULL, STRBUF_INIT }; + struct note_data d = { .buf = STRBUF_INIT, .stripspace = UNSPECIFIED }; + struct option options[] = { OPT_CALLBACK_F('m', "message", &d, N_("message"), N_("note contents as a string"), PARSE_OPT_NONEG, @@ -419,6 +498,12 @@ static int add(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "allow-empty", &allow_empty, N_("allow storing empty note")), OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE), + OPT_CALLBACK_F(0, "separator", &separator, + N_("<paragraph-break>"), + N_("insert <paragraph-break> between paragraphs"), + PARSE_OPT_OPTARG, parse_separator_arg), + OPT_BOOL(0, "stripspace", &d.stripspace, + N_("remove unnecessary whitespace")), OPT_END() }; @@ -430,9 +515,13 @@ static int add(int argc, const char **argv, const char *prefix) usage_with_options(git_notes_add_usage, options); } + if (d.msg_nr) + concat_messages(&d); + d.given = !!d.buf.len; + object_ref = argc > 1 ? argv[1] : "HEAD"; - if (get_oid(object_ref, &object)) + if (repo_get_oid(the_repository, object_ref, &object)) die(_("failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check("add", NOTES_INIT_WRITABLE); @@ -520,12 +609,12 @@ static int copy(int argc, const char **argv, const char *prefix) usage_with_options(git_notes_copy_usage, options); } - if (get_oid(argv[0], &from_obj)) + if (repo_get_oid(the_repository, argv[0], &from_obj)) die(_("failed to resolve '%s' as a valid ref."), argv[0]); object_ref = 1 < argc ? argv[1] : "HEAD"; - if (get_oid(object_ref, &object)) + if (repo_get_oid(the_repository, object_ref, &object)) die(_("failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check("copy", NOTES_INIT_WRITABLE); @@ -568,7 +657,7 @@ static int append_edit(int argc, const char **argv, const char *prefix) const struct object_id *note; char *logmsg; const char * const *usage; - struct note_data d = { 0, 0, NULL, STRBUF_INIT }; + struct note_data d = { .buf = STRBUF_INIT, .stripspace = UNSPECIFIED }; struct option options[] = { OPT_CALLBACK_F('m', "message", &d, N_("message"), N_("note contents as a string"), PARSE_OPT_NONEG, @@ -584,6 +673,12 @@ static int append_edit(int argc, const char **argv, const char *prefix) parse_reuse_arg), OPT_BOOL(0, "allow-empty", &allow_empty, N_("allow storing empty note")), + OPT_CALLBACK_F(0, "separator", &separator, + N_("<paragraph-break>"), + N_("insert <paragraph-break> between paragraphs"), + PARSE_OPT_OPTARG, parse_separator_arg), + OPT_BOOL(0, "stripspace", &d.stripspace, + N_("remove unnecessary whitespace")), OPT_END() }; int edit = !strcmp(argv[0], "edit"); @@ -597,6 +692,10 @@ static int append_edit(int argc, const char **argv, const char *prefix) usage_with_options(usage, options); } + if (d.msg_nr) + concat_messages(&d); + d.given = !!d.buf.len; + if (d.given && edit) fprintf(stderr, _("The -m/-F/-c/-C options have been deprecated " "for the 'edit' subcommand.\n" @@ -604,7 +703,7 @@ static int append_edit(int argc, const char **argv, const char *prefix) object_ref = 1 < argc ? argv[1] : "HEAD"; - if (get_oid(object_ref, &object)) + if (repo_get_oid(the_repository, object_ref, &object)) die(_("failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check(argv[0], NOTES_INIT_WRITABLE); @@ -616,14 +715,17 @@ static int append_edit(int argc, const char **argv, const char *prefix) /* Append buf to previous note contents */ unsigned long size; enum object_type type; - char *prev_buf = read_object_file(note, &type, &size); + struct strbuf buf = STRBUF_INIT; + char *prev_buf = repo_read_object_file(the_repository, note, &type, &size); - strbuf_grow(&d.buf, size + 1); - if (d.buf.len && prev_buf && size) - strbuf_insertstr(&d.buf, 0, "\n"); if (prev_buf && size) - strbuf_insert(&d.buf, 0, prev_buf, size); + strbuf_add(&buf, prev_buf, size); + if (d.buf.len && prev_buf && size) + append_separator(&buf); + strbuf_insert(&d.buf, 0, buf.buf, buf.len); + free(prev_buf); + strbuf_release(&buf); } if (d.buf.len || allow_empty) { @@ -666,7 +768,7 @@ static int show(int argc, const char **argv, const char *prefix) object_ref = argc ? argv[0] : "HEAD"; - if (get_oid(object_ref, &object)) + if (repo_get_oid(the_repository, object_ref, &object)) die(_("failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check("show", 0); @@ -716,11 +818,11 @@ static int merge_commit(struct notes_merge_options *o) * and target notes ref from .git/NOTES_MERGE_REF. */ - if (get_oid("NOTES_MERGE_PARTIAL", &oid)) + if (repo_get_oid(the_repository, "NOTES_MERGE_PARTIAL", &oid)) die(_("failed to read ref NOTES_MERGE_PARTIAL")); else if (!(partial = lookup_commit_reference(the_repository, &oid))) die(_("could not find commit from NOTES_MERGE_PARTIAL.")); - else if (parse_commit(partial)) + else if (repo_parse_commit(the_repository, partial)) die(_("could not parse commit from NOTES_MERGE_PARTIAL.")); if (partial->parents) @@ -741,7 +843,8 @@ static int merge_commit(struct notes_merge_options *o) /* Reuse existing commit message in reflog message */ memset(&pretty_ctx, 0, sizeof(pretty_ctx)); - format_commit_message(partial, "%s", &msg, &pretty_ctx); + repo_format_commit_message(the_repository, partial, "%s", &msg, + &pretty_ctx); strbuf_trim(&msg); strbuf_insertstr(&msg, 0, "notes: "); update_ref(msg.buf, o->local_ref, &oid, @@ -895,7 +998,7 @@ static int remove_one_note(struct notes_tree *t, const char *name, unsigned flag { int status; struct object_id oid; - if (get_oid(name, &oid)) + if (repo_get_oid(the_repository, name, &oid)) return error(_("Failed to resolve '%s' as a valid ref."), name); status = remove_note(t, oid.hash); if (status) diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 74a167a180..62c540b4db 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -1,5 +1,7 @@ #include "builtin.h" -#include "cache.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "repository.h" #include "config.h" #include "attr.h" @@ -30,13 +32,16 @@ #include "strvec.h" #include "list.h" #include "packfile.h" -#include "object-store.h" +#include "object-file.h" +#include "object-store-ll.h" +#include "replace-object.h" #include "dir.h" #include "midx.h" #include "trace2.h" #include "shallow.h" #include "promisor-remote.h" #include "pack-mtimes.h" +#include "parse-options.h" /* * Objects we are going to pack are collected in the `to_pack` structure. @@ -288,11 +293,13 @@ static void *get_delta(struct object_entry *entry) void *buf, *base_buf, *delta_buf; enum object_type type; - buf = read_object_file(&entry->idx.oid, &type, &size); + buf = repo_read_object_file(the_repository, &entry->idx.oid, &type, + &size); if (!buf) die(_("unable to read %s"), oid_to_hex(&entry->idx.oid)); - base_buf = read_object_file(&DELTA(entry)->idx.oid, &type, - &base_size); + base_buf = repo_read_object_file(the_repository, + &DELTA(entry)->idx.oid, &type, + &base_size); if (!base_buf) die("unable to read %s", oid_to_hex(&DELTA(entry)->idx.oid)); @@ -454,7 +461,9 @@ static unsigned long write_no_reuse_object(struct hashfile *f, struct object_ent &size, NULL)) != NULL) buf = NULL; else { - buf = read_object_file(&entry->idx.oid, &type, &size); + buf = repo_read_object_file(the_repository, + &entry->idx.oid, &type, + &size); if (!buf) die(_("unable to read %s"), oid_to_hex(&entry->idx.oid)); @@ -1320,7 +1329,7 @@ static int no_try_delta(const char *path) if (!check) check = attr_check_initl("delta", NULL); - git_check_attr(the_repository->index, NULL, path, check); + git_check_attr(the_repository->index, path, check); if (ATTR_FALSE(check->items[0].value)) return 1; return 0; @@ -1590,7 +1599,7 @@ static int add_object_entry(const struct object_id *oid, enum object_type type, static int add_object_entry_from_bitmap(const struct object_id *oid, enum object_type type, - int flags, uint32_t name_hash, + int flags UNUSED, uint32_t name_hash, struct packed_git *pack, off_t offset) { display_progress(progress_state, ++nr_seen); @@ -1665,7 +1674,7 @@ static struct pbase_tree_cache *pbase_tree_get(const struct object_id *oid) /* Did not find one. Either we got a bogus request or * we need to read and perhaps cache. */ - data = read_object_file(oid, &type, &size); + data = repo_read_object_file(the_repository, oid, &type, &size); if (!data) return NULL; if (type != OBJ_TREE) { @@ -2074,7 +2083,7 @@ static void check_object(struct object_entry *entry, uint32_t object_index) if (oid_object_info_extended(the_repository, &entry->idx.oid, &oi, OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_LOOKUP_REPLACE) < 0) { - if (has_promisor_remote()) { + if (repo_has_promisor_remote(the_repository)) { prefetch_to_pack(object_index); if (oid_object_info_extended(the_repository, &entry->idx.oid, &oi, OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_LOOKUP_REPLACE) < 0) @@ -2525,7 +2534,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, /* Load data if not already done */ if (!trg->data) { packing_data_lock(&to_pack); - trg->data = read_object_file(&trg_entry->idx.oid, &type, &sz); + trg->data = repo_read_object_file(the_repository, + &trg_entry->idx.oid, &type, + &sz); packing_data_unlock(&to_pack); if (!trg->data) die(_("object %s cannot be read"), @@ -2538,7 +2549,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, } if (!src->data) { packing_data_lock(&to_pack); - src->data = read_object_file(&src_entry->idx.oid, &type, &sz); + src->data = repo_read_object_file(the_repository, + &src_entry->idx.oid, &type, + &sz); packing_data_unlock(&to_pack); if (!src->data) { if (src_entry->preferred_base) { @@ -3120,26 +3133,27 @@ static void prepare_pack(int window, int depth) free(delta_list); } -static int git_pack_config(const char *k, const char *v, void *cb) +static int git_pack_config(const char *k, const char *v, + const struct config_context *ctx, void *cb) { if (!strcmp(k, "pack.window")) { - window = git_config_int(k, v); + window = git_config_int(k, v, ctx->kvi); return 0; } if (!strcmp(k, "pack.windowmemory")) { - window_memory_limit = git_config_ulong(k, v); + window_memory_limit = git_config_ulong(k, v, ctx->kvi); return 0; } if (!strcmp(k, "pack.depth")) { - depth = git_config_int(k, v); + depth = git_config_int(k, v, ctx->kvi); return 0; } if (!strcmp(k, "pack.deltacachesize")) { - max_delta_cache_size = git_config_int(k, v); + max_delta_cache_size = git_config_int(k, v, ctx->kvi); return 0; } if (!strcmp(k, "pack.deltacachelimit")) { - cache_max_small_delta_size = git_config_int(k, v); + cache_max_small_delta_size = git_config_int(k, v, ctx->kvi); return 0; } if (!strcmp(k, "pack.writebitmaphashcache")) { @@ -3165,7 +3179,7 @@ static int git_pack_config(const char *k, const char *v, void *cb) return 0; } if (!strcmp(k, "pack.threads")) { - delta_search_threads = git_config_int(k, v); + delta_search_threads = git_config_int(k, v, ctx->kvi); if (delta_search_threads < 0) die(_("invalid number of threads specified (%d)"), delta_search_threads); @@ -3176,7 +3190,7 @@ static int git_pack_config(const char *k, const char *v, void *cb) return 0; } if (!strcmp(k, "pack.indexversion")) { - pack_idx_opts.version = git_config_int(k, v); + pack_idx_opts.version = git_config_int(k, v, ctx->kvi); if (pack_idx_opts.version > 2) die(_("bad pack.indexVersion=%"PRIu32), pack_idx_opts.version); @@ -3190,7 +3204,7 @@ static int git_pack_config(const char *k, const char *v, void *cb) return 0; } if (!strcmp(k, "uploadpack.blobpackfileuri")) { - struct configured_exclusion *ex = xmalloc(sizeof(*ex)); + struct configured_exclusion *ex; const char *oid_end, *pack_end; /* * Stores the pack hash. This is not a true object ID, but is @@ -3198,6 +3212,10 @@ static int git_pack_config(const char *k, const char *v, void *cb) */ struct object_id pack_hash; + if (!v) + return config_error_nonbool(k); + + ex = xmalloc(sizeof(*ex)); if (parse_oid_hex(v, &ex->e.oid, &oid_end) || *oid_end != ' ' || parse_oid_hex(oid_end + 1, &pack_hash, &pack_end) || @@ -3212,7 +3230,7 @@ static int git_pack_config(const char *k, const char *v, void *cb) ex->uri = xstrdup(pack_end + 1); oidmap_put(&configured_exclusions, ex); } - return git_default_config(k, v, cb); + return git_default_config(k, v, ctx, cb); } /* Counters for trace2 output when in --stdin-packs mode. */ @@ -3260,13 +3278,14 @@ static int add_object_entry_from_pack(const struct object_id *oid, return 0; } -static void show_commit_pack_hint(struct commit *commit, void *_data) +static void show_commit_pack_hint(struct commit *commit UNUSED, + void *data UNUSED) { /* nothing to do; commits don't have a namehash */ } static void show_object_pack_hint(struct object *object, const char *name, - void *_data) + void *data UNUSED) { struct object_entry *oe = packlist_find(&to_pack, &object->oid); if (!oe) @@ -3344,16 +3363,16 @@ static void read_packs_list_from_stdin(void) } string_list_sort(&include_packs); + string_list_remove_duplicates(&include_packs, 0); string_list_sort(&exclude_packs); + string_list_remove_duplicates(&exclude_packs, 0); for (p = get_all_packs(the_repository); p; p = p->next) { const char *pack_name = pack_basename(p); - item = string_list_lookup(&include_packs, pack_name); - if (!item) - item = string_list_lookup(&exclude_packs, pack_name); - - if (item) + if ((item = string_list_lookup(&include_packs, pack_name))) + item->util = p; + if ((item = string_list_lookup(&exclude_packs, pack_name))) item->util = p; } @@ -3464,7 +3483,7 @@ static void add_cruft_object_entry(const struct object_id *oid, enum object_type return; } -static void show_cruft_object(struct object *obj, const char *name, void *data) +static void show_cruft_object(struct object *obj, const char *name, void *data UNUSED) { /* * if we did not record it earlier, it's at least as old as our @@ -3484,7 +3503,7 @@ static void show_cruft_commit(struct commit *commit, void *data) show_cruft_object((struct object*)commit, NULL, data); } -static int cruft_include_check_obj(struct object *obj, void *data) +static int cruft_include_check_obj(struct object *obj, void *data UNUSED) { return !has_object_kept_pack(&obj->oid, IN_CORE_KEEP_PACKS); } @@ -3588,7 +3607,6 @@ static void read_cruft_objects(void) string_list_append(&discard_packs, buf.buf + 1); else string_list_append(&fresh_packs, buf.buf); - strbuf_reset(&buf); } string_list_sort(&discard_packs); @@ -3663,7 +3681,7 @@ static void read_object_list_from_stdin(void) } } -static void show_commit(struct commit *commit, void *data) +static void show_commit(struct commit *commit, void *data UNUSED) { add_object_entry(&commit->object.oid, OBJ_COMMIT, NULL, 0); @@ -3674,7 +3692,8 @@ static void show_commit(struct commit *commit, void *data) propagate_island_marks(commit); } -static void show_object(struct object *obj, const char *name, void *data) +static void show_object(struct object *obj, const char *name, + void *data UNUSED) { add_preferred_base_object(name); add_object_entry(&obj->oid, obj->type, name, 0); @@ -3723,7 +3742,7 @@ static void show_object__ma_allow_promisor(struct object *obj, const char *name, show_object(obj, name, data); } -static int option_parse_missing_action(const struct option *opt, +static int option_parse_missing_action(const struct option *opt UNUSED, const char *arg, int unset) { assert(arg); @@ -3761,7 +3780,7 @@ static void show_edge(struct commit *commit) static int add_object_in_unpacked_pack(const struct object_id *oid, struct packed_git *pack, uint32_t pos, - void *_data) + void *data UNUSED) { if (cruft) { off_t offset; @@ -3795,7 +3814,7 @@ static void add_objects_in_unpacked_packs(void) } static int add_loose_object(const struct object_id *oid, const char *path, - void *data) + void *data UNUSED) { enum object_type type = oid_object_info(the_repository, oid, NULL); @@ -3946,13 +3965,13 @@ static int get_object_list_from_bitmap(struct rev_info *revs) } static void record_recent_object(struct object *obj, - const char *name, - void *data) + const char *name UNUSED, + void *data UNUSED) { oid_array_append(&recent_objects, &obj->oid); } -static void record_recent_commit(struct commit *commit, void *data) +static void record_recent_commit(struct commit *commit, void *data UNUSED) { oid_array_append(&recent_objects, &commit->object.oid); } @@ -4101,25 +4120,40 @@ static void add_extra_kept_packs(const struct string_list *names) } } +static int option_parse_quiet(const struct option *opt, const char *arg, + int unset) +{ + int *val = opt->value; + + BUG_ON_OPT_ARG(arg); + + if (!unset) + *val = 0; + else if (!*val) + *val = 1; + return 0; +} + static int option_parse_index_version(const struct option *opt, const char *arg, int unset) { + struct pack_idx_option *popts = opt->value; char *c; const char *val = arg; BUG_ON_OPT_NEG(unset); - pack_idx_opts.version = strtoul(val, &c, 10); - if (pack_idx_opts.version > 2) + popts->version = strtoul(val, &c, 10); + if (popts->version > 2) die(_("unsupported index version %s"), val); if (*c == ',' && c[1]) - pack_idx_opts.off32_limit = strtoul(c+1, &c, 0); - if (*c || pack_idx_opts.off32_limit & 0x80000000) + popts->off32_limit = strtoul(c+1, &c, 0); + if (*c || popts->off32_limit & 0x80000000) die(_("bad index version '%s'"), val); return 0; } -static int option_parse_unpack_unreachable(const struct option *opt, +static int option_parse_unpack_unreachable(const struct option *opt UNUSED, const char *arg, int unset) { if (unset) { @@ -4134,7 +4168,7 @@ static int option_parse_unpack_unreachable(const struct option *opt, return 0; } -static int option_parse_cruft_expiration(const struct option *opt, +static int option_parse_cruft_expiration(const struct option *opt UNUSED, const char *arg, int unset) { if (unset) { @@ -4162,8 +4196,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) LIST_OBJECTS_FILTER_INIT; struct option pack_objects_options[] = { - OPT_SET_INT('q', "quiet", &progress, - N_("do not show progress meter"), 0), + OPT_CALLBACK_F('q', "quiet", &progress, NULL, + N_("do not show progress meter"), + PARSE_OPT_NOARG, option_parse_quiet), OPT_SET_INT(0, "progress", &progress, N_("show progress meter"), 1), OPT_SET_INT(0, "all-progress", &progress, @@ -4171,7 +4206,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "all-progress-implied", &all_progress_implied, N_("similar to --all-progress when progress meter is shown")), - OPT_CALLBACK_F(0, "index-version", NULL, N_("<version>[,<offset>]"), + OPT_CALLBACK_F(0, "index-version", &pack_idx_opts, N_("<version>[,<offset>]"), N_("write the pack index file in the specified idx format version"), PARSE_OPT_NONEG, option_parse_index_version), OPT_MAGNITUDE(0, "max-pack-size", &pack_size_limit, @@ -4239,8 +4274,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) N_("ignore this pack")), OPT_INTEGER(0, "compression", &pack_compression_level, N_("pack compression level")), - OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents, - N_("do not hide commits by grafts"), 0), + OPT_BOOL(0, "keep-true-parents", &grafts_keep_true_parents, + N_("do not hide commits by grafts")), OPT_BOOL(0, "use-bitmap-index", &use_bitmap_index, N_("use a bitmap index if available to speed up counting objects")), OPT_SET_INT(0, "write-bitmap-index", &write_bitmap_index, @@ -4267,7 +4302,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (DFS_NUM_STATES > (1 << OE_DFS_STATE_BITS)) BUG("too many dfs states, increase OE_DFS_STATE_BITS"); - read_replace_refs = 0; + disable_replace_refs(); sparse = git_env_bool("GIT_TEST_PACK_SPARSE", -1); if (the_repository->gitdir) { @@ -4277,9 +4312,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) } reset_pack_idx_option(&pack_idx_opts); + pack_idx_opts.flags |= WRITE_REV; git_config(git_pack_config, NULL); - if (git_env_bool(GIT_TEST_WRITE_REV_INDEX, 0)) - pack_idx_opts.flags |= WRITE_REV; + if (git_env_bool(GIT_TEST_NO_WRITE_REV_INDEX, 0)) + pack_idx_opts.flags &= ~WRITE_REV; progress = isatty(2); argc = parse_options(argc, argv, prefix, pack_objects_options, @@ -4353,7 +4389,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (!HAVE_THREADS && delta_search_threads != 1) warning(_("no threads support, ignoring --threads")); - if (!pack_to_stdout && !pack_size_limit && !cruft) + if (!pack_to_stdout && !pack_size_limit) pack_size_limit = pack_size_limit_cfg; if (pack_to_stdout && pack_size_limit) die(_("--max-pack-size cannot be used to build a pack for transfer")); @@ -4370,12 +4406,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (!rev_list_all || !rev_list_reflog || !rev_list_index) unpack_unreachable_expiration = 0; - if (filter_options.choice) { - if (!pack_to_stdout) - die(_("cannot use --filter without --stdout")); - if (stdin_packs) - die(_("cannot use --filter with --stdin-packs")); - } + if (stdin_packs && filter_options.choice) + die(_("cannot use --filter with --stdin-packs")); if (stdin_packs && use_internal_rev_list) die(_("cannot use internal rev list with --stdin-packs")); @@ -4385,8 +4417,6 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) die(_("cannot use internal rev list with --cruft")); if (stdin_packs) die(_("cannot use --stdin-packs with --cruft")); - if (pack_size_limit) - die(_("cannot use --max-pack-size with --cruft")); } /* diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c index ecd49ca268..4c735ba069 100644 --- a/builtin/pack-redundant.c +++ b/builtin/pack-redundant.c @@ -7,9 +7,11 @@ */ #include "builtin.h" +#include "gettext.h" +#include "hex.h" #include "repository.h" #include "packfile.h" -#include "object-store.h" +#include "object-store-ll.h" #define BLKSIZE 512 @@ -557,7 +559,7 @@ static void load_all(void) } } -int cmd_pack_redundant(int argc, const char **argv, const char *prefix) +int cmd_pack_redundant(int argc, const char **argv, const char *prefix UNUSED) { int i; int i_still_use_this = 0; @@ -603,6 +605,7 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix) "option, '--i-still-use-this', on the command line\n" "and let us know you still use it by sending an e-mail\n" "to <git@vger.kernel.org>. Thanks.\n"), stderr); + die(_("refusing to run without --i-still-use-this")); } if (load_all_packs) diff --git a/builtin/pack-refs.c b/builtin/pack-refs.c index 27c2ca06ac..bcf383cac9 100644 --- a/builtin/pack-refs.c +++ b/builtin/pack-refs.c @@ -1,24 +1,48 @@ #include "builtin.h" #include "config.h" +#include "gettext.h" #include "parse-options.h" #include "refs.h" #include "repository.h" +#include "revision.h" static char const * const pack_refs_usage[] = { - N_("git pack-refs [--all] [--no-prune]"), + N_("git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude <pattern>]"), NULL }; int cmd_pack_refs(int argc, const char **argv, const char *prefix) { unsigned int flags = PACK_REFS_PRUNE; + static struct ref_exclusions excludes = REF_EXCLUSIONS_INIT; + static struct string_list included_refs = STRING_LIST_INIT_NODUP; + struct pack_refs_opts pack_refs_opts = { .exclusions = &excludes, + .includes = &included_refs, + .flags = flags }; + static struct string_list option_excluded_refs = STRING_LIST_INIT_NODUP; + struct string_list_item *item; + struct option opts[] = { - OPT_BIT(0, "all", &flags, N_("pack everything"), PACK_REFS_ALL), - OPT_BIT(0, "prune", &flags, N_("prune loose refs (default)"), PACK_REFS_PRUNE), + OPT_BIT(0, "all", &pack_refs_opts.flags, N_("pack everything"), PACK_REFS_ALL), + OPT_BIT(0, "prune", &pack_refs_opts.flags, N_("prune loose refs (default)"), PACK_REFS_PRUNE), + 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(), }; git_config(git_default_config, NULL); if (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0)) usage_with_options(pack_refs_usage, opts); - return refs_pack_refs(get_main_ref_store(the_repository), flags); + + for_each_string_list_item(item, &option_excluded_refs) + add_ref_exclusion(pack_refs_opts.exclusions, item->string); + + if (pack_refs_opts.flags & PACK_REFS_ALL) + string_list_append(pack_refs_opts.includes, "*"); + + if (!pack_refs_opts.includes->nr) + string_list_append(pack_refs_opts.includes, "refs/tags/*"); + + return refs_pack_refs(get_main_ref_store(the_repository), &pack_refs_opts); } diff --git a/builtin/patch-id.c b/builtin/patch-id.c index f840fbf1c7..3894d2b970 100644 --- a/builtin/patch-id.c +++ b/builtin/patch-id.c @@ -1,7 +1,9 @@ -#include "cache.h" #include "builtin.h" #include "config.h" #include "diff.h" +#include "gettext.h" +#include "hash.h" +#include "hex.h" #include "parse-options.h" static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result) @@ -194,7 +196,8 @@ struct patch_id_opts { int verbatim; }; -static int git_patch_id_config(const char *var, const char *value, void *cb) +static int git_patch_id_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { struct patch_id_opts *opts = cb; @@ -207,7 +210,7 @@ static int git_patch_id_config(const char *var, const char *value, void *cb) return 0; } - return git_default_config(var, value, cb); + return git_default_config(var, value, ctx, cb); } int cmd_patch_id(int argc, const char **argv, const char *prefix) diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c index da3273a268..ca3578e158 100644 --- a/builtin/prune-packed.c +++ b/builtin/prune-packed.c @@ -1,4 +1,5 @@ #include "builtin.h" +#include "gettext.h" #include "parse-options.h" #include "prune-packed.h" diff --git a/builtin/prune.c b/builtin/prune.c index 2719220108..57fe31467f 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -1,13 +1,20 @@ -#include "cache.h" +#include "builtin.h" #include "commit.h" #include "diff.h" +#include "dir.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "revision.h" -#include "builtin.h" #include "reachable.h" #include "parse-options.h" +#include "path.h" #include "progress.h" #include "prune-packed.h" -#include "object-store.h" +#include "replace-object.h" +#include "object-file.h" +#include "object-name.h" +#include "object-store-ll.h" #include "shallow.h" static const char * const prune_usage[] = { @@ -98,7 +105,8 @@ static int prune_object(const struct object_id *oid, const char *fullpath, return 0; } -static int prune_cruft(const char *basename, const char *path, void *data) +static int prune_cruft(const char *basename, const char *path, + void *data UNUSED) { if (starts_with(basename, "tmp_obj_")) prune_tmp_file(path); @@ -107,7 +115,8 @@ static int prune_cruft(const char *basename, const char *path, void *data) return 0; } -static int prune_subdir(unsigned int nr, const char *path, void *data) +static int prune_subdir(unsigned int nr UNUSED, const char *path, + void *data UNUSED) { if (!show_only) rmdir(path); @@ -156,7 +165,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix) expire = TIME_MAX; save_commit_buffer = 0; - read_replace_refs = 0; + disable_replace_refs(); repo_init_revisions(the_repository, &revs, prefix); argc = parse_options(argc, argv, prefix, options, prune_usage, 0); @@ -168,7 +177,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix) struct object_id oid; const char *name = *argv++; - if (!get_oid(name, &oid)) { + if (!repo_get_oid(the_repository, name, &oid)) { struct object *object = parse_object_or_die(&oid, name); add_pending_object(&revs, object, ""); diff --git a/builtin/pull.c b/builtin/pull.c index 1ab4de0005..be2b2c9ebc 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -6,15 +6,21 @@ * Fetch one or more remote refs and merge it/them into the current HEAD. */ #define USE_THE_INDEX_VARIABLE -#include "cache.h" -#include "config.h" #include "builtin.h" +#include "advice.h" +#include "config.h" +#include "gettext.h" +#include "hex.h" +#include "merge.h" +#include "object-name.h" #include "parse-options.h" #include "exec-cmd.h" #include "run-command.h" #include "oid-array.h" #include "remote.h" #include "dir.h" +#include "path.h" +#include "read-cache-ll.h" #include "rebase.h" #include "refs.h" #include "refspec.h" @@ -357,10 +363,9 @@ static enum rebase_type config_get_rebase(int *rebase_unspecified) /** * Read config variables. */ -static int git_pull_config(const char *var, const char *value, void *cb) +static int git_pull_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { - int status; - if (!strcmp(var, "rebase.autostash")) { config_autostash = git_config_bool(var, value); return 0; @@ -372,11 +377,7 @@ static int git_pull_config(const char *var, const char *value, void *cb) check_trust_level = 0; } - status = git_gpg_config(var, value, cb); - if (status) - return status; - - return git_default_config(var, value, cb); + return git_default_config(var, value, ctx, cb); } /** @@ -1036,7 +1037,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix) if (file_exists(git_path_merge_head(the_repository))) die_conclude_merge(); - if (get_oid("HEAD", &orig_head)) + if (repo_get_oid(the_repository, "HEAD", &orig_head)) oidclr(&orig_head); if (opt_rebase) { @@ -1049,7 +1050,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix) if (!opt_autostash) require_clean_work_tree(the_repository, N_("pull with rebase"), - _("please commit or stash them."), 1, 0); + _("Please commit or stash them."), 1, 0); if (get_rebase_fork_point(&rebase_fork_point, repo, *refspecs)) oidclr(&rebase_fork_point); @@ -1061,7 +1062,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix) if (opt_dry_run) return 0; - if (get_oid("HEAD", &curr_head)) + if (repo_get_oid(the_repository, "HEAD", &curr_head)) oidclr(&curr_head); if (!is_null_oid(&orig_head) && !is_null_oid(&curr_head) && diff --git a/builtin/push.c b/builtin/push.c index 8f7d326ab3..6ec6165725 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -1,19 +1,24 @@ /* * "git push" */ -#include "cache.h" +#include "builtin.h" +#include "advice.h" #include "branch.h" #include "config.h" +#include "environment.h" +#include "gettext.h" #include "refs.h" #include "refspec.h" #include "run-command.h" -#include "builtin.h" #include "remote.h" #include "transport.h" #include "parse-options.h" +#include "pkt-line.h" +#include "repository.h" #include "submodule.h" #include "submodule-config.h" #include "send-pack.h" +#include "trace2.h" #include "color.h" static const char * const push_usage[] = { @@ -296,21 +301,21 @@ static void setup_default_push_refspecs(int *flags, struct remote *remote) static const char message_advice_pull_before_push[] = N_("Updates were rejected because the tip of your current branch is behind\n" - "its remote counterpart. Integrate the remote changes (e.g.\n" - "'git pull ...') before pushing again.\n" + "its remote counterpart. If you want to integrate the remote changes,\n" + "use 'git pull' before pushing again.\n" "See the 'Note about fast-forwards' in 'git push --help' for details."); static const char message_advice_checkout_pull_push[] = N_("Updates were rejected because a pushed branch tip is behind its remote\n" - "counterpart. Check out this branch and integrate the remote changes\n" - "(e.g. 'git pull ...') before pushing again.\n" + "counterpart. If you want to integrate the remote changes, use 'git pull'\n" + "before pushing again.\n" "See the 'Note about fast-forwards' in 'git push --help' for details."); static const char message_advice_ref_fetch_first[] = - N_("Updates were rejected because the remote contains work that you do\n" - "not have locally. This is usually caused by another repository pushing\n" - "to the same ref. You may want to first integrate the remote changes\n" - "(e.g., 'git pull ...') before pushing again.\n" + N_("Updates were rejected because the remote contains work that you do not\n" + "have locally. This is usually caused by another repository pushing to\n" + "the same ref. If you want to integrate the remote changes, use\n" + "'git pull' before pushing again.\n" "See the 'Note about fast-forwards' in 'git push --help' for details."); static const char message_advice_ref_already_exists[] = @@ -322,10 +327,10 @@ static const char message_advice_ref_needs_force[] = "without using the '--force' option.\n"); static const char message_advice_ref_needs_update[] = - N_("Updates were rejected because the tip of the remote-tracking\n" - "branch has been updated since the last checkout. You may want\n" - "to integrate those changes locally (e.g., 'git pull ...')\n" - "before forcing an update.\n"); + N_("Updates were rejected because the tip of the remote-tracking branch has\n" + "been updated since the last checkout. If you want to integrate the\n" + "remote changes, use 'git pull' before pushing again.\n" + "See the 'Note about fast-forwards' in 'git push --help' for details."); static void advise_pull_before_push(void) { @@ -504,15 +509,11 @@ static void set_push_cert_flags(int *flags, int v) } -static int git_push_config(const char *k, const char *v, void *cb) +static int git_push_config(const char *k, const char *v, + const struct config_context *ctx, void *cb) { const char *slot_name; int *flags = cb; - int status; - - status = git_gpg_config(k, v, NULL); - if (status) - return status; if (!strcmp(k, "push.followtags")) { if (git_config_bool(k, v)) @@ -525,26 +526,21 @@ static int git_push_config(const char *k, const char *v, void *cb) *flags |= TRANSPORT_PUSH_AUTO_UPSTREAM; return 0; } else if (!strcmp(k, "push.gpgsign")) { - const char *value; - if (!git_config_get_value("push.gpgsign", &value)) { - switch (git_parse_maybe_bool(value)) { - case 0: - set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_NEVER); - break; - case 1: - set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_ALWAYS); - break; - default: - if (value && !strcasecmp(value, "if-asked")) - set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_IF_ASKED); - else - return error(_("invalid value for '%s'"), k); - } + switch (git_parse_maybe_bool(v)) { + case 0: + set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_NEVER); + break; + case 1: + set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_ALWAYS); + break; + default: + if (!strcasecmp(v, "if-asked")) + set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_IF_ASKED); + else + return error(_("invalid value for '%s'"), k); } } else if (!strcmp(k, "push.recursesubmodules")) { - const char *value; - if (!git_config_get_value("push.recursesubmodules", &value)) - recurse_submodules = parse_push_recurse_submodules_arg(k, value); + recurse_submodules = parse_push_recurse_submodules_arg(k, v); } else if (!strcmp(k, "submodule.recurse")) { int val = git_config_bool(k, v) ? RECURSE_SUBMODULES_ON_DEMAND : RECURSE_SUBMODULES_OFF; @@ -576,7 +572,7 @@ static int git_push_config(const char *k, const char *v, void *cb) return 0; } - return git_default_config(k, v, NULL); + return git_default_config(k, v, ctx, NULL); } int cmd_push(int argc, const char **argv, const char *prefix) @@ -594,11 +590,12 @@ int cmd_push(int argc, const char **argv, const char *prefix) struct option options[] = { OPT__VERBOSITY(&verbosity), OPT_STRING( 0 , "repo", &repo, N_("repository"), N_("repository")), - OPT_BIT( 0 , "all", &flags, N_("push all refs"), TRANSPORT_PUSH_ALL), + OPT_BIT( 0 , "all", &flags, N_("push all branches"), TRANSPORT_PUSH_ALL), + OPT_ALIAS( 0 , "branches", "all"), OPT_BIT( 0 , "mirror", &flags, N_("mirror all refs"), (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)), OPT_BOOL('d', "delete", &deleterefs, N_("delete refs")), - OPT_BOOL( 0 , "tags", &tags, N_("push tags (can't be used with --all or --mirror)")), + OPT_BOOL( 0 , "tags", &tags, N_("push tags (can't be used with --all or --branches or --mirror)")), OPT_BIT('n' , "dry-run", &flags, N_("dry run"), TRANSPORT_PUSH_DRY_RUN), OPT_BIT( 0, "porcelain", &flags, N_("machine-readable output"), TRANSPORT_PUSH_PORCELAIN), OPT_BIT('f', "force", &flags, N_("force updates"), TRANSPORT_PUSH_FORCE), @@ -625,10 +622,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) PARSE_OPT_OPTARG, option_parse_push_signed), OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC), OPT_STRING_LIST('o', "push-option", &push_options_cmdline, N_("server-specific"), N_("option to transmit")), - OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"), - TRANSPORT_FAMILY_IPV4), - OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"), - TRANSPORT_FAMILY_IPV6), + OPT_IPVERSION(&family), OPT_END() }; @@ -640,8 +634,10 @@ int cmd_push(int argc, const char **argv, const char *prefix) : &push_options_config); set_push_cert_flags(&flags, push_cert); - if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR)))) - die(_("options '%s' and '%s' cannot be used together"), "--delete", "--all/--mirror/--tags"); + die_for_incompatible_opt4(deleterefs, "--delete", + tags, "--tags", + flags & TRANSPORT_PUSH_ALL, "--all/--branches", + flags & TRANSPORT_PUSH_MIRROR, "--mirror"); if (deleterefs && argc < 2) die(_("--delete doesn't make sense without any refs")); @@ -678,19 +674,13 @@ int cmd_push(int argc, const char **argv, const char *prefix) flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE); if (flags & TRANSPORT_PUSH_ALL) { - if (tags) - die(_("options '%s' and '%s' cannot be used together"), "--all", "--tags"); if (argc >= 2) die(_("--all can't be combined with refspecs")); } if (flags & TRANSPORT_PUSH_MIRROR) { - if (tags) - die(_("options '%s' and '%s' cannot be used together"), "--mirror", "--tags"); if (argc >= 2) die(_("--mirror can't be combined with refspecs")); } - if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR)) - die(_("options '%s' and '%s' cannot be used together"), "--all", "--mirror"); if (!is_empty_cas(&cas) && (flags & TRANSPORT_PUSH_FORCE_IF_INCLUDES)) cas.use_force_if_includes = 1; diff --git a/builtin/range-diff.c b/builtin/range-diff.c index aecfae12d3..e455a4795c 100644 --- a/builtin/range-diff.c +++ b/builtin/range-diff.c @@ -1,8 +1,10 @@ -#include "cache.h" #include "builtin.h" +#include "gettext.h" +#include "object-name.h" #include "parse-options.h" #include "range-diff.h" #include "config.h" +#include "repository.h" #include "revision.h" static const char * const builtin_range_diff_usage[] = { @@ -65,20 +67,20 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) if (dash_dash == 3 || (dash_dash < 0 && argc > 2 && - !get_oid_committish(argv[0], &oid) && - !get_oid_committish(argv[1], &oid) && - !get_oid_committish(argv[2], &oid))) { + !repo_get_oid_committish(the_repository, argv[0], &oid) && + !repo_get_oid_committish(the_repository, argv[1], &oid) && + !repo_get_oid_committish(the_repository, argv[2], &oid))) { if (dash_dash < 0) ; /* already validated arguments */ - else if (get_oid_committish(argv[0], &oid)) + else if (repo_get_oid_committish(the_repository, argv[0], &oid)) usage_msg_optf(_("not a revision: '%s'"), builtin_range_diff_usage, options, argv[0]); - else if (get_oid_committish(argv[1], &oid)) + else if (repo_get_oid_committish(the_repository, argv[1], &oid)) usage_msg_optf(_("not a revision: '%s'"), builtin_range_diff_usage, options, argv[1]); - else if (get_oid_committish(argv[2], &oid)) + else if (repo_get_oid_committish(the_repository, argv[2], &oid)) usage_msg_optf(_("not a revision: '%s'"), builtin_range_diff_usage, options, argv[2]); diff --git a/builtin/read-tree.c b/builtin/read-tree.c index 3ce7541783..8196ca9dd8 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -5,18 +5,23 @@ */ #define USE_THE_INDEX_VARIABLE -#include "cache.h" +#include "builtin.h" #include "config.h" +#include "gettext.h" +#include "hex.h" #include "lockfile.h" #include "object.h" +#include "object-name.h" #include "tree.h" #include "tree-walk.h" #include "cache-tree.h" #include "unpack-trees.h" #include "dir.h" -#include "builtin.h" #include "parse-options.h" +#include "repository.h" #include "resolve-undo.h" +#include "setup.h" +#include "sparse-index.h" #include "submodule.h" #include "submodule-config.h" @@ -44,7 +49,7 @@ static const char * const read_tree_usage[] = { NULL }; -static int index_output_cb(const struct option *opt, const char *arg, +static int index_output_cb(const struct option *opt UNUSED, const char *arg, int unset) { BUG_ON_OPT_NEG(unset); @@ -87,9 +92,9 @@ static int debug_merge(const struct cache_entry * const *stages, { int i; - printf("* %d-way merge\n", o->merge_size); + printf("* %d-way merge\n", o->internal.merge_size); debug_stage("index", stages[0], o); - for (i = 1; i <= o->merge_size; i++) { + for (i = 1; i <= o->internal.merge_size; i++) { char buf[24]; xsnprintf(buf, sizeof(buf), "ent#%d", i); debug_stage(buf, stages[i], o); @@ -97,12 +102,13 @@ static int debug_merge(const struct cache_entry * const *stages, return 0; } -static int git_read_tree_config(const char *var, const char *value, void *cb) +static int git_read_tree_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { if (!strcmp(var, "submodule.recurse")) return git_default_submodule_config(var, value, cb); - return git_default_config(var, value, cb); + return git_default_config(var, value, ctx, cb); } int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) @@ -144,7 +150,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) OPT__DRY_RUN(&opts.dry_run, N_("don't update the index or the work tree")), OPT_BOOL(0, "no-sparse-checkout", &opts.skip_sparse_checkout, N_("skip applying sparse checkout filter")), - OPT_BOOL(0, "debug-unpack", &opts.debug_unpack, + OPT_BOOL(0, "debug-unpack", &opts.internal.debug_unpack, N_("debug unpack-trees")), OPT_CALLBACK_F(0, "recurse-submodules", NULL, "checkout", "control recursive updating of submodules", @@ -198,7 +204,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) for (i = 0; i < argc; i++) { const char *arg = argv[i]; - if (get_oid(arg, &oid)) + if (repo_get_oid(the_repository, arg, &oid)) die("Not a valid object name %s", arg); if (list_tree(&oid) < 0) die("failed to unpack tree object %s", arg); @@ -247,7 +253,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) opts.head_idx = 1; } - if (opts.debug_unpack) + if (opts.internal.debug_unpack) opts.fn = debug_merge; /* If we're going to prime_cache_tree later, skip cache tree update */ @@ -263,7 +269,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) if (unpack_trees(nr_trees, t, &opts)) return 128; - if (opts.debug_unpack || opts.dry_run) + if (opts.internal.debug_unpack || opts.dry_run) return 0; /* do not write the index out */ /* diff --git a/builtin/rebase.c b/builtin/rebase.c index 6635f10d52..9f8192e0a5 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -6,6 +6,10 @@ #define USE_THE_INDEX_VARIABLE #include "builtin.h" +#include "abspath.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "run-command.h" #include "exec-cmd.h" #include "strvec.h" @@ -17,7 +21,10 @@ #include "cache-tree.h" #include "unpack-trees.h" #include "lockfile.h" +#include "object-file.h" +#include "object-name.h" #include "parse-options.h" +#include "path.h" #include "commit.h" #include "diff.h" #include "wt-status.h" @@ -28,6 +35,7 @@ #include "sequencer.h" #include "rebase-interactive.h" #include "reset.h" +#include "trace2.h" #include "hook.h" static char const * const builtin_rebase_usage[] = { @@ -116,13 +124,15 @@ struct rebase_options { struct string_list exec; int allow_empty_message; int rebase_merges, rebase_cousins; - char *strategy, *strategy_opts; + char *strategy; + struct string_list strategy_opts; struct strbuf git_format_patch_opt; int reschedule_failed_exec; int reapply_cherry_picks; int fork_point; int update_refs; int config_autosquash; + int config_rebase_merges; int config_update_refs; }; @@ -139,9 +149,11 @@ struct rebase_options { .reapply_cherry_picks = -1, \ .allow_empty_message = 1, \ .autosquash = -1, \ - .config_autosquash = -1, \ + .rebase_merges = -1, \ + .config_rebase_merges = -1, \ .update_refs = -1, \ .config_update_refs = -1, \ + .strategy_opts = STRING_LIST_INIT_NODUP,\ } static struct replay_opts get_replay_opts(const struct rebase_options *opts) @@ -175,8 +187,8 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts) replay.default_strategy = NULL; } - if (opts->strategy_opts) - parse_strategy_opts(&replay, opts->strategy_opts); + for (size_t i = 0; i < opts->strategy_opts.nr; i++) + strvec_push(&replay.xopts, opts->strategy_opts.items[i].string); if (opts->squash_onto) { oidcpy(&replay.squash_onto, opts->squash_onto); @@ -196,7 +208,7 @@ static int edit_todo_file(unsigned flags) if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0) return error_errno(_("could not read '%s'."), todo_file); - strbuf_stripspace(&todo_list.buf, 1); + strbuf_stripspace(&todo_list.buf, comment_line_char); res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags); if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file, NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS))) @@ -218,13 +230,15 @@ static int get_revision_ranges(struct commit *upstream, struct commit *onto, *revisions = xstrfmt("%s...%s", oid_to_hex(&base_rev->object.oid), oid_to_hex(orig_head)); - shorthead = find_unique_abbrev(orig_head, DEFAULT_ABBREV); + shorthead = repo_find_unique_abbrev(the_repository, orig_head, + DEFAULT_ABBREV); if (upstream) { const char *shortrev; - shortrev = find_unique_abbrev(&base_rev->object.oid, - DEFAULT_ABBREV); + shortrev = repo_find_unique_abbrev(the_repository, + &base_rev->object.oid, + DEFAULT_ABBREV); *shortrevisions = xstrfmt("%s..%s", shortrev, shorthead); } else @@ -361,20 +375,6 @@ static int run_sequencer_rebase(struct rebase_options *opts) return ret; } -static void imply_merge(struct rebase_options *opts, const char *option); -static int parse_opt_keep_empty(const struct option *opt, const char *arg, - int unset) -{ - struct rebase_options *opts = opt->value; - - BUG_ON_OPT_ARG(arg); - - imply_merge(opts, unset ? "--no-keep-empty" : "--keep-empty"); - opts->keep_empty = !unset; - opts->type = REBASE_MERGE; - return 0; -} - static int is_merge(struct rebase_options *opts) { return opts->type == REBASE_MERGE; @@ -482,24 +482,6 @@ static int read_basic_state(struct rebase_options *opts) opts->gpg_sign_opt = xstrdup(buf.buf); } - if (file_exists(state_dir_path("strategy", opts))) { - strbuf_reset(&buf); - if (!read_oneliner(&buf, state_dir_path("strategy", opts), - READ_ONELINER_WARN_MISSING)) - return -1; - free(opts->strategy); - opts->strategy = xstrdup(buf.buf); - } - - if (file_exists(state_dir_path("strategy_opts", opts))) { - strbuf_reset(&buf); - if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts), - READ_ONELINER_WARN_MISSING)) - return -1; - free(opts->strategy_opts); - opts->strategy_opts = xstrdup(buf.buf); - } - strbuf_release(&buf); return 0; @@ -517,12 +499,6 @@ static int rebase_write_basic_state(struct rebase_options *opts) write_file(state_dir_path("quiet", opts), "%s", ""); if (opts->flags & REBASE_VERBOSE) write_file(state_dir_path("verbose", opts), "%s", ""); - if (opts->strategy) - write_file(state_dir_path("strategy", opts), "%s", - opts->strategy); - if (opts->strategy_opts) - write_file(state_dir_path("strategy_opts", opts), "%s", - opts->strategy_opts); if (opts->allow_rerere_autoupdate > 0) write_file(state_dir_path("allow_rerere_autoupdate", opts), "-%s-rerere-autoupdate", @@ -660,7 +636,7 @@ static int run_am(struct rebase_options *opts) format_patch.git_cmd = 1; strvec_pushl(&format_patch.args, "format-patch", "-k", "--stdout", "--full-index", "--cherry-pick", "--right-only", - "--src-prefix=a/", "--dst-prefix=b/", "--no-renames", + "--default-prefix", "--no-renames", "--no-cover-letter", "--pretty=mboxrd", "--topo-order", "--no-base", NULL); if (opts->git_format_patch_opt.len) @@ -734,10 +710,8 @@ static int run_specific_rebase(struct rebase_options *opts) if (opts->type == REBASE_MERGE) { /* Run sequencer-based rebase */ setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1); - if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) { + if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) setenv("GIT_SEQUENCE_EDITOR", ":", 1); - opts->autosquash = 0; - } if (opts->gpg_sign_opt) { /* remove the leading "-S" */ char *tmp = xstrdup(opts->gpg_sign_opt + 2); @@ -771,7 +745,18 @@ static int run_specific_rebase(struct rebase_options *opts) return status ? -1 : 0; } -static int rebase_config(const char *var, const char *value, void *data) +static void parse_rebase_merges_value(struct rebase_options *options, const char *value) +{ + if (!strcmp("no-rebase-cousins", value)) + options->rebase_cousins = 0; + else if (!strcmp("rebase-cousins", value)) + options->rebase_cousins = 1; + else + die(_("Unknown rebase-merges mode: %s"), value); +} + +static int rebase_config(const char *var, const char *value, + const struct config_context *ctx, void *data) { struct rebase_options *opts = data; @@ -800,6 +785,17 @@ static int rebase_config(const char *var, const char *value, void *data) return 0; } + if (!strcmp(var, "rebase.rebasemerges")) { + opts->config_rebase_merges = git_parse_maybe_bool(value); + if (opts->config_rebase_merges < 0) { + opts->config_rebase_merges = 1; + parse_rebase_merges_value(opts, value); + } else { + opts->rebase_cousins = 0; + } + return 0; + } + if (!strcmp(var, "rebase.updaterefs")) { opts->config_update_refs = git_config_bool(var, value); return 0; @@ -819,7 +815,7 @@ static int rebase_config(const char *var, const char *value, void *data) return git_config_string(&opts->default_backend, var, value); } - return git_default_config(var, value, data); + return git_default_config(var, value, ctx, data); } static int checkout_up_to_date(struct rebase_options *options) @@ -851,7 +847,7 @@ static int checkout_up_to_date(struct rebase_options *options) static int is_linear_history(struct commit *from, struct commit *to) { while (to && to != from) { - parse_commit(to); + repo_parse_commit(the_repository, to); if (!to->parents) return 1; if (to->parents->next) @@ -880,7 +876,7 @@ static int can_fast_forward(struct commit *onto, struct commit *upstream, if (!upstream) goto done; - merge_bases = get_merge_bases(upstream, head); + merge_bases = repo_get_merge_bases(the_repository, upstream, head); if (!merge_bases || merge_bases->next) goto done; @@ -899,7 +895,8 @@ static void fill_branch_base(struct rebase_options *options, { struct commit_list *merge_bases = NULL; - merge_bases = get_merge_bases(options->onto, options->orig_head); + merge_bases = repo_get_merge_bases(the_repository, options->onto, + options->orig_head); if (!merge_bases || merge_bases->next) oidcpy(branch_base, null_oid()); else @@ -969,6 +966,18 @@ static enum empty_type parse_empty_value(const char *value) die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value); } +static int parse_opt_keep_empty(const struct option *opt, const char *arg, + int unset) +{ + struct rebase_options *opts = opt->value; + + BUG_ON_OPT_ARG(arg); + + imply_merge(opts, unset ? "--no-keep-empty" : "--keep-empty"); + opts->keep_empty = !unset; + return 0; +} + static int parse_opt_empty(const struct option *opt, const char *arg, int unset) { struct rebase_options *options = opt->value; @@ -980,6 +989,28 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset) return 0; } +static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset) +{ + struct rebase_options *options = opt->value; + + options->rebase_merges = !unset; + options->rebase_cousins = 0; + + if (arg) { + if (!*arg) { + warning(_("--rebase-merges with an empty string " + "argument is deprecated and will stop " + "working in a future version of Git. Use " + "--rebase-merges without an argument " + "instead, which does the same thing.")); + return 0; + } + parse_rebase_merges_value(options, arg); + } + + return 0; +} + static void NORETURN error_on_missing_default_upstream(void) { struct branch *current_branch = branch_get(NULL); @@ -1035,8 +1066,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) struct object_id branch_base; int ignore_whitespace = 0; const char *gpg_sign = NULL; - const char *rebase_merges = NULL; - struct string_list strategy_options = STRING_LIST_INIT_NODUP; struct object_id squash_onto; char *squash_onto_name = NULL; char *keep_base_onto_name = NULL; @@ -1113,7 +1142,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) "instead of ignoring them"), 1, PARSE_OPT_HIDDEN), OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate), - OPT_CALLBACK_F(0, "empty", &options, "{drop,keep,ask}", + OPT_CALLBACK_F(0, "empty", &options, "(drop|keep|ask)", N_("how to handle commits that become empty"), PARSE_OPT_NONEG, parse_opt_empty), OPT_CALLBACK_F('k', "keep-empty", &options, NULL, @@ -1137,15 +1166,14 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) &options.allow_empty_message, N_("allow rebasing commits with empty messages"), PARSE_OPT_HIDDEN), - {OPTION_STRING, 'r', "rebase-merges", &rebase_merges, - N_("mode"), + OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"), N_("try to rebase merges instead of skipping them"), - PARSE_OPT_OPTARG, NULL, (intptr_t)""}, + PARSE_OPT_OPTARG, parse_opt_rebase_merges), OPT_BOOL(0, "fork-point", &options.fork_point, N_("use 'merge-base --fork-point' to refine upstream")), OPT_STRING('s', "strategy", &options.strategy, N_("strategy"), N_("use the given merge strategy")), - OPT_STRING_LIST('X', "strategy-option", &strategy_options, + OPT_STRING_LIST('X', "strategy-option", &options.strategy_opts, N_("option"), N_("pass the argument through to the merge " "strategy")), @@ -1261,7 +1289,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) int fd; /* Sanity check */ - if (get_oid("HEAD", &head)) + if (repo_get_oid(the_repository, "HEAD", &head)) die(_("Cannot read HEAD")); fd = repo_hold_locked_index(the_repository, &lock_file, 0); @@ -1374,7 +1402,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) || (options.action != ACTION_NONE) || (options.exec.nr > 0) || - (options.autosquash == -1 && options.config_autosquash == 1) || options.autosquash == 1) { allow_preemptive_ff = 0; } @@ -1436,17 +1463,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (options.exec.nr) imply_merge(&options, "--exec"); - if (rebase_merges) { - if (!*rebase_merges) - ; /* default mode; do nothing */ - else if (!strcmp("rebase-cousins", rebase_merges)) - options.rebase_cousins = 1; - else if (strcmp("no-rebase-cousins", rebase_merges)) - die(_("Unknown mode: %s"), rebase_merges); - options.rebase_merges = 1; - imply_merge(&options, "--rebase-merges"); - } - if (options.type == REBASE_APPLY) { if (ignore_whitespace) strvec_push(&options.git_am_opts, @@ -1459,43 +1475,19 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } else { /* REBASE_MERGE */ if (ignore_whitespace) { - string_list_append(&strategy_options, + string_list_append(&options.strategy_opts, "ignore-space-change"); } } - if (strategy_options.nr) { - int i; - - if (!options.strategy) - options.strategy = "ort"; - - strbuf_reset(&buf); - for (i = 0; i < strategy_options.nr; i++) - strbuf_addf(&buf, " --%s", - strategy_options.items[i].string); - options.strategy_opts = xstrdup(buf.buf); - } + if (options.strategy_opts.nr && !options.strategy) + options.strategy = "ort"; if (options.strategy) { options.strategy = xstrdup(options.strategy); - switch (options.type) { - case REBASE_APPLY: - die(_("--strategy requires --merge or --interactive")); - case REBASE_MERGE: - /* compatible */ - break; - case REBASE_UNSPECIFIED: - options.type = REBASE_MERGE; - break; - default: - BUG("unhandled rebase type (%d)", options.type); - } + imply_merge(&options, "--strategy"); } - if (options.type == REBASE_MERGE) - imply_merge(&options, "--merge"); - if (options.root && !options.onto_name) imply_merge(&options, "--root without --onto"); @@ -1512,8 +1504,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (is_merge(&options)) die(_("apply options and merge options " "cannot be used together")); - else if (options.autosquash == -1 && options.config_autosquash == 1) - die(_("apply options are incompatible with rebase.autosquash. Consider adding --no-autosquash")); + else if (options.rebase_merges == -1 && options.config_rebase_merges == 1) + die(_("apply options are incompatible with rebase.rebaseMerges. Consider adding --no-rebase-merges")); else if (options.update_refs == -1 && options.config_update_refs == 1) die(_("apply options are incompatible with rebase.updateRefs. Consider adding --no-update-refs")); else @@ -1526,14 +1518,22 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) options.update_refs = (options.update_refs >= 0) ? options.update_refs : ((options.config_update_refs >= 0) ? options.config_update_refs : 0); - if (options.autosquash == 1) + if (options.rebase_merges == 1) + imply_merge(&options, "--rebase-merges"); + options.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges : + ((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0); + + if (options.autosquash == 1) { imply_merge(&options, "--autosquash"); - options.autosquash = (options.autosquash >= 0) ? options.autosquash : - ((options.config_autosquash >= 0) ? options.config_autosquash : 0); + } else if (options.autosquash == -1) { + options.autosquash = + options.config_autosquash && + (options.flags & REBASE_INTERACTIVE_EXPLICIT); + } if (options.type == REBASE_UNSPECIFIED) { if (!strcmp(options.default_backend, "merge")) - imply_merge(&options, "--merge"); + options.type = REBASE_MERGE; else if (!strcmp(options.default_backend, "apply")) options.type = REBASE_APPLY; else @@ -1680,7 +1680,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } else if (!options.onto_name) options.onto_name = options.upstream_name; if (strstr(options.onto_name, "...")) { - if (get_oid_mb(options.onto_name, &branch_base) < 0) { + if (repo_get_oid_mb(the_repository, options.onto_name, &branch_base) < 0) { if (keep_base) die(_("'%s': need exactly one merge base with branch"), options.upstream_name); @@ -1783,9 +1783,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } /* We want color (if set), but no pager */ - diff_setup(&opts); - opts.stat_width = -1; /* use full terminal width */ - opts.stat_graph_width = -1; /* respect statGraphWidth config */ + repo_diff_setup(the_repository, &opts); + init_diffstat_widths(&opts); opts.output_format |= DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; opts.detect_rename = DIFF_DETECT_RENAME; @@ -1850,10 +1849,9 @@ cleanup: free(options.gpg_sign_opt); string_list_clear(&options.exec, 0); free(options.strategy); - free(options.strategy_opts); + string_list_clear(&options.strategy_opts, 0); strbuf_release(&options.git_format_patch_opt); free(squash_onto_name); free(keep_base_onto_name); - string_list_clear(&strategy_options, 0); return !!ret; } diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index cd5c7a28ef..ccf9738bce 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -1,6 +1,10 @@ #include "builtin.h" +#include "abspath.h" #include "repository.h" #include "config.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "lockfile.h" #include "pack.h" #include "refs.h" @@ -25,11 +29,17 @@ #include "tmp-objdir.h" #include "oidset.h" #include "packfile.h" -#include "object-store.h" +#include "object-name.h" +#include "object-store-ll.h" +#include "path.h" #include "protocol.h" #include "commit-reach.h" +#include "server-info.h" +#include "trace.h" +#include "trace2.h" #include "worktree.h" #include "shallow.h" +#include "parse-options.h" static const char * const receive_pack_usage[] = { N_("git receive-pack <git-dir>"), @@ -80,7 +90,7 @@ static struct object_id push_cert_oid; static struct signature_check sigcheck; static const char *push_cert_nonce; static const char *cert_nonce_seed; -static struct string_list hidden_refs = STRING_LIST_INIT_DUP; +static struct strvec hidden_refs = STRVEC_INIT; static const char *NONCE_UNSOLICITED = "UNSOLICITED"; static const char *NONCE_BAD = "BAD"; @@ -129,17 +139,15 @@ static enum deny_action parse_deny_action(const char *var, const char *value) return DENY_IGNORE; } -static int receive_pack_config(const char *var, const char *value, void *cb) +static int receive_pack_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { + const char *msg_id; int status = parse_hide_refs_config(var, value, "receive", &hidden_refs); if (status) return status; - status = git_gpg_config(var, value, NULL); - if (status) - return status; - if (strcmp(var, "receive.denydeletes") == 0) { deny_deletes = git_config_bool(var, value); return 0; @@ -151,12 +159,12 @@ static int receive_pack_config(const char *var, const char *value, void *cb) } if (strcmp(var, "receive.unpacklimit") == 0) { - receive_unpack_limit = git_config_int(var, value); + receive_unpack_limit = git_config_int(var, value, ctx->kvi); return 0; } if (strcmp(var, "transfer.unpacklimit") == 0) { - transfer_unpack_limit = git_config_int(var, value); + transfer_unpack_limit = git_config_int(var, value, ctx->kvi); return 0; } @@ -171,12 +179,14 @@ static int receive_pack_config(const char *var, const char *value, void *cb) return 0; } - if (skip_prefix(var, "receive.fsck.", &var)) { - if (is_valid_msg_type(var, value)) + if (skip_prefix(var, "receive.fsck.", &msg_id)) { + if (!value) + return config_error_nonbool(var); + if (is_valid_msg_type(msg_id, value)) strbuf_addf(&fsck_msg_types, "%c%s=%s", - fsck_msg_types.len ? ',' : '=', var, value); + fsck_msg_types.len ? ',' : '=', msg_id, value); else - warning("skipping unknown msg id '%s'", var); + warning("skipping unknown msg id '%s'", msg_id); return 0; } @@ -224,7 +234,7 @@ static int receive_pack_config(const char *var, const char *value, void *cb) return git_config_string(&cert_nonce_seed, var, value); if (strcmp(var, "receive.certnonceslop") == 0) { - nonce_stamp_slop_limit = git_config_ulong(var, value); + nonce_stamp_slop_limit = git_config_ulong(var, value, ctx->kvi); return 0; } @@ -239,12 +249,12 @@ static int receive_pack_config(const char *var, const char *value, void *cb) } if (strcmp(var, "receive.keepalive") == 0) { - keepalive_in_sec = git_config_int(var, value); + keepalive_in_sec = git_config_int(var, value, ctx->kvi); return 0; } if (strcmp(var, "receive.maxinputsize") == 0) { - max_input_size = git_config_int64(var, value); + max_input_size = git_config_int64(var, value, ctx->kvi); return 0; } @@ -260,7 +270,7 @@ static int receive_pack_config(const char *var, const char *value, void *cb) return 0; } - return git_default_config(var, value, cb); + return git_default_config(var, value, ctx, cb); } static void show_ref(const char *path, const struct object_id *oid) @@ -331,7 +341,9 @@ static void write_head_info(void) { static struct oidset seen = OIDSET_INIT; - for_each_ref(show_ref_cb, &seen); + refs_for_each_fullref_in(get_main_ref_store(the_repository), "", + hidden_refs_to_excludes(&hidden_refs), + show_ref_cb, &seen); for_each_alternate_ref(show_one_alternate_ref, &seen); oidset_clear(&seen); if (!sent_capabilities) @@ -1347,7 +1359,7 @@ static int head_has_history(void) { struct object_id oid; - return !get_oid("HEAD", &oid); + return !repo_get_oid(the_repository, "HEAD", &oid); } static const char *push_to_deploy(unsigned char *sha1, @@ -1463,8 +1475,10 @@ static const char *update(struct command *cmd, struct shallow_info *si) find_shared_symref(worktrees, "HEAD", name); /* only refs/... are allowed */ - if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) { - rp_error("refusing to create funny ref '%s' remotely", name); + if (!starts_with(name, "refs/") || + check_refname_format(name + 5, is_null_oid(new_oid) ? + REFNAME_ALLOW_ONELEVEL : 0)) { + rp_error("refusing to update funny ref '%s' remotely", name); ret = "funny refname"; goto out; } @@ -1494,7 +1508,7 @@ static const char *update(struct command *cmd, struct shallow_info *si) } } - if (!is_null_oid(new_oid) && !has_object_file(new_oid)) { + if (!is_null_oid(new_oid) && !repo_has_object_file(the_repository, new_oid)) { error("unpack should have generated %s, " "but I can't find it!", oid_to_hex(new_oid)); ret = "bad pack"; @@ -1548,7 +1562,7 @@ static const char *update(struct command *cmd, struct shallow_info *si) } old_commit = (struct commit *)old_object; new_commit = (struct commit *)new_object; - if (!in_merge_bases(old_commit, new_commit)) { + if (!repo_in_merge_bases(the_repository, old_commit, new_commit)) { rp_error("denying non-fast-forward %s" " (you should pull first)", name); ret = "non-fast-forward"; @@ -1681,11 +1695,11 @@ static void check_aliased_update_internal(struct command *cmd, rp_error("refusing inconsistent update between symref '%s' (%s..%s) and" " its target '%s' (%s..%s)", cmd->ref_name, - find_unique_abbrev(&cmd->old_oid, DEFAULT_ABBREV), - find_unique_abbrev(&cmd->new_oid, DEFAULT_ABBREV), + repo_find_unique_abbrev(the_repository, &cmd->old_oid, DEFAULT_ABBREV), + repo_find_unique_abbrev(the_repository, &cmd->new_oid, DEFAULT_ABBREV), dst_cmd->ref_name, - find_unique_abbrev(&dst_cmd->old_oid, DEFAULT_ABBREV), - find_unique_abbrev(&dst_cmd->new_oid, DEFAULT_ABBREV)); + repo_find_unique_abbrev(the_repository, &dst_cmd->old_oid, DEFAULT_ABBREV), + repo_find_unique_abbrev(the_repository, &dst_cmd->new_oid, DEFAULT_ABBREV)); cmd->error_string = dst_cmd->error_string = "inconsistent aliased update"; @@ -2089,7 +2103,7 @@ static struct command *read_head_info(struct packet_reader *reader, const char *feature_list = reader->line + linelen + 1; const char *hash = NULL; const char *client_sid; - int len = 0; + size_t len = 0; if (parse_feature_request(feature_list, "report-status")) report_status = 1; if (parse_feature_request(feature_list, "report-status-v2")) @@ -2184,7 +2198,7 @@ static const char *parse_pack_header(struct pack_header *hdr) } } -static const char *pack_lockfile; +static struct tempfile *pack_lockfile; static void push_header_arg(struct strvec *args, struct pack_header *hdr) { @@ -2251,6 +2265,7 @@ static const char *unpack(int err_fd, struct shallow_info *si) return "unpack-objects abnormal exit"; } else { char hostname[HOST_NAME_MAX + 1]; + char *lockfile; strvec_pushl(&child.args, "index-pack", "--stdin", NULL); push_header_arg(&child.args, &hdr); @@ -2280,8 +2295,14 @@ static const char *unpack(int err_fd, struct shallow_info *si) status = start_command(&child); if (status) return "index-pack fork failed"; - pack_lockfile = index_pack_lockfile(child.out, NULL); + + lockfile = index_pack_lockfile(child.out, NULL); + if (lockfile) { + pack_lockfile = register_tempfile(lockfile); + free(lockfile); + } close(child.out); + status = finish_command(&child); if (status) return "index-pack abnormal exit"; @@ -2509,10 +2530,10 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) if (cert_nonce_seed) push_cert_nonce = prepare_push_cert_nonce(service_dir, time(NULL)); - if (0 <= transfer_unpack_limit) - unpack_limit = transfer_unpack_limit; - else if (0 <= receive_unpack_limit) + if (0 <= receive_unpack_limit) unpack_limit = receive_unpack_limit; + else if (0 <= transfer_unpack_limit) + unpack_limit = transfer_unpack_limit; switch (determine_protocol_version_server()) { case protocol_v2: @@ -2568,8 +2589,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) use_keepalive = KEEPALIVE_ALWAYS; execute_commands(commands, unpack_status, &si, &push_options); - if (pack_lockfile) - unlink_or_warn(pack_lockfile); + delete_tempfile(&pack_lockfile); sigchain_push(SIGPIPE, SIG_IGN); if (report_status_v2) report_v2(commands, unpack_status); @@ -2605,7 +2625,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) packet_flush(1); oid_array_clear(&shallow); oid_array_clear(&ref); - string_list_clear(&hidden_refs, 0); + strvec_clear(&hidden_refs); free((void *)push_cert_nonce); return 0; } diff --git a/builtin/reflog.c b/builtin/reflog.c index 270681dcdf..a5a4099f61 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -1,9 +1,13 @@ #include "builtin.h" #include "config.h" +#include "gettext.h" +#include "repository.h" #include "revision.h" #include "reachable.h" +#include "wildmatch.h" #include "worktree.h" #include "reflog.h" +#include "parse-options.h" #define BUILTIN_REFLOG_SHOW_USAGE \ N_("git reflog [show] [<log-options>] [<ref>]") @@ -106,7 +110,8 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len) #define EXPIRE_TOTAL 01 #define EXPIRE_UNREACH 02 -static int reflog_expire_config(const char *var, const char *value, void *cb) +static int reflog_expire_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { const char *pattern, *key; size_t pattern_len; @@ -115,7 +120,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb) struct reflog_expire_cfg *ent; if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0) - return git_default_config(var, value, cb); + return git_default_config(var, value, ctx, cb); if (!strcmp(key, "reflogexpire")) { slot = EXPIRE_TOTAL; @@ -126,7 +131,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb) if (git_config_expiry_date(&expire, var, value)) return -1; } else - return git_default_config(var, value, cb); + return git_default_config(var, value, ctx, cb); if (!pattern) { switch (slot) { @@ -238,12 +243,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) { struct cmd_reflog_expire_cb cmd = { 0 }; timestamp_t now = time(NULL); - int i, status, do_all, all_worktrees = 1; + int i, status, do_all, single_worktree = 0; unsigned int flags = 0; int verbose = 0; reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent; const struct option options[] = { - OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"), + OPT_BIT('n', "dry-run", &flags, N_("do not actually prune any entries"), EXPIRE_REFLOGS_DRY_RUN), OPT_BIT(0, "rewrite", &flags, N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"), @@ -263,7 +268,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "stale-fix", &cmd.stalefix, N_("prune any reflog entries that point to broken commits")), OPT_BOOL(0, "all", &do_all, N_("process the reflogs of all references")), - OPT_BOOL(1, "single-worktree", &all_worktrees, + OPT_BOOL(0, "single-worktree", &single_worktree, N_("limits processing to reflogs from the current worktree only")), OPT_END() }; @@ -293,7 +298,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) struct rev_info revs; repo_init_revisions(the_repository, &revs, prefix); - revs.do_not_die_on_missing_tree = 1; + revs.do_not_die_on_missing_objects = 1; revs.ignore_missing = 1; revs.ignore_missing_links = 1; if (verbose) @@ -313,7 +318,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) worktrees = get_worktrees(); for (p = worktrees; *p; p++) { - if (!all_worktrees && !(*p)->is_current) + if (single_worktree && !(*p)->is_current) continue; collected.worktree = *p; refs_for_each_reflog(get_worktree_ref_store(*p), @@ -363,7 +368,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) int verbose = 0; const struct option options[] = { - OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"), + OPT_BIT('n', "dry-run", &flags, N_("do not actually prune any entries"), EXPIRE_REFLOGS_DRY_RUN), OPT_BIT(0, "rewrite", &flags, N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"), diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c index ee338bf440..282782eccd 100644 --- a/builtin/remote-ext.c +++ b/builtin/remote-ext.c @@ -197,6 +197,8 @@ static int command_loop(const char *child) int cmd_remote_ext(int argc, const char **argv, const char *prefix) { + BUG_ON_NON_EMPTY_PREFIX(prefix); + if (argc != 3) usage(usage_msg); diff --git a/builtin/remote-fd.c b/builtin/remote-fd.c index b2a3980b1d..9020fab9c5 100644 --- a/builtin/remote-fd.c +++ b/builtin/remote-fd.c @@ -59,6 +59,8 @@ int cmd_remote_fd(int argc, const char **argv, const char *prefix) int output_fd = -1; char *end; + BUG_ON_NON_EMPTY_PREFIX(prefix); + if (argc != 3) usage(usage_msg); diff --git a/builtin/remote.c b/builtin/remote.c index 729f6f3643..d91bbe728d 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -1,6 +1,8 @@ #include "builtin.h" #include "config.h" +#include "gettext.h" #include "parse-options.h" +#include "path.h" #include "transport.h" #include "remote.h" #include "string-list.h" @@ -9,7 +11,7 @@ #include "rebase.h" #include "refs.h" #include "refspec.h" -#include "object-store.h" +#include "object-store-ll.h" #include "strvec.h" #include "commit-reach.h" #include "progress.h" @@ -166,10 +168,9 @@ static int add(int argc, const char **argv, const char *prefix) struct option options[] = { OPT_BOOL('f', "fetch", &fetch, N_("fetch the remote branches")), OPT_SET_INT(0, "tags", &fetch_tags, - N_("import all tags and associated objects when fetching"), + N_("import all tags and associated objects when fetching\n" + "or do not fetch any tag at all (--no-tags)"), TAGS_SET), - OPT_SET_INT(0, NULL, &fetch_tags, - N_("or do not fetch any tag at all (--no-tags)"), TAGS_UNSET), OPT_STRING_LIST('t', "track", &track, N_("branch"), N_("branch(es) to track")), OPT_STRING('m', "master", &master, N_("branch"), N_("master branch")), @@ -267,6 +268,7 @@ static const char *abbrev_ref(const char *name, const char *prefix) #define abbrev_branch(name) abbrev_ref((name), "refs/heads/") static int config_read_branches(const char *key, const char *value, + const struct config_context *ctx UNUSED, void *data UNUSED) { const char *orig_key = key; @@ -443,7 +445,7 @@ static int get_push_ref_states(const struct ref *remote_refs, info->status = PUSH_STATUS_UPTODATE; else if (is_null_oid(&ref->old_oid)) info->status = PUSH_STATUS_CREATE; - else if (has_object_file(&ref->old_oid) && + else if (repo_has_object_file(the_repository, &ref->old_oid) && ref_newer(&ref->new_oid, &ref->old_oid)) info->status = PUSH_STATUS_FASTFORWARD; else @@ -644,17 +646,19 @@ struct push_default_info }; static int config_read_push_default(const char *key, const char *value, - void *cb) + const struct config_context *ctx, void *cb) { + const struct key_value_info *kvi = ctx->kvi; + struct push_default_info* info = cb; if (strcmp(key, "remote.pushdefault") || !value || strcmp(value, info->old_name)) return 0; - info->scope = current_config_scope(); + info->scope = kvi->scope; strbuf_reset(&info->origin); - strbuf_addstr(&info->origin, current_config_name()); - info->linenr = current_config_line(); + strbuf_addstr(&info->origin, config_origin_type_name(kvi->origin_type)); + info->linenr = kvi->linenr; return 0; } @@ -1493,7 +1497,9 @@ static int prune(int argc, const char **argv, const char *prefix) return result; } -static int get_remote_default(const char *key, const char *value UNUSED, void *priv) +static int get_remote_default(const char *key, const char *value UNUSED, + const struct config_context *ctx UNUSED, + void *priv) { if (strcmp(key, "remotes.default") == 0) { int *found = priv; diff --git a/builtin/repack.c b/builtin/repack.c index f649379531..c54777bbe5 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -1,9 +1,13 @@ #include "builtin.h" -#include "cache.h" #include "config.h" #include "dir.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "parse-options.h" +#include "path.h" #include "run-command.h" +#include "server-info.h" #include "sigchain.h" #include "strbuf.h" #include "string-list.h" @@ -11,19 +15,20 @@ #include "midx.h" #include "packfile.h" #include "prune-packed.h" -#include "object-store.h" +#include "object-store-ll.h" #include "promisor-remote.h" #include "shallow.h" #include "pack.h" #include "pack-bitmap.h" #include "refs.h" +#include "list-objects-filter-options.h" #define ALL_INTO_ONE 1 #define LOOSEN_UNREACHABLE 2 #define PACK_CRUFT 4 #define DELETE_PACK 1 -#define CRUFT_PACK 2 +#define RETAIN_PACK 2 static int pack_everything; static int delta_base_offset = 1; @@ -48,14 +53,16 @@ struct pack_objects_args { const char *window_memory; const char *depth; const char *threads; - const char *max_pack_size; + unsigned long max_pack_size; int no_reuse_delta; int no_reuse_object; int quiet; int local; + struct list_objects_filter_options filter_options; }; -static int repack_config(const char *var, const char *value, void *cb) +static int repack_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { struct pack_objects_args *cruft_po_args = cb; if (!strcmp(var, "repack.usedeltabaseoffset")) { @@ -87,53 +94,102 @@ static int repack_config(const char *var, const char *value, void *cb) return git_config_string(&cruft_po_args->depth, var, value); if (!strcmp(var, "repack.cruftthreads")) return git_config_string(&cruft_po_args->threads, var, value); - return git_default_config(var, value, cb); + return git_default_config(var, value, ctx, cb); } -/* - * Adds all packs hex strings to either fname_nonkept_list or - * fname_kept_list based on whether each pack has a corresponding - * .keep file or not. Packs without a .keep file are not to be kept - * if we are going to pack everything into one file. - */ -static void collect_pack_filenames(struct string_list *fname_nonkept_list, - struct string_list *fname_kept_list, - const struct string_list *extra_keep) +struct existing_packs { + struct string_list kept_packs; + struct string_list non_kept_packs; + struct string_list cruft_packs; +}; + +#define EXISTING_PACKS_INIT { \ + .kept_packs = STRING_LIST_INIT_DUP, \ + .non_kept_packs = STRING_LIST_INIT_DUP, \ + .cruft_packs = STRING_LIST_INIT_DUP, \ +} + +static int has_existing_non_kept_packs(const struct existing_packs *existing) { - DIR *dir; - struct dirent *e; - char *fname; + return existing->non_kept_packs.nr || existing->cruft_packs.nr; +} - if (!(dir = opendir(packdir))) - return; +static void pack_mark_for_deletion(struct string_list_item *item) +{ + item->util = (void*)((uintptr_t)item->util | DELETE_PACK); +} - while ((e = readdir(dir)) != NULL) { - size_t len; - int i; +static void pack_unmark_for_deletion(struct string_list_item *item) +{ + item->util = (void*)((uintptr_t)item->util & ~DELETE_PACK); +} - if (!strip_suffix(e->d_name, ".pack", &len)) - continue; +static int pack_is_marked_for_deletion(struct string_list_item *item) +{ + return (uintptr_t)item->util & DELETE_PACK; +} - for (i = 0; i < extra_keep->nr; i++) - if (!fspathcmp(e->d_name, extra_keep->items[i].string)) - break; +static void pack_mark_retained(struct string_list_item *item) +{ + item->util = (void*)((uintptr_t)item->util | RETAIN_PACK); +} - fname = xmemdupz(e->d_name, len); +static int pack_is_retained(struct string_list_item *item) +{ + return (uintptr_t)item->util & RETAIN_PACK; +} - if ((extra_keep->nr > 0 && i < extra_keep->nr) || - (file_exists(mkpath("%s/%s.keep", packdir, fname)))) { - string_list_append_nodup(fname_kept_list, fname); - } else { - struct string_list_item *item; - item = string_list_append_nodup(fname_nonkept_list, - fname); - if (file_exists(mkpath("%s/%s.mtimes", packdir, fname))) - item->util = (void*)(uintptr_t)CRUFT_PACK; +static void mark_packs_for_deletion_1(struct string_list *names, + struct string_list *list) +{ + struct string_list_item *item; + const int hexsz = the_hash_algo->hexsz; + + for_each_string_list_item(item, list) { + char *sha1; + size_t len = strlen(item->string); + if (len < hexsz) + continue; + sha1 = item->string + len - hexsz; + + if (pack_is_retained(item)) { + pack_unmark_for_deletion(item); + } else if (!string_list_has_string(names, sha1)) { + /* + * Mark this pack for deletion, which ensures + * that this pack won't be included in a MIDX + * (if `--write-midx` was given) and that we + * will actually delete this pack (if `-d` was + * given). + */ + pack_mark_for_deletion(item); } } - closedir(dir); +} + +static void retain_cruft_pack(struct existing_packs *existing, + struct packed_git *cruft) +{ + struct strbuf buf = STRBUF_INIT; + struct string_list_item *item; + + strbuf_addstr(&buf, pack_basename(cruft)); + strbuf_strip_suffix(&buf, ".pack"); + + item = string_list_lookup(&existing->cruft_packs, buf.buf); + if (!item) + BUG("could not find cruft pack '%s'", pack_basename(cruft)); + + pack_mark_retained(item); + strbuf_release(&buf); +} + +static void mark_packs_for_deletion(struct existing_packs *existing, + struct string_list *names) - string_list_sort(fname_kept_list); +{ + mark_packs_for_deletion_1(names, &existing->non_kept_packs); + mark_packs_for_deletion_1(names, &existing->cruft_packs); } static void remove_redundant_pack(const char *dir_name, const char *base_name) @@ -148,6 +204,72 @@ static void remove_redundant_pack(const char *dir_name, const char *base_name) strbuf_release(&buf); } +static void remove_redundant_packs_1(struct string_list *packs) +{ + struct string_list_item *item; + for_each_string_list_item(item, packs) { + if (!pack_is_marked_for_deletion(item)) + continue; + remove_redundant_pack(packdir, item->string); + } +} + +static void remove_redundant_existing_packs(struct existing_packs *existing) +{ + remove_redundant_packs_1(&existing->non_kept_packs); + remove_redundant_packs_1(&existing->cruft_packs); +} + +static void existing_packs_release(struct existing_packs *existing) +{ + string_list_clear(&existing->kept_packs, 0); + string_list_clear(&existing->non_kept_packs, 0); + string_list_clear(&existing->cruft_packs, 0); +} + +/* + * Adds all packs hex strings (pack-$HASH) to either packs->non_kept + * or packs->kept based on whether each pack has a corresponding + * .keep file or not. Packs without a .keep file are not to be kept + * if we are going to pack everything into one file. + */ +static void collect_pack_filenames(struct existing_packs *existing, + const struct string_list *extra_keep) +{ + struct packed_git *p; + struct strbuf buf = STRBUF_INIT; + + for (p = get_all_packs(the_repository); p; p = p->next) { + int i; + const char *base; + + if (!p->pack_local) + continue; + + base = pack_basename(p); + + for (i = 0; i < extra_keep->nr; i++) + if (!fspathcmp(base, extra_keep->items[i].string)) + break; + + strbuf_reset(&buf); + strbuf_addstr(&buf, base); + strbuf_strip_suffix(&buf, ".pack"); + + if ((extra_keep->nr > 0 && i < extra_keep->nr) || p->pack_keep) + string_list_append(&existing->kept_packs, buf.buf); + else if (p->is_cruft) + string_list_append(&existing->cruft_packs, buf.buf); + else + string_list_append(&existing->non_kept_packs, buf.buf); + } + + string_list_sort(&existing->kept_packs); + string_list_sort(&existing->non_kept_packs); + string_list_sort(&existing->cruft_packs); + strbuf_release(&buf); +} + static void prepare_pack_objects(struct child_process *cmd, const struct pack_objects_args *args, const char *out) @@ -162,7 +284,7 @@ static void prepare_pack_objects(struct child_process *cmd, if (args->threads) strvec_pushf(&cmd->args, "--threads=%s", args->threads); if (args->max_pack_size) - strvec_pushf(&cmd->args, "--max-pack-size=%s", args->max_pack_size); + strvec_pushf(&cmd->args, "--max-pack-size=%lu", args->max_pack_size); if (args->no_reuse_delta) strvec_pushf(&cmd->args, "--no-reuse-delta"); if (args->no_reuse_object) @@ -182,8 +304,9 @@ static void prepare_pack_objects(struct child_process *cmd, * Write oid to the given struct child_process's stdin, starting it first if * necessary. */ -static int write_oid(const struct object_id *oid, struct packed_git *pack, - uint32_t pos, void *data) +static int write_oid(const struct object_id *oid, + struct packed_git *pack UNUSED, + uint32_t pos UNUSED, void *data) { struct child_process *cmd = data; @@ -234,6 +357,18 @@ static struct generated_pack_data *populate_pack_exts(const char *name) return data; } +static int has_pack_ext(const struct generated_pack_data *data, + const char *ext) +{ + int i; + for (i = 0; i < ARRAY_SIZE(exts); i++) { + if (strcmp(exts[i].name, ext)) + continue; + return !!data->tempfiles[i]; + } + BUG("unknown pack extension: '%s'", ext); +} + static void repack_promisor_objects(const struct pack_objects_args *args, struct string_list *names) { @@ -299,6 +434,8 @@ struct pack_geometry { struct packed_git **pack; uint32_t pack_nr, pack_alloc; uint32_t split; + + int split_factor; }; static uint32_t geometry_pack_weight(struct packed_git *p) @@ -320,36 +457,42 @@ static int geometry_cmp(const void *va, const void *vb) return 0; } -static void init_pack_geometry(struct pack_geometry **geometry_p, - struct string_list *existing_kept_packs) +static void init_pack_geometry(struct pack_geometry *geometry, + struct existing_packs *existing, + const struct pack_objects_args *args) { struct packed_git *p; - struct pack_geometry *geometry; struct strbuf buf = STRBUF_INIT; - *geometry_p = xcalloc(1, sizeof(struct pack_geometry)); - geometry = *geometry_p; - for (p = get_all_packs(the_repository); p; p = p->next) { + if (args->local && !p->pack_local) + /* + * When asked to only repack local packfiles we skip + * over any packfiles that are borrowed from alternate + * object directories. + */ + continue; + if (!pack_kept_objects) { /* - * Any pack that has its pack_keep bit set will appear - * in existing_kept_packs below, but this saves us from - * doing a more expensive check. + * Any pack that has its pack_keep bit set will + * appear in existing->kept_packs below, but + * this saves us from doing a more expensive + * check. */ if (p->pack_keep) continue; /* - * The pack may be kept via the --keep-pack option; - * check 'existing_kept_packs' to determine whether to - * ignore it. + * The pack may be kept via the --keep-pack + * option; check 'existing->kept_packs' to + * determine whether to ignore it. */ strbuf_reset(&buf); strbuf_addstr(&buf, pack_basename(p)); strbuf_strip_suffix(&buf, ".pack"); - if (string_list_has_string(existing_kept_packs, buf.buf)) + if (string_list_has_string(&existing->kept_packs, buf.buf)) continue; } if (p->is_cruft) @@ -367,7 +510,7 @@ static void init_pack_geometry(struct pack_geometry **geometry_p, strbuf_release(&buf); } -static void split_pack_geometry(struct pack_geometry *geometry, int factor) +static void split_pack_geometry(struct pack_geometry *geometry) { uint32_t i; uint32_t split; @@ -386,12 +529,14 @@ static void split_pack_geometry(struct pack_geometry *geometry, int factor) struct packed_git *ours = geometry->pack[i]; struct packed_git *prev = geometry->pack[i - 1]; - if (unsigned_mult_overflows(factor, geometry_pack_weight(prev))) + if (unsigned_mult_overflows(geometry->split_factor, + geometry_pack_weight(prev))) die(_("pack %s too large to consider in geometric " "progression"), prev->pack_name); - if (geometry_pack_weight(ours) < factor * geometry_pack_weight(prev)) + if (geometry_pack_weight(ours) < + geometry->split_factor * geometry_pack_weight(prev)) break; } @@ -426,10 +571,12 @@ static void split_pack_geometry(struct pack_geometry *geometry, int factor) for (i = split; i < geometry->pack_nr; i++) { struct packed_git *ours = geometry->pack[i]; - if (unsigned_mult_overflows(factor, total_size)) + if (unsigned_mult_overflows(geometry->split_factor, + total_size)) die(_("pack %s too large to roll up"), ours->pack_name); - if (geometry_pack_weight(ours) < factor * total_size) { + if (geometry_pack_weight(ours) < + geometry->split_factor * total_size) { if (unsigned_add_overflows(total_size, geometry_pack_weight(ours))) die(_("pack %s too large to roll up"), @@ -444,8 +591,10 @@ static void split_pack_geometry(struct pack_geometry *geometry, int factor) geometry->split = split; } -static struct packed_git *get_largest_active_pack(struct pack_geometry *geometry) +static struct packed_git *get_preferred_pack(struct pack_geometry *geometry) { + uint32_t i; + if (!geometry) { /* * No geometry means either an all-into-one repack (in which @@ -460,18 +609,55 @@ static struct packed_git *get_largest_active_pack(struct pack_geometry *geometry } if (geometry->split == geometry->pack_nr) return NULL; - return geometry->pack[geometry->pack_nr - 1]; + + /* + * The preferred pack is the largest pack above the split line. In + * other words, it is the largest pack that does not get rolled up in + * the geometric repack. + */ + for (i = geometry->pack_nr; i > geometry->split; i--) + /* + * A pack that is not local would never be included in a + * multi-pack index. We thus skip over any non-local packs. + */ + if (geometry->pack[i - 1]->pack_local) + return geometry->pack[i - 1]; + + return NULL; } -static void clear_pack_geometry(struct pack_geometry *geometry) +static void geometry_remove_redundant_packs(struct pack_geometry *geometry, + struct string_list *names, + struct existing_packs *existing) +{ + struct strbuf buf = STRBUF_INIT; + uint32_t i; + + for (i = 0; i < geometry->split; i++) { + struct packed_git *p = geometry->pack[i]; + if (string_list_has_string(names, hash_to_hex(p->hash))) + continue; + + strbuf_reset(&buf); + strbuf_addstr(&buf, pack_basename(p)); + strbuf_strip_suffix(&buf, ".pack"); + + if ((p->pack_keep) || + (string_list_has_string(&existing->kept_packs, buf.buf))) + continue; + + remove_redundant_pack(packdir, buf.buf); + } + + strbuf_release(&buf); +} + +static void free_pack_geometry(struct pack_geometry *geometry) { if (!geometry) return; free(geometry->pack); - geometry->pack_nr = 0; - geometry->pack_alloc = 0; - geometry->split = 0; } struct midx_snapshot_ref_data { @@ -537,57 +723,76 @@ static void midx_snapshot_refs(struct tempfile *f) } static void midx_included_packs(struct string_list *include, - struct string_list *existing_nonkept_packs, - struct string_list *existing_kept_packs, + struct existing_packs *existing, struct string_list *names, struct pack_geometry *geometry) { struct string_list_item *item; - for_each_string_list_item(item, existing_kept_packs) + for_each_string_list_item(item, &existing->kept_packs) string_list_insert(include, xstrfmt("%s.idx", item->string)); for_each_string_list_item(item, names) string_list_insert(include, xstrfmt("pack-%s.idx", item->string)); - if (geometry) { + if (geometry->split_factor) { struct strbuf buf = STRBUF_INIT; uint32_t i; for (i = geometry->split; i < geometry->pack_nr; i++) { struct packed_git *p = geometry->pack[i]; + /* + * The multi-pack index never refers to packfiles part + * of an alternate object database, so we skip these. + * While git-multi-pack-index(1) would silently ignore + * them anyway, this allows us to skip executing the + * command completely when we have only non-local + * packfiles. + */ + if (!p->pack_local) + continue; + strbuf_addstr(&buf, pack_basename(p)); strbuf_strip_suffix(&buf, ".pack"); strbuf_addstr(&buf, ".idx"); string_list_insert(include, strbuf_detach(&buf, NULL)); } - - for_each_string_list_item(item, existing_nonkept_packs) { - if (!((uintptr_t)item->util & CRUFT_PACK)) { - /* - * no need to check DELETE_PACK, since we're not - * doing an ALL_INTO_ONE repack - */ - continue; - } - string_list_insert(include, xstrfmt("%s.idx", item->string)); - } } else { - for_each_string_list_item(item, existing_nonkept_packs) { - if ((uintptr_t)item->util & DELETE_PACK) + for_each_string_list_item(item, &existing->non_kept_packs) { + if (pack_is_marked_for_deletion(item)) continue; string_list_insert(include, xstrfmt("%s.idx", item->string)); } } + + for_each_string_list_item(item, &existing->cruft_packs) { + /* + * When doing a --geometric repack, there is no need to check + * for deleted packs, since we're by definition not doing an + * ALL_INTO_ONE repack (hence no packs will be deleted). + * Otherwise we must check for and exclude any packs which are + * enqueued for deletion. + * + * So we could omit the conditional below in the --geometric + * case, but doing so is unnecessary since no packs are marked + * as pending deletion (since we only call + * `mark_packs_for_deletion()` when doing an all-into-one + * repack). + */ + if (pack_is_marked_for_deletion(item)) + continue; + string_list_insert(include, xstrfmt("%s.idx", item->string)); + } } static int write_midx_included_packs(struct string_list *include, struct pack_geometry *geometry, + struct string_list *names, const char *refs_snapshot, int show_progress, int write_bitmaps) { struct child_process cmd = CHILD_PROCESS_INIT; struct string_list_item *item; - struct packed_git *largest = get_largest_active_pack(geometry); + struct packed_git *preferred = get_preferred_pack(geometry); FILE *in; int ret; @@ -608,9 +813,41 @@ static int write_midx_included_packs(struct string_list *include, if (write_bitmaps) strvec_push(&cmd.args, "--bitmap"); - if (largest) + if (preferred) strvec_pushf(&cmd.args, "--preferred-pack=%s", - pack_basename(largest)); + pack_basename(preferred)); + else if (names->nr) { + /* The largest pack was repacked, meaning that either + * one or two packs exist depending on whether the + * repository has a cruft pack or not. + * + * Select the non-cruft one as preferred to encourage + * pack-reuse among packs containing reachable objects + * over unreachable ones. + * + * (Note we could write multiple packs here if + * `--max-pack-size` was given, but any one of them + * will suffice, so pick the first one.) + */ + for_each_string_list_item(item, names) { + struct generated_pack_data *data = item->util; + if (has_pack_ext(data, ".mtimes")) + continue; + + strvec_pushf(&cmd.args, "--preferred-pack=pack-%s.pack", + item->string); + break; + } + } else { + /* + * No packs were kept, and no packs were written. The + * only thing remaining are .keep packs (unless + * --pack-kept-objects was given). + * + * Set the `--preferred-pack` arbitrarily here. + */ + ; + } if (refs_snapshot) strvec_pushf(&cmd.args, "--refs-snapshot=%s", refs_snapshot); @@ -656,18 +893,163 @@ static void remove_redundant_bitmaps(struct string_list *include, strbuf_release(&path); } +static int finish_pack_objects_cmd(struct child_process *cmd, + struct string_list *names, + int local) +{ + FILE *out; + struct strbuf line = STRBUF_INIT; + + out = xfdopen(cmd->out, "r"); + while (strbuf_getline_lf(&line, out) != EOF) { + struct string_list_item *item; + + if (line.len != the_hash_algo->hexsz) + die(_("repack: Expecting full hex object ID lines only " + "from pack-objects.")); + /* + * Avoid putting packs written outside of the repository in the + * list of names. + */ + if (local) { + item = string_list_append(names, line.buf); + item->util = populate_pack_exts(line.buf); + } + } + fclose(out); + + strbuf_release(&line); + + return finish_command(cmd); +} + +static int write_filtered_pack(const struct pack_objects_args *args, + const char *destination, + const char *pack_prefix, + struct existing_packs *existing, + struct string_list *names) +{ + struct child_process cmd = CHILD_PROCESS_INIT; + struct string_list_item *item; + FILE *in; + int ret; + const char *caret; + const char *scratch; + int local = skip_prefix(destination, packdir, &scratch); + + prepare_pack_objects(&cmd, args, destination); + + strvec_push(&cmd.args, "--stdin-packs"); + + if (!pack_kept_objects) + strvec_push(&cmd.args, "--honor-pack-keep"); + for_each_string_list_item(item, &existing->kept_packs) + strvec_pushf(&cmd.args, "--keep-pack=%s", item->string); + + cmd.in = -1; + + ret = start_command(&cmd); + if (ret) + return ret; + + /* + * Here 'names' contains only the pack(s) that were just + * written, which is exactly the packs we want to keep. Also + * 'existing_kept_packs' already contains the packs in + * 'keep_pack_list'. + */ + in = xfdopen(cmd.in, "w"); + for_each_string_list_item(item, names) + fprintf(in, "^%s-%s.pack\n", pack_prefix, item->string); + for_each_string_list_item(item, &existing->non_kept_packs) + fprintf(in, "%s.pack\n", item->string); + for_each_string_list_item(item, &existing->cruft_packs) + fprintf(in, "%s.pack\n", item->string); + caret = pack_kept_objects ? "" : "^"; + for_each_string_list_item(item, &existing->kept_packs) + fprintf(in, "%s%s.pack\n", caret, item->string); + fclose(in); + + return finish_pack_objects_cmd(&cmd, names, local); +} + +static int existing_cruft_pack_cmp(const void *va, const void *vb) +{ + struct packed_git *a = *(struct packed_git **)va; + struct packed_git *b = *(struct packed_git **)vb; + + if (a->pack_size < b->pack_size) + return -1; + if (a->pack_size > b->pack_size) + return 1; + return 0; +} + +static void collapse_small_cruft_packs(FILE *in, size_t max_size, + struct existing_packs *existing) +{ + struct packed_git **existing_cruft, *p; + struct strbuf buf = STRBUF_INIT; + size_t total_size = 0; + size_t existing_cruft_nr = 0; + size_t i; + + ALLOC_ARRAY(existing_cruft, existing->cruft_packs.nr); + + for (p = get_all_packs(the_repository); p; p = p->next) { + if (!(p->is_cruft && p->pack_local)) + continue; + + strbuf_reset(&buf); + strbuf_addstr(&buf, pack_basename(p)); + strbuf_strip_suffix(&buf, ".pack"); + + if (!string_list_has_string(&existing->cruft_packs, buf.buf)) + continue; + + if (existing_cruft_nr >= existing->cruft_packs.nr) + BUG("too many cruft packs (found %"PRIuMAX", but knew " + "of %"PRIuMAX")", + (uintmax_t)existing_cruft_nr + 1, + (uintmax_t)existing->cruft_packs.nr); + existing_cruft[existing_cruft_nr++] = p; + } + + QSORT(existing_cruft, existing_cruft_nr, existing_cruft_pack_cmp); + + for (i = 0; i < existing_cruft_nr; i++) { + size_t proposed; + + p = existing_cruft[i]; + proposed = st_add(total_size, p->pack_size); + + if (proposed <= max_size) { + total_size = proposed; + fprintf(in, "-%s\n", pack_basename(p)); + } else { + retain_cruft_pack(existing, p); + fprintf(in, "%s\n", pack_basename(p)); + } + } + + for (i = 0; i < existing->non_kept_packs.nr; i++) + fprintf(in, "-%s.pack\n", + existing->non_kept_packs.items[i].string); + + strbuf_release(&buf); + free(existing_cruft); +} + static int write_cruft_pack(const struct pack_objects_args *args, const char *destination, const char *pack_prefix, const char *cruft_expiration, struct string_list *names, - struct string_list *existing_packs, - struct string_list *existing_kept_packs) + struct existing_packs *existing) { struct child_process cmd = CHILD_PROCESS_INIT; - struct strbuf line = STRBUF_INIT; struct string_list_item *item; - FILE *in, *out; + FILE *in; int ret; const char *scratch; int local = skip_prefix(destination, packdir, &scratch); @@ -681,7 +1063,6 @@ static int write_cruft_pack(const struct pack_objects_args *args, strvec_push(&cmd.args, "--honor-pack-keep"); strvec_push(&cmd.args, "--non-empty"); - strvec_push(&cmd.args, "--max-pack-size=0"); cmd.in = -1; @@ -705,33 +1086,30 @@ static int write_cruft_pack(const struct pack_objects_args *args, in = xfdopen(cmd.in, "w"); for_each_string_list_item(item, names) fprintf(in, "%s-%s.pack\n", pack_prefix, item->string); - for_each_string_list_item(item, existing_packs) - fprintf(in, "-%s.pack\n", item->string); - for_each_string_list_item(item, existing_kept_packs) + if (args->max_pack_size && !cruft_expiration) { + collapse_small_cruft_packs(in, args->max_pack_size, existing); + } else { + for_each_string_list_item(item, &existing->non_kept_packs) + fprintf(in, "-%s.pack\n", item->string); + for_each_string_list_item(item, &existing->cruft_packs) + fprintf(in, "-%s.pack\n", item->string); + } + for_each_string_list_item(item, &existing->kept_packs) fprintf(in, "%s.pack\n", item->string); fclose(in); - out = xfdopen(cmd.out, "r"); - while (strbuf_getline_lf(&line, out) != EOF) { - struct string_list_item *item; - - if (line.len != the_hash_algo->hexsz) - die(_("repack: Expecting full hex object ID lines only " - "from pack-objects.")); - /* - * avoid putting packs written outside of the repository in the - * list of names - */ - if (local) { - item = string_list_append(names, line.buf); - item->util = populate_pack_exts(line.buf); - } - } - fclose(out); - - strbuf_release(&line); + return finish_pack_objects_cmd(&cmd, names, local); +} - return finish_command(&cmd); +static const char *find_pack_prefix(const char *packdir, const char *packtmp) +{ + const char *pack_prefix; + if (!skip_prefix(packtmp, packdir, &pack_prefix)) + die(_("pack prefix %s does not begin with objdir %s"), + packtmp, packdir); + if (*pack_prefix == '/') + pack_prefix++; + return pack_prefix; } int cmd_repack(int argc, const char **argv, const char *prefix) @@ -739,13 +1117,10 @@ int cmd_repack(int argc, const char **argv, const char *prefix) struct child_process cmd = CHILD_PROCESS_INIT; struct string_list_item *item; struct string_list names = STRING_LIST_INIT_DUP; - struct string_list existing_nonkept_packs = STRING_LIST_INIT_DUP; - struct string_list existing_kept_packs = STRING_LIST_INIT_DUP; - struct pack_geometry *geometry = NULL; - struct strbuf line = STRBUF_INIT; + struct existing_packs existing = EXISTING_PACKS_INIT; + struct pack_geometry geometry = { 0 }; struct tempfile *refs_snapshot = NULL; int i, ext, ret; - FILE *out; int show_progress; /* variables to be filled by option parsing */ @@ -755,10 +1130,10 @@ int cmd_repack(int argc, const char **argv, const char *prefix) struct string_list keep_pack_list = STRING_LIST_INIT_NODUP; struct pack_objects_args po_args = {NULL}; struct pack_objects_args cruft_po_args = {NULL}; - int geometric_factor = 0; int write_midx = 0; const char *cruft_expiration = NULL; const char *expire_to = NULL; + const char *filter_to = NULL; struct option builtin_repack_options[] = { OPT_BIT('a', NULL, &pack_everything, @@ -770,7 +1145,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix) N_("same as -a, pack unreachable cruft objects separately"), PACK_CRUFT), OPT_STRING(0, "cruft-expiration", &cruft_expiration, N_("approxidate"), - N_("with -C, expire objects older than this")), + N_("with --cruft, expire objects older than this")), + OPT_MAGNITUDE(0, "max-cruft-size", &cruft_po_args.max_pack_size, + N_("with --cruft, limit the size of new cruft packs")), OPT_BOOL('d', NULL, &delete_redundant, N_("remove redundant packs, and run git-prune-packed")), OPT_BOOL('f', NULL, &po_args.no_reuse_delta, @@ -798,21 +1175,26 @@ int cmd_repack(int argc, const char **argv, const char *prefix) N_("limits the maximum delta depth")), OPT_STRING(0, "threads", &po_args.threads, N_("n"), N_("limits the maximum number of threads")), - OPT_STRING(0, "max-pack-size", &po_args.max_pack_size, N_("bytes"), + OPT_MAGNITUDE(0, "max-pack-size", &po_args.max_pack_size, N_("maximum size of each packfile")), + OPT_PARSE_LIST_OBJECTS_FILTER(&po_args.filter_options), OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects, N_("repack objects in packs marked with .keep")), OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"), N_("do not repack this pack")), - OPT_INTEGER('g', "geometric", &geometric_factor, + OPT_INTEGER('g', "geometric", &geometry.split_factor, N_("find a geometric progression with factor <N>")), OPT_BOOL('m', "write-midx", &write_midx, N_("write a multi-pack index of the resulting packs")), OPT_STRING(0, "expire-to", &expire_to, N_("dir"), N_("pack prefix to store a pack containing pruned objects")), + OPT_STRING(0, "filter-to", &filter_to, N_("dir"), + N_("pack prefix to store a pack containing filtered out objects")), OPT_END() }; + list_objects_filter_init(&po_args.filter_options); + git_config(repack_config, &cruft_po_args); argc = parse_options(argc, argv, prefix, builtin_repack_options, @@ -821,19 +1203,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (delete_redundant && repository_format_precious_objects) die(_("cannot delete packs in a precious-objects repo")); - if (keep_unreachable && - (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE))) - die(_("options '%s' and '%s' cannot be used together"), "--keep-unreachable", "-A"); + die_for_incompatible_opt3(unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE), "-A", + keep_unreachable, "-k/--keep-unreachable", + pack_everything & PACK_CRUFT, "--cruft"); - if (pack_everything & PACK_CRUFT) { + if (pack_everything & PACK_CRUFT) pack_everything |= ALL_INTO_ONE; - if (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)) - die(_("options '%s' and '%s' cannot be used together"), "--cruft", "-A"); - if (keep_unreachable) - die(_("options '%s' and '%s' cannot be used together"), "--cruft", "-k"); - } - if (write_bitmaps < 0) { if (!write_midx && (!(pack_everything & ALL_INTO_ONE) || !is_bare_repository())) @@ -849,6 +1225,18 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (write_bitmaps && !(pack_everything & ALL_INTO_ONE) && !write_midx) die(_(incremental_bitmap_conflict_error)); + if (write_bitmaps && po_args.local && has_alt_odb(the_repository)) { + /* + * When asked to do a local repack, but we have + * packfiles that are inherited from an alternate, then + * we cannot guarantee that the multi-pack-index would + * have full coverage of all objects. We thus disable + * writing bitmaps in that case. + */ + warning(_("disabling bitmap writing, as some objects are not being packed")); + write_bitmaps = 0; + } + if (write_midx && write_bitmaps) { struct strbuf path = STRBUF_INIT; @@ -865,14 +1253,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix) packtmp_name = xstrfmt(".tmp-%d-pack", (int)getpid()); packtmp = mkpathdup("%s/%s", packdir, packtmp_name); - collect_pack_filenames(&existing_nonkept_packs, &existing_kept_packs, - &keep_pack_list); + collect_pack_filenames(&existing, &keep_pack_list); - if (geometric_factor) { + if (geometry.split_factor) { if (pack_everything) die(_("options '%s' and '%s' cannot be used together"), "--geometric", "-A/-a"); - init_pack_geometry(&geometry, &existing_kept_packs); - split_pack_geometry(geometry, geometric_factor); + init_pack_geometry(&geometry, &existing, &po_args); + split_pack_geometry(&geometry); } prepare_pack_objects(&cmd, &po_args, packtmp); @@ -886,7 +1273,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) strvec_pushf(&cmd.args, "--keep-pack=%s", keep_pack_list.items[i].string); strvec_push(&cmd.args, "--non-empty"); - if (!geometry) { + if (!geometry.split_factor) { /* * We need to grab all reachable objects, including those that * are reachable from reflogs and the index. @@ -901,7 +1288,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) strvec_push(&cmd.args, "--reflog"); strvec_push(&cmd.args, "--indexed-objects"); } - if (has_promisor_remote()) + if (repo_has_promisor_remote(the_repository)) strvec_push(&cmd.args, "--exclude-promisor-objects"); if (!write_midx) { if (write_bitmaps > 0) @@ -915,7 +1302,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (pack_everything & ALL_INTO_ONE) { repack_promisor_objects(&po_args, &names); - if (existing_nonkept_packs.nr && delete_redundant && + if (has_existing_non_kept_packs(&existing) && + delete_redundant && !(pack_everything & PACK_CRUFT)) { for_each_string_list_item(item, &names) { strvec_pushf(&cmd.args, "--keep-pack=%s-%s.pack", @@ -933,7 +1321,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) strvec_push(&cmd.args, "--pack-loose-unreachable"); } } - } else if (geometry) { + } else if (geometry.split_factor) { strvec_push(&cmd.args, "--stdin-packs"); strvec_push(&cmd.args, "--unpacked"); } else { @@ -941,7 +1329,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix) strvec_push(&cmd.args, "--incremental"); } - if (geometry) + if (po_args.filter_options.choice) + strvec_pushf(&cmd.args, "--filter=%s", + expand_list_objects_filter_spec(&po_args.filter_options)); + else if (filter_to) + die(_("option '%s' can only be used along with '%s'"), "--filter-to", "--filter"); + + if (geometry.split_factor) cmd.in = -1; else cmd.no_stdin = 1; @@ -950,32 +1344,21 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (ret) goto cleanup; - if (geometry) { + if (geometry.split_factor) { FILE *in = xfdopen(cmd.in, "w"); /* * The resulting pack should contain all objects in packs that * are going to be rolled up, but exclude objects in packs which * are being left alone. */ - for (i = 0; i < geometry->split; i++) - fprintf(in, "%s\n", pack_basename(geometry->pack[i])); - for (i = geometry->split; i < geometry->pack_nr; i++) - fprintf(in, "^%s\n", pack_basename(geometry->pack[i])); + for (i = 0; i < geometry.split; i++) + fprintf(in, "%s\n", pack_basename(geometry.pack[i])); + for (i = geometry.split; i < geometry.pack_nr; i++) + fprintf(in, "^%s\n", pack_basename(geometry.pack[i])); fclose(in); } - out = xfdopen(cmd.out, "r"); - while (strbuf_getline_lf(&line, out) != EOF) { - struct string_list_item *item; - - if (line.len != the_hash_algo->hexsz) - die(_("repack: Expecting full hex object ID lines only from pack-objects.")); - item = string_list_append(&names, line.buf); - item->util = populate_pack_exts(item->string); - } - strbuf_release(&line); - fclose(out); - ret = finish_command(&cmd); + ret = finish_pack_objects_cmd(&cmd, &names, 1); if (ret) goto cleanup; @@ -983,12 +1366,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) printf_ln(_("Nothing new to pack.")); if (pack_everything & PACK_CRUFT) { - const char *pack_prefix; - if (!skip_prefix(packtmp, packdir, &pack_prefix)) - die(_("pack prefix %s does not begin with objdir %s"), - packtmp, packdir); - if (*pack_prefix == '/') - pack_prefix++; + const char *pack_prefix = find_pack_prefix(packdir, packtmp); if (!cruft_po_args.window) cruft_po_args.window = po_args.window; @@ -998,14 +1376,15 @@ int cmd_repack(int argc, const char **argv, const char *prefix) cruft_po_args.depth = po_args.depth; if (!cruft_po_args.threads) cruft_po_args.threads = po_args.threads; + if (!cruft_po_args.max_pack_size) + cruft_po_args.max_pack_size = po_args.max_pack_size; cruft_po_args.local = po_args.local; cruft_po_args.quiet = po_args.quiet; ret = write_cruft_pack(&cruft_po_args, packtmp, pack_prefix, cruft_expiration, &names, - &existing_nonkept_packs, - &existing_kept_packs); + &existing); if (ret) goto cleanup; @@ -1036,13 +1415,25 @@ int cmd_repack(int argc, const char **argv, const char *prefix) pack_prefix, NULL, &names, - &existing_nonkept_packs, - &existing_kept_packs); + &existing); if (ret) goto cleanup; } } + if (po_args.filter_options.choice) { + if (!filter_to) + filter_to = packtmp; + + ret = write_filtered_pack(&po_args, + filter_to, + find_pack_prefix(packdir, packtmp), + &existing, + &names); + if (ret) + goto cleanup; + } + string_list_sort(&names); close_object_store(the_repository->objects); @@ -1081,31 +1472,14 @@ int cmd_repack(int argc, const char **argv, const char *prefix) } /* End of pack replacement. */ - if (delete_redundant && pack_everything & ALL_INTO_ONE) { - const int hexsz = the_hash_algo->hexsz; - for_each_string_list_item(item, &existing_nonkept_packs) { - char *sha1; - size_t len = strlen(item->string); - if (len < hexsz) - continue; - sha1 = item->string + len - hexsz; - /* - * Mark this pack for deletion, which ensures that this - * pack won't be included in a MIDX (if `--write-midx` - * was given) and that we will actually delete this pack - * (if `-d` was given). - */ - if (!string_list_has_string(&names, sha1)) - item->util = (void*)(uintptr_t)((size_t)item->util | DELETE_PACK); - } - } + if (delete_redundant && pack_everything & ALL_INTO_ONE) + mark_packs_for_deletion(&existing, &names); if (write_midx) { struct string_list include = STRING_LIST_INIT_NODUP; - midx_included_packs(&include, &existing_nonkept_packs, - &existing_kept_packs, &names, geometry); + midx_included_packs(&include, &existing, &names, &geometry); - ret = write_midx_included_packs(&include, geometry, + ret = write_midx_included_packs(&include, &geometry, &names, refs_snapshot ? get_tempfile_path(refs_snapshot) : NULL, show_progress, write_bitmaps > 0); @@ -1122,35 +1496,11 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (delete_redundant) { int opts = 0; - for_each_string_list_item(item, &existing_nonkept_packs) { - if (!((uintptr_t)item->util & DELETE_PACK)) - continue; - remove_redundant_pack(packdir, item->string); - } - - if (geometry) { - struct strbuf buf = STRBUF_INIT; - - uint32_t i; - for (i = 0; i < geometry->split; i++) { - struct packed_git *p = geometry->pack[i]; - if (string_list_has_string(&names, - hash_to_hex(p->hash))) - continue; + remove_redundant_existing_packs(&existing); - strbuf_reset(&buf); - strbuf_addstr(&buf, pack_basename(p)); - strbuf_strip_suffix(&buf, ".pack"); - - if ((p->pack_keep) || - (string_list_has_string(&existing_kept_packs, - buf.buf))) - continue; - - remove_redundant_pack(packdir, buf.buf); - } - strbuf_release(&buf); - } + if (geometry.split_factor) + geometry_remove_redundant_packs(&geometry, &names, + &existing); if (show_progress) opts |= PRUNE_PACKED_VERBOSE; prune_packed_objects(opts); @@ -1174,9 +1524,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix) cleanup: string_list_clear(&names, 1); - string_list_clear(&existing_nonkept_packs, 0); - string_list_clear(&existing_kept_packs, 0); - clear_pack_geometry(geometry); + existing_packs_release(&existing); + free_pack_geometry(&geometry); + list_objects_filter_release(&po_args.filter_options); return ret; } diff --git a/builtin/replace.c b/builtin/replace.c index a29e911d30..da59600ad2 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -8,15 +8,23 @@ * git-tag.sh and mktag.c by Linus Torvalds. */ -#include "cache.h" -#include "config.h" #include "builtin.h" +#include "config.h" +#include "editor.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "refs.h" #include "parse-options.h" +#include "path.h" #include "run-command.h" -#include "object-store.h" +#include "object-file.h" +#include "object-name.h" +#include "object-store-ll.h" +#include "replace-object.h" #include "repository.h" #include "tag.h" +#include "wildmatch.h" static const char * const git_replace_usage[] = { N_("git replace [-f] <object> <replacement>"), @@ -41,7 +49,7 @@ struct show_data { static int show_reference(struct repository *r, const char *refname, const struct object_id *oid, - int flag, void *cb_data) + int flag UNUSED, void *cb_data) { struct show_data *data = cb_data; @@ -54,7 +62,7 @@ static int show_reference(struct repository *r, const char *refname, struct object_id object; enum object_type obj_type, repl_type; - if (get_oid(refname, &object)) + if (repo_get_oid(r, refname, &object)) return error(_("failed to resolve '%s' as a valid ref"), refname); obj_type = oid_object_info(r, &object, NULL); @@ -112,7 +120,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn) base_len = ref.len; for (p = argv; *p; p++) { - if (get_oid(*p, &oid)) { + if (repo_get_oid(the_repository, *p, &oid)) { error("failed to resolve '%s' as a valid ref", *p); had_error = 1; continue; @@ -206,10 +214,10 @@ static int replace_object(const char *object_ref, const char *replace_ref, int f { struct object_id object, repl; - if (get_oid(object_ref, &object)) + if (repo_get_oid(the_repository, object_ref, &object)) return error(_("failed to resolve '%s' as a valid ref"), object_ref); - if (get_oid(replace_ref, &repl)) + if (repo_get_oid(the_repository, replace_ref, &repl)) return error(_("failed to resolve '%s' as a valid ref"), replace_ref); @@ -320,7 +328,7 @@ static int edit_and_replace(const char *object_ref, int force, int raw) struct object_id old_oid, new_oid, prev; struct strbuf ref = STRBUF_INIT; - if (get_oid(object_ref, &old_oid) < 0) + if (repo_get_oid(the_repository, object_ref, &old_oid) < 0) return error(_("not a valid object name: '%s'"), object_ref); type = oid_object_info(the_repository, &old_oid, NULL); @@ -375,7 +383,7 @@ static int replace_parents(struct strbuf *buf, int argc, const char **argv) struct object_id oid; struct commit *commit; - if (get_oid(argv[i], &oid) < 0) { + if (repo_get_oid(the_repository, argv[i], &oid) < 0) { strbuf_release(&new_parents); return error(_("not a valid object name: '%s'"), argv[i]); @@ -401,7 +409,7 @@ struct check_mergetag_data { const char **argv; }; -static int check_one_mergetag(struct commit *commit, +static int check_one_mergetag(struct commit *commit UNUSED, struct commit_extra_header *extra, void *data) { @@ -422,7 +430,7 @@ static int check_one_mergetag(struct commit *commit, /* iterate over new parents */ for (i = 1; i < mergetag_data->argc; i++) { struct object_id oid; - if (get_oid(mergetag_data->argv[i], &oid) < 0) + if (repo_get_oid(the_repository, mergetag_data->argv[i], &oid) < 0) return error(_("not a valid object name: '%s'"), mergetag_data->argv[i]); if (oideq(get_tagged_oid(tag), &oid)) @@ -452,15 +460,15 @@ static int create_graft(int argc, const char **argv, int force, int gentle) const char *buffer; unsigned long size; - if (get_oid(old_ref, &old_oid) < 0) + if (repo_get_oid(the_repository, old_ref, &old_oid) < 0) return error(_("not a valid object name: '%s'"), old_ref); commit = lookup_commit_reference(the_repository, &old_oid); if (!commit) return error(_("could not parse %s"), old_ref); - buffer = get_commit_buffer(commit, &size); + buffer = repo_get_commit_buffer(the_repository, commit, &size); strbuf_add(&buf, buffer, size); - unuse_commit_buffer(commit, buffer); + repo_unuse_commit_buffer(the_repository, commit, buffer); if (replace_parents(&buf, argc - 1, &argv[1]) < 0) { strbuf_release(&buf); @@ -559,7 +567,7 @@ int cmd_replace(int argc, const char **argv, const char *prefix) OPT_END() }; - read_replace_refs = 0; + disable_replace_refs(); git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0); diff --git a/builtin/replay.c b/builtin/replay.c new file mode 100644 index 0000000000..6bc4b47f09 --- /dev/null +++ b/builtin/replay.c @@ -0,0 +1,446 @@ +/* + * "git replay" builtin command + */ + +#define USE_THE_INDEX_VARIABLE +#include "git-compat-util.h" + +#include "builtin.h" +#include "environment.h" +#include "hex.h" +#include "lockfile.h" +#include "merge-ort.h" +#include "object-name.h" +#include "parse-options.h" +#include "refs.h" +#include "revision.h" +#include "strmap.h" +#include <oidset.h> +#include <tree.h> + +static const char *short_commit_name(struct commit *commit) +{ + return repo_find_unique_abbrev(the_repository, &commit->object.oid, + DEFAULT_ABBREV); +} + +static struct commit *peel_committish(const char *name) +{ + struct object *obj; + struct object_id oid; + + if (repo_get_oid(the_repository, name, &oid)) + return NULL; + obj = parse_object(the_repository, &oid); + return (struct commit *)repo_peel_to_type(the_repository, name, 0, obj, + OBJ_COMMIT); +} + +static char *get_author(const char *message) +{ + size_t len; + const char *a; + + a = find_commit_header(message, "author", &len); + if (a) + return xmemdupz(a, len); + + return NULL; +} + +static struct commit *create_commit(struct tree *tree, + struct commit *based_on, + struct commit *parent) +{ + struct object_id ret; + struct object *obj; + struct commit_list *parents = NULL; + char *author; + char *sign_commit = NULL; /* FIXME: cli users might want to sign again */ + struct commit_extra_header *extra; + struct strbuf msg = STRBUF_INIT; + const char *out_enc = get_commit_output_encoding(); + const char *message = repo_logmsg_reencode(the_repository, based_on, + NULL, out_enc); + const char *orig_message = NULL; + const char *exclude_gpgsig[] = { "gpgsig", NULL }; + + commit_list_insert(parent, &parents); + extra = read_commit_extra_headers(based_on, exclude_gpgsig); + find_commit_subject(message, &orig_message); + strbuf_addstr(&msg, orig_message); + author = get_author(message); + reset_ident_date(); + if (commit_tree_extended(msg.buf, msg.len, &tree->object.oid, parents, + &ret, author, NULL, sign_commit, extra)) { + error(_("failed to write commit object")); + return NULL; + } + free(author); + strbuf_release(&msg); + + obj = parse_object(the_repository, &ret); + return (struct commit *)obj; +} + +struct ref_info { + struct commit *onto; + struct strset positive_refs; + struct strset negative_refs; + int positive_refexprs; + int negative_refexprs; +}; + +static void get_ref_information(struct rev_cmdline_info *cmd_info, + struct ref_info *ref_info) +{ + int i; + + ref_info->onto = NULL; + strset_init(&ref_info->positive_refs); + strset_init(&ref_info->negative_refs); + ref_info->positive_refexprs = 0; + ref_info->negative_refexprs = 0; + + /* + * When the user specifies e.g. + * git replay origin/main..mybranch + * git replay ^origin/next mybranch1 mybranch2 + * we want to be able to determine where to replay the commits. In + * these examples, the branches are probably based on an old version + * of either origin/main or origin/next, so we want to replay on the + * newest version of that branch. In contrast we would want to error + * out if they ran + * git replay ^origin/master ^origin/next mybranch + * git replay mybranch~2..mybranch + * the first of those because there's no unique base to choose, and + * the second because they'd likely just be replaying commits on top + * of the same commit and not making any difference. + */ + for (i = 0; i < cmd_info->nr; i++) { + struct rev_cmdline_entry *e = cmd_info->rev + i; + struct object_id oid; + const char *refexpr = e->name; + char *fullname = NULL; + int can_uniquely_dwim = 1; + + if (*refexpr == '^') + refexpr++; + if (repo_dwim_ref(the_repository, refexpr, strlen(refexpr), &oid, &fullname, 0) != 1) + can_uniquely_dwim = 0; + + if (e->flags & BOTTOM) { + if (can_uniquely_dwim) + strset_add(&ref_info->negative_refs, fullname); + if (!ref_info->negative_refexprs) + ref_info->onto = lookup_commit_reference_gently(the_repository, + &e->item->oid, 1); + ref_info->negative_refexprs++; + } else { + if (can_uniquely_dwim) + strset_add(&ref_info->positive_refs, fullname); + ref_info->positive_refexprs++; + } + + free(fullname); + } +} + +static void determine_replay_mode(struct rev_cmdline_info *cmd_info, + const char *onto_name, + const char **advance_name, + struct commit **onto, + struct strset **update_refs) +{ + struct ref_info rinfo; + + get_ref_information(cmd_info, &rinfo); + if (!rinfo.positive_refexprs) + die(_("need some commits to replay")); + if (onto_name && *advance_name) + die(_("--onto and --advance are incompatible")); + else if (onto_name) { + *onto = peel_committish(onto_name); + if (rinfo.positive_refexprs < + strset_get_size(&rinfo.positive_refs)) + die(_("all positive revisions given must be references")); + } else if (*advance_name) { + struct object_id oid; + char *fullname = NULL; + + *onto = peel_committish(*advance_name); + if (repo_dwim_ref(the_repository, *advance_name, strlen(*advance_name), + &oid, &fullname, 0) == 1) { + *advance_name = fullname; + } else { + die(_("argument to --advance must be a reference")); + } + if (rinfo.positive_refexprs > 1) + die(_("cannot advance target with multiple sources because ordering would be ill-defined")); + } else { + int positive_refs_complete = ( + rinfo.positive_refexprs == + strset_get_size(&rinfo.positive_refs)); + int negative_refs_complete = ( + rinfo.negative_refexprs == + strset_get_size(&rinfo.negative_refs)); + /* + * We need either positive_refs_complete or + * negative_refs_complete, but not both. + */ + if (rinfo.negative_refexprs > 0 && + positive_refs_complete == negative_refs_complete) + die(_("cannot implicitly determine whether this is an --advance or --onto operation")); + if (negative_refs_complete) { + struct hashmap_iter iter; + struct strmap_entry *entry; + + if (rinfo.negative_refexprs == 0) + die(_("all positive revisions given must be references")); + else if (rinfo.negative_refexprs > 1) + die(_("cannot implicitly determine whether this is an --advance or --onto operation")); + else if (rinfo.positive_refexprs > 1) + die(_("cannot advance target with multiple source branches because ordering would be ill-defined")); + + /* Only one entry, but we have to loop to get it */ + strset_for_each_entry(&rinfo.negative_refs, + &iter, entry) { + *advance_name = entry->key; + } + } else { /* positive_refs_complete */ + if (rinfo.negative_refexprs > 1) + die(_("cannot implicitly determine correct base for --onto")); + if (rinfo.negative_refexprs == 1) + *onto = rinfo.onto; + } + } + if (!*advance_name) { + *update_refs = xcalloc(1, sizeof(**update_refs)); + **update_refs = rinfo.positive_refs; + memset(&rinfo.positive_refs, 0, sizeof(**update_refs)); + } + strset_clear(&rinfo.negative_refs); + strset_clear(&rinfo.positive_refs); +} + +static struct commit *mapped_commit(kh_oid_map_t *replayed_commits, + struct commit *commit, + struct commit *fallback) +{ + khint_t pos = kh_get_oid_map(replayed_commits, commit->object.oid); + if (pos == kh_end(replayed_commits)) + return fallback; + return kh_value(replayed_commits, pos); +} + +static struct commit *pick_regular_commit(struct commit *pickme, + kh_oid_map_t *replayed_commits, + struct commit *onto, + struct merge_options *merge_opt, + struct merge_result *result) +{ + struct commit *base, *replayed_base; + struct tree *pickme_tree, *base_tree; + + base = pickme->parents->item; + replayed_base = mapped_commit(replayed_commits, base, onto); + + result->tree = repo_get_commit_tree(the_repository, replayed_base); + pickme_tree = repo_get_commit_tree(the_repository, pickme); + base_tree = repo_get_commit_tree(the_repository, base); + + merge_opt->branch1 = short_commit_name(replayed_base); + merge_opt->branch2 = short_commit_name(pickme); + merge_opt->ancestor = xstrfmt("parent of %s", merge_opt->branch2); + + merge_incore_nonrecursive(merge_opt, + base_tree, + result->tree, + pickme_tree, + result); + + free((char*)merge_opt->ancestor); + merge_opt->ancestor = NULL; + if (!result->clean) + return NULL; + return create_commit(result->tree, pickme, replayed_base); +} + +int cmd_replay(int argc, const char **argv, const char *prefix) +{ + const char *advance_name = NULL; + struct commit *onto = NULL; + const char *onto_name = NULL; + int contained = 0; + + struct rev_info revs; + struct commit *last_commit = NULL; + struct commit *commit; + struct merge_options merge_opt; + struct merge_result result; + struct strset *update_refs = NULL; + kh_oid_map_t *replayed_commits; + int ret = 0; + + const char * const replay_usage[] = { + N_("(EXPERIMENTAL!) git replay " + "([--contained] --onto <newbase> | --advance <branch>) " + "<revision-range>..."), + NULL + }; + struct option replay_options[] = { + OPT_STRING(0, "advance", &advance_name, + N_("branch"), + N_("make replay advance given branch")), + OPT_STRING(0, "onto", &onto_name, + N_("revision"), + N_("replay onto given commit")), + OPT_BOOL(0, "contained", &contained, + N_("advance all branches contained in revision-range")), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, replay_options, replay_usage, + PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT); + + if (!onto_name && !advance_name) { + error(_("option --onto or --advance is mandatory")); + usage_with_options(replay_usage, replay_options); + } + + if (advance_name && contained) + die(_("options '%s' and '%s' cannot be used together"), + "--advance", "--contained"); + + repo_init_revisions(the_repository, &revs, prefix); + + /* + * Set desired values for rev walking options here. If they + * are changed by some user specified option in setup_revisions() + * below, we will detect that below and then warn. + * + * TODO: In the future we might want to either die(), or allow + * some options changing these values if we think they could + * be useful. + */ + revs.reverse = 1; + revs.sort_order = REV_SORT_IN_GRAPH_ORDER; + revs.topo_order = 1; + revs.simplify_history = 0; + + argc = setup_revisions(argc, argv, &revs, NULL); + if (argc > 1) { + ret = error(_("unrecognized argument: %s"), argv[1]); + goto cleanup; + } + + /* + * Detect and warn if we override some user specified rev + * walking options. + */ + if (revs.reverse != 1) { + warning(_("some rev walking options will be overridden as " + "'%s' bit in 'struct rev_info' will be forced"), + "reverse"); + revs.reverse = 1; + } + if (revs.sort_order != REV_SORT_IN_GRAPH_ORDER) { + warning(_("some rev walking options will be overridden as " + "'%s' bit in 'struct rev_info' will be forced"), + "sort_order"); + revs.sort_order = REV_SORT_IN_GRAPH_ORDER; + } + if (revs.topo_order != 1) { + warning(_("some rev walking options will be overridden as " + "'%s' bit in 'struct rev_info' will be forced"), + "topo_order"); + revs.topo_order = 1; + } + if (revs.simplify_history != 0) { + warning(_("some rev walking options will be overridden as " + "'%s' bit in 'struct rev_info' will be forced"), + "simplify_history"); + revs.simplify_history = 0; + } + + determine_replay_mode(&revs.cmdline, onto_name, &advance_name, + &onto, &update_refs); + + if (!onto) /* FIXME: Should handle replaying down to root commit */ + die("Replaying down to root commit is not supported yet!"); + + if (prepare_revision_walk(&revs) < 0) { + ret = error(_("error preparing revisions")); + goto cleanup; + } + + init_merge_options(&merge_opt, the_repository); + memset(&result, 0, sizeof(result)); + merge_opt.show_rename_progress = 0; + last_commit = onto; + replayed_commits = kh_init_oid_map(); + while ((commit = get_revision(&revs))) { + const struct name_decoration *decoration; + khint_t pos; + int hr; + + if (!commit->parents) + die(_("replaying down to root commit is not supported yet!")); + if (commit->parents->next) + die(_("replaying merge commits is not supported yet!")); + + last_commit = pick_regular_commit(commit, replayed_commits, onto, + &merge_opt, &result); + if (!last_commit) + break; + + /* Record commit -> last_commit mapping */ + pos = kh_put_oid_map(replayed_commits, commit->object.oid, &hr); + if (hr == 0) + BUG("Duplicate rewritten commit: %s\n", + oid_to_hex(&commit->object.oid)); + kh_value(replayed_commits, pos) = last_commit; + + /* Update any necessary branches */ + if (advance_name) + continue; + decoration = get_name_decoration(&commit->object); + if (!decoration) + continue; + while (decoration) { + if (decoration->type == DECORATION_REF_LOCAL && + (contained || strset_contains(update_refs, + decoration->name))) { + printf("update %s %s %s\n", + decoration->name, + oid_to_hex(&last_commit->object.oid), + oid_to_hex(&commit->object.oid)); + } + decoration = decoration->next; + } + } + + /* In --advance mode, advance the target ref */ + if (result.clean == 1 && advance_name) { + printf("update %s %s %s\n", + advance_name, + oid_to_hex(&last_commit->object.oid), + oid_to_hex(&onto->object.oid)); + } + + merge_finalize(&merge_opt, &result); + kh_destroy_oid_map(replayed_commits); + if (update_refs) { + strset_clear(update_refs); + free(update_refs); + } + ret = result.clean; + +cleanup: + release_revisions(&revs); + + /* Return */ + if (ret < 0) + exit(128); + return ret ? 0 : 1; +} diff --git a/builtin/rerere.c b/builtin/rerere.c index 94ffb8c21a..07a9d37275 100644 --- a/builtin/rerere.c +++ b/builtin/rerere.c @@ -1,8 +1,9 @@ #include "builtin.h" -#include "cache.h" #include "config.h" #include "dir.h" +#include "gettext.h" #include "parse-options.h" +#include "repository.h" #include "string-list.h" #include "rerere.h" #include "xdiff/xdiff.h" diff --git a/builtin/reset.c b/builtin/reset.c index 0697fa89de..4b018d20e3 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -9,7 +9,12 @@ */ #define USE_THE_INDEX_VARIABLE #include "builtin.h" +#include "advice.h" #include "config.h" +#include "environment.h" +#include "gettext.h" +#include "hash.h" +#include "hex.h" #include "lockfile.h" #include "tag.h" #include "object.h" @@ -20,11 +25,17 @@ #include "diffcore.h" #include "tree.h" #include "branch.h" +#include "object-name.h" #include "parse-options.h" +#include "path.h" #include "unpack-trees.h" #include "cache-tree.h" +#include "setup.h" +#include "sparse-index.h" #include "submodule.h" #include "submodule-config.h" +#include "trace.h" +#include "trace2.h" #include "dir.h" #include "add-interactive.h" @@ -89,7 +100,7 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t if (reset_type == KEEP) { struct object_id head_oid; - if (get_oid("HEAD", &head_oid)) + if (repo_get_oid(the_repository, "HEAD", &head_oid)) return error(_("You do not have a valid HEAD.")); if (!fill_tree_descriptor(the_repository, desc + nr, &head_oid)) return error(_("Failed to find tree of HEAD.")); @@ -124,7 +135,7 @@ static void print_new_head_line(struct commit *commit) struct strbuf buf = STRBUF_INIT; printf(_("HEAD is now at %s"), - find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV)); + repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV)); pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf); if (buf.len > 0) @@ -260,8 +271,8 @@ static void parse_args(struct pathspec *pathspec, * has to be unambiguous. If there is a single argument, it * can not be a tree */ - else if ((!argv[1] && !get_oid_committish(argv[0], &unused)) || - (argv[1] && !get_oid_treeish(argv[0], &unused))) { + else if ((!argv[1] && !repo_get_oid_committish(the_repository, argv[0], &unused)) || + (argv[1] && !repo_get_oid_treeish(the_repository, argv[0], &unused))) { /* * Ok, argv[0] looks like a commit/tree; it should not * be a filename. @@ -288,9 +299,9 @@ static int reset_refs(const char *rev, const struct object_id *oid) struct object_id *orig = NULL, oid_orig, *old_orig = NULL, oid_old_orig; - if (!get_oid("ORIG_HEAD", &oid_old_orig)) + if (!repo_get_oid(the_repository, "ORIG_HEAD", &oid_old_orig)) old_orig = &oid_old_orig; - if (!get_oid("HEAD", &oid_orig)) { + if (!repo_get_oid(the_repository, "HEAD", &oid_orig)) { orig = &oid_orig; set_reflog_message(&msg, "updating ORIG_HEAD", NULL); update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0, @@ -304,12 +315,13 @@ static int reset_refs(const char *rev, const struct object_id *oid) return update_ref_status; } -static int git_reset_config(const char *var, const char *value, void *cb) +static int git_reset_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { if (!strcmp(var, "submodule.recurse")) return git_default_submodule_config(var, value, cb); - return git_default_config(var, value, cb); + return git_default_config(var, value, ctx, cb); } int cmd_reset(int argc, const char **argv, const char *prefix) @@ -317,7 +329,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix) int reset_type = NONE, update_ref_status = 0, quiet = 0; int no_refresh = 0; int patch_mode = 0, pathspec_file_nul = 0, unborn; - const char *rev, *pathspec_from_file = NULL; + const char *rev; + char *pathspec_from_file = NULL; struct object_id oid; struct pathspec pathspec; int intent_to_add = 0; @@ -325,18 +338,25 @@ int cmd_reset(int argc, const char **argv, const char *prefix) OPT__QUIET(&quiet, N_("be quiet, only report errors")), OPT_BOOL(0, "no-refresh", &no_refresh, N_("skip refreshing the index after reset")), - OPT_SET_INT(0, "mixed", &reset_type, - N_("reset HEAD and index"), MIXED), - OPT_SET_INT(0, "soft", &reset_type, N_("reset only HEAD"), SOFT), - OPT_SET_INT(0, "hard", &reset_type, - N_("reset HEAD, index and working tree"), HARD), - OPT_SET_INT(0, "merge", &reset_type, - N_("reset HEAD, index and working tree"), MERGE), - OPT_SET_INT(0, "keep", &reset_type, - N_("reset HEAD but keep local changes"), KEEP), + OPT_SET_INT_F(0, "mixed", &reset_type, + N_("reset HEAD and index"), + MIXED, PARSE_OPT_NONEG), + OPT_SET_INT_F(0, "soft", &reset_type, + N_("reset only HEAD"), + SOFT, PARSE_OPT_NONEG), + OPT_SET_INT_F(0, "hard", &reset_type, + N_("reset HEAD, index and working tree"), + HARD, PARSE_OPT_NONEG), + OPT_SET_INT_F(0, "merge", &reset_type, + N_("reset HEAD, index and working tree"), + MERGE, PARSE_OPT_NONEG), + OPT_SET_INT_F(0, "keep", &reset_type, + N_("reset HEAD but keep local changes"), + KEEP, PARSE_OPT_NONEG), OPT_CALLBACK_F(0, "recurse-submodules", NULL, - "reset", "control recursive updating of submodules", - PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater), + "reset", "control recursive updating of submodules", + PARSE_OPT_OPTARG, + option_parse_recurse_submodules_worktree_updater), OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that removed paths will be added later")), @@ -365,13 +385,14 @@ int cmd_reset(int argc, const char **argv, const char *prefix) die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file"); } - unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid); + unborn = !strcmp(rev, "HEAD") && repo_get_oid(the_repository, "HEAD", + &oid); if (unborn) { /* reset on unborn branch: treat as reset to empty tree */ oidcpy(&oid, the_hash_algo->empty_tree); } else if (!pathspec.nr && !patch_mode) { struct commit *commit; - if (get_oid_committish(rev, &oid)) + if (repo_get_oid_committish(the_repository, rev, &oid)) die(_("Failed to resolve '%s' as a valid revision."), rev); commit = lookup_commit_reference(the_repository, &oid); if (!commit) @@ -379,7 +400,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) oidcpy(&oid, &commit->object.oid); } else { struct tree *tree; - if (get_oid_treeish(rev, &oid)) + if (repo_get_oid_treeish(the_repository, rev, &oid)) die(_("Failed to resolve '%s' as a valid tree."), rev); tree = parse_tree_indirect(&oid); if (!tree) @@ -464,7 +485,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix) char *ref = NULL; int err; - dwim_ref(rev, strlen(rev), &dummy, &ref, 0); + repo_dwim_ref(the_repository, rev, strlen(rev), + &dummy, &ref, 0); if (ref && !starts_with(ref, "refs/")) FREE_AND_NULL(ref); @@ -495,5 +517,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix) cleanup: clear_pathspec(&pathspec); + free(pathspec_from_file); return update_ref_status; } diff --git a/builtin/rev-list.c b/builtin/rev-list.c index d42db0b0cc..181353dcf5 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -1,16 +1,20 @@ -#include "cache.h" +#include "builtin.h" #include "config.h" #include "commit.h" #include "diff.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "revision.h" #include "list-objects.h" #include "list-objects-filter.h" #include "list-objects-filter-options.h" #include "object.h" -#include "object-store.h" +#include "object-name.h" +#include "object-file.h" +#include "object-store-ll.h" #include "pack.h" #include "pack-bitmap.h" -#include "builtin.h" #include "log-tree.h" #include "graph.h" #include "bisect.h" @@ -38,7 +42,7 @@ static const char rev_list_usage[] = " --tags\n" " --remotes\n" " --stdin\n" -" --exclude-hidden=[receive|uploadpack]\n" +" --exclude-hidden=[fetch|receive|uploadpack]\n" " --quiet\n" " ordering output:\n" " --topo-order\n" @@ -96,7 +100,48 @@ static off_t get_object_disk_usage(struct object *obj) return size; } -static void finish_commit(struct commit *commit); +static inline void finish_object__ma(struct object *obj) +{ + /* + * Whether or not we try to dynamically fetch missing objects + * from the server, we currently DO NOT have the object. We + * can either print, allow (ignore), or conditionally allow + * (ignore) them. + */ + switch (arg_missing_action) { + case MA_ERROR: + die("missing %s object '%s'", + type_name(obj->type), oid_to_hex(&obj->oid)); + return; + + case MA_ALLOW_ANY: + return; + + case MA_PRINT: + oidset_insert(&missing_objects, &obj->oid); + return; + + case MA_ALLOW_PROMISOR: + if (is_promisor_object(&obj->oid)) + return; + die("unexpected missing %s object '%s'", + type_name(obj->type), oid_to_hex(&obj->oid)); + return; + + default: + BUG("unhandled missing_action"); + return; + } +} + +static void finish_commit(struct commit *commit) +{ + free_commit_list(commit->parents); + commit->parents = NULL; + free_commit_buffer(the_repository->parsed_objects, + commit); +} + static void show_commit(struct commit *commit, void *data) { struct rev_list_info *info = data; @@ -104,6 +149,12 @@ static void show_commit(struct commit *commit, void *data) display_progress(progress, ++progress_counter); + if (revs->do_not_die_on_missing_objects && + oidset_contains(&revs->missing_commits, &commit->object.oid)) { + finish_object__ma(&commit->object); + return; + } + if (show_disk_usage) total_disk_usage += get_object_disk_usage(&commit->object); @@ -134,7 +185,7 @@ static void show_commit(struct commit *commit, void *data) if (!revs->graph) fputs(get_revision_mark(revs, commit), stdout); if (revs->abbrev_commit && revs->abbrev) - fputs(find_unique_abbrev(&commit->object.oid, revs->abbrev), + fputs(repo_find_unique_abbrev(the_repository, &commit->object.oid, revs->abbrev), stdout); else fputs(oid_to_hex(&commit->object.oid), stdout); @@ -215,49 +266,8 @@ static void show_commit(struct commit *commit, void *data) finish_commit(commit); } -static void finish_commit(struct commit *commit) -{ - free_commit_list(commit->parents); - commit->parents = NULL; - free_commit_buffer(the_repository->parsed_objects, - commit); -} - -static inline void finish_object__ma(struct object *obj) -{ - /* - * Whether or not we try to dynamically fetch missing objects - * from the server, we currently DO NOT have the object. We - * can either print, allow (ignore), or conditionally allow - * (ignore) them. - */ - switch (arg_missing_action) { - case MA_ERROR: - die("missing %s object '%s'", - type_name(obj->type), oid_to_hex(&obj->oid)); - return; - - case MA_ALLOW_ANY: - return; - - case MA_PRINT: - oidset_insert(&missing_objects, &obj->oid); - return; - - case MA_ALLOW_PROMISOR: - if (is_promisor_object(&obj->oid)) - return; - die("unexpected missing %s object '%s'", - type_name(obj->type), oid_to_hex(&obj->oid)); - return; - - default: - BUG("unhandled missing_action"); - return; - } -} - -static int finish_object(struct object *obj, const char *name, void *cb_data) +static int finish_object(struct object *obj, const char *name UNUSED, + void *cb_data) { struct rev_list_info *info = cb_data; if (oid_object_info_extended(the_repository, &obj->oid, NULL, 0) < 0) { @@ -362,11 +372,11 @@ static int show_bisect_vars(struct rev_list_info *info, int reaches, int all) static int show_object_fast( const struct object_id *oid, - enum object_type type, - int exclude, - uint32_t name_hash, - struct packed_git *found_pack, - off_t found_offset) + enum object_type type UNUSED, + int exclude UNUSED, + uint32_t name_hash UNUSED, + struct packed_git *found_pack UNUSED, + off_t found_offset UNUSED) { fprintf(stdout, "%s\n", oid_to_hex(oid)); return 1; @@ -556,7 +566,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) } if (arg_missing_action) - revs.do_not_die_on_missing_tree = 1; + revs.do_not_die_on_missing_objects = 1; argc = setup_revisions(argc, argv, &revs, &s_r_opt); diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index e67999e5eb..917f122440 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -4,15 +4,23 @@ * Copyright (C) Linus Torvalds, 2005 */ #define USE_THE_INDEX_VARIABLE -#include "cache.h" +#include "builtin.h" +#include "abspath.h" #include "config.h" #include "commit.h" +#include "environment.h" +#include "gettext.h" +#include "hash.h" +#include "hex.h" #include "refs.h" #include "quote.h" -#include "builtin.h" +#include "object-name.h" #include "parse-options.h" +#include "path.h" #include "diff.h" +#include "read-cache-ll.h" #include "revision.h" +#include "setup.h" #include "split-index.h" #include "submodule.h" #include "commit-reach.h" @@ -136,7 +144,9 @@ static void show_rev(int type, const struct object_id *oid, const char *name) struct object_id discard; char *full; - switch (dwim_ref(name, strlen(name), &discard, &full, 0)) { + switch (repo_dwim_ref(the_repository, name, + strlen(name), &discard, &full, + 0)) { case 0: /* * Not found -- not a ref. We could @@ -147,9 +157,12 @@ static void show_rev(int type, const struct object_id *oid, const char *name) */ break; case 1: /* happy */ - if (abbrev_ref) + if (abbrev_ref) { + char *old = full; full = shorten_unambiguous_ref(full, abbrev_ref_strict); + free(old); + } show_with_type(type, full); break; default: /* ambiguous */ @@ -162,7 +175,8 @@ static void show_rev(int type, const struct object_id *oid, const char *name) } } else if (abbrev) - show_with_type(type, find_unique_abbrev(oid, abbrev)); + show_with_type(type, + repo_find_unique_abbrev(the_repository, oid, abbrev)); else show_with_type(type, oid_to_hex(oid)); } @@ -187,7 +201,7 @@ static int show_default(void) struct object_id oid; def = NULL; - if (!get_oid(s, &oid)) { + if (!repo_get_oid(the_repository, s, &oid)) { show_rev(NORMAL, &oid, s); return 1; } @@ -211,7 +225,7 @@ static int anti_reference(const char *refname, const struct object_id *oid, return 0; } -static int show_abbrev(const struct object_id *oid, void *cb_data) +static int show_abbrev(const struct object_id *oid, void *cb_data UNUSED) { show_rev(NORMAL, oid, NULL); return 0; @@ -279,7 +293,7 @@ static int try_difference(const char *arg) return 0; } - if (!get_oid_committish(start, &start_oid) && !get_oid_committish(end, &end_oid)) { + if (!repo_get_oid_committish(the_repository, start, &start_oid) && !repo_get_oid_committish(the_repository, end, &end_oid)) { show_rev(NORMAL, &end_oid, end); show_rev(symmetric ? NORMAL : REVERSED, &start_oid, start); if (symmetric) { @@ -291,7 +305,7 @@ static int try_difference(const char *arg) *dotdot = '.'; return 0; } - exclude = get_merge_bases(a, b); + exclude = repo_get_merge_bases(the_repository, a, b); while (exclude) { struct commit *commit = pop_commit(&exclude); show_rev(REVERSED, &commit->object.oid, NULL); @@ -337,7 +351,7 @@ static int try_parent_shorthands(const char *arg) return 0; *dotdot = 0; - if (get_oid_committish(arg, &oid) || + if (repo_get_oid_committish(the_repository, arg, &oid) || !(commit = lookup_commit_reference(the_repository, &oid))) { *dotdot = '^'; return 0; @@ -868,7 +882,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; } if (skip_prefix(arg, "--disambiguate=", &arg)) { - for_each_abbrev(arg, show_abbrev, NULL); + repo_for_each_abbrev(the_repository, arg, + show_abbrev, NULL); continue; } if (!strcmp(arg, "--bisect")) { @@ -878,13 +893,15 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) } if (opt_with_value(arg, "--branches", &arg)) { if (ref_excludes.hidden_refs_configured) - return error(_("--exclude-hidden cannot be used together with --branches")); + return error(_("options '%s' and '%s' cannot be used together"), + "--exclude-hidden", "--branches"); handle_ref_opt(arg, "refs/heads/"); continue; } if (opt_with_value(arg, "--tags", &arg)) { if (ref_excludes.hidden_refs_configured) - return error(_("--exclude-hidden cannot be used together with --tags")); + return error(_("options '%s' and '%s' cannot be used together"), + "--exclude-hidden", "--tags"); handle_ref_opt(arg, "refs/tags/"); continue; } @@ -894,7 +911,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) } if (opt_with_value(arg, "--remotes", &arg)) { if (ref_excludes.hidden_refs_configured) - return error(_("--exclude-hidden cannot be used together with --remotes")); + return error(_("options '%s' and '%s' cannot be used together"), + "--exclude-hidden", "--remotes"); handle_ref_opt(arg, "refs/remotes/"); continue; } diff --git a/builtin/revert.c b/builtin/revert.c index 77d2035616..e6f9a1ad26 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -1,8 +1,10 @@ -#include "cache.h" +#include "git-compat-util.h" #include "config.h" #include "builtin.h" #include "parse-options.h" #include "diff.h" +#include "gettext.h" +#include "repository.h" #include "revision.h" #include "rerere.h" #include "dir.h" @@ -43,20 +45,6 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts) return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage; } -static int option_parse_x(const struct option *opt, - const char *arg, int unset) -{ - struct replay_opts **opts_ptr = opt->value; - struct replay_opts *opts = *opts_ptr; - - if (unset) - return 0; - - ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc); - opts->xopts[opts->xopts_nr++] = xstrdup(arg); - return 0; -} - static int option_parse_m(const struct option *opt, const char *arg, int unset) { @@ -93,7 +81,8 @@ static void verify_opt_compatible(const char *me, const char *base_opt, ...) die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt); } -static int run_sequencer(int argc, const char **argv, struct replay_opts *opts) +static int run_sequencer(int argc, const char **argv, const char *prefix, + struct replay_opts *opts) { const char * const * usage_str = revert_or_cherry_pick_usage(opts); const char *me = action_name(opts); @@ -113,8 +102,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts) N_("select mainline parent"), option_parse_m), OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto), OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")), - OPT_CALLBACK('X', "strategy-option", &opts, N_("option"), - N_("option for merge strategy"), option_parse_x), + OPT_STRVEC('X', "strategy-option", &opts->xopts, N_("option"), + N_("option for merge strategy")), { OPTION_STRING, 'S', "gpg-sign", &opts->gpg_sign, N_("key-id"), N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, OPT_END() @@ -140,7 +129,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts) options = parse_options_concat(options, cp_extra); } - argc = parse_options(argc, argv, NULL, options, usage_str, + argc = parse_options(argc, argv, prefix, options, usage_str, PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT); @@ -175,7 +164,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts) "--signoff", opts->signoff, "--mainline", opts->mainline, "--strategy", opts->strategy ? 1 : 0, - "--strategy-option", opts->xopts ? 1 : 0, + "--strategy-option", opts->xopts.nr ? 1 : 0, "-x", opts->record_origin, "--ff", opts->allow_ff, "--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE, @@ -245,7 +234,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix) opts.action = REPLAY_REVERT; sequencer_init_config(&opts); - res = run_sequencer(argc, argv, &opts); + res = run_sequencer(argc, argv, prefix, &opts); if (res < 0) die(_("revert failed")); replay_opts_release(&opts); @@ -259,7 +248,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix) opts.action = REPLAY_PICK; sequencer_init_config(&opts); - res = run_sequencer(argc, argv, &opts); + res = run_sequencer(argc, argv, prefix, &opts); if (res < 0) die(_("cherry-pick failed")); replay_opts_release(&opts); diff --git a/builtin/rm.c b/builtin/rm.c index 8844f90655..dff819ae50 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -10,9 +10,16 @@ #include "lockfile.h" #include "dir.h" #include "cache-tree.h" +#include "gettext.h" +#include "hash.h" #include "tree-walk.h" +#include "object-name.h" #include "parse-options.h" +#include "read-cache.h" +#include "repository.h" #include "string-list.h" +#include "setup.h" +#include "sparse-index.h" #include "submodule.h" #include "pathspec.h" @@ -370,7 +377,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) */ if (!force) { struct object_id oid; - if (get_oid("HEAD", &oid)) + if (repo_get_oid(the_repository, "HEAD", &oid)) oidclr(&oid); if (check_local_mod(&oid, index_only)) exit(1); diff --git a/builtin/send-pack.c b/builtin/send-pack.c index 4c5d125fa0..00e6c90477 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -1,6 +1,7 @@ #include "builtin.h" #include "config.h" #include "commit.h" +#include "hex.h" #include "refs.h" #include "pkt-line.h" #include "sideband.h" @@ -15,6 +16,8 @@ #include "gpg-interface.h" #include "gettext.h" #include "protocol.h" +#include "parse-options.h" +#include "write-or-die.h" static const char * const send_pack_usage[] = { N_("git send-pack [--mirror] [--dry-run] [--force]\n" @@ -128,29 +131,25 @@ static void print_helper_status(struct ref *ref) strbuf_release(&buf); } -static int send_pack_config(const char *k, const char *v, void *cb) +static int send_pack_config(const char *k, const char *v, + const struct config_context *ctx, void *cb) { - git_gpg_config(k, v, NULL); - if (!strcmp(k, "push.gpgsign")) { - const char *value; - if (!git_config_get_value("push.gpgsign", &value)) { - switch (git_parse_maybe_bool(value)) { - case 0: - args.push_cert = SEND_PACK_PUSH_CERT_NEVER; - break; - case 1: - args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS; - break; - default: - if (value && !strcasecmp(value, "if-asked")) - args.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED; - else - return error(_("invalid value for '%s'"), k); - } + switch (git_parse_maybe_bool(v)) { + case 0: + args.push_cert = SEND_PACK_PUSH_CERT_NEVER; + break; + case 1: + args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS; + break; + default: + if (!strcasecmp(v, "if-asked")) + args.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED; + else + return error(_("invalid value for '%s'"), k); } } - return git_default_config(k, v, cb); + return git_default_config(k, v, ctx, cb); } int cmd_send_pack(int argc, const char **argv, const char *prefix) @@ -275,7 +274,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) fd[0] = 0; fd[1] = 1; } else { - conn = git_connect(fd, dest, receivepack, + conn = git_connect(fd, dest, "git-receive-pack", receivepack, args.verbose ? CONNECT_VERBOSE : 0); } diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 27a87167e1..1307ed2b88 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -1,12 +1,15 @@ #include "builtin.h" -#include "cache.h" #include "config.h" #include "commit.h" #include "diff.h" +#include "environment.h" +#include "gettext.h" #include "string-list.h" +#include "repository.h" #include "revision.h" #include "utf8.h" #include "mailmap.h" +#include "setup.h" #include "shortlog.h" #include "parse-options.h" #include "trailer.h" @@ -176,10 +179,11 @@ static void insert_records_from_trailers(struct shortlog *log, return; /* - * Using format_commit_message("%B") would be simpler here, but + * Using repo_format_commit_message("%B") would be simpler here, but * this saves us copying the message. */ - commit_buffer = logmsg_reencode(commit, NULL, ctx->output_encoding); + commit_buffer = repo_logmsg_reencode(the_repository, commit, NULL, + ctx->output_encoding); body = strstr(commit_buffer, "\n\n"); if (!body) return; @@ -202,7 +206,7 @@ static void insert_records_from_trailers(struct shortlog *log, trailer_iterator_release(&iter); strbuf_release(&ident); - unuse_commit_buffer(commit, commit_buffer); + repo_unuse_commit_buffer(the_repository, commit, commit_buffer); } static int shortlog_needs_dedup(const struct shortlog *log) @@ -222,7 +226,8 @@ static void insert_records_from_format(struct shortlog *log, for_each_string_list_item(item, &log->format) { strbuf_reset(&buf); - format_commit_message(commit, item->string, &buf, ctx); + repo_format_commit_message(the_repository, commit, + item->string, &buf, ctx); if (!shortlog_needs_dedup(log) || strset_add(dups, buf.buf)) insert_one_record(log, buf.buf, oneline); @@ -248,7 +253,8 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit) if (log->user_format) pretty_print_commit(&ctx, commit, &oneline); else - format_commit_message(commit, "%s", &oneline, &ctx); + repo_format_commit_message(the_repository, commit, + "%s", &oneline, &ctx); } oneline_str = oneline.len ? oneline.buf : "<none>"; diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 358ac3e519..b01ec761d2 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -1,14 +1,20 @@ -#include "cache.h" +#include "builtin.h" #include "config.h" +#include "environment.h" +#include "gettext.h" +#include "hash.h" +#include "hex.h" #include "pretty.h" #include "refs.h" -#include "builtin.h" #include "color.h" #include "strvec.h" +#include "object-name.h" #include "parse-options.h" +#include "repository.h" #include "dir.h" #include "commit-slab.h" #include "date.h" +#include "wildmatch.h" static const char* show_branch_usage[] = { N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n" @@ -240,7 +246,7 @@ static void join_revs(struct commit_list **list_p, parents = parents->next; if ((this_flag & flags) == flags) continue; - parse_commit(p); + repo_parse_commit(the_repository, p); if (mark_seen(p, seen_p) && !still_interesting) extra--; p->object.flags |= flags; @@ -312,8 +318,8 @@ static void show_one_commit(struct commit *commit, int no_name) } else printf("[%s] ", - find_unique_abbrev(&commit->object.oid, - DEFAULT_ABBREV)); + repo_find_unique_abbrev(the_repository, &commit->object.oid, + DEFAULT_ABBREV)); } puts(pretty_str); strbuf_release(&pretty); @@ -414,7 +420,7 @@ static int append_head_ref(const char *refname, const struct object_id *oid, /* If both heads/foo and tags/foo exists, get_sha1 would * get confused. */ - if (get_oid(refname + ofs, &tmp) || !oideq(&tmp, oid)) + if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid)) ofs = 5; return append_ref(refname + ofs, oid, 0); } @@ -429,7 +435,7 @@ static int append_remote_ref(const char *refname, const struct object_id *oid, /* If both heads/foo and tags/foo exists, get_sha1 would * get confused. */ - if (get_oid(refname + ofs, &tmp) || !oideq(&tmp, oid)) + if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid)) ofs = 5; return append_ref(refname + ofs, oid, 0); } @@ -533,7 +539,7 @@ static int show_independent(struct commit **rev, static void append_one_rev(const char *av) { struct object_id revkey; - if (!get_oid(av, &revkey)) { + if (!repo_get_oid(the_repository, av, &revkey)) { append_ref(av, &revkey, 0); return; } @@ -553,7 +559,8 @@ static void append_one_rev(const char *av) die("bad sha1 reference %s", av); } -static int git_show_branch_config(const char *var, const char *value, void *cb) +static int git_show_branch_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { if (!strcmp(var, "showbranch.default")) { if (!value) @@ -573,7 +580,10 @@ static int git_show_branch_config(const char *var, const char *value, void *cb) return 0; } - return git_color_default_config(var, value, cb); + if (git_color_config(var, value, cb) < 0) + return -1; + + return git_default_config(var, value, ctx, cb); } static int omit_in_dense(struct commit *commit, struct commit **rev, int n) @@ -639,7 +649,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) int with_current_branch = 0; int head_at = -1; int topics = 0; - int dense = 1; + int sparse = 0; const char *reflog_base = NULL; struct option builtin_show_branch_options[] = { OPT_BOOL('a', "all", &all_heads, @@ -661,17 +671,17 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) N_("show possible merge bases")), OPT_BOOL(0, "independent", &independent, N_("show refs unreachable from any other ref")), - OPT_SET_INT(0, "topo-order", &sort_order, - N_("show commits in topological order"), - REV_SORT_IN_GRAPH_ORDER), + OPT_SET_INT_F(0, "topo-order", &sort_order, + N_("show commits in topological order"), + REV_SORT_IN_GRAPH_ORDER, PARSE_OPT_NONEG), OPT_BOOL(0, "topics", &topics, N_("show only commits not on the first branch")), - OPT_SET_INT(0, "sparse", &dense, - N_("show merges reachable from only one tip"), 0), - OPT_SET_INT(0, "date-order", &sort_order, - N_("topologically sort, maintaining date order " - "where possible"), - REV_SORT_BY_COMMIT_DATE), + OPT_SET_INT(0, "sparse", &sparse, + N_("show merges reachable from only one tip"), 1), + OPT_SET_INT_F(0, "date-order", &sort_order, + N_("topologically sort, maintaining date order " + "where possible"), + REV_SORT_BY_COMMIT_DATE, PARSE_OPT_NONEG), OPT_CALLBACK_F('g', "reflog", &reflog_base, N_("<n>[,<base>]"), N_("show <n> most recent ref-log entries starting at " "base"), @@ -746,7 +756,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) die(Q_("only %d entry can be shown at one time.", "only %d entries can be shown at one time.", MAX_REVS), MAX_REVS); - if (!dwim_ref(*av, strlen(*av), &oid, &ref, 0)) + if (!repo_dwim_ref(the_repository, *av, strlen(*av), &oid, + &ref, 0)) die(_("no such ref %s"), *av); /* Has the base been specified? */ @@ -836,13 +847,13 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) die(Q_("cannot handle more than %d rev.", "cannot handle more than %d revs.", MAX_REVS), MAX_REVS); - if (get_oid(ref_name[num_rev], &revkey)) + if (repo_get_oid(the_repository, ref_name[num_rev], &revkey)) die(_("'%s' is not a valid ref."), ref_name[num_rev]); commit = lookup_commit_reference(the_repository, &revkey); if (!commit) die(_("cannot find commit %s (%s)"), ref_name[num_rev], oid_to_hex(&revkey)); - parse_commit(commit); + repo_parse_commit(the_repository, commit); mark_seen(commit, &seen); /* rev#0 uses bit REV_SHIFT, rev#1 uses bit REV_SHIFT+1, @@ -929,7 +940,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) !is_merge_point && (this_flag & (1u << REV_SHIFT))) continue; - if (dense && is_merge && + if (!sparse && is_merge && omit_in_dense(commit, rev, num_rev)) continue; for (i = 0; i < num_rev; i++) { diff --git a/builtin/show-index.c b/builtin/show-index.c index 0e0b9fb95b..540dc3dad1 100644 --- a/builtin/show-index.c +++ b/builtin/show-index.c @@ -1,7 +1,10 @@ #include "builtin.h" -#include "cache.h" +#include "gettext.h" +#include "hash.h" +#include "hex.h" #include "pack.h" #include "parse-options.h" +#include "repository.h" static const char *const show_index_usage[] = { "git show-index [--object-format=<hash-algorithm>]", diff --git a/builtin/show-ref.c b/builtin/show-ref.c index 3af6a53ee9..59d2291cbf 100644 --- a/builtin/show-ref.c +++ b/builtin/show-ref.c @@ -1,62 +1,80 @@ #include "builtin.h" -#include "cache.h" #include "config.h" -#include "refs.h" -#include "object-store.h" +#include "gettext.h" +#include "hex.h" +#include "refs/refs-internal.h" +#include "object-name.h" +#include "object-store-ll.h" #include "object.h" #include "tag.h" #include "string-list.h" #include "parse-options.h" static const char * const show_ref_usage[] = { - N_("git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n" + N_("git show-ref [--head] [-d | --dereference]\n" " [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n" " [--heads] [--] [<pattern>...]"), + N_("git show-ref --verify [-q | --quiet] [-d | --dereference]\n" + " [-s | --hash[=<n>]] [--abbrev[=<n>]]\n" + " [--] [<ref>...]"), N_("git show-ref --exclude-existing[=<pattern>]"), + N_("git show-ref --exists <ref>"), NULL }; -static int deref_tags, show_head, tags_only, heads_only, found_match, verify, - quiet, hash_only, abbrev, exclude_arg; -static const char **pattern; -static const char *exclude_existing_arg; +struct show_one_options { + int quiet; + int hash_only; + int abbrev; + int deref_tags; +}; -static void show_one(const char *refname, const struct object_id *oid) +static void show_one(const struct show_one_options *opts, + const char *refname, const struct object_id *oid) { const char *hex; struct object_id peeled; - if (!has_object_file(oid)) + if (!repo_has_object_file(the_repository, oid)) die("git show-ref: bad ref %s (%s)", refname, oid_to_hex(oid)); - if (quiet) + if (opts->quiet) return; - hex = find_unique_abbrev(oid, abbrev); - if (hash_only) + hex = repo_find_unique_abbrev(the_repository, oid, opts->abbrev); + if (opts->hash_only) printf("%s\n", hex); else printf("%s %s\n", hex, refname); - if (!deref_tags) + if (!opts->deref_tags) return; if (!peel_iterated_oid(oid, &peeled)) { - hex = find_unique_abbrev(&peeled, abbrev); + hex = repo_find_unique_abbrev(the_repository, &peeled, opts->abbrev); printf("%s %s^{}\n", hex, refname); } } +struct show_ref_data { + const struct show_one_options *show_one_opts; + const char **patterns; + int found_match; + int show_head; +}; + static int show_ref(const char *refname, const struct object_id *oid, - int flag UNUSED, void *cbdata UNUSED) + int flag UNUSED, void *cbdata) { - if (show_head && !strcmp(refname, "HEAD")) + struct show_ref_data *data = cbdata; + + if (data->show_head && !strcmp(refname, "HEAD")) goto match; - if (pattern) { + if (data->patterns) { int reflen = strlen(refname); - const char **p = pattern, *m; + const char **p = data->patterns, *m; while ((m = *p++) != NULL) { int len = strlen(m); if (len > reflen) @@ -72,9 +90,9 @@ static int show_ref(const char *refname, const struct object_id *oid, } match: - found_match++; + data->found_match++; - show_one(refname, oid); + show_one(data->show_one_opts, refname, oid); return 0; } @@ -88,6 +106,15 @@ static int add_existing(const char *refname, return 0; } +struct exclude_existing_options { + /* + * We need an explicit `enabled` field because it is perfectly valid + * for `pattern` to be `NULL` even if `--exclude-existing` was given. + */ + int enabled; + const char *pattern; +}; + /* * read "^(?:<anything>\s)?<refname>(?:\^\{\})?$" from the standard input, * and @@ -97,11 +124,11 @@ static int add_existing(const char *refname, * (4) ignore if refname is a ref that exists in the local repository; * (5) otherwise output the line. */ -static int exclude_existing(const char *match) +static int cmd_show_ref__exclude_existing(const struct exclude_existing_options *opts) { - static struct string_list existing_refs = STRING_LIST_INIT_DUP; + struct string_list existing_refs = STRING_LIST_INIT_DUP; char buf[1024]; - int matchlen = match ? strlen(match) : 0; + int patternlen = opts->pattern ? strlen(opts->pattern) : 0; for_each_ref(add_existing, &existing_refs); while (fgets(buf, sizeof(buf), stdin)) { @@ -117,11 +144,11 @@ static int exclude_existing(const char *match) for (ref = buf + len; buf < ref; ref--) if (isspace(ref[-1])) break; - if (match) { + if (opts->pattern) { int reflen = buf + len - ref; - if (reflen < matchlen) + if (reflen < patternlen) continue; - if (strncmp(ref, match, matchlen)) + if (strncmp(ref, opts->pattern, patternlen)) continue; } if (check_refname_format(ref, 0)) { @@ -132,97 +159,172 @@ static int exclude_existing(const char *match) printf("%s\n", buf); } } + + string_list_clear(&existing_refs, 0); + return 0; +} + +static int cmd_show_ref__verify(const struct show_one_options *show_one_opts, + const char **refs) +{ + if (!refs || !*refs) + die("--verify requires a reference"); + + while (*refs) { + struct object_id oid; + + if ((starts_with(*refs, "refs/") || !strcmp(*refs, "HEAD")) && + !read_ref(*refs, &oid)) { + show_one(show_one_opts, *refs, &oid); + } + else if (!show_one_opts->quiet) + die("'%s' - not a valid ref", *refs); + else + return 1; + refs++; + } + return 0; } +struct patterns_options { + int show_head; + int heads_only; + int tags_only; +}; + +static int cmd_show_ref__patterns(const struct patterns_options *opts, + const struct show_one_options *show_one_opts, + const char **patterns) +{ + struct show_ref_data show_ref_data = { + .show_one_opts = show_one_opts, + .show_head = opts->show_head, + }; + + if (patterns && *patterns) + show_ref_data.patterns = patterns; + + if (opts->show_head) + head_ref(show_ref, &show_ref_data); + if (opts->heads_only || opts->tags_only) { + if (opts->heads_only) + for_each_fullref_in("refs/heads/", show_ref, &show_ref_data); + if (opts->tags_only) + for_each_fullref_in("refs/tags/", show_ref, &show_ref_data); + } else { + for_each_ref(show_ref, &show_ref_data); + } + if (!show_ref_data.found_match) + return 1; + + return 0; +} + +static int cmd_show_ref__exists(const char **refs) +{ + 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; + + if (!refs || !*refs) + die("--exists requires a reference"); + ref = *refs++; + if (*refs) + die("--exists requires exactly one reference"); + + if (refs_read_raw_ref(get_main_ref_store(the_repository), ref, + &unused_oid, &unused_referent, &unused_type, + &failure_errno)) { + if (failure_errno == ENOENT) { + 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 hash_callback(const struct option *opt, const char *arg, int unset) { - hash_only = 1; + struct show_one_options *opts = opt->value; + struct option abbrev_opt = *opt; + + opts->hash_only = 1; /* Use full length SHA1 if no argument */ if (!arg) return 0; - return parse_opt_abbrev_cb(opt, arg, unset); + + abbrev_opt.value = &opts->abbrev; + return parse_opt_abbrev_cb(&abbrev_opt, arg, unset); } static int exclude_existing_callback(const struct option *opt, const char *arg, int unset) { + struct exclude_existing_options *opts = opt->value; BUG_ON_OPT_NEG(unset); - exclude_arg = 1; - *(const char **)opt->value = arg; + opts->enabled = 1; + opts->pattern = arg; return 0; } -static const struct option show_ref_options[] = { - OPT_BOOL(0, "tags", &tags_only, N_("only show tags (can be combined with heads)")), - OPT_BOOL(0, "heads", &heads_only, N_("only show heads (can be combined with tags)")), - OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, " - "requires exact ref path")), - OPT_HIDDEN_BOOL('h', NULL, &show_head, - N_("show the HEAD reference, even if it would be filtered out")), - OPT_BOOL(0, "head", &show_head, - N_("show the HEAD reference, even if it would be filtered out")), - OPT_BOOL('d', "dereference", &deref_tags, - N_("dereference tags into object IDs")), - OPT_CALLBACK_F('s', "hash", &abbrev, N_("n"), - N_("only show SHA1 hash using <n> digits"), - PARSE_OPT_OPTARG, &hash_callback), - OPT__ABBREV(&abbrev), - OPT__QUIET(&quiet, - N_("do not print results to stdout (useful with --verify)")), - OPT_CALLBACK_F(0, "exclude-existing", &exclude_existing_arg, - N_("pattern"), N_("show refs from stdin that aren't in local repository"), - PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback), - OPT_END() -}; - int cmd_show_ref(int argc, const char **argv, const char *prefix) { + struct exclude_existing_options exclude_existing_opts = {0}; + struct patterns_options patterns_opts = {0}; + struct show_one_options show_one_opts = {0}; + int verify = 0, exists = 0; + const struct option show_ref_options[] = { + OPT_BOOL(0, "tags", &patterns_opts.tags_only, N_("only show tags (can be combined with heads)")), + OPT_BOOL(0, "heads", &patterns_opts.heads_only, N_("only show heads (can be combined with tags)")), + OPT_BOOL(0, "exists", &exists, N_("check for reference existence without resolving")), + OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, " + "requires exact ref path")), + OPT_HIDDEN_BOOL('h', NULL, &patterns_opts.show_head, + N_("show the HEAD reference, even if it would be filtered out")), + OPT_BOOL(0, "head", &patterns_opts.show_head, + N_("show the HEAD reference, even if it would be filtered out")), + OPT_BOOL('d', "dereference", &show_one_opts.deref_tags, + N_("dereference tags into object IDs")), + OPT_CALLBACK_F('s', "hash", &show_one_opts, N_("n"), + N_("only show SHA1 hash using <n> digits"), + PARSE_OPT_OPTARG, &hash_callback), + OPT__ABBREV(&show_one_opts.abbrev), + OPT__QUIET(&show_one_opts.quiet, + N_("do not print results to stdout (useful with --verify)")), + OPT_CALLBACK_F(0, "exclude-existing", &exclude_existing_opts, + N_("pattern"), N_("show refs from stdin that aren't in local repository"), + PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback), + OPT_END() + }; + git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, show_ref_options, show_ref_usage, 0); - if (exclude_arg) - return exclude_existing(exclude_existing_arg); - - pattern = argv; - if (!*pattern) - pattern = NULL; - - if (verify) { - if (!pattern) - die("--verify requires a reference"); - while (*pattern) { - struct object_id oid; - - if ((starts_with(*pattern, "refs/") || !strcmp(*pattern, "HEAD")) && - !read_ref(*pattern, &oid)) { - show_one(*pattern, &oid); - } - else if (!quiet) - die("'%s' - not a valid ref", *pattern); - else - return 1; - pattern++; - } - return 0; - } + die_for_incompatible_opt3(exclude_existing_opts.enabled, "--exclude-existing", + verify, "--verify", + exists, "--exists"); - if (show_head) - head_ref(show_ref, NULL); - if (heads_only || tags_only) { - if (heads_only) - for_each_fullref_in("refs/heads/", show_ref, NULL); - if (tags_only) - for_each_fullref_in("refs/tags/", show_ref, NULL); - } else { - for_each_ref(show_ref, NULL); - } - if (!found_match) { - if (verify && !quiet) - die("No match"); - return 1; - } - return 0; + if (exclude_existing_opts.enabled) + return cmd_show_ref__exclude_existing(&exclude_existing_opts); + else if (verify) + return cmd_show_ref__verify(&show_one_opts, argv); + else if (exists) + return cmd_show_ref__exists(argv); + else + return cmd_show_ref__patterns(&patterns_opts, &show_one_opts, argv); } diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c index c373815491..5c8ffb1f75 100644 --- a/builtin/sparse-checkout.c +++ b/builtin/sparse-checkout.c @@ -1,7 +1,10 @@ #include "builtin.h" -#include "cache.h" #include "config.h" #include "dir.h" +#include "environment.h" +#include "gettext.h" +#include "object-file.h" +#include "object-name.h" #include "parse-options.h" #include "pathspec.h" #include "repository.h" @@ -14,13 +17,14 @@ #include "unpack-trees.h" #include "wt-status.h" #include "quote.h" +#include "setup.h" #include "sparse-index.h" #include "worktree.h" static const char *empty_base = ""; static char const * const builtin_sparse_checkout_usage[] = { - N_("git sparse-checkout (init | list | set | add | reapply | disable) [<options>]"), + N_("git sparse-checkout (init | list | set | add | reapply | disable | check-rules) [<options>]"), NULL }; @@ -57,6 +61,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix) char *sparse_filename; int res; + setup_work_tree(); if (!core_apply_sparse_checkout) die(_("this worktree is not sparse")); @@ -217,16 +222,14 @@ static int update_working_directory(struct pattern_list *pl) o.head_idx = -1; o.src_index = r->index; o.dst_index = r->index; - index_state_init(&o.result, r); o.skip_sparse_checkout = 0; - o.pl = pl; setup_work_tree(); repo_hold_locked_index(r, &lock_file, LOCK_DIE_ON_ERROR); setup_unpack_trees_porcelain(&o, "sparse-checkout"); - result = update_sparsity(&o); + result = update_sparsity(&o, pl); clear_unpack_trees_porcelain(&o); if (result == UPDATE_SPARSITY_WARNINGS) @@ -383,13 +386,7 @@ static int set_config(enum sparse_checkout_mode mode) return 0; } -static int update_modes(int *cone_mode, int *sparse_index) -{ - int mode, record_mode; - - /* Determine if we need to record the mode; ensure sparse checkout on */ - record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout; - +static enum sparse_checkout_mode update_cone_mode(int *cone_mode) { /* If not specified, use previous definition of cone mode */ if (*cone_mode == -1 && core_apply_sparse_checkout) *cone_mode = core_sparse_checkout_cone; @@ -397,12 +394,21 @@ static int update_modes(int *cone_mode, int *sparse_index) /* Set cone/non-cone mode appropriately */ core_apply_sparse_checkout = 1; if (*cone_mode == 1 || *cone_mode == -1) { - mode = MODE_CONE_PATTERNS; core_sparse_checkout_cone = 1; - } else { - mode = MODE_ALL_PATTERNS; - core_sparse_checkout_cone = 0; + return MODE_CONE_PATTERNS; } + core_sparse_checkout_cone = 0; + return MODE_ALL_PATTERNS; +} + +static int update_modes(int *cone_mode, int *sparse_index) +{ + int mode, record_mode; + + /* Determine if we need to record the mode; ensure sparse checkout on */ + record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout; + + mode = update_cone_mode(cone_mode); if (record_mode && set_config(mode)) return 1; @@ -448,6 +454,7 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix) OPT_END(), }; + setup_work_tree(); repo_read_index(the_repository); init_opts.cone_mode = -1; @@ -471,7 +478,7 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix) return update_working_directory(NULL); } - if (get_oid("HEAD", &oid)) { + if (repo_get_oid(the_repository, "HEAD", &oid)) { FILE *fp; /* assume we are in a fresh repo, but update the sparse-checkout file */ @@ -545,7 +552,7 @@ static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl) static void add_patterns_from_input(struct pattern_list *pl, int argc, const char **argv, - int use_stdin) + FILE *file) { int i; if (core_sparse_checkout_cone) { @@ -555,9 +562,9 @@ static void add_patterns_from_input(struct pattern_list *pl, hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0); pl->use_cone_patterns = 1; - if (use_stdin) { + if (file) { struct strbuf unquoted = STRBUF_INIT; - while (!strbuf_getline(&line, stdin)) { + while (!strbuf_getline(&line, file)) { if (line.buf[0] == '"') { strbuf_reset(&unquoted); if (unquote_c_style(&unquoted, line.buf, NULL)) @@ -579,10 +586,10 @@ static void add_patterns_from_input(struct pattern_list *pl, } } } else { - if (use_stdin) { + if (file) { struct strbuf line = STRBUF_INIT; - while (!strbuf_getline(&line, stdin)) { + while (!strbuf_getline(&line, file)) { size_t len; char *buf = strbuf_detach(&line, &len); add_pattern(buf, empty_base, 0, pl, 0); @@ -609,7 +616,8 @@ static void add_patterns_cone_mode(int argc, const char **argv, struct pattern_list existing; char *sparse_filename = get_sparse_checkout_filename(); - add_patterns_from_input(pl, argc, argv, use_stdin); + add_patterns_from_input(pl, argc, argv, + use_stdin ? stdin : NULL); memset(&existing, 0, sizeof(existing)); existing.use_cone_patterns = core_sparse_checkout_cone; @@ -646,7 +654,7 @@ static void add_patterns_literal(int argc, const char **argv, pl, NULL, 0)) die(_("unable to load existing sparse-checkout patterns")); free(sparse_filename); - add_patterns_from_input(pl, argc, argv, use_stdin); + add_patterns_from_input(pl, argc, argv, use_stdin ? stdin : NULL); } static int modify_pattern_list(int argc, const char **argv, int use_stdin, @@ -665,7 +673,8 @@ static int modify_pattern_list(int argc, const char **argv, int use_stdin, break; case REPLACE: - add_patterns_from_input(pl, argc, argv, use_stdin); + add_patterns_from_input(pl, argc, argv, + use_stdin ? stdin : NULL); break; } @@ -760,6 +769,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix) OPT_END(), }; + setup_work_tree(); if (!core_apply_sparse_checkout) die(_("no sparse-checkout to add to")); @@ -806,6 +816,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix) OPT_END(), }; + setup_work_tree(); repo_read_index(the_repository); set_opts.cone_mode = -1; @@ -855,6 +866,7 @@ static int sparse_checkout_reapply(int argc, const char **argv, OPT_END(), }; + setup_work_tree(); if (!core_apply_sparse_checkout) die(_("must be in a sparse-checkout to reapply sparsity patterns")); @@ -898,6 +910,7 @@ static int sparse_checkout_disable(int argc, const char **argv, * forcibly return to a dense checkout regardless of initial state. */ + setup_work_tree(); argc = parse_options(argc, argv, prefix, builtin_sparse_checkout_disable_options, builtin_sparse_checkout_disable_usage, 0); @@ -923,6 +936,91 @@ static int sparse_checkout_disable(int argc, const char **argv, return set_config(MODE_NO_PATTERNS); } +static char const * const builtin_sparse_checkout_check_rules_usage[] = { + N_("git sparse-checkout check-rules [-z] [--skip-checks]" + "[--[no-]cone] [--rules-file <file>]"), + NULL +}; + +static struct sparse_checkout_check_rules_opts { + int cone_mode; + int null_termination; + char *rules_file; +} check_rules_opts; + +static int check_rules(struct pattern_list *pl, int null_terminated) { + struct strbuf line = STRBUF_INIT; + struct strbuf unquoted = STRBUF_INIT; + char *path; + int line_terminator = null_terminated ? 0 : '\n'; + strbuf_getline_fn getline_fn = null_terminated ? strbuf_getline_nul + : strbuf_getline; + the_repository->index->sparse_checkout_patterns = pl; + while (!getline_fn(&line, stdin)) { + path = line.buf; + if (!null_terminated && line.buf[0] == '"') { + strbuf_reset(&unquoted); + if (unquote_c_style(&unquoted, line.buf, NULL)) + die(_("unable to unquote C-style string '%s'"), + line.buf); + + path = unquoted.buf; + } + + if (path_in_sparse_checkout(path, the_repository->index)) + write_name_quoted(path, stdout, line_terminator); + } + strbuf_release(&line); + strbuf_release(&unquoted); + + return 0; +} + +static int sparse_checkout_check_rules(int argc, const char **argv, const char *prefix) +{ + static struct option builtin_sparse_checkout_check_rules_options[] = { + OPT_BOOL('z', NULL, &check_rules_opts.null_termination, + N_("terminate input and output files by a NUL character")), + OPT_BOOL(0, "cone", &check_rules_opts.cone_mode, + N_("when used with --rules-file interpret patterns as cone mode patterns")), + OPT_FILENAME(0, "rules-file", &check_rules_opts.rules_file, + N_("use patterns in <file> instead of the current ones.")), + OPT_END(), + }; + + FILE *fp; + int ret; + struct pattern_list pl = {0}; + char *sparse_filename; + check_rules_opts.cone_mode = -1; + + argc = parse_options(argc, argv, prefix, + builtin_sparse_checkout_check_rules_options, + builtin_sparse_checkout_check_rules_usage, + PARSE_OPT_KEEP_UNKNOWN_OPT); + + if (check_rules_opts.rules_file && check_rules_opts.cone_mode < 0) + check_rules_opts.cone_mode = 1; + + update_cone_mode(&check_rules_opts.cone_mode); + pl.use_cone_patterns = core_sparse_checkout_cone; + if (check_rules_opts.rules_file) { + fp = xfopen(check_rules_opts.rules_file, "r"); + add_patterns_from_input(&pl, argc, argv, fp); + fclose(fp); + } else { + sparse_filename = get_sparse_checkout_filename(); + if (add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, + NULL, 0)) + die(_("unable to load existing sparse-checkout patterns")); + free(sparse_filename); + } + + ret = check_rules(&pl, check_rules_opts.null_termination); + clear_pattern_list(&pl); + return ret; +} + int cmd_sparse_checkout(int argc, const char **argv, const char *prefix) { parse_opt_subcommand_fn *fn = NULL; @@ -933,6 +1031,7 @@ int cmd_sparse_checkout(int argc, const char **argv, const char *prefix) OPT_SUBCOMMAND("add", &fn, sparse_checkout_add), OPT_SUBCOMMAND("reapply", &fn, sparse_checkout_reapply), OPT_SUBCOMMAND("disable", &fn, sparse_checkout_disable), + OPT_SUBCOMMAND("check-rules", &fn, sparse_checkout_check_rules), OPT_END(), }; diff --git a/builtin/stash.c b/builtin/stash.c index 3a4f9fd566..4a6771c9f4 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -1,6 +1,12 @@ #define USE_THE_INDEX_VARIABLE #include "builtin.h" +#include "abspath.h" #include "config.h" +#include "environment.h" +#include "gettext.h" +#include "hash.h" +#include "hex.h" +#include "object-name.h" #include "parse-options.h" #include "refs.h" #include "lockfile.h" @@ -12,8 +18,12 @@ #include "run-command.h" #include "dir.h" #include "entry.h" +#include "preload-index.h" +#include "read-cache.h" #include "rerere.h" #include "revision.h" +#include "setup.h" +#include "sparse-index.h" #include "log-tree.h" #include "diffcore.h" #include "exec-cmd.h" @@ -201,7 +211,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv) revision = info->revision.buf; - if (get_oid(revision, &info->w_commit)) + if (repo_get_oid(the_repository, revision, &info->w_commit)) return error(_("%s is not a valid reference"), revision); assert_stash_like(info, revision); @@ -211,7 +221,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv) end_of_rev = strchrnul(revision, '@'); strbuf_add(&symbolic, revision, end_of_rev - revision); - ret = dwim_ref(symbolic.buf, symbolic.len, &dummy, &expanded_ref, 0); + ret = repo_dwim_ref(the_repository, symbolic.buf, symbolic.len, + &dummy, &expanded_ref, 0); strbuf_release(&symbolic); switch (ret) { case 0: /* Not found, but valid ref */ @@ -231,7 +242,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv) static int do_clear_stash(void) { struct object_id obj; - if (get_oid(ref_stash, &obj)) + if (repo_get_oid(the_repository, ref_stash, &obj)) return 0; return delete_ref(NULL, ref_stash, &obj, 0); @@ -351,7 +362,7 @@ static int is_path_a_directory(const char *path) } static void add_diff_to_buf(struct diff_queue_struct *q, - struct diff_options *options, + struct diff_options *options UNUSED, void *data) { int i; @@ -427,7 +438,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree) * to the index before a merge was run) and the current index * (reflecting the changes brought in by the merge). */ - diff_setup(&diff_opts); + repo_diff_setup(the_repository, &diff_opts); diff_opts.flags.recursive = 1; diff_opts.detect_rename = 0; diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; @@ -600,7 +611,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info, ret = error(_("could not write index")); if (ret) { - rerere(0); + repo_rerere(the_repository, 0); if (index) fprintf_ln(stderr, _("Index was not unstashed.")); @@ -830,7 +841,8 @@ 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, void *cb) +static int git_stash_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { if (!strcmp(var, "stash.showstat")) { show_stat = git_config_bool(var, value); @@ -844,7 +856,7 @@ static int git_stash_config(const char *var, const char *value, void *cb) show_include_untracked = git_config_bool(var, value); return 0; } - return git_diff_basic_config(var, value, cb); + return git_diff_basic_config(var, value, ctx, cb); } static void diff_include_untracked(const struct stash_info *info, struct diff_options *diff_opt) @@ -900,7 +912,7 @@ static int show_stash(int argc, const char **argv, const char *prefix) init_diff_ui_defaults(); git_config(git_diff_ui_config, NULL); - init_revisions(&rev, prefix); + repo_init_revisions(the_repository, &rev, prefix); argc = parse_options(argc, argv, prefix, options, git_stash_show_usage, PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT | @@ -961,7 +973,7 @@ static int show_stash(int argc, const char **argv, const char *prefix) } log_tree_diff_flush(&rev); - ret = diff_result_code(&rev.diffopt, 0); + ret = diff_result_code(&rev.diffopt); cleanup: strvec_clear(&stash_args); free_stash_info(&info); @@ -977,6 +989,12 @@ usage: static int do_store_stash(const struct object_id *w_commit, const char *stash_msg, int quiet) { + struct stash_info info; + char revision[GIT_MAX_HEXSZ]; + + oid_to_hex_r(revision, w_commit); + assert_stash_like(&info, revision); + if (!stash_msg) stash_msg = "Created via \"git stash store\"."; @@ -1077,19 +1095,18 @@ static int get_untracked_files(const struct pathspec *ps, int include_untracked, */ static int check_changes_tracked_files(const struct pathspec *ps) { - int result; struct rev_info rev; struct object_id dummy; int ret = 0; /* No initial commit. */ - if (get_oid("HEAD", &dummy)) + if (repo_get_oid(the_repository, "HEAD", &dummy)) return -1; if (repo_read_index(the_repository) < 0) return -1; - init_revisions(&rev, NULL); + repo_init_revisions(the_repository, &rev, NULL); copy_pathspec(&rev.prune_data, ps); rev.diffopt.flags.quick = 1; @@ -1099,14 +1116,14 @@ static int check_changes_tracked_files(const struct pathspec *ps) add_head_to_pending(&rev); diff_setup_done(&rev.diffopt); - result = run_diff_index(&rev, 1); - if (diff_result_code(&rev.diffopt, result)) { + run_diff_index(&rev, DIFF_INDEX_CACHED); + if (diff_result_code(&rev.diffopt)) { ret = 1; goto done; } - result = run_diff_files(&rev, 0); - if (diff_result_code(&rev.diffopt, result)) { + run_diff_files(&rev, 0); + if (diff_result_code(&rev.diffopt)) { ret = 1; goto done; } @@ -1276,7 +1293,7 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps struct strbuf diff_output = STRBUF_INIT; struct index_state istate = INDEX_STATE_INIT(the_repository); - init_revisions(&rev, NULL); + repo_init_revisions(the_repository, &rev, NULL); copy_pathspec(&rev.prune_data, ps); set_alternate_index_output(stash_index_path.buf); @@ -1297,10 +1314,7 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps add_pending_object(&rev, parse_object(the_repository, &info->b_commit), ""); - if (run_diff_index(&rev, 0)) { - ret = -1; - goto done; - } + run_diff_index(&rev, 0); cp_upd_index.git_cmd = 1; strvec_pushl(&cp_upd_index.args, "update-index", @@ -1355,7 +1369,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b goto done; } - if (get_oid("HEAD", &info->b_commit)) { + if (repo_get_oid(the_repository, "HEAD", &info->b_commit)) { if (!quiet) fprintf_ln(stderr, _("You do not have " "the initial commit yet")); @@ -1373,8 +1387,9 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b branch_ref = resolve_ref_unsafe("HEAD", 0, NULL, &flags); if (flags & REF_ISSYMREF) skip_prefix(branch_ref, "refs/heads/", &branch_name); - head_short_sha1 = find_unique_abbrev(&head_commit->object.oid, - DEFAULT_ABBREV); + head_short_sha1 = repo_find_unique_abbrev(the_repository, + &head_commit->object.oid, + DEFAULT_ABBREV); strbuf_addf(&msg, "%s: %s ", branch_name, head_short_sha1); pp_commit_easy(CMIT_FMT_ONELINE, head_commit, &msg); @@ -1465,7 +1480,7 @@ done: return ret; } -static int create_stash(int argc, const char **argv, const char *prefix) +static int create_stash(int argc, const char **argv, const char *prefix UNUSED) { int ret; struct strbuf stash_msg_buf = STRBUF_INIT; diff --git a/builtin/stripspace.c b/builtin/stripspace.c index 1e34cf2beb..7b700a9fb1 100644 --- a/builtin/stripspace.c +++ b/builtin/stripspace.c @@ -1,8 +1,11 @@ #include "builtin.h" -#include "cache.h" #include "config.h" +#include "environment.h" +#include "gettext.h" #include "parse-options.h" +#include "setup.h" #include "strbuf.h" +#include "write-or-die.h" static void comment_lines(struct strbuf *buf) { @@ -10,7 +13,7 @@ static void comment_lines(struct strbuf *buf) size_t len; msg = strbuf_detach(buf, &len); - strbuf_add_commented_lines(buf, msg, len); + strbuf_add_commented_lines(buf, msg, len, comment_line_char); free(msg); } @@ -55,7 +58,8 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix) die_errno("could not read the input"); if (mode == STRIP_DEFAULT || mode == STRIP_COMMENTS) - strbuf_stripspace(&buf, mode == STRIP_COMMENTS); + strbuf_stripspace(&buf, + mode == STRIP_COMMENTS ? comment_line_char : '\0'); else comment_lines(&buf); diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 4c173d8b37..cce46450ab 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -1,12 +1,20 @@ #define USE_THE_INDEX_VARIABLE #include "builtin.h" +#include "abspath.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "repository.h" -#include "cache.h" #include "config.h" #include "parse-options.h" #include "quote.h" +#include "path.h" #include "pathspec.h" +#include "preload-index.h" #include "dir.h" +#include "read-cache.h" +#include "setup.h" +#include "sparse-index.h" #include "submodule.h" #include "submodule-config.h" #include "string-list.h" @@ -18,7 +26,9 @@ #include "revision.h" #include "diffcore.h" #include "diff.h" -#include "object-store.h" +#include "object-file.h" +#include "object-name.h" +#include "object-store-ll.h" #include "advice.h" #include "branch.h" #include "list-objects-filter-options.h" @@ -557,7 +567,7 @@ static int module_init(int argc, const char **argv, const char *prefix) * If there are no path args and submodule.active is set then, * by default, only initialize 'active' modules. */ - if (!argc && git_config_get_value_multi("submodule.active")) + if (!argc && !git_config_get("submodule.active")) module_list_active(&list); info.prefix = prefix; @@ -619,7 +629,6 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, char *displaypath; struct strvec diff_files_args = STRVEC_INIT; struct rev_info rev = REV_INFO_INIT; - int diff_files_result; struct strbuf buf = STRBUF_INIT; const char *git_dir; struct setup_revision_opt opt = { @@ -659,9 +668,9 @@ 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); - diff_files_result = run_diff_files(&rev, 0); + run_diff_files(&rev, 0); - if (!diff_result_code(&rev.diffopt, diff_files_result)) { + if (!diff_result_code(&rev.diffopt)) { print_status(flags, ' ', path, ce_oid, displaypath); } else if (!(flags & OPT_CACHED)) { @@ -1108,7 +1117,7 @@ static int compute_summary_module_list(struct object_id *head_oid, strvec_pushv(&diff_args, info->argv); git_config(git_diff_basic_config, NULL); - init_revisions(&rev, info->prefix); + 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); @@ -1131,7 +1140,7 @@ static int compute_summary_module_list(struct object_id *head_oid, } if (diff_cmd == DIFF_INDEX) - run_diff_index(&rev, info->cached); + run_diff_index(&rev, info->cached ? DIFF_INDEX_CACHED : 0); else run_diff_files(&rev, 0); prepare_submodule_summary(info, &list); @@ -1174,7 +1183,7 @@ static int module_summary(int argc, const char **argv, const char *prefix) if (!summary_limit) return 0; - if (!get_oid(argc ? argv[0] : "HEAD", &head_oid)) { + if (!repo_get_oid(the_repository, argc ? argv[0] : "HEAD", &head_oid)) { if (argc) { argv++; argc--; @@ -1187,7 +1196,7 @@ static int module_summary(int argc, const char **argv, const char *prefix) argc--; } } else { - if (get_oid("HEAD", &head_oid)) + if (repo_get_oid(the_repository, "HEAD", &head_oid)) die(_("could not fetch a revision for HEAD")); } @@ -2016,14 +2025,17 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, strbuf_reset(&sb); strbuf_addf(&sb, "submodule.%s.url", sub->name); if (repo_config_get_string_tmp(the_repository, sb.buf, &url)) { - if (starts_with_dot_slash(sub->url) || - starts_with_dot_dot_slash(sub->url)) { + if (sub->url && (starts_with_dot_slash(sub->url) || + starts_with_dot_dot_slash(sub->url))) { url = resolve_relative_url(sub->url, NULL, 0); need_free_url = 1; } else url = sub->url; } + if (!url) + die(_("cannot clone submodule '%s' without a URL"), sub->name); + strbuf_reset(&sb); strbuf_addf(&sb, "%s/.git", ce->name); needs_cloning = !file_exists(sb.buf); @@ -2132,9 +2144,9 @@ static int update_clone_get_next_task(struct child_process *child, return 0; } -static int update_clone_start_failure(struct strbuf *err, +static int update_clone_start_failure(struct strbuf *err UNUSED, void *suc_cb, - void *idx_task_cb) + void *idx_task_cb UNUSED) { struct submodule_update_clone *suc = suc_cb; @@ -2181,12 +2193,13 @@ static int update_clone_task_finished(int result, } static int git_update_clone_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { int *max_jobs = cb; if (!strcmp(var, "submodule.fetchjobs")) - *max_jobs = parse_submodule_fetchjobs(var, value); + *max_jobs = parse_submodule_fetchjobs(var, value, ctx->kvi); return 0; } @@ -2743,7 +2756,7 @@ static int module_update(int argc, const char **argv, const char *prefix) * If there are no path args and submodule.active is set then, * by default, only initialize 'active' modules. */ - if (!argc && git_config_get_value_multi("submodule.active")) + if (!argc && !git_config_get("submodule.active")) module_list_active(&list); info.prefix = opt.prefix; @@ -2764,7 +2777,7 @@ cleanup: return ret; } -static int push_check(int argc, const char **argv, const char *prefix) +static int push_check(int argc, const char **argv, const char *prefix UNUSED) { struct remote *remote; const char *superproject_head; @@ -2876,7 +2889,7 @@ cleanup: static int module_set_url(int argc, const char **argv, const char *prefix) { - int quiet = 0; + int quiet = 0, ret; const char *newurl; const char *path; char *config_name; @@ -2888,20 +2901,29 @@ static int module_set_url(int argc, const char **argv, const char *prefix) N_("git submodule set-url [--quiet] <path> <newurl>"), NULL }; + const struct submodule *sub; argc = parse_options(argc, argv, prefix, options, usage, 0); if (argc != 2 || !(path = argv[0]) || !(newurl = argv[1])) usage_with_options(usage, options); - config_name = xstrfmt("submodule.%s.url", path); + sub = submodule_from_path(the_repository, null_oid(), path); - config_set_in_gitmodules_file_gently(config_name, newurl); - sync_submodule(path, prefix, NULL, quiet ? OPT_QUIET : 0); + if (!sub) + die(_("no submodule mapping found in .gitmodules for path '%s'"), + path); - free(config_name); + config_name = xstrfmt("submodule.%s.url", sub->name); + ret = config_set_in_gitmodules_file_gently(config_name, newurl); - return 0; + if (!ret) { + repo_read_gitmodules(the_repository, 0); + sync_submodule(sub->path, prefix, NULL, quiet ? OPT_QUIET : 0); + } + + free(config_name); + return !!ret; } static int module_set_branch(int argc, const char **argv, const char *prefix) @@ -2928,6 +2950,7 @@ static int module_set_branch(int argc, const char **argv, const char *prefix) N_("git submodule set-branch [-q|--quiet] (-b|--branch) <branch> <path>"), NULL }; + const struct submodule *sub; argc = parse_options(argc, argv, prefix, options, usage, 0); @@ -2940,7 +2963,13 @@ static int module_set_branch(int argc, const char **argv, const char *prefix) if (argc != 1 || !(path = argv[0])) usage_with_options(usage, options); - config_name = xstrfmt("submodule.%s.branch", path); + sub = submodule_from_path(the_repository, null_oid(), path); + + if (!sub) + die(_("no submodule mapping found in .gitmodules for path '%s'"), + path); + + config_name = xstrfmt("submodule.%s.branch", sub->name); ret = config_set_in_gitmodules_file_gently(config_name, opt_branch); free(config_name); @@ -3140,7 +3169,6 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con static void configure_added_submodule(struct add_data *add_data) { char *key; - const char *val; struct child_process add_submod = CHILD_PROCESS_INIT; struct child_process add_gitmodules = CHILD_PROCESS_INIT; @@ -3185,7 +3213,7 @@ static void configure_added_submodule(struct add_data *add_data) * is_submodule_active(), since that function needs to find * out the value of "submodule.active" again anyway. */ - if (!git_config_get_string_tmp("submodule.active", &val)) { + if (!git_config_get("submodule.active")) { /* * If the submodule being added isn't already covered by the * current configured pathspec, set the submodule's active flag diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c index e00768a8b7..c9defe4d2e 100644 --- a/builtin/symbolic-ref.c +++ b/builtin/symbolic-ref.c @@ -1,8 +1,9 @@ #include "builtin.h" #include "config.h" -#include "cache.h" +#include "gettext.h" #include "refs.h" #include "parse-options.h" +#include "strbuf.h" static const char * const git_symbolic_ref_usage[] = { N_("git symbolic-ref [-m <reason>] <name> <ref>"), diff --git a/builtin/tag.c b/builtin/tag.c index d428c45dc8..2528d499dd 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -6,11 +6,17 @@ * Based on git-tag.sh and mktag.c by Linus Torvalds. */ -#include "cache.h" -#include "config.h" #include "builtin.h" +#include "advice.h" +#include "config.h" +#include "editor.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "refs.h" -#include "object-store.h" +#include "object-name.h" +#include "object-store-ll.h" +#include "path.h" #include "tag.h" #include "run-command.h" #include "parse-options.h" @@ -21,6 +27,7 @@ #include "column.h" #include "ref-filter.h" #include "date.h" +#include "write-or-die.h" static const char * const git_tag_usage[] = { N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n" @@ -41,13 +48,7 @@ static int config_sign_tag = -1; /* unspecified */ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, struct ref_format *format) { - struct ref_array array; - struct strbuf output = STRBUF_INIT; - struct strbuf err = STRBUF_INIT; char *to_free = NULL; - int i; - - memset(&array, 0, sizeof(array)); if (filter->lines == -1) filter->lines = 0; @@ -65,21 +66,8 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, if (verify_ref_format(format)) die(_("unable to parse format string")); filter->with_commit_tag_algo = 1; - filter_refs(&array, filter, FILTER_REFS_TAGS); - ref_array_sort(sorting, &array); - - for (i = 0; i < array.nr; i++) { - strbuf_reset(&output); - strbuf_reset(&err); - if (format_ref_array_item(array.items[i], format, &output, &err)) - die("%s", err.buf); - fwrite(output.buf, 1, output.len, stdout); - putchar('\n'); - } + filter_and_format_refs(filter, FILTER_REFS_TAGS, sorting, format); - strbuf_release(&err); - strbuf_release(&output); - ref_array_clear(&array); free(to_free); return 0; @@ -111,7 +99,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn, return had_error; } -static int collect_tags(const char *name, const char *ref, +static int collect_tags(const char *name UNUSED, const char *ref, const struct object_id *oid, void *cb_data) { struct string_list *ref_list = cb_data; @@ -137,7 +125,7 @@ static int delete_tags(const char **argv) if (!ref_exists(name)) printf(_("Deleted tag '%s' (was %s)\n"), item->string + 10, - find_unique_abbrev(oid, DEFAULT_ABBREV)); + repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV)); free(oid); } @@ -145,7 +133,7 @@ static int delete_tags(const char **argv) return result; } -static int verify_tag(const char *name, const char *ref, +static int verify_tag(const char *name, const char *ref UNUSED, const struct object_id *oid, void *cb_data) { int flags; @@ -178,10 +166,9 @@ static const char tag_template_nocleanup[] = "Lines starting with '%c' will be kept; you may remove them" " yourself if you want to.\n"); -static int git_tag_config(const char *var, const char *value, void *cb) +static int git_tag_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { - int status; - if (!strcmp(var, "tag.gpgsign")) { config_sign_tag = git_config_bool(var, value); return 0; @@ -194,9 +181,6 @@ static int git_tag_config(const char *var, const char *value, void *cb) return 0; } - status = git_gpg_config(var, value, cb); - if (status) - return status; if (!strcmp(var, "tag.forcesignannotated")) { force_sign_annotate = git_config_bool(var, value); return 0; @@ -204,7 +188,11 @@ static int git_tag_config(const char *var, const char *value, void *cb) if (starts_with(var, "column.")) return git_column_config(var, value, "tag", &colopts); - return git_color_default_config(var, value, cb); + + if (git_color_config(var, value, cb) < 0) + return -1; + + return git_default_config(var, value, ctx, cb); } static void write_tag_body(int fd, const struct object_id *oid) @@ -215,7 +203,7 @@ static void write_tag_body(int fd, const struct object_id *oid) struct strbuf payload = STRBUF_INIT; struct strbuf signature = STRBUF_INIT; - orig = buf = read_object_file(oid, &type, &size); + orig = buf = repo_read_object_file(the_repository, oid, &type, &size); if (!buf) return; if (parse_signature(buf, size, &payload, &signature)) { @@ -266,11 +254,10 @@ static const char message_advice_nested_tag[] = static void create_tag(const struct object_id *object, const char *object_ref, const char *tag, struct strbuf *buf, struct create_tag_options *opt, - struct object_id *prev, struct object_id *result) + struct object_id *prev, struct object_id *result, char *path) { enum object_type type; struct strbuf header = STRBUF_INIT; - char *path = NULL; type = oid_object_info(the_repository, object, NULL); if (type <= OBJ_NONE) @@ -294,7 +281,6 @@ static void create_tag(const struct object_id *object, const char *object_ref, int fd; /* write the template message before editing: */ - path = git_pathdup("TAG_EDITMSG"); fd = xopen(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); if (opt->message_given) { @@ -306,9 +292,11 @@ static void create_tag(const struct object_id *object, const char *object_ref, struct strbuf buf = STRBUF_INIT; strbuf_addch(&buf, '\n'); if (opt->cleanup_mode == CLEANUP_ALL) - strbuf_commented_addf(&buf, _(tag_template), tag, comment_line_char); + strbuf_commented_addf(&buf, comment_line_char, + _(tag_template), tag, comment_line_char); else - strbuf_commented_addf(&buf, _(tag_template_nocleanup), tag, comment_line_char); + strbuf_commented_addf(&buf, comment_line_char, + _(tag_template_nocleanup), tag, comment_line_char); write_or_die(fd, buf.buf, buf.len); strbuf_release(&buf); } @@ -322,7 +310,8 @@ static void create_tag(const struct object_id *object, const char *object_ref, } if (opt->cleanup_mode != CLEANUP_NONE) - strbuf_stripspace(buf, opt->cleanup_mode == CLEANUP_ALL); + strbuf_stripspace(buf, + opt->cleanup_mode == CLEANUP_ALL ? comment_line_char : '\0'); if (!opt->message_given && !buf->len) die(_("no tag message?")); @@ -336,10 +325,6 @@ static void create_tag(const struct object_id *object, const char *object_ref, path); exit(128); } - if (path) { - unlink_or_warn(path); - free(path); - } } static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb) @@ -366,7 +351,7 @@ static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb) strbuf_addstr(sb, "object of unknown type"); break; case OBJ_COMMIT: - if ((buf = read_object_file(oid, &type, &size))) { + if ((buf = repo_read_object_file(the_repository, oid, &type, &size))) { subject_len = find_commit_subject(buf, &subject_start); strbuf_insert(sb, sb->len, subject_start, subject_len); } else { @@ -433,11 +418,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix) int create_reflog = 0; int annotate = 0, force = 0; int cmdmode = 0, create_tag_object = 0; - const char *msgfile = NULL, *keyid = NULL; + char *msgfile = NULL; + const char *keyid = NULL; struct msg_arg msg = { .buf = STRBUF_INIT }; struct ref_transaction *transaction; struct strbuf err = STRBUF_INIT; - struct ref_filter filter; + struct ref_filter filter = REF_FILTER_INIT; struct ref_sorting *sorting; struct string_list sorting_options = STRING_LIST_INIT_DUP; struct ref_format format = REF_FORMAT_INIT; @@ -473,6 +459,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix) OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")), OPT_MERGED(&filter, N_("print only tags that are merged")), OPT_NO_MERGED(&filter, N_("print only tags that are not merged")), + OPT_BOOL(0, "omit-empty", &format.array_opts.omit_empty, + N_("do not output a newline after empty formatted refs")), OPT_REF_SORT(&sorting_options), { OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"), @@ -487,13 +475,19 @@ int cmd_tag(int argc, const char **argv, const char *prefix) }; int ret = 0; const char *only_in_list = NULL; + char *path = NULL; setup_ref_filter_porcelain_msg(); + /* + * Try to set sort keys from config. If config does not set any, + * fall back on default (refname) sorting. + */ git_config(git_tag_config, &sorting_options); + if (!sorting_options.nr) + string_list_append(&sorting_options, "refname"); memset(&opt, 0, sizeof(opt)); - memset(&filter, 0, sizeof(filter)); filter.lines = -1; opt.sign = -1; @@ -593,7 +587,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) if (argc > 2) die(_("too many arguments")); - if (get_oid(object_ref, &object)) + if (repo_get_oid(the_repository, object_ref, &object)) die(_("Failed to resolve '%s' as a valid ref."), object_ref); if (strbuf_check_tag_ref(&ref, tag)) @@ -621,7 +615,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix) if (create_tag_object) { if (force_sign_annotate && !annotate) opt.sign = 1; - create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object); + path = git_pathdup("TAG_EDITMSG"); + create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object, + path); } transaction = ref_transaction_begin(&err); @@ -629,19 +625,30 @@ int cmd_tag(int argc, const char **argv, const char *prefix) ref_transaction_update(transaction, ref.buf, &object, &prev, create_reflog ? REF_FORCE_CREATE_REFLOG : 0, reflog_msg.buf, &err) || - ref_transaction_commit(transaction, &err)) + ref_transaction_commit(transaction, &err)) { + if (path) + fprintf(stderr, + _("The tag message has been left in %s\n"), + path); die("%s", err.buf); + } + if (path) { + unlink_or_warn(path); + free(path); + } ref_transaction_free(transaction); if (force && !is_null_oid(&prev) && !oideq(&prev, &object)) printf(_("Updated tag '%s' (was %s)\n"), tag, - find_unique_abbrev(&prev, DEFAULT_ABBREV)); + repo_find_unique_abbrev(the_repository, &prev, DEFAULT_ABBREV)); cleanup: ref_sorting_release(sorting); + ref_filter_clear(&filter); strbuf_release(&buf); strbuf_release(&ref); strbuf_release(&reflog_msg); strbuf_release(&msg.buf); strbuf_release(&err); + free(msgfile); return ret; } diff --git a/builtin/unpack-file.c b/builtin/unpack-file.c index 88de32b7d7..c129e2bb6c 100644 --- a/builtin/unpack-file.c +++ b/builtin/unpack-file.c @@ -1,6 +1,8 @@ #include "builtin.h" #include "config.h" -#include "object-store.h" +#include "hex.h" +#include "object-name.h" +#include "object-store-ll.h" static char *create_temp_file(struct object_id *oid) { @@ -10,7 +12,7 @@ static char *create_temp_file(struct object_id *oid) unsigned long size; int fd; - buf = read_object_file(oid, &type, &size); + buf = repo_read_object_file(the_repository, oid, &type, &size); if (!buf || type != OBJ_BLOB) die("unable to read blob object %s", oid_to_hex(oid)); @@ -23,13 +25,13 @@ static char *create_temp_file(struct object_id *oid) return path; } -int cmd_unpack_file(int argc, const char **argv, const char *prefix) +int cmd_unpack_file(int argc, const char **argv, const char *prefix UNUSED) { struct object_id oid; if (argc != 2 || !strcmp(argv[1], "-h")) usage("git unpack-file <blob>"); - if (get_oid(argv[1], &oid)) + if (repo_get_oid(the_repository, argv[1], &oid)) die("Not a valid object name %s", argv[1]); git_config(git_default_config, NULL); diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 43789b8ef2..fef7423448 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -1,13 +1,18 @@ #include "builtin.h" -#include "cache.h" #include "bulk-checkin.h" #include "config.h" -#include "object-store.h" +#include "environment.h" +#include "gettext.h" +#include "git-zlib.h" +#include "hex.h" +#include "object-store-ll.h" #include "object.h" #include "delta.h" #include "pack.h" #include "blob.h" #include "commit.h" +#include "replace-object.h" +#include "strbuf.h" #include "tag.h" #include "tree.h" #include "tree-walk.h" @@ -210,7 +215,8 @@ static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf) * Verify its reachability and validity recursively and write it out. */ static int check_object(struct object *obj, enum object_type type, - void *data, struct fsck_options *options) + void *data UNUSED, + struct fsck_options *options UNUSED) { struct obj_buffer *obj_buf; @@ -442,7 +448,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, delta_data = get_data(delta_size); if (!delta_data) return; - if (has_object_file(&base_oid)) + if (repo_has_object_file(the_repository, &base_oid)) ; /* Ok we have this one */ else if (resolve_against_held(nr, &base_oid, delta_data, delta_size)) @@ -508,7 +514,8 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, if (resolve_against_held(nr, &base_oid, delta_data, delta_size)) return; - base = read_object_file(&base_oid, &type, &base_size); + base = repo_read_object_file(the_repository, &base_oid, &type, + &base_size); if (!base) { error("failed to read delta-pack base object %s", oid_to_hex(&base_oid)); @@ -598,12 +605,13 @@ static void unpack_all(void) die("unresolved deltas left after unpacking"); } -int cmd_unpack_objects(int argc, const char **argv, const char *prefix) +int cmd_unpack_objects(int argc, const char **argv, const char *prefix UNUSED) { int i; struct object_id oid; + git_hash_ctx tmp_ctx; - read_replace_refs = 0; + disable_replace_refs(); git_config(git_default_config, NULL); @@ -662,7 +670,9 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix) the_hash_algo->init_fn(&ctx); unpack_all(); the_hash_algo->update_fn(&ctx, buffer, offset); - the_hash_algo->final_oid_fn(&oid, &ctx); + the_hash_algo->init_fn(&tmp_ctx); + the_hash_algo->clone_fn(&tmp_ctx, &ctx); + the_hash_algo->final_oid_fn(&oid, &tmp_ctx); if (strict) { write_rest(); if (fsck_finish(&fsck_options)) diff --git a/builtin/update-index.c b/builtin/update-index.c index bf38885d54..7bcaa1476c 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -4,21 +4,31 @@ * Copyright (C) Linus Torvalds, 2005 */ #define USE_THE_INDEX_VARIABLE -#include "cache.h" +#include "builtin.h" #include "bulk-checkin.h" #include "config.h" +#include "environment.h" +#include "gettext.h" +#include "hash.h" +#include "hex.h" #include "lockfile.h" #include "quote.h" #include "cache-tree.h" #include "tree-walk.h" -#include "builtin.h" +#include "object-file.h" #include "refs.h" #include "resolve-undo.h" #include "parse-options.h" #include "pathspec.h" #include "dir.h" +#include "read-cache.h" +#include "repository.h" +#include "setup.h" +#include "sparse-index.h" #include "split-index.h" +#include "symlinks.h" #include "fsmonitor.h" +#include "write-or-die.h" /* * Default to not allowing changes to the list of files. The @@ -599,9 +609,6 @@ static const char * const update_index_usage[] = { NULL }; -static struct object_id head_oid; -static struct object_id merge_head_oid; - static struct cache_entry *read_one_ent(const char *which, struct object_id *ent, const char *path, int namelen, int stage) @@ -632,84 +639,17 @@ static struct cache_entry *read_one_ent(const char *which, static int unresolve_one(const char *path) { - int namelen = strlen(path); - int pos; - int ret = 0; - struct cache_entry *ce_2 = NULL, *ce_3 = NULL; - - /* See if there is such entry in the index. */ - pos = index_name_pos(&the_index, path, namelen); - if (0 <= pos) { - /* already merged */ - pos = unmerge_index_entry_at(&the_index, pos); - if (pos < the_index.cache_nr) { - const struct cache_entry *ce = the_index.cache[pos]; - if (ce_stage(ce) && - ce_namelen(ce) == namelen && - !memcmp(ce->name, path, namelen)) - return 0; - } - /* no resolve-undo information; fall back */ - } else { - /* If there isn't, either it is unmerged, or - * resolved as "removed" by mistake. We do not - * want to do anything in the former case. - */ - pos = -pos-1; - if (pos < the_index.cache_nr) { - const struct cache_entry *ce = the_index.cache[pos]; - if (ce_namelen(ce) == namelen && - !memcmp(ce->name, path, namelen)) { - fprintf(stderr, - "%s: skipping still unmerged path.\n", - path); - goto free_return; - } - } - } - - /* Grab blobs from given path from HEAD and MERGE_HEAD, - * stuff HEAD version in stage #2, - * stuff MERGE_HEAD version in stage #3. - */ - ce_2 = read_one_ent("our", &head_oid, path, namelen, 2); - ce_3 = read_one_ent("their", &merge_head_oid, path, namelen, 3); - - if (!ce_2 || !ce_3) { - ret = -1; - goto free_return; - } - if (oideq(&ce_2->oid, &ce_3->oid) && - ce_2->ce_mode == ce_3->ce_mode) { - fprintf(stderr, "%s: identical in both, skipping.\n", - path); - goto free_return; - } - - remove_file_from_index(&the_index, path); - if (add_index_entry(&the_index, ce_2, ADD_CACHE_OK_TO_ADD)) { - error("%s: cannot add our version to the index.", path); - ret = -1; - goto free_return; - } - if (!add_index_entry(&the_index, ce_3, ADD_CACHE_OK_TO_ADD)) - return 0; - error("%s: cannot add their version to the index.", path); - ret = -1; - free_return: - discard_cache_entry(ce_2); - discard_cache_entry(ce_3); - return ret; -} - -static void read_head_pointers(void) -{ - if (read_ref("HEAD", &head_oid)) - die("No HEAD -- no initial commit yet?"); - if (read_ref("MERGE_HEAD", &merge_head_oid)) { - fprintf(stderr, "Not in the middle of a merge.\n"); - exit(0); - } + struct string_list_item *item; + int res = 0; + + if (!the_index.resolve_undo) + return res; + item = string_list_lookup(the_index.resolve_undo, path); + if (!item) + return res; /* no resolve-undo record for the path */ + res = unmerge_index_entry(&the_index, path, item->util, 0); + FREE_AND_NULL(item->util); + return res; } static int do_unresolve(int ac, const char **av, @@ -718,11 +658,6 @@ static int do_unresolve(int ac, const char **av, int i; int err = 0; - /* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we - * are not doing a merge, so exit with success status. - */ - read_head_pointers(); - for (i = 1; i < ac; i++) { const char *arg = av[i]; char *p = prefix_path(prefix, prefix_length, arg); @@ -741,6 +676,7 @@ static int do_reupdate(const char **paths, int pos; int has_head = 1; struct pathspec pathspec; + struct object_id head_oid; parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD, @@ -846,7 +782,7 @@ static int chmod_callback(const struct option *opt, return 0; } -static int resolve_undo_clear_callback(const struct option *opt, +static int resolve_undo_clear_callback(const struct option *opt UNUSED, const char *arg, int unset) { BUG_ON_OPT_NEG(unset); @@ -880,7 +816,7 @@ static int parse_new_style_cacheinfo(const char *arg, } static enum parse_opt_result cacheinfo_callback( - struct parse_opt_ctx_t *ctx, const struct option *opt, + struct parse_opt_ctx_t *ctx, const struct option *opt UNUSED, const char *arg, int unset) { struct object_id oid; @@ -1080,6 +1016,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) resolve_undo_clear_callback), OPT_INTEGER(0, "index-version", &preferred_index_format, N_("write index in this format")), + OPT_SET_INT(0, "show-index-version", &preferred_index_format, + N_("report on-disk index format version"), -1), OPT_BOOL(0, "split-index", &split_index, N_("enable or disable split index")), OPT_BOOL(0, "untracked-cache", &untracked_cache, @@ -1172,15 +1110,20 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; if (preferred_index_format) { - if (preferred_index_format < INDEX_FORMAT_LB || - INDEX_FORMAT_UB < preferred_index_format) + if (preferred_index_format < 0) { + printf(_("%d\n"), the_index.version); + } else if (preferred_index_format < INDEX_FORMAT_LB || + INDEX_FORMAT_UB < preferred_index_format) { die("index-version %d not in range: %d..%d", preferred_index_format, INDEX_FORMAT_LB, INDEX_FORMAT_UB); - - if (the_index.version != preferred_index_format) - the_index.cache_changed |= SOMETHING_CHANGED; - the_index.version = preferred_index_format; + } else { + if (the_index.version != preferred_index_format) + the_index.cache_changed |= SOMETHING_CHANGED; + report(_("index-version: was %d, set to %d"), + the_index.version, preferred_index_format); + the_index.version = preferred_index_format; + } } if (read_from_stdin) { diff --git a/builtin/update-ref.c b/builtin/update-ref.c index a84e7b47a2..c0c4e65e6f 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -1,9 +1,12 @@ -#include "cache.h" +#include "builtin.h" #include "config.h" +#include "gettext.h" +#include "hash.h" #include "refs.h" -#include "builtin.h" +#include "object-name.h" #include "parse-options.h" #include "quote.h" +#include "repository.h" #include "strvec.h" static const char * const git_update_ref_usage[] = { @@ -116,7 +119,7 @@ static int parse_next_oid(const char **next, const char *end, (*next)++; *next = parse_arg(*next, &arg); if (arg.len) { - if (get_oid(arg.buf, oid)) + if (repo_get_oid(the_repository, arg.buf, oid)) goto invalid; } else { /* Without -z, an empty value means all zeros: */ @@ -134,7 +137,7 @@ static int parse_next_oid(const char **next, const char *end, *next += arg.len; if (arg.len) { - if (get_oid(arg.buf, oid)) + if (repo_get_oid(the_repository, arg.buf, oid)) goto invalid; } else if (flags & PARSE_SHA1_ALLOW_EMPTY) { /* With -z, treat an empty value as all zeros: */ @@ -308,8 +311,8 @@ static void report_ok(const char *command) fflush(stdout); } -static void parse_cmd_option(struct ref_transaction *transaction, - const char *next, const char *end) +static void parse_cmd_option(struct ref_transaction *transaction UNUSED, + const char *next, const char *end UNUSED) { const char *rest; if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination) @@ -318,8 +321,8 @@ static void parse_cmd_option(struct ref_transaction *transaction, die("option unknown: %s", next); } -static void parse_cmd_start(struct ref_transaction *transaction, - const char *next, const char *end) +static void parse_cmd_start(struct ref_transaction *transaction UNUSED, + const char *next, const char *end UNUSED) { if (*next != line_termination) die("start: extra input: %s", next); @@ -327,7 +330,7 @@ static void parse_cmd_start(struct ref_transaction *transaction, } static void parse_cmd_prepare(struct ref_transaction *transaction, - const char *next, const char *end) + const char *next, const char *end UNUSED) { struct strbuf error = STRBUF_INIT; if (*next != line_termination) @@ -338,7 +341,7 @@ static void parse_cmd_prepare(struct ref_transaction *transaction, } static void parse_cmd_abort(struct ref_transaction *transaction, - const char *next, const char *end) + const char *next, const char *end UNUSED) { struct strbuf error = STRBUF_INIT; if (*next != line_termination) @@ -349,7 +352,7 @@ static void parse_cmd_abort(struct ref_transaction *transaction, } static void parse_cmd_commit(struct ref_transaction *transaction, - const char *next, const char *end) + const char *next, const char *end UNUSED) { struct strbuf error = STRBUF_INIT; if (*next != line_termination) @@ -549,7 +552,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) refname = argv[0]; value = argv[1]; oldval = argv[2]; - if (get_oid(value, &oid)) + if (repo_get_oid(the_repository, value, &oid)) die("%s: not a valid SHA1", value); } @@ -560,7 +563,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) * must not already exist: */ oidclr(&oldoid); - else if (get_oid(oldval, &oldoid)) + else if (repo_get_oid(the_repository, oldval, &oldoid)) die("%s: not a valid old SHA1", oldval); } diff --git a/builtin/update-server-info.c b/builtin/update-server-info.c index d2239c9ef4..1dc3971ede 100644 --- a/builtin/update-server-info.c +++ b/builtin/update-server-info.c @@ -1,7 +1,8 @@ -#include "cache.h" -#include "config.h" #include "builtin.h" +#include "config.h" +#include "gettext.h" #include "parse-options.h" +#include "server-info.h" static const char * const update_server_info_usage[] = { "git update-server-info [-f | --force]", diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c index 945ee2b412..1b09e5e1aa 100644 --- a/builtin/upload-archive.c +++ b/builtin/upload-archive.c @@ -1,11 +1,12 @@ /* * Copyright (c) 2006 Franck Bui-Huu */ -#include "cache.h" #include "builtin.h" #include "archive.h" +#include "path.h" #include "pkt-line.h" #include "sideband.h" +#include "repository.h" #include "run-command.h" #include "strvec.h" @@ -79,6 +80,8 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix) { struct child_process writer = CHILD_PROCESS_INIT; + BUG_ON_NON_EMPTY_PREFIX(prefix); + if (argc == 2 && !strcmp(argv[1], "-h")) usage(upload_archive_usage); diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c index 25b69da2bf..9b021ef026 100644 --- a/builtin/upload-pack.c +++ b/builtin/upload-pack.c @@ -1,9 +1,11 @@ -#include "cache.h" #include "builtin.h" #include "exec-cmd.h" +#include "gettext.h" #include "pkt-line.h" #include "parse-options.h" +#include "path.h" #include "protocol.h" +#include "replace-object.h" #include "upload-pack.h" #include "serve.h" @@ -34,7 +36,7 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix) }; packet_trace_identity("upload-pack"); - read_replace_refs = 0; + disable_replace_refs(); argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0); diff --git a/builtin/var.c b/builtin/var.c index a80c1df86f..8cf7dd9e2e 100644 --- a/builtin/var.c +++ b/builtin/var.c @@ -4,57 +4,188 @@ * Copyright (C) Eric Biederman, 2005 */ #include "builtin.h" +#include "attr.h" #include "config.h" +#include "editor.h" +#include "ident.h" +#include "pager.h" #include "refs.h" +#include "path.h" +#include "strbuf.h" static const char var_usage[] = "git var (-l | <variable>)"; -static const char *editor(int flag) +static char *committer(int ident_flag) { - return git_editor(); + return xstrdup_or_null(git_committer_info(ident_flag)); } -static const char *sequence_editor(int flag) +static char *author(int ident_flag) { - return git_sequence_editor(); + return xstrdup_or_null(git_author_info(ident_flag)); } -static const char *pager(int flag) +static char *editor(int ident_flag UNUSED) +{ + return xstrdup_or_null(git_editor()); +} + +static char *sequence_editor(int ident_flag UNUSED) +{ + return xstrdup_or_null(git_sequence_editor()); +} + +static char *pager(int ident_flag UNUSED) { const char *pgm = git_pager(1); if (!pgm) pgm = "cat"; - return pgm; + return xstrdup(pgm); +} + +static char *default_branch(int ident_flag UNUSED) +{ + return xstrdup_or_null(git_default_branch_name(1)); +} + +static char *shell_path(int ident_flag UNUSED) +{ + return xstrdup(SHELL_PATH); } -static const char *default_branch(int flag) +static char *git_attr_val_system(int ident_flag UNUSED) { - return git_default_branch_name(1); + if (git_attr_system_is_enabled()) { + char *file = xstrdup(git_attr_system_file()); + normalize_path_copy(file, file); + return file; + } + return NULL; +} + +static char *git_attr_val_global(int ident_flag UNUSED) +{ + char *file = xstrdup_or_null(git_attr_global_file()); + if (file) { + normalize_path_copy(file, file); + return file; + } + return NULL; +} + +static char *git_config_val_system(int ident_flag UNUSED) +{ + if (git_config_system()) { + char *file = git_system_config(); + normalize_path_copy(file, file); + return file; + } + return NULL; +} + +static char *git_config_val_global(int ident_flag UNUSED) +{ + struct strbuf buf = STRBUF_INIT; + char *user, *xdg; + size_t unused; + + git_global_config(&user, &xdg); + if (xdg && *xdg) { + normalize_path_copy(xdg, xdg); + strbuf_addf(&buf, "%s\n", xdg); + } + if (user && *user) { + normalize_path_copy(user, user); + strbuf_addf(&buf, "%s\n", user); + } + free(xdg); + free(user); + strbuf_trim_trailing_newline(&buf); + if (buf.len == 0) { + strbuf_release(&buf); + return NULL; + } + return strbuf_detach(&buf, &unused); } struct git_var { const char *name; - const char *(*read)(int); + char *(*read)(int); + int multivalued; }; static struct git_var git_vars[] = { - { "GIT_COMMITTER_IDENT", git_committer_info }, - { "GIT_AUTHOR_IDENT", git_author_info }, - { "GIT_EDITOR", editor }, - { "GIT_SEQUENCE_EDITOR", sequence_editor }, - { "GIT_PAGER", pager }, - { "GIT_DEFAULT_BRANCH", default_branch }, - { "", NULL }, + { + .name = "GIT_COMMITTER_IDENT", + .read = committer, + }, + { + .name = "GIT_AUTHOR_IDENT", + .read = author, + }, + { + .name = "GIT_EDITOR", + .read = editor, + }, + { + .name = "GIT_SEQUENCE_EDITOR", + .read = sequence_editor, + }, + { + .name = "GIT_PAGER", + .read = pager, + }, + { + .name = "GIT_DEFAULT_BRANCH", + .read = default_branch, + }, + { + .name = "GIT_SHELL_PATH", + .read = shell_path, + }, + { + .name = "GIT_ATTR_SYSTEM", + .read = git_attr_val_system, + }, + { + .name = "GIT_ATTR_GLOBAL", + .read = git_attr_val_global, + }, + { + .name = "GIT_CONFIG_SYSTEM", + .read = git_config_val_system, + }, + { + .name = "GIT_CONFIG_GLOBAL", + .read = git_config_val_global, + .multivalued = 1, + }, + { + .name = "", + .read = NULL, + }, }; static void list_vars(void) { struct git_var *ptr; - const char *val; + char *val; for (ptr = git_vars; ptr->read; ptr++) - if ((val = ptr->read(0))) - printf("%s=%s\n", ptr->name, val); + if ((val = ptr->read(0))) { + if (ptr->multivalued && *val) { + struct string_list list = STRING_LIST_INIT_DUP; + int i; + + string_list_split(&list, val, '\n', -1); + for (i = 0; i < list.nr; i++) + printf("%s=%s\n", ptr->name, list.items[i].string); + string_list_clear(&list, 0); + } else { + printf("%s=%s\n", ptr->name, val); + } + free(val); + } } static const struct git_var *get_git_var(const char *var) @@ -68,19 +199,20 @@ static const struct git_var *get_git_var(const char *var) return NULL; } -static int show_config(const char *var, const char *value, void *cb) +static int show_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { if (value) printf("%s=%s\n", var, value); else printf("%s\n", var); - return git_default_config(var, value, cb); + return git_default_config(var, value, ctx, cb); } -int cmd_var(int argc, const char **argv, const char *prefix) +int cmd_var(int argc, const char **argv, const char *prefix UNUSED) { const struct git_var *git_var; - const char *val; + char *val; if (argc != 2) usage(var_usage); @@ -101,6 +233,7 @@ int cmd_var(int argc, const char **argv, const char *prefix) return 1; printf("%s\n", val); + free(val); return 0; } diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c index 3ebad32b0f..9680b58701 100644 --- a/builtin/verify-commit.c +++ b/builtin/verify-commit.c @@ -5,10 +5,11 @@ * * Based on git-verify-tag */ -#include "cache.h" -#include "config.h" #include "builtin.h" -#include "object-store.h" +#include "config.h" +#include "gettext.h" +#include "object-name.h" +#include "object-store-ll.h" #include "repository.h" #include "commit.h" #include "run-command.h" @@ -39,7 +40,7 @@ static int verify_commit(const char *name, unsigned flags) struct object_id oid; struct object *obj; - if (get_oid(name, &oid)) + if (repo_get_oid(the_repository, name, &oid)) return error("commit '%s' not found.", name); obj = parse_object(the_repository, &oid); @@ -52,14 +53,6 @@ static int verify_commit(const char *name, unsigned flags) return run_gpg_verify((struct commit *)obj, flags); } -static int git_verify_commit_config(const char *var, const char *value, void *cb) -{ - int status = git_gpg_config(var, value, cb); - if (status) - return status; - return git_default_config(var, value, cb); -} - int cmd_verify_commit(int argc, const char **argv, const char *prefix) { int i = 1, verbose = 0, had_error = 0; @@ -70,7 +63,7 @@ int cmd_verify_commit(int argc, const char **argv, const char *prefix) OPT_END() }; - git_config(git_verify_commit_config, NULL); + git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, verify_commit_options, verify_commit_usage, PARSE_OPT_KEEP_ARGV0); diff --git a/builtin/verify-pack.c b/builtin/verify-pack.c index 27d6f75fd8..011dddd2dc 100644 --- a/builtin/verify-pack.c +++ b/builtin/verify-pack.c @@ -1,8 +1,9 @@ #include "builtin.h" -#include "cache.h" #include "config.h" +#include "gettext.h" #include "run-command.h" #include "parse-options.h" +#include "strbuf.h" #define VERIFY_PACK_VERBOSE 01 #define VERIFY_PACK_STAT_ONLY 02 diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c index 217566952d..d8753270eb 100644 --- a/builtin/verify-tag.c +++ b/builtin/verify-tag.c @@ -5,11 +5,12 @@ * * Based on git-verify-tag.sh */ -#include "cache.h" -#include "config.h" #include "builtin.h" +#include "config.h" +#include "gettext.h" #include "tag.h" #include "run-command.h" +#include "object-name.h" #include "parse-options.h" #include "gpg-interface.h" #include "ref-filter.h" @@ -19,14 +20,6 @@ static const char * const verify_tag_usage[] = { NULL }; -static int git_verify_tag_config(const char *var, const char *value, void *cb) -{ - int status = git_gpg_config(var, value, cb); - if (status) - return status; - return git_default_config(var, value, cb); -} - int cmd_verify_tag(int argc, const char **argv, const char *prefix) { int i = 1, verbose = 0, had_error = 0; @@ -39,7 +32,7 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix) OPT_END() }; - git_config(git_verify_tag_config, NULL); + git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, verify_tag_options, verify_tag_usage, PARSE_OPT_KEEP_ARGV0); @@ -60,7 +53,7 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix) struct object_id oid; const char *name = argv[i++]; - if (get_oid(name, &oid)) { + if (repo_get_oid(the_repository, name, &oid)) { had_error = !!error("tag '%s' not found.", name); continue; } diff --git a/builtin/worktree.c b/builtin/worktree.c index 254283aa6f..4ac1621541 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -1,12 +1,23 @@ -#include "cache.h" +#include "builtin.h" +#include "abspath.h" +#include "advice.h" #include "checkout.h" #include "config.h" -#include "builtin.h" +#include "copy.h" #include "dir.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" +#include "object-file.h" +#include "object-name.h" #include "parse-options.h" +#include "path.h" #include "strvec.h" #include "branch.h" +#include "read-cache-ll.h" #include "refs.h" +#include "remote.h" +#include "repository.h" #include "run-command.h" #include "hook.h" #include "sigchain.h" @@ -17,7 +28,8 @@ #define BUILTIN_WORKTREE_ADD_USAGE \ N_("git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n" \ - " [-b <new-branch>] <path> [<commit-ish>]") + " [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]") + #define BUILTIN_WORKTREE_LIST_USAGE \ N_("git worktree list [-v | --porcelain [-z]]") #define BUILTIN_WORKTREE_LOCK_USAGE \ @@ -33,6 +45,23 @@ #define BUILTIN_WORKTREE_UNLOCK_USAGE \ N_("git worktree unlock <worktree>") +#define WORKTREE_ADD_DWIM_ORPHAN_INFER_TEXT \ + _("No possible source branch, inferring '--orphan'") + +#define WORKTREE_ADD_ORPHAN_WITH_DASH_B_HINT_TEXT \ + _("If you meant to create a worktree containing a new orphan branch\n" \ + "(branch with no commits) for this repository, you can do so\n" \ + "using the --orphan flag:\n" \ + "\n" \ + " git worktree add --orphan -b %s %s\n") + +#define WORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT \ + _("If you meant to create a worktree containing a new orphan branch\n" \ + "(branch with no commits) for this repository, you can do so\n" \ + "using the --orphan flag:\n" \ + "\n" \ + " git worktree add --orphan %s\n") + static const char * const git_worktree_usage[] = { BUILTIN_WORKTREE_ADD_USAGE, BUILTIN_WORKTREE_LIST_USAGE, @@ -90,6 +119,7 @@ struct add_opts { int detach; int quiet; int checkout; + int orphan; const char *keep_locked; }; @@ -98,14 +128,15 @@ static int verbose; static int guess_remote; static timestamp_t expire; -static int git_worktree_config(const char *var, const char *value, void *cb) +static int git_worktree_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { if (!strcmp(var, "worktree.guessremote")) { guess_remote = git_config_bool(var, value); return 0; } - return git_default_config(var, value, cb); + return git_default_config(var, value, ctx, cb); } static int delete_git_dir(const char *id) @@ -319,7 +350,6 @@ static void copy_filtered_worktree_config(const char *worktree_git_dir) if (file_exists(from_file)) { struct config_set cs = { { 0 } }; - const char *core_worktree; int bare; if (safe_create_leading_directories(to_file) || @@ -338,7 +368,7 @@ static void copy_filtered_worktree_config(const char *worktree_git_dir) to_file, "core.bare", NULL, "true", 0)) error(_("failed to unset '%s' in '%s'"), "core.bare", to_file); - if (!git_configset_get_value(&cs, "core.worktree", &core_worktree) && + if (!git_configset_get(&cs, "core.worktree") && git_config_set_in_file_gently(to_file, "core.worktree", NULL)) error(_("failed to unset '%s' in '%s'"), @@ -364,6 +394,22 @@ static int checkout_worktree(const struct add_opts *opts, return run_command(&cp); } +static int make_worktree_orphan(const char * ref, const struct add_opts *opts, + struct strvec *child_env) +{ + struct strbuf symref = STRBUF_INIT; + struct child_process cp = CHILD_PROCESS_INIT; + + validate_new_branchname(ref, &symref, 0); + strvec_pushl(&cp.args, "symbolic-ref", "HEAD", symref.buf, NULL); + if (opts->quiet) + strvec_push(&cp.args, "--quiet"); + strvec_pushv(&cp.env, child_env->v); + strbuf_release(&symref); + cp.git_cmd = 1; + return run_command(&cp); +} + static int add_worktree(const char *path, const char *refname, const struct add_opts *opts) { @@ -393,7 +439,7 @@ static int add_worktree(const char *path, const char *refname, die_if_checked_out(symref.buf, 0); } commit = lookup_commit_reference_by_name(refname); - if (!commit) + if (!commit && !opts->orphan) die(_("invalid reference: %s"), refname); name = worktree_basename(path, &len); @@ -475,17 +521,17 @@ static int add_worktree(const char *path, const char *refname, * values from the current worktree into the new one, that way the * new worktree behaves the same as this one. */ - if (repository_format_worktree_config) + if (the_repository->repository_format_worktree_config) copy_filtered_worktree_config(sb_repo.buf); strvec_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf); strvec_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path); cp.git_cmd = 1; - if (!is_branch) + if (!is_branch && commit) { strvec_pushl(&cp.args, "update-ref", "HEAD", oid_to_hex(&commit->object.oid), NULL); - else { + } else { strvec_pushl(&cp.args, "symbolic-ref", "HEAD", symref.buf, NULL); if (opts->quiet) @@ -497,6 +543,10 @@ static int add_worktree(const char *path, const char *refname, if (ret) goto done; + if (opts->orphan && + (ret = make_worktree_orphan(refname, opts, &child_env))) + goto done; + if (opts->checkout && (ret = checkout_worktree(opts, &child_env))) goto done; @@ -516,7 +566,7 @@ done: * Hook failure does not warrant worktree deletion, so run hook after * is_junk is cleared, but do return appropriate code when hook fails. */ - if (!ret && opts->checkout) { + if (!ret && opts->checkout && !opts->orphan) { struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT; strvec_pushl(&opt.env, "GIT_DIR", "GIT_WORK_TREE", NULL); @@ -552,7 +602,7 @@ static void print_preparing_worktree_line(int detach, else fprintf_ln(stderr, _("Preparing worktree (resetting branch '%s'; was at %s)"), new_branch, - find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV)); + repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV)); } else if (new_branch) { fprintf_ln(stderr, _("Preparing worktree (new branch '%s')"), new_branch); } else { @@ -564,14 +614,131 @@ static void print_preparing_worktree_line(int detach, else { struct commit *commit = lookup_commit_reference_by_name(branch); if (!commit) - die(_("invalid reference: %s"), branch); + BUG(_("unreachable: invalid reference: %s"), branch); fprintf_ln(stderr, _("Preparing worktree (detached HEAD %s)"), - find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV)); + repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV)); } strbuf_release(&s); } } +/** + * Callback to short circuit iteration over refs on the first reference + * corresponding to a valid oid. + * + * Returns 0 on failure and non-zero on success. + */ +static int first_valid_ref(const char *refname UNUSED, + const struct object_id *oid UNUSED, + int flags UNUSED, + void *cb_data UNUSED) +{ + return 1; +} + +/** + * Verifies HEAD and determines whether there exist any valid local references. + * + * - Checks whether HEAD points to a valid reference. + * + * - Checks whether any valid local branches exist. + * + * - Emits a warning if there exist any valid branches but HEAD does not point + * to a valid reference. + * + * Returns 1 if any of the previous checks are true, otherwise returns 0. + */ +static int can_use_local_refs(const struct add_opts *opts) +{ + if (head_ref(first_valid_ref, NULL)) { + return 1; + } else if (for_each_branch_ref(first_valid_ref, NULL)) { + if (!opts->quiet) { + struct strbuf path = STRBUF_INIT; + struct strbuf contents = STRBUF_INIT; + + strbuf_add_real_path(&path, get_worktree_git_dir(NULL)); + strbuf_addstr(&path, "/HEAD"); + strbuf_read_file(&contents, path.buf, 64); + strbuf_stripspace(&contents, 0); + strbuf_strip_suffix(&contents, "\n"); + + warning(_("HEAD points to an invalid (or orphaned) reference.\n" + "HEAD path: '%s'\n" + "HEAD contents: '%s'"), + path.buf, contents.buf); + strbuf_release(&path); + strbuf_release(&contents); + } + return 1; + } + return 0; +} + +/** + * Reports whether the necessary flags were set and whether the repository has + * remote references to attempt DWIM tracking of upstream branches. + * + * 1. Checks that `--guess-remote` was used or `worktree.guessRemote = true`. + * + * 2. Checks whether any valid remote branches exist. + * + * 3. Checks that there exists at least one remote and emits a warning/error + * if both checks 1. and 2. are false (can be bypassed with `--force`). + * + * Returns 1 if checks 1. and 2. are true, otherwise 0. + */ +static int can_use_remote_refs(const struct add_opts *opts) +{ + if (!guess_remote) { + return 0; + } else if (for_each_remote_ref(first_valid_ref, NULL)) { + return 1; + } else if (!opts->force && remote_get(NULL)) { + die(_("No local or remote refs exist despite at least one remote\n" + "present, stopping; use 'add -f' to override or fetch a remote first")); + } + return 0; +} + +/** + * Determines whether `--orphan` should be inferred in the evaluation of + * `worktree add path/` or `worktree add -b branch path/` and emits an error + * if the supplied arguments would produce an illegal combination when the + * `--orphan` flag is included. + * + * `opts` and `opt_track` contain the other options & flags supplied to the + * command. + * + * remote determines whether to check `can_use_remote_refs()` or not. This + * is primarily to differentiate between the basic `add` DWIM and `add -b`. + * + * Returns 1 when inferring `--orphan`, 0 otherwise, and emits an error when + * `--orphan` is inferred but doing so produces an illegal combination of + * options and flags. Additionally produces an error when remote refs are + * checked and the repo is in a state that looks like the user added a remote + * but forgot to fetch (and did not override the warning with -f). + */ +static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote) +{ + if (can_use_local_refs(opts)) { + return 0; + } else if (remote && can_use_remote_refs(opts)) { + return 0; + } else if (!opts->quiet) { + fprintf_ln(stderr, WORKTREE_ADD_DWIM_ORPHAN_INFER_TEXT); + } + + if (opt_track) { + die(_("options '%s' and '%s' cannot be used together"), + "--orphan", "--track"); + } else if (!opts->checkout) { + die(_("options '%s' and '%s' cannot be used together"), + "--orphan", "--no-checkout"); + } + return 1; +} + static const char *dwim_branch(const char *path, const char **new_branch) { int n; @@ -608,6 +775,7 @@ static int add(int ac, const char **av, const char *prefix) const char *opt_track = NULL; const char *lock_reason = NULL; int keep_locked = 0; + int used_new_branch_options; struct option options[] = { OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree"), @@ -616,6 +784,7 @@ static int add(int ac, const char **av, const char *prefix) N_("create a new branch")), OPT_STRING('B', NULL, &new_branch_force, N_("branch"), N_("create or reset a branch")), + OPT_BOOL(0, "orphan", &opts.orphan, N_("create unborn/orphaned branch")), OPT_BOOL('d', "detach", &opts.detach, N_("detach HEAD at named commit")), OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")), OPT_BOOL(0, "lock", &keep_locked, N_("keep the new working tree locked")), @@ -636,6 +805,18 @@ static int add(int ac, const char **av, const char *prefix) ac = parse_options(ac, av, prefix, options, git_worktree_add_usage, 0); if (!!opts.detach + !!new_branch + !!new_branch_force > 1) die(_("options '%s', '%s', and '%s' cannot be used together"), "-b", "-B", "--detach"); + if (opts.detach && opts.orphan) + die(_("options '%s' and '%s' cannot be used together"), + "--orphan", "--detach"); + if (opts.orphan && opt_track) + die(_("options '%s' and '%s' cannot be used together"), + "--orphan", "--track"); + if (opts.orphan && !opts.checkout) + die(_("options '%s' and '%s' cannot be used together"), + "--orphan", "--no-checkout"); + if (opts.orphan && ac == 2) + die(_("option '%s' and commit-ish cannot be used together"), + "--orphan"); if (lock_reason && !keep_locked) die(_("the option '%s' requires '%s'"), "--reason", "--lock"); if (lock_reason) @@ -648,6 +829,7 @@ static int add(int ac, const char **av, const char *prefix) path = prefix_filename(prefix, av[0]); branch = ac < 2 ? "HEAD" : av[1]; + used_new_branch_options = new_branch || new_branch_force; if (!strcmp(branch, "-")) branch = "@{-1}"; @@ -664,13 +846,28 @@ static int add(int ac, const char **av, const char *prefix) strbuf_release(&symref); } - if (ac < 2 && !new_branch && !opts.detach) { + if (opts.orphan && !new_branch) { + int n; + const char *s = worktree_basename(path, &n); + new_branch = xstrndup(s, n); + } else if (opts.orphan) { + // No-op + } else if (opts.detach) { + // Check HEAD + if (!strcmp(branch, "HEAD")) + can_use_local_refs(&opts); + } else if (ac < 2 && new_branch) { + // DWIM: Infer --orphan when repo has no refs. + opts.orphan = dwim_orphan(&opts, !!opt_track, 0); + } else if (ac < 2) { + // DWIM: Guess branch name from path. const char *s = dwim_branch(path, &new_branch); if (s) branch = s; - } - if (ac == 2 && !new_branch && !opts.detach) { + // DWIM: Infer --orphan when repo has no refs. + opts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1); + } else if (ac == 2) { struct object_id oid; struct commit *commit; const char *remote; @@ -683,11 +880,31 @@ static int add(int ac, const char **av, const char *prefix) branch = remote; } } + + if (!strcmp(branch, "HEAD")) + can_use_local_refs(&opts); + + } + + if (!opts.orphan && !lookup_commit_reference_by_name(branch)) { + int attempt_hint = !opts.quiet && (ac < 2); + if (attempt_hint && used_new_branch_options) { + advise_if_enabled(ADVICE_WORKTREE_ADD_ORPHAN, + WORKTREE_ADD_ORPHAN_WITH_DASH_B_HINT_TEXT, + new_branch, path); + } else if (attempt_hint) { + advise_if_enabled(ADVICE_WORKTREE_ADD_ORPHAN, + WORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT, path); + } + die(_("invalid reference: %s"), branch); } + if (!opts.quiet) print_preparing_worktree_line(opts.detach, branch, new_branch, !!new_branch_force); - if (new_branch) { + if (opts.orphan) { + branch = new_branch; + } else if (new_branch) { struct child_process cp = CHILD_PROCESS_INIT; cp.git_cmd = 1; strvec_push(&cp.args, "branch"); @@ -756,7 +973,7 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len) strbuf_addstr(&sb, "(bare)"); else { strbuf_addf(&sb, "%-*s ", abbrev_len, - find_unique_abbrev(&wt->head_oid, DEFAULT_ABBREV)); + repo_find_unique_abbrev(the_repository, &wt->head_oid, DEFAULT_ABBREV)); if (wt->is_detached) strbuf_addstr(&sb, "(detached HEAD)"); else if (wt->head_ref) { @@ -793,7 +1010,7 @@ static void measure_widths(struct worktree **wt, int *abbrev, int *maxlen) if (path_len > *maxlen) *maxlen = path_len; - sha1_len = strlen(find_unique_abbrev(&wt[i]->head_oid, *abbrev)); + sha1_len = strlen(repo_find_unique_abbrev(the_repository, &wt[i]->head_oid, *abbrev)); if (sha1_len > *abbrev) *abbrev = sha1_len; } @@ -1192,5 +1409,9 @@ int cmd_worktree(int ac, const char **av, const char *prefix) prefix = ""; ac = parse_options(ac, av, prefix, options, git_worktree_usage, 0); + + prepare_repo_settings(the_repository); + the_repository->settings.command_requires_full_index = 0; + return fn(ac, av, prefix); } diff --git a/builtin/write-tree.c b/builtin/write-tree.c index 078010315f..66e83d0ecb 100644 --- a/builtin/write-tree.c +++ b/builtin/write-tree.c @@ -5,11 +5,14 @@ */ #define USE_THE_INDEX_VARIABLE #include "builtin.h" -#include "cache.h" #include "config.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "tree.h" #include "cache-tree.h" #include "parse-options.h" +#include "repository.h" static const char * const write_tree_usage[] = { N_("git write-tree [--missing-ok] [--prefix=<prefix>/]"), @@ -38,6 +41,9 @@ int cmd_write_tree(int argc, const char **argv, const char *cmd_prefix) argc = parse_options(argc, argv, cmd_prefix, write_tree_options, write_tree_usage, 0); + prepare_repo_settings(the_repository); + the_repository->settings.command_requires_full_index = 0; + ret = write_index_as_tree(&oid, &the_index, get_index_file(), flags, tree_prefix); switch (ret) { |
