diff options
Diffstat (limited to 'builtin')
39 files changed, 612 insertions, 347 deletions
diff --git a/builtin/add.c b/builtin/add.c index a010b2c325..84dff3e796 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -507,9 +507,9 @@ int cmd_add(int argc, const char **argv, const char *prefix) add_interactive = 1; if (add_interactive) { if (show_only) - die(_("--dry-run is incompatible with --interactive/--patch")); + die(_("options '%s' and '%s' cannot be used together"), "--dry-run", "--interactive/--patch"); if (pathspec_from_file) - die(_("--pathspec-from-file is incompatible with --interactive/--patch")); + die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch"); exit(interactive_add(argv + 1, prefix, patch_interactive)); } if (legacy_stash_p) { @@ -526,7 +526,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (edit_interactive) { if (pathspec_from_file) - die(_("--pathspec-from-file is incompatible with --edit")); + die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--edit"); return(edit_patch(argc, argv, prefix)); } argc--; @@ -538,10 +538,10 @@ int cmd_add(int argc, const char **argv, const char *prefix) addremove = 0; /* "-u" was given but not "-A" */ if (addremove && take_worktree_changes) - die(_("-A and -u are mutually incompatible")); + die(_("options '%s' and '%s' cannot be used together"), "-A", "-u"); if (!show_only && ignore_missing) - die(_("Option --ignore-missing can only be used together with --dry-run")); + die(_("the option '%s' requires '%s'"), "--ignore-missing", "--dry-run"); if (chmod_arg && ((chmod_arg[0] != '-' && chmod_arg[0] != '+') || chmod_arg[1] != 'x' || chmod_arg[2])) @@ -566,14 +566,14 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (pathspec_from_file) { if (pathspec.nr) - die(_("--pathspec-from-file is incompatible with pathspec arguments")); + die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file"); parse_pathspec_file(&pathspec, PATHSPEC_ATTR, PATHSPEC_PREFER_FULL | PATHSPEC_SYMLINK_LEADING_PATH, prefix, pathspec_from_file, pathspec_file_nul); } else if (pathspec_file_nul) { - die(_("--pathspec-file-nul requires --pathspec-from-file")); + die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file"); } if (require_pathspec && pathspec.nr == 0) { diff --git a/builtin/am.c b/builtin/am.c index 8677ea2348..b6be1f1cb1 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -87,6 +87,12 @@ enum show_patch_type { SHOW_PATCH_DIFF = 1, }; +enum empty_action { + STOP_ON_EMPTY_COMMIT = 0, /* output errors and stop in the middle of an am session */ + DROP_EMPTY_COMMIT, /* skip with a notice message, unless "--quiet" has been passed */ + KEEP_EMPTY_COMMIT, /* keep recording as empty commits */ +}; + struct am_state { /* state directory path */ char *dir; @@ -118,6 +124,7 @@ struct am_state { int message_id; int scissors; /* enum scissors_type */ int quoted_cr; /* enum quoted_cr_action */ + int empty_type; /* enum empty_action */ struct strvec git_apply_opts; const char *resolvemsg; int committer_date_is_author_date; @@ -178,6 +185,25 @@ static int am_option_parse_quoted_cr(const struct option *opt, return 0; } +static int am_option_parse_empty(const struct option *opt, + const char *arg, int unset) +{ + int *opt_value = opt->value; + + BUG_ON_OPT_NEG(unset); + + if (!strcmp(arg, "stop")) + *opt_value = STOP_ON_EMPTY_COMMIT; + else if (!strcmp(arg, "drop")) + *opt_value = DROP_EMPTY_COMMIT; + else if (!strcmp(arg, "keep")) + *opt_value = KEEP_EMPTY_COMMIT; + else + return error(_("Invalid value for --empty: %s"), arg); + + return 0; +} + /** * Returns path relative to the am_state directory. */ @@ -1126,6 +1152,12 @@ static void NORETURN die_user_resolve(const struct am_state *state) printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline); printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline); + + if (advice_enabled(ADVICE_AM_WORK_DIR) && + is_empty_or_missing_file(am_path(state, "patch")) && + !repo_index_has_changes(the_repository, NULL, NULL)) + printf_ln(_("To record the empty patch as an empty commit, run \"%s --allow-empty\"."), cmdline); + printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline); } @@ -1248,11 +1280,6 @@ static int parse_mail(struct am_state *state, const char *mail) goto finish; } - if (is_empty_or_missing_file(am_path(state, "patch"))) { - printf_ln(_("Patch is empty.")); - die_user_resolve(state); - } - strbuf_addstr(&msg, "\n\n"); strbuf_addbuf(&msg, &mi.log_message); strbuf_stripspace(&msg, 0); @@ -1763,6 +1790,7 @@ static void am_run(struct am_state *state, int resume) while (state->cur <= state->last) { const char *mail = am_path(state, msgnum(state)); int apply_status; + int to_keep; reset_ident_date(); @@ -1792,8 +1820,29 @@ static void am_run(struct am_state *state, int resume) if (state->interactive && do_interactive(state)) goto next; + to_keep = 0; + if (is_empty_or_missing_file(am_path(state, "patch"))) { + switch (state->empty_type) { + case DROP_EMPTY_COMMIT: + say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg); + goto next; + break; + case KEEP_EMPTY_COMMIT: + to_keep = 1; + say(state, stdout, _("Creating an empty commit: %.*s"), + linelen(state->msg), state->msg); + break; + case STOP_ON_EMPTY_COMMIT: + printf_ln(_("Patch is empty.")); + die_user_resolve(state); + break; + } + } + if (run_applypatch_msg_hook(state)) exit(1); + if (to_keep) + goto commit; say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg); @@ -1827,6 +1876,7 @@ static void am_run(struct am_state *state, int resume) die_user_resolve(state); } +commit: do_commit(state); next: @@ -1856,19 +1906,24 @@ next: /** * Resume the current am session after patch application failure. The user did * all the hard work, and we do not have to do any patch application. Just - * trust and commit what the user has in the index and working tree. + * trust and commit what the user has in the index and working tree. If `allow_empty` + * is true, commit as an empty commit when index has not changed and lacking a patch. */ -static void am_resolve(struct am_state *state) +static void am_resolve(struct am_state *state, int allow_empty) { validate_resume_state(state); say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg); if (!repo_index_has_changes(the_repository, NULL, NULL)) { - printf_ln(_("No changes - did you forget to use 'git add'?\n" - "If there is nothing left to stage, chances are that something else\n" - "already introduced the same changes; you might want to skip this patch.")); - die_user_resolve(state); + if (allow_empty && is_empty_or_missing_file(am_path(state, "patch"))) { + printf_ln(_("No changes - recorded it as an empty commit.")); + } else { + printf_ln(_("No changes - did you forget to use 'git add'?\n" + "If there is nothing left to stage, chances are that something else\n" + "already introduced the same changes; you might want to skip this patch.")); + die_user_resolve(state); + } } if (unmerged_cache()) { @@ -2195,7 +2250,8 @@ enum resume_type { RESUME_SKIP, RESUME_ABORT, RESUME_QUIT, - RESUME_SHOW_PATCH + RESUME_SHOW_PATCH, + RESUME_ALLOW_EMPTY, }; struct resume_mode { @@ -2230,9 +2286,9 @@ static int parse_opt_show_current_patch(const struct option *opt, const char *ar } if (resume->mode == RESUME_SHOW_PATCH && new_value != resume->sub_mode) - return error(_("--show-current-patch=%s is incompatible with " - "--show-current-patch=%s"), - arg, valid_modes[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; @@ -2348,6 +2404,9 @@ int cmd_am(int argc, const char **argv, const char *prefix) 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, + N_("record the empty patch as an empty commit"), + RESUME_ALLOW_EMPTY), OPT_BOOL(0, "committer-date-is-author-date", &state.committer_date_is_author_date, N_("lie about committer date")), @@ -2357,6 +2416,9 @@ 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}", + N_("how to handle empty patches"), + PARSE_OPT_NONEG, am_option_parse_empty), OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing, N_("(internal use for git-rebase)")), OPT_END() @@ -2453,7 +2515,8 @@ int cmd_am(int argc, const char **argv, const char *prefix) am_run(&state, 1); break; case RESUME_RESOLVED: - am_resolve(&state); + case RESUME_ALLOW_EMPTY: + am_resolve(&state, resume.mode == RESUME_ALLOW_EMPTY ? 1 : 0); break; case RESUME_SKIP: am_skip(&state); diff --git a/builtin/branch.c b/builtin/branch.c index 6c8b0fcc11..4ce2a24754 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -638,8 +638,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT__VERBOSE(&filter.verbose, N_("show hash and subject, give twice for upstream branch")), OPT__QUIET(&quiet, N_("suppress informational messages")), - OPT_SET_INT('t', "track", &track, N_("set up tracking mode (see git-pull(1))"), - BRANCH_TRACK_EXPLICIT), + OPT_CALLBACK_F('t', "track", &track, "(direct|inherit)", + N_("set branch tracking configuration"), + PARSE_OPT_OPTARG, + parse_opt_tracking_mode), OPT_SET_INT_F(0, "set-upstream", &track, N_("do not use"), BRANCH_TRACK_OVERRIDE, PARSE_OPT_HIDDEN), OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")), @@ -722,7 +724,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) finalize_colopts(&colopts, -1); if (filter.verbose) { if (explicitly_enable_column(colopts)) - die(_("--column and --verbose are incompatible")); + die(_("options '%s' and '%s' cannot be used together"), "--column", "--verbose"); colopts = 0; } diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 86fc03242b..d94050e6c1 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -729,7 +729,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) } if (force_path && batch.enabled) { - error("--path=<path> incompatible with --batch"); + error("options '--path=<path>' and '--batch' cannot be used together"); usage_with_options(cat_file_usage, options); } diff --git a/builtin/checkout.c b/builtin/checkout.c index 72beeb49aa..cc804ba8e1 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -464,10 +464,10 @@ static int checkout_paths(const struct checkout_opts *opts, die(_("'%s' cannot be used with updating paths"), "--detach"); if (opts->merge && opts->patch_mode) - die(_("'%s' cannot be used with %s"), "--merge", "--patch"); + die(_("options '%s' and '%s' cannot be used together"), "--merge", "--patch"); if (opts->ignore_unmerged && opts->merge) - die(_("'%s' cannot be used with %s"), + die(_("options '%s' and '%s' cannot be used together"), opts->ignore_unmerged_opt, "-m"); if (opts->new_branch) @@ -1094,9 +1094,6 @@ static int switch_branches(const struct checkout_opts *opts, const char *p; if (skip_prefix(old_branch_info.path, prefix, &p)) old_branch_info.name = xstrdup(p); - else - BUG("should be able to skip past '%s' in '%s'!", - prefix, old_branch_info.path); } if (opts->new_orphan_branch && opts->orphan_from_empty_tree) { @@ -1549,8 +1546,10 @@ static struct option *add_common_switch_branch_options( { struct option options[] = { OPT_BOOL('d', "detach", &opts->force_detach, N_("detach HEAD at named commit")), - OPT_SET_INT('t', "track", &opts->track, N_("set upstream info for new branch"), - BRANCH_TRACK_EXPLICIT), + OPT_CALLBACK_F('t', "track", &opts->track, "(direct|inherit)", + N_("set branch tracking configuration"), + PARSE_OPT_OPTARG, + parse_opt_tracking_mode), OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"), PARSE_OPT_NOCOMPLETE), OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")), @@ -1635,11 +1634,11 @@ static int checkout_main(int argc, const char **argv, const char *prefix, } if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1) - die(_("-%c, -%c and --orphan are mutually exclusive"), - cb_option, toupper(cb_option)); + die(_("options '-%c', '-%c', and '%s' cannot be used together"), + cb_option, toupper(cb_option), "--orphan"); if (opts->overlay_mode == 1 && opts->patch_mode) - die(_("-p and --overlay are mutually exclusive")); + die(_("options '%s' and '%s' cannot be used together"), "-p", "--overlay"); if (opts->checkout_index >= 0 || opts->checkout_worktree >= 0) { if (opts->checkout_index < 0) @@ -1746,19 +1745,19 @@ static int checkout_main(int argc, const char **argv, const char *prefix, if (opts->pathspec_from_file) { if (opts->pathspec.nr) - die(_("--pathspec-from-file is incompatible with pathspec arguments")); + die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file"); if (opts->force_detach) - die(_("--pathspec-from-file is incompatible with --detach")); + die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--detach"); if (opts->patch_mode) - die(_("--pathspec-from-file is incompatible with --patch")); + die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--patch"); parse_pathspec_file(&opts->pathspec, 0, 0, prefix, opts->pathspec_from_file, opts->pathspec_file_nul); } else if (opts->pathspec_file_nul) { - die(_("--pathspec-file-nul requires --pathspec-from-file")); + die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file"); } opts->pathspec.recursive = 1; diff --git a/builtin/clean.c b/builtin/clean.c index 98a2860409..3ff02bbbff 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -36,6 +36,8 @@ static const char *msg_skip_git_dir = N_("Skipping repository %s\n"); static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n"); static const char *msg_warn_remove_failed = N_("failed to remove %s"); static const char *msg_warn_lstat_failed = N_("could not lstat %s\n"); +static const char *msg_skip_cwd = N_("Refusing to remove current working directory\n"); +static const char *msg_would_skip_cwd = N_("Would refuse to remove current working directory\n"); enum color_clean { CLEAN_COLOR_RESET = 0, @@ -153,6 +155,8 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, { DIR *dir; struct strbuf quoted = STRBUF_INIT; + struct strbuf realpath = STRBUF_INIT; + struct strbuf real_ocwd = STRBUF_INIT; struct dirent *e; int res = 0, ret = 0, gone = 1, original_len = path->len, len; struct string_list dels = STRING_LIST_INIT_DUP; @@ -231,16 +235,36 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, strbuf_setlen(path, original_len); if (*dir_gone) { - res = dry_run ? 0 : rmdir(path->buf); - if (!res) - *dir_gone = 1; - else { - int saved_errno = errno; - quote_path(path->buf, prefix, "ed, 0); - errno = saved_errno; - warning_errno(_(msg_warn_remove_failed), quoted.buf); + /* + * Normalize path components in path->buf, e.g. change '\' to + * '/' on Windows. + */ + strbuf_realpath(&realpath, path->buf, 1); + + /* + * path and realpath are absolute; for comparison, we would + * like to transform startup_info->original_cwd to an absolute + * path too. + */ + if (startup_info->original_cwd) + strbuf_realpath(&real_ocwd, + startup_info->original_cwd, 1); + + if (!strbuf_cmp(&realpath, &real_ocwd)) { + printf("%s", dry_run ? _(msg_would_skip_cwd) : _(msg_skip_cwd)); *dir_gone = 0; - ret = 1; + } else { + res = dry_run ? 0 : rmdir(path->buf); + if (!res) + *dir_gone = 1; + else { + int saved_errno = errno; + quote_path(path->buf, prefix, "ed, 0); + errno = saved_errno; + warning_errno(_(msg_warn_remove_failed), quoted.buf); + *dir_gone = 0; + ret = 1; + } } } @@ -250,6 +274,8 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, printf(dry_run ? _(msg_would_remove) : _(msg_remove), dels.items[i].string); } out: + strbuf_release(&realpath); + strbuf_release(&real_ocwd); strbuf_release("ed); string_list_clear(&dels, 0); return ret; diff --git a/builtin/clone.c b/builtin/clone.c index fb377b2765..727e16e0ae 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -633,7 +633,7 @@ static int git_sparse_checkout_init(const char *repo) { struct strvec argv = STRVEC_INIT; int result = 0; - strvec_pushl(&argv, "-C", repo, "sparse-checkout", "init", NULL); + strvec_pushl(&argv, "-C", repo, "sparse-checkout", "set", NULL); /* * We must apply the setting in the current process @@ -900,10 +900,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (option_bare) { if (option_origin) - die(_("--bare and --origin %s options are incompatible."), - option_origin); + die(_("options '%s' and '%s %s' cannot be used together"), + "--bare", "--origin", option_origin); if (real_git_dir) - die(_("--bare and --separate-git-dir are incompatible.")); + die(_("options '%s' and '%s' cannot be used together"), "--bare", "--separate-git-dir"); option_no_checkout = 1; } @@ -1290,7 +1290,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) */ submodule_progress = transport->progress; - transport_unlock_pack(transport); + transport_unlock_pack(transport, 0); transport_disconnect(transport); if (option_dissociate) { diff --git a/builtin/commit.c b/builtin/commit.c index 883c16256c..b9ed0374e3 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -355,19 +355,19 @@ static const char *prepare_index(const char **argv, const char *prefix, if (pathspec_from_file) { if (interactive) - die(_("--pathspec-from-file is incompatible with --interactive/--patch")); + die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch"); if (all) - die(_("--pathspec-from-file with -a does not make sense")); + die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "-a"); if (pathspec.nr) - die(_("--pathspec-from-file is incompatible with pathspec arguments")); + die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file"); parse_pathspec_file(&pathspec, 0, PATHSPEC_PREFER_FULL, prefix, pathspec_from_file, pathspec_file_nul); } else if (pathspec_file_nul) { - die(_("--pathspec-file-nul requires --pathspec-from-file")); + die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file"); } if (!pathspec.nr && (also || (only && !allow_empty && @@ -799,7 +799,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, if (!strcmp(fixup_prefix, "amend")) { if (have_option_m) - die(_("cannot combine -m with --fixup:%s"), fixup_message); + die(_("options '%s' and '%s:%s' cannot be used together"), "-m", "--fixup", fixup_message); prepare_amend_commit(commit, &sb, &ctx); } } else if (!stat(git_path_merge_msg(the_repository), &statbuf)) { @@ -1193,7 +1193,7 @@ static void finalize_deferred_config(struct wt_status *s) status_format == STATUS_FORMAT_UNSPECIFIED) status_format = STATUS_FORMAT_PORCELAIN; else if (status_format == STATUS_FORMAT_LONG) - die(_("--long and -z are incompatible")); + die(_("options '%s' and '%s' cannot be used together"), "--long", "-z"); } if (use_deferred_config && status_format == STATUS_FORMAT_UNSPECIFIED) @@ -1229,9 +1229,10 @@ static void check_fixup_reword_options(int argc, const char *argv[]) { die(_("You are in the middle of a cherry-pick -- cannot reword.")); } if (argc) - die(_("cannot combine reword option of --fixup with path '%s'"), *argv); + die(_("reword option of '%s' and path '%s' cannot be used together"), "--fixup", *argv); if (patch_interactive || interactive || all || also || only) - die(_("reword option of --fixup is mutually exclusive with --patch/--interactive/--all/--include/--only")); + die(_("reword option of '%s' and '%s' cannot be used together"), + "--fixup", "--patch/--interactive/--all/--include/--only"); } static int parse_and_validate_options(int argc, const char *argv[], diff --git a/builtin/describe.c b/builtin/describe.c index e912ba50d7..42159cd26b 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -590,7 +590,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix) save_commit_buffer = 0; if (longformat && abbrev == 0) - die(_("--long is incompatible with --abbrev=0")); + die(_("options '%s' and '%s' cannot be used together"), "--long", "--abbrev=0"); if (contains) { struct string_list_item *item; @@ -670,9 +670,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix) } describe("HEAD", 1); } else if (dirty) { - die(_("--dirty is incompatible with commit-ishes")); + die(_("option '%s' and commit-ishes cannot be used together"), "--dirty"); } else if (broken) { - die(_("--broken is incompatible with commit-ishes")); + die(_("option '%s' and commit-ishes cannot be used together"), "--broken"); } else { while (argc-- > 0) describe(*argv++, argc == 0); diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index f33d30d57b..0e0ac1f167 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -152,7 +152,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) } if (read_stdin && merge_base) - die(_("--stdin and --merge-base are mutually exclusive")); + die(_("options '%s' and '%s' cannot be used together"), "--stdin", "--merge-base"); if (merge_base && opt->pending.nr != 2) die(_("--merge-base only works with two commits")); diff --git a/builtin/difftool.c b/builtin/difftool.c index 4ee40fe3a0..c79fbbf67e 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -730,10 +730,10 @@ int cmd_difftool(int argc, const char **argv, const char *prefix) setenv(GIT_DIR_ENVIRONMENT, absolute_path(get_git_dir()), 1); setenv(GIT_WORK_TREE_ENVIRONMENT, absolute_path(get_git_work_tree()), 1); } else if (dir_diff) - die(_("--dir-diff is incompatible with --no-index")); + die(_("options '%s' and '%s' cannot be used together"), "--dir-diff", "--no-index"); if (use_gui_tool + !!difftool_cmd + !!extcmd > 1) - die(_("--gui, --tool and --extcmd are mutually exclusive")); + die(_("options '%s', '%s', and '%s' cannot be used together"), "--gui", "--tool", "--extcmd"); if (use_gui_tool) setenv("GIT_MERGETOOL_GUI", "true", 1); diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 8e2caf7281..9f1c730e58 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -107,18 +107,6 @@ static int parse_opt_reencode_mode(const struct option *opt, static struct decoration idnums; static uint32_t last_idnum; - -static int has_unshown_parent(struct commit *commit) -{ - struct commit_list *parent; - - for (parent = commit->parents; parent; parent = parent->next) - if (!(parent->item->object.flags & SHOWN) && - !(parent->item->object.flags & UNINTERESTING)) - return 1; - return 0; -} - struct anonymized_entry { struct hashmap_entry hash; const char *anon; @@ -752,20 +740,6 @@ static char *anonymize_tag(void *data) return strbuf_detach(&out, NULL); } -static void handle_tail(struct object_array *commits, struct rev_info *revs, - struct string_list *paths_of_changed_objects) -{ - struct commit *commit; - while (commits->nr) { - commit = (struct commit *)object_array_pop(commits); - if (has_unshown_parent(commit)) { - /* Queue again, to be handled later */ - add_object_array(&commit->object, NULL, commits); - return; - } - handle_commit(commit, revs, paths_of_changed_objects); - } -} static void handle_tag(const char *name, struct tag *tag) { @@ -1185,7 +1159,6 @@ static int parse_opt_anonymize_map(const struct option *opt, int cmd_fast_export(int argc, const char **argv, const char *prefix) { struct rev_info revs; - struct object_array commits = OBJECT_ARRAY_INIT; struct commit *commit; char *export_filename = NULL, *import_filename = NULL, @@ -1254,7 +1227,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) usage_with_options (fast_export_usage, options); if (anonymized_seeds.cmpfn && !anonymize) - die(_("--anonymize-map without --anonymize does not make sense")); + die(_("the option '%s' requires '%s'"), "--anonymize-map", "--anonymize"); if (refspecs_list.nr) { int i; @@ -1269,7 +1242,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) printf("feature done\n"); if (import_filename && import_filename_if_exists) - die(_("Cannot pass both --import-marks and --import-marks-if-exists")); + die(_("options '%s' and '%s' cannot be used together"), "--import-marks", "--import-marks-if-exists"); if (import_filename) import_marks(import_filename, 0); else if (import_filename_if_exists) @@ -1283,18 +1256,13 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) if (prepare_revision_walk(&revs)) die("revision walk setup failed"); + + revs.reverse = 1; revs.diffopt.format_callback = show_filemodify; revs.diffopt.format_callback_data = &paths_of_changed_objects; revs.diffopt.flags.recursive = 1; - while ((commit = get_revision(&revs))) { - if (has_unshown_parent(commit)) { - add_object_array(&commit->object, NULL, &commits); - } - else { - handle_commit(commit, &revs, &paths_of_changed_objects); - handle_tail(&commits, &revs, &paths_of_changed_objects); - } - } + while ((commit = get_revision(&revs))) + handle_commit(commit, &revs, &paths_of_changed_objects); handle_tags_and_duplicates(&extra_refs); handle_tags_and_duplicates(&tag_refs); diff --git a/builtin/fast-import.c b/builtin/fast-import.c index 20406f6775..2b2e28bad7 100644 --- a/builtin/fast-import.c +++ b/builtin/fast-import.c @@ -401,16 +401,18 @@ static void dump_marks(void); static NORETURN void die_nicely(const char *err, va_list params) { + va_list cp; static int zombie; - char message[2 * PATH_MAX]; + report_fn die_message_fn = get_die_message_routine(); - vsnprintf(message, sizeof(message), err, params); - fputs("fatal: ", stderr); - fputs(message, stderr); - fputc('\n', stderr); + va_copy(cp, params); + die_message_fn(err, params); if (!zombie) { + char message[2 * PATH_MAX]; + zombie = 1; + vsnprintf(message, sizeof(message), err, cp); write_crash_report(message); end_packfile(); unkeep_all_packs(); diff --git a/builtin/fetch.c b/builtin/fetch.c index f1fe73a3e0..5f06b21f8e 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -223,17 +223,22 @@ static struct option builtin_fetch_options[] = { OPT_END() }; -static void unlock_pack(void) +static void unlock_pack(unsigned int flags) { if (gtransport) - transport_unlock_pack(gtransport); + transport_unlock_pack(gtransport, flags); if (gsecondary) - transport_unlock_pack(gsecondary); + transport_unlock_pack(gsecondary, flags); +} + +static void unlock_pack_atexit(void) +{ + unlock_pack(0); } static void unlock_pack_on_signal(int signo) { - unlock_pack(); + unlock_pack(TRANSPORT_UNLOCK_PACK_IN_SIGNAL_HANDLER); sigchain_pop(signo); raise(signo); } @@ -996,7 +1001,7 @@ static int open_fetch_head(struct fetch_head *fetch_head) if (write_fetch_head) { fetch_head->fp = fopen(filename, "a"); if (!fetch_head->fp) - return error_errno(_("cannot open %s"), filename); + return error_errno(_("cannot open '%s'"), filename); strbuf_init(&fetch_head->buf, 0); } else { fetch_head->fp = NULL; @@ -1329,7 +1334,7 @@ static int fetch_and_consume_refs(struct transport *transport, trace2_region_leave("fetch", "consume_refs", the_repository); out: - transport_unlock_pack(transport); + transport_unlock_pack(transport, 0); return ret; } @@ -1408,7 +1413,7 @@ static int truncate_fetch_head(void) FILE *fp = fopen_for_writing(filename); if (!fp) - return error_errno(_("cannot open %s"), filename); + return error_errno(_("cannot open '%s'"), filename); fclose(fp); return 0; } @@ -1978,7 +1983,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, gtransport->server_options = &server_options; sigchain_push_common(unlock_pack_on_signal); - atexit(unlock_pack); + atexit(unlock_pack_atexit); sigchain_push(SIGPIPE, SIG_IGN); exit_code = do_fetch(gtransport, &rs); sigchain_pop(SIGPIPE); @@ -2009,6 +2014,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) } git_config(git_fetch_config, NULL); + prepare_repo_settings(the_repository); + the_repository->settings.command_requires_full_index = 0; argc = parse_options(argc, argv, prefix, builtin_fetch_options, builtin_fetch_usage, 0); @@ -2028,12 +2035,12 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) if (deepen_relative < 0) die(_("negative depth in --deepen is not supported")); if (depth) - die(_("--deepen and --depth are mutually exclusive")); + die(_("options '%s' and '%s' cannot be used together"), "--deepen", "--depth"); depth = xstrfmt("%d", deepen_relative); } if (unshallow) { if (depth) - die(_("--depth and --unshallow cannot be used together")); + die(_("options '%s' and '%s' cannot be used together"), "--depth", "--unshallow"); else if (!is_repository_shallow(the_repository)) die(_("--unshallow on a complete repository does not make sense")); else diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index 48a8699de7..8d8fd393f8 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -12,6 +12,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) { const char *inpath = NULL; const char *message = NULL; + char *into_name = NULL; int shortlog_len = -1; struct option options[] = { { OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"), @@ -23,6 +24,8 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) DEFAULT_MERGE_LOG_LEN }, OPT_STRING('m', "message", &message, N_("text"), N_("use <text> as start of message")), + OPT_STRING(0, "into-name", &into_name, N_("name"), + N_("use <name> instead of the real target branch")), OPT_FILENAME('F', "file", &inpath, N_("file to read from")), OPT_END() }; @@ -56,6 +59,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) opts.add_title = !message; opts.credit_people = 1; opts.shortlog_len = shortlog_len; + opts.into_name = into_name; ret = fmt_merge_msg(&input, &output, &opts); if (ret) diff --git a/builtin/gc.c b/builtin/gc.c index bcef6a4c8d..8e60ef1eab 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -470,7 +470,8 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid) /* * Returns 0 if there was no previous error and gc can proceed, 1 if * gc should not proceed due to an error in the last run. Prints a - * message and returns -1 if an error occurred while reading gc.log + * message and returns with a non-[01] status code if an error occurred + * while reading gc.log */ static int report_last_gc_error(void) { @@ -484,7 +485,7 @@ static int report_last_gc_error(void) if (errno == ENOENT) goto done; - ret = error_errno(_("cannot stat '%s'"), gc_log_path); + ret = die_message_errno(_("cannot stat '%s'"), gc_log_path); goto done; } @@ -493,7 +494,7 @@ static int report_last_gc_error(void) len = strbuf_read_file(&sb, gc_log_path, 0); if (len < 0) - ret = error_errno(_("cannot read '%s'"), gc_log_path); + ret = die_message_errno(_("cannot read '%s'"), gc_log_path); else if (len > 0) { /* * A previous gc failed. Report the error, and don't @@ -611,12 +612,13 @@ int cmd_gc(int argc, const char **argv, const char *prefix) } if (detach_auto) { int ret = report_last_gc_error(); - if (ret < 0) - /* an I/O error occurred, already reported */ - exit(128); + if (ret == 1) /* Last gc --auto failed. Skip this one. */ return 0; + else if (ret) + /* an I/O error occurred, already reported */ + return ret; if (lock_repo_for_gc(force, &pid)) return 0; diff --git a/builtin/index-pack.c b/builtin/index-pack.c index c23d01de7d..3c2e6aee3c 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1845,11 +1845,11 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) if (!pack_name && !from_stdin) usage(index_pack_usage); if (fix_thin_pack && !from_stdin) - die(_("--fix-thin cannot be used without --stdin")); + die(_("the option '%s' requires '%s'"), "--fix-thin", "--stdin"); if (from_stdin && !startup_info->have_repository) die(_("--stdin requires a git repository")); if (from_stdin && hash_algo) - die(_("--object-format cannot be used with --stdin")); + die(_("options '%s' and '%s' cannot be used together"), "--object-format", "--stdin"); if (!index_name && pack_name) index_name = derive_filename(pack_name, "pack", "idx", &index_name_buf); diff --git a/builtin/init-db.c b/builtin/init-db.c index 2167796ff2..546f9c595e 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -557,7 +557,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0); if (real_git_dir && is_bare_repository_cfg == 1) - die(_("--separate-git-dir and --bare are mutually exclusive")); + die(_("options '%s' and '%s' cannot be used together"), "--separate-git-dir", "--bare"); if (real_git_dir && !is_absolute_path(real_git_dir)) real_git_dir = real_pathdup(real_git_dir, 1); diff --git a/builtin/log.c b/builtin/log.c index 93ace0dde7..4b493408cc 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -1943,9 +1943,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) numbered = 0; if (numbered && keep_subject) - die(_("-n and -k are mutually exclusive")); + die(_("options '%s' and '%s' cannot be used together"), "-n", "-k"); if (keep_subject && subject_prefix) - die(_("--subject-prefix/--rfc and -k are mutually exclusive")); + die(_("options '%s' and '%s' cannot be used together"), "--subject-prefix/--rfc", "-k"); rev.preserve_subject = keep_subject; argc = setup_revisions(argc, argv, &rev, &s_r_opt); @@ -1979,7 +1979,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) load_display_notes(&rev.notes_opt); if (use_stdout + rev.diffopt.close_file + !!output_directory > 1) - die(_("--stdout, --output, and --output-directory are mutually exclusive")); + die(_("options '%s', '%s', and '%s' cannot be used together"), "--stdout", "--output", "--output-directory"); if (use_stdout) { setup_pager(); @@ -2112,7 +2112,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (creation_factor < 0) creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT; else if (!rdiff_prev) - die(_("--creation-factor requires --range-diff")); + die(_("the option '%s' requires '%s'"), "--creation-factor", "--range-diff"); if (rdiff_prev) { if (!cover_letter && total != 1) @@ -2256,6 +2256,7 @@ done: strbuf_release(&rdiff1); strbuf_release(&rdiff2); strbuf_release(&rdiff_title); + UNLEAK(rev); return 0; } diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 031fef1bca..f7ea56cc63 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -37,6 +37,7 @@ static int debug_mode; static int show_eol; static int recurse_submodules; static int skipping_duplicates; +static int show_sparse_dirs; static const char *prefix; static int max_prefix_len; @@ -315,8 +316,10 @@ static void show_files(struct repository *repo, struct dir_struct *dir) if (!(show_cached || show_stage || show_deleted || show_modified)) return; - /* TODO: audit for interaction with sparse-index. */ - ensure_full_index(repo->index); + + if (!show_sparse_dirs) + ensure_full_index(repo->index); + for (i = 0; i < repo->index->cache_nr; i++) { const struct cache_entry *ce = repo->index->cache[i]; struct stat st; @@ -670,6 +673,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")), OPT_BOOL(0, "deduplicate", &skipping_duplicates, N_("suppress duplicate entries")), + OPT_BOOL(0, "sparse", &show_sparse_dirs, + N_("show sparse directories in the presence of a sparse index")), OPT_END() }; int ret = 0; @@ -677,6 +682,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(ls_files_usage, builtin_ls_files_options); + prepare_repo_settings(the_repository); + the_repository->settings.command_requires_full_index = 0; + prefix = cmd_prefix; if (prefix) prefix_len = strlen(prefix); @@ -767,7 +775,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) * would not make any sense with this option. */ if (show_stage || show_unmerged) - die("ls-files --with-tree is incompatible with -s or -u"); + die(_("options '%s' and '%s' cannot be used together"), "ls-files --with-tree", "-s/-u"); overlay_tree_on_index(the_repository->index, with_tree, max_prefix); } diff --git a/builtin/merge.c b/builtin/merge.c index 5f0476b0b7..74e53cf20a 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -87,6 +87,7 @@ static int signoff; static const char *sign_commit; static int autostash; static int no_verify; +static char *into_name; static struct strategy all_strategy[] = { { "recursive", NO_TRIVIAL }, @@ -286,6 +287,8 @@ static struct option builtin_merge_options[] = { { OPTION_LOWLEVEL_CALLBACK, 'F', "file", &merge_msg, N_("path"), N_("read message from file"), PARSE_OPT_NONEG, NULL, 0, option_read_message }, + OPT_STRING(0, "into-name", &into_name, N_("name"), + N_("use <name> instead of the real target")), OPT__VERBOSITY(&verbosity), OPT_BOOL(0, "abort", &abort_current_merge, N_("abort the current in-progress merge")), @@ -1121,6 +1124,7 @@ static void prepare_merge_message(struct strbuf *merge_names, struct strbuf *mer opts.add_title = !have_message; opts.shortlog_len = shortlog_len; opts.credit_people = (0 < option_edit); + opts.into_name = into_name; fmt_merge_msg(merge_names, merge_msg, &opts); if (merge_msg->len) @@ -1396,9 +1400,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (squash) { if (fast_forward == FF_NO) - die(_("You cannot combine --squash with --no-ff.")); + die(_("options '%s' and '%s' cannot be used together"), "--squash", "--no-ff."); if (option_commit > 0) - die(_("You cannot combine --squash with --commit.")); + die(_("options '%s' and '%s' cannot be used together"), "--squash", "--commit."); /* * squash can now silently disable option_commit - this is not * a problem as it is only overriding the default, not a user diff --git a/builtin/notes.c b/builtin/notes.c index 118db9e455..05d60483e8 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -200,11 +200,12 @@ static void prepare_note_data(const struct object_id *object, struct note_data * static void write_note_data(struct note_data *d, struct object_id *oid) { if (write_object_file(d->buf.buf, d->buf.len, blob_type, oid)) { - error(_("unable to write note object")); + int status = die_message(_("unable to write note object")); + if (d->edit_path) - error(_("the note contents have been left in %s"), - d->edit_path); - exit(128); + die_message(_("the note contents have been left in %s"), + d->edit_path); + exit(status); } } diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index b36ed828d8..ba2006f221 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -4070,7 +4070,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) die(_("--thin cannot be used to build an indexable pack")); if (keep_unreachable && unpack_unreachable) - die(_("--keep-unreachable and --unpack-unreachable are incompatible")); + die(_("options '%s' and '%s' cannot be used together"), "--keep-unreachable", "--unpack-unreachable"); if (!rev_list_all || !rev_list_reflog || !rev_list_index) unpack_unreachable_expiration = 0; diff --git a/builtin/prune.c b/builtin/prune.c index 485c9a3c56..c2bcdc07db 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -26,10 +26,22 @@ static int prune_tmp_file(const char *fullpath) return error("Could not stat '%s'", fullpath); if (st.st_mtime > expire) return 0; - if (show_only || verbose) - printf("Removing stale temporary file %s\n", fullpath); - if (!show_only) - unlink_or_warn(fullpath); + if (S_ISDIR(st.st_mode)) { + if (show_only || verbose) + printf("Removing stale temporary directory %s\n", fullpath); + if (!show_only) { + struct strbuf remove_dir_buf = STRBUF_INIT; + + strbuf_addstr(&remove_dir_buf, fullpath); + remove_dir_recursively(&remove_dir_buf, 0); + strbuf_release(&remove_dir_buf); + } + } else { + if (show_only || verbose) + printf("Removing stale temporary file %s\n", fullpath); + if (!show_only) + unlink_or_warn(fullpath); + } return 0; } diff --git a/builtin/pull.c b/builtin/pull.c index c8457619d8..8f37880a48 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -994,6 +994,8 @@ int cmd_pull(int argc, const char **argv, const char *prefix) set_reflog_message(argc, argv); git_config(git_pull_config, NULL); + prepare_repo_settings(the_repository); + the_repository->settings.command_requires_full_index = 0; argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0); @@ -1036,14 +1038,13 @@ int cmd_pull(int argc, const char **argv, const char *prefix) oidclr(&orig_head); if (opt_rebase) { - int autostash = config_autostash; - if (opt_autostash != -1) - autostash = opt_autostash; + if (opt_autostash == -1) + opt_autostash = config_autostash; if (is_null_oid(&orig_head) && !is_cache_unborn()) die(_("Updating an unborn branch with changes added to the index.")); - if (!autostash) + if (!opt_autostash) require_clean_work_tree(the_repository, N_("pull with rebase"), _("please commit or stash them."), 1, 0); diff --git a/builtin/push.c b/builtin/push.c index 4b026ce6c6..359db90321 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -589,7 +589,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) set_push_cert_flags(&flags, push_cert); if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR)))) - die(_("--delete is incompatible with --all, --mirror and --tags")); + die(_("options '%s' and '%s' cannot be used together"), "--delete", "--all/--mirror/--tags"); if (deleterefs && argc < 2) die(_("--delete doesn't make sense without any refs")); @@ -627,18 +627,18 @@ int cmd_push(int argc, const char **argv, const char *prefix) if (flags & TRANSPORT_PUSH_ALL) { if (tags) - die(_("--all and --tags are incompatible")); + 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(_("--mirror and --tags are incompatible")); + 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(_("--all and --mirror are incompatible")); + 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/rebase.c b/builtin/rebase.c index 34b4744e5f..36490d06c8 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -1190,13 +1190,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (keep_base) { if (options.onto_name) - die(_("cannot combine '--keep-base' with '--onto'")); + die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--onto"); if (options.root) - die(_("cannot combine '--keep-base' with '--root'")); + die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--root"); } if (options.root && options.fork_point > 0) - die(_("cannot combine '--root' with '--fork-point'")); + die(_("options '%s' and '%s' cannot be used together"), "--root", "--fork-point"); if (action != ACTION_NONE && !in_progress) die(_("No rebase in progress?")); @@ -1460,8 +1460,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (i >= 0) { if (is_merge(&options)) - die(_("cannot combine apply options with " - "merge options")); + die(_("apply options and merge options " + "cannot be used together")); else options.type = REBASE_APPLY; } diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 37f77b9600..5c2732a0d0 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -2193,7 +2193,7 @@ static const char *unpack(int err_fd, struct shallow_info *si) strvec_push(&child.args, alt_shallow_file); } - tmp_objdir = tmp_objdir_create(); + tmp_objdir = tmp_objdir_create("incoming"); if (!tmp_objdir) { if (err_fd > 0) close(err_fd); diff --git a/builtin/reflog.c b/builtin/reflog.c index 175c83e7cc..a4b1dd27e1 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -28,7 +28,6 @@ static timestamp_t default_reflog_expire; static timestamp_t default_reflog_expire_unreachable; struct cmd_reflog_expire_cb { - struct rev_info revs; int stalefix; timestamp_t expire_total; timestamp_t expire_unreachable; @@ -46,18 +45,12 @@ struct expire_reflog_policy_cb { struct cmd_reflog_expire_cb cmd; struct commit *tip_commit; struct commit_list *tips; + unsigned int dry_run:1; }; -struct collected_reflog { - struct object_id oid; - char reflog[FLEX_ARRAY]; -}; - -struct collect_reflog_cb { - struct collected_reflog **e; - int alloc; - int nr; - struct worktree *wt; +struct worktree_reflogs { + struct worktree *worktree; + struct string_list reflogs; }; /* Remember to update object flag allocation in object.h */ @@ -310,10 +303,15 @@ static int should_expire_reflog_ent(struct object_id *ooid, struct object_id *no return 1; if (timestamp < cb->cmd.expire_unreachable) { - if (cb->unreachable_expire_kind == UE_ALWAYS) - return 1; - if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid)) + switch (cb->unreachable_expire_kind) { + case UE_ALWAYS: return 1; + case UE_NORMAL: + case UE_HEAD: + if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid)) + return 1; + break; + } } if (cb->cmd.recno && --(cb->cmd.recno) == 0) @@ -322,6 +320,28 @@ static int should_expire_reflog_ent(struct object_id *ooid, struct object_id *no return 0; } +static int should_expire_reflog_ent_verbose(struct object_id *ooid, + struct object_id *noid, + const char *email, + timestamp_t timestamp, int tz, + const char *message, void *cb_data) +{ + struct expire_reflog_policy_cb *cb = cb_data; + int expire; + + expire = should_expire_reflog_ent(ooid, noid, email, timestamp, tz, + message, cb); + + if (!expire) + printf("keep %s", message); + else if (cb->dry_run) + printf("would prune %s", message); + else + printf("prune %s", message); + + return expire; +} + static int push_tip_to_list(const char *refname, const struct object_id *oid, int flags, void *cb_data) { @@ -355,75 +375,71 @@ static void reflog_expiry_prepare(const char *refname, void *cb_data) { struct expire_reflog_policy_cb *cb = cb_data; + struct commit_list *elem; + struct commit *commit = NULL; if (!cb->cmd.expire_unreachable || is_head(refname)) { - cb->tip_commit = NULL; cb->unreachable_expire_kind = UE_HEAD; } else { - cb->tip_commit = lookup_commit_reference_gently(the_repository, - oid, 1); - if (!cb->tip_commit) - cb->unreachable_expire_kind = UE_ALWAYS; - else - cb->unreachable_expire_kind = UE_NORMAL; + commit = lookup_commit(the_repository, oid); + cb->unreachable_expire_kind = commit ? UE_NORMAL : UE_ALWAYS; } if (cb->cmd.expire_unreachable <= cb->cmd.expire_total) cb->unreachable_expire_kind = UE_ALWAYS; - cb->mark_list = NULL; - cb->tips = NULL; - if (cb->unreachable_expire_kind != UE_ALWAYS) { - if (cb->unreachable_expire_kind == UE_HEAD) { - struct commit_list *elem; - - for_each_ref(push_tip_to_list, &cb->tips); - for (elem = cb->tips; elem; elem = elem->next) - commit_list_insert(elem->item, &cb->mark_list); - } else { - commit_list_insert(cb->tip_commit, &cb->mark_list); - } - cb->mark_limit = cb->cmd.expire_total; - mark_reachable(cb); + switch (cb->unreachable_expire_kind) { + case UE_ALWAYS: + return; + case UE_HEAD: + for_each_ref(push_tip_to_list, &cb->tips); + for (elem = cb->tips; elem; elem = elem->next) + commit_list_insert(elem->item, &cb->mark_list); + break; + case UE_NORMAL: + commit_list_insert(commit, &cb->mark_list); + /* For reflog_expiry_cleanup() below */ + cb->tip_commit = commit; } + cb->mark_limit = cb->cmd.expire_total; + mark_reachable(cb); } static void reflog_expiry_cleanup(void *cb_data) { struct expire_reflog_policy_cb *cb = cb_data; + struct commit_list *elem; - if (cb->unreachable_expire_kind != UE_ALWAYS) { - if (cb->unreachable_expire_kind == UE_HEAD) { - struct commit_list *elem; - for (elem = cb->tips; elem; elem = elem->next) - clear_commit_marks(elem->item, REACHABLE); - free_commit_list(cb->tips); - } else { - clear_commit_marks(cb->tip_commit, REACHABLE); - } + switch (cb->unreachable_expire_kind) { + case UE_ALWAYS: + return; + case UE_HEAD: + for (elem = cb->tips; elem; elem = elem->next) + clear_commit_marks(elem->item, REACHABLE); + free_commit_list(cb->tips); + break; + case UE_NORMAL: + clear_commit_marks(cb->tip_commit, REACHABLE); + break; } } static int collect_reflog(const char *ref, const struct object_id *oid, int unused, void *cb_data) { - struct collected_reflog *e; - struct collect_reflog_cb *cb = cb_data; + struct worktree_reflogs *cb = cb_data; + struct worktree *worktree = cb->worktree; struct strbuf newref = STRBUF_INIT; /* * Avoid collecting the same shared ref multiple times because * they are available via all worktrees. */ - if (!cb->wt->is_current && ref_type(ref) == REF_TYPE_NORMAL) + if (!worktree->is_current && ref_type(ref) == REF_TYPE_NORMAL) return 0; - strbuf_worktree_ref(cb->wt, &newref, ref); - FLEX_ALLOC_STR(e, reflog, newref.buf); - strbuf_release(&newref); + strbuf_worktree_ref(worktree, &newref, ref); + string_list_append_nodup(&cb->reflogs, strbuf_detach(&newref, NULL)); - oidcpy(&e->oid, oid); - ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc); - cb->e[cb->nr++] = e; return 0; } @@ -541,11 +557,13 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) { - struct expire_reflog_policy_cb cb; + struct cmd_reflog_expire_cb cmd = { 0 }; timestamp_t now = time(NULL); int i, status, do_all, all_worktrees = 1; int explicit_expiry = 0; unsigned int flags = 0; + int verbose = 0; + reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent; default_reflog_expire_unreachable = now - 30 * 24 * 3600; default_reflog_expire = now - 90 * 24 * 3600; @@ -553,10 +571,9 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) save_commit_buffer = 0; do_all = status = 0; - memset(&cb, 0, sizeof(cb)); - cb.cmd.expire_total = default_reflog_expire; - cb.cmd.expire_unreachable = default_reflog_expire_unreachable; + cmd.expire_total = default_reflog_expire; + cmd.expire_unreachable = default_reflog_expire_unreachable; for (i = 1; i < argc; i++) { const char *arg = argv[i]; @@ -564,17 +581,17 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n")) flags |= EXPIRE_REFLOGS_DRY_RUN; else if (skip_prefix(arg, "--expire=", &arg)) { - if (parse_expiry_date(arg, &cb.cmd.expire_total)) + if (parse_expiry_date(arg, &cmd.expire_total)) die(_("'%s' is not a valid timestamp"), arg); explicit_expiry |= EXPIRE_TOTAL; } else if (skip_prefix(arg, "--expire-unreachable=", &arg)) { - if (parse_expiry_date(arg, &cb.cmd.expire_unreachable)) + if (parse_expiry_date(arg, &cmd.expire_unreachable)) die(_("'%s' is not a valid timestamp"), arg); explicit_expiry |= EXPIRE_UNREACH; } else if (!strcmp(arg, "--stale-fix")) - cb.cmd.stalefix = 1; + cmd.stalefix = 1; else if (!strcmp(arg, "--rewrite")) flags |= EXPIRE_REFLOGS_REWRITE; else if (!strcmp(arg, "--updateref")) @@ -584,7 +601,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) else if (!strcmp(arg, "--single-worktree")) all_worktrees = 0; else if (!strcmp(arg, "--verbose")) - flags |= EXPIRE_REFLOGS_VERBOSE; + verbose = 1; else if (!strcmp(arg, "--")) { i++; break; @@ -595,54 +612,65 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) break; } + if (verbose) + should_prune_fn = should_expire_reflog_ent_verbose; + /* * We can trust the commits and objects reachable from refs * even in older repository. We cannot trust what's reachable * from reflog if the repository was pruned with older git. */ - if (cb.cmd.stalefix) { - repo_init_revisions(the_repository, &cb.cmd.revs, prefix); - cb.cmd.revs.do_not_die_on_missing_tree = 1; - cb.cmd.revs.ignore_missing = 1; - cb.cmd.revs.ignore_missing_links = 1; - if (flags & EXPIRE_REFLOGS_VERBOSE) + if (cmd.stalefix) { + struct rev_info revs; + + repo_init_revisions(the_repository, &revs, prefix); + revs.do_not_die_on_missing_tree = 1; + revs.ignore_missing = 1; + revs.ignore_missing_links = 1; + if (verbose) printf(_("Marking reachable objects...")); - mark_reachable_objects(&cb.cmd.revs, 0, 0, NULL); - if (flags & EXPIRE_REFLOGS_VERBOSE) + mark_reachable_objects(&revs, 0, 0, NULL); + if (verbose) putchar('\n'); } if (do_all) { - struct collect_reflog_cb collected; + struct worktree_reflogs collected = { + .reflogs = STRING_LIST_INIT_DUP, + }; + struct string_list_item *item; struct worktree **worktrees, **p; - int i; - memset(&collected, 0, sizeof(collected)); worktrees = get_worktrees(); for (p = worktrees; *p; p++) { if (!all_worktrees && !(*p)->is_current) continue; - collected.wt = *p; + collected.worktree = *p; refs_for_each_reflog(get_worktree_ref_store(*p), collect_reflog, &collected); } free_worktrees(worktrees); - for (i = 0; i < collected.nr; i++) { - struct collected_reflog *e = collected.e[i]; - set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog); - status |= reflog_expire(e->reflog, flags, + for_each_string_list_item(item, &collected.reflogs) { + struct expire_reflog_policy_cb cb = { + .cmd = cmd, + .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN), + }; + + set_reflog_expiry_param(&cb.cmd, explicit_expiry, item->string); + status |= reflog_expire(item->string, flags, reflog_expiry_prepare, - should_expire_reflog_ent, + should_prune_fn, reflog_expiry_cleanup, &cb); - free(e); } - free(collected.e); + string_list_clear(&collected.reflogs, 0); } for (; i < argc; i++) { char *ref; + struct expire_reflog_policy_cb cb = { .cmd = cmd }; + if (!dwim_log(argv[i], strlen(argv[i]), NULL, &ref)) { status |= error(_("%s points nowhere!"), argv[i]); continue; @@ -650,7 +678,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref); status |= reflog_expire(ref, flags, reflog_expiry_prepare, - should_expire_reflog_ent, + should_prune_fn, reflog_expiry_cleanup, &cb); free(ref); @@ -662,19 +690,19 @@ static int count_reflog_ent(struct object_id *ooid, struct object_id *noid, const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { - struct expire_reflog_policy_cb *cb = cb_data; - if (!cb->cmd.expire_total || timestamp < cb->cmd.expire_total) - cb->cmd.recno++; + struct cmd_reflog_expire_cb *cb = cb_data; + if (!cb->expire_total || timestamp < cb->expire_total) + cb->recno++; return 0; } static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) { - struct expire_reflog_policy_cb cb; + struct cmd_reflog_expire_cb cmd = { 0 }; int i, status = 0; unsigned int flags = 0; - - memset(&cb, 0, sizeof(cb)); + int verbose = 0; + reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent; for (i = 1; i < argc; i++) { const char *arg = argv[i]; @@ -685,7 +713,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) else if (!strcmp(arg, "--updateref")) flags |= EXPIRE_REFLOGS_UPDATE_REF; else if (!strcmp(arg, "--verbose")) - flags |= EXPIRE_REFLOGS_VERBOSE; + verbose = 1; else if (!strcmp(arg, "--")) { i++; break; @@ -696,6 +724,9 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) break; } + if (verbose) + should_prune_fn = should_expire_reflog_ent_verbose; + if (argc - i < 1) return error(_("no reflog specified to delete")); @@ -703,6 +734,9 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) const char *spec = strstr(argv[i], "@{"); char *ep, *ref; int recno; + struct expire_reflog_policy_cb cb = { + .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN), + }; if (!spec) { status |= error(_("not a reflog: %s"), argv[i]); @@ -716,17 +750,18 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) recno = strtoul(spec + 2, &ep, 10); if (*ep == '}') { - cb.cmd.recno = -recno; - for_each_reflog_ent(ref, count_reflog_ent, &cb); + cmd.recno = -recno; + for_each_reflog_ent(ref, count_reflog_ent, &cmd); } else { - cb.cmd.expire_total = approxidate(spec + 2); - for_each_reflog_ent(ref, count_reflog_ent, &cb); - cb.cmd.expire_total = 0; + cmd.expire_total = approxidate(spec + 2); + for_each_reflog_ent(ref, count_reflog_ent, &cmd); + cmd.expire_total = 0; } + cb.cmd = cmd; status |= reflog_expire(ref, flags, reflog_expiry_prepare, - should_expire_reflog_ent, + should_prune_fn, reflog_expiry_cleanup, &cb); free(ref); diff --git a/builtin/repack.c b/builtin/repack.c index 9b0be6a6ab..da1e364a75 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -612,7 +612,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) struct tempfile *refs_snapshot = NULL; int i, ext, ret; FILE *out; - int show_progress = isatty(2); + int show_progress; /* variables to be filled by option parsing */ int pack_everything = 0; @@ -681,7 +681,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (keep_unreachable && (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE))) - die(_("--keep-unreachable and -A are incompatible")); + die(_("options '%s' and '%s' cannot be used together"), "--keep-unreachable", "-A"); if (write_bitmaps < 0) { if (!write_midx && @@ -693,7 +693,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) write_bitmaps = 0; } if (pack_kept_objects < 0) - pack_kept_objects = write_bitmaps > 0; + pack_kept_objects = write_bitmaps > 0 && !write_midx; if (write_bitmaps && !(pack_everything & ALL_INTO_ONE) && !write_midx) die(_(incremental_bitmap_conflict_error)); @@ -712,7 +712,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (geometric_factor) { if (pack_everything) - die(_("--geometric is incompatible with -A, -a")); + die(_("options '%s' and '%s' cannot be used together"), "--geometric", "-A/-a"); init_pack_geometry(&geometry); split_pack_geometry(geometry, geometric_factor); } @@ -725,6 +725,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) prepare_pack_objects(&cmd, &po_args); + show_progress = !po_args.quiet && isatty(2); + strvec_push(&cmd.args, "--keep-true-parents"); if (!pack_kept_objects) strvec_push(&cmd.args, "--honor-pack-keep"); @@ -926,7 +928,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) } strbuf_release(&buf); } - if (!po_args.quiet && show_progress) + if (show_progress) opts |= PRUNE_PACKED_VERBOSE; prune_packed_objects(opts); diff --git a/builtin/reset.c b/builtin/reset.c index b1ff699b43..b97745ee94 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -423,16 +423,16 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (pathspec_from_file) { if (patch_mode) - die(_("--pathspec-from-file is incompatible with --patch")); + die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--patch"); if (pathspec.nr) - die(_("--pathspec-from-file is incompatible with pathspec arguments")); + die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file"); parse_pathspec_file(&pathspec, 0, PATHSPEC_PREFER_FULL, prefix, pathspec_from_file, pathspec_file_nul); } else if (pathspec_file_nul) { - die(_("--pathspec-file-nul requires --pathspec-from-file")); + die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file"); } unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid); @@ -459,7 +459,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (patch_mode) { if (reset_type != NONE) - die(_("--patch is incompatible with --{hard,mixed,soft}")); + die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}"); trace2_cmd_mode("patch-interactive"); return run_add_interactive(rev, "--patch=reset", &pathspec); } @@ -490,7 +490,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) _(reset_type_names[reset_type])); if (intent_to_add && reset_type != MIXED) - die(_("-N can only be used with --mixed")); + die(_("the option '%s' requires '%s'"), "-N", "--mixed"); prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; diff --git a/builtin/rev-list.c b/builtin/rev-list.c index 36cb909eba..777558e9b0 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -538,7 +538,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) const char *arg = argv[i]; if (skip_prefix(arg, "--missing=", &arg)) { if (revs.exclude_promisor_objects) - die(_("cannot combine --exclude-promisor-objects and --missing")); + die(_("options '%s' and '%s' cannot be used together"), "--exclude-promisor-objects", "--missing"); if (parse_missing_action_value(arg)) break; } @@ -676,7 +676,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) if (revs.count && (revs.tag_objects || revs.tree_objects || revs.blob_objects) && (revs.left_right || revs.cherry_mark)) - die(_("marked counting is incompatible with --objects")); + die(_("marked counting and '%s' cannot be used together"), "--objects"); save_commit_buffer = (revs.verbose_header || revs.grep_filter.pattern_list || diff --git a/builtin/rm.c b/builtin/rm.c index 3d0967cdc1..84a935a16e 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -272,13 +272,13 @@ int cmd_rm(int argc, const char **argv, const char *prefix) if (pathspec_from_file) { if (pathspec.nr) - die(_("--pathspec-from-file is incompatible with pathspec arguments")); + die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file"); parse_pathspec_file(&pathspec, 0, PATHSPEC_PREFER_CWD, prefix, pathspec_from_file, pathspec_file_nul); } else if (pathspec_file_nul) { - die(_("--pathspec-file-nul requires --pathspec-from-file")); + die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file"); } if (!pathspec.nr) @@ -399,12 +399,13 @@ int cmd_rm(int argc, const char **argv, const char *prefix) if (!index_only) { int removed = 0, gitmodules_modified = 0; struct strbuf buf = STRBUF_INIT; + int flag = force ? REMOVE_DIR_PURGE_ORIGINAL_CWD : 0; for (i = 0; i < list.nr; i++) { const char *path = list.entry[i].name; if (list.entry[i].is_submodule) { strbuf_reset(&buf); strbuf_addstr(&buf, path); - if (remove_dir_recursively(&buf, 0)) + if (remove_dir_recursively(&buf, flag)) die(_("could not remove '%s'"), path); removed = 1; diff --git a/builtin/show-branch.c b/builtin/show-branch.c index f1e8318592..e12c5e80e3 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -707,8 +707,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) * * Also --all and --remotes do not make sense either. */ - die(_("--reflog is incompatible with --all, --remotes, " - "--independent or --merge-base")); + die(_("options '%s' and '%s' cannot be used together"), "--reflog", + "--all/--remotes/--independent/--merge-base"); } /* If nothing is specified, show all branches by default */ diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c index d0f5c4702b..679c107036 100644 --- a/builtin/sparse-checkout.c +++ b/builtin/sparse-checkout.c @@ -56,6 +56,9 @@ static int sparse_checkout_list(int argc, const char **argv) char *sparse_filename; int res; + if (!core_apply_sparse_checkout) + die(_("this worktree is not sparse")); + argc = parse_options(argc, argv, NULL, builtin_sparse_checkout_list_options, builtin_sparse_checkout_list_usage, 0); @@ -380,6 +383,41 @@ 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; + + /* If not specified, use previous definition of cone mode */ + if (*cone_mode == -1 && core_apply_sparse_checkout) + *cone_mode = core_sparse_checkout_cone; + + /* Set cone/non-cone mode appropriately */ + core_apply_sparse_checkout = 1; + if (*cone_mode == 1) { + mode = MODE_CONE_PATTERNS; + core_sparse_checkout_cone = 1; + } else { + mode = MODE_ALL_PATTERNS; + } + if (record_mode && set_config(mode)) + return 1; + + /* Set sparse-index/non-sparse-index mode if specified */ + if (*sparse_index >= 0) { + if (set_sparse_index_config(the_repository, *sparse_index) < 0) + die(_("failed to modify sparse-index config")); + + /* force an index rewrite */ + repo_read_index(the_repository); + the_repository->index->updated_workdir = 1; + } + + return 0; +} + static char const * const builtin_sparse_checkout_init_usage[] = { N_("git sparse-checkout init [--cone] [--[no-]sparse-index]"), NULL @@ -396,7 +434,6 @@ static int sparse_checkout_init(int argc, const char **argv) char *sparse_filename; int res; struct object_id oid; - int mode; struct strbuf pattern = STRBUF_INIT; static struct option builtin_sparse_checkout_init_options[] = { @@ -409,19 +446,14 @@ static int sparse_checkout_init(int argc, const char **argv) repo_read_index(the_repository); + init_opts.cone_mode = -1; init_opts.sparse_index = -1; argc = parse_options(argc, argv, NULL, builtin_sparse_checkout_init_options, builtin_sparse_checkout_init_usage, 0); - if (init_opts.cone_mode) { - mode = MODE_CONE_PATTERNS; - core_sparse_checkout_cone = 1; - } else - mode = MODE_ALL_PATTERNS; - - if (set_config(mode)) + if (update_modes(&init_opts.cone_mode, &init_opts.sparse_index)) return 1; memset(&pl, 0, sizeof(pl)); @@ -429,17 +461,6 @@ static int sparse_checkout_init(int argc, const char **argv) sparse_filename = get_sparse_checkout_filename(); res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL, 0); - if (init_opts.sparse_index >= 0) { - if (set_sparse_index_config(the_repository, init_opts.sparse_index) < 0) - die(_("failed to modify sparse-index config")); - - /* force an index rewrite */ - repo_read_index(the_repository); - the_repository->index->updated_workdir = 1; - } - - core_apply_sparse_checkout = 1; - /* If we already have a sparse-checkout file, use it. */ if (res >= 0) { free(sparse_filename); @@ -483,7 +504,7 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat char *oldpattern = e->pattern; size_t newlen; - if (slash == e->pattern) + if (!slash || slash == e->pattern) break; newlen = slash - e->pattern; @@ -515,17 +536,9 @@ static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl) insert_recursive_pattern(pl, line); } -static char const * const builtin_sparse_checkout_set_usage[] = { - N_("git sparse-checkout (set|add) (--stdin | <patterns>)"), - NULL -}; - -static struct sparse_checkout_set_opts { - int use_stdin; -} set_opts; - static void add_patterns_from_input(struct pattern_list *pl, - int argc, const char **argv) + int argc, const char **argv, + int use_stdin) { int i; if (core_sparse_checkout_cone) { @@ -535,7 +548,7 @@ 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 (set_opts.use_stdin) { + if (use_stdin) { struct strbuf unquoted = STRBUF_INIT; while (!strbuf_getline(&line, stdin)) { if (line.buf[0] == '"') { @@ -559,7 +572,7 @@ static void add_patterns_from_input(struct pattern_list *pl, } } } else { - if (set_opts.use_stdin) { + if (use_stdin) { struct strbuf line = STRBUF_INIT; while (!strbuf_getline(&line, stdin)) { @@ -580,7 +593,8 @@ enum modify_type { }; static void add_patterns_cone_mode(int argc, const char **argv, - struct pattern_list *pl) + struct pattern_list *pl, + int use_stdin) { struct strbuf buffer = STRBUF_INIT; struct pattern_entry *pe; @@ -588,7 +602,7 @@ 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); + add_patterns_from_input(pl, argc, argv, use_stdin); memset(&existing, 0, sizeof(existing)); existing.use_cone_patterns = core_sparse_checkout_cone; @@ -598,6 +612,9 @@ static void add_patterns_cone_mode(int argc, const char **argv, die(_("unable to load existing sparse-checkout patterns")); free(sparse_filename); + if (!existing.use_cone_patterns) + die(_("existing sparse-checkout patterns do not use cone mode")); + hashmap_for_each_entry(&existing.recursive_hashmap, &iter, pe, ent) { if (!hashmap_contains_parent(&pl->recursive_hashmap, pe->pattern, &buffer) || @@ -614,17 +631,19 @@ static void add_patterns_cone_mode(int argc, const char **argv, } static void add_patterns_literal(int argc, const char **argv, - struct pattern_list *pl) + struct pattern_list *pl, + int use_stdin) { char *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); - add_patterns_from_input(pl, argc, argv); + add_patterns_from_input(pl, argc, argv, use_stdin); } -static int modify_pattern_list(int argc, const char **argv, enum modify_type m) +static int modify_pattern_list(int argc, const char **argv, int use_stdin, + enum modify_type m) { int result; int changed_config = 0; @@ -633,13 +652,13 @@ static int modify_pattern_list(int argc, const char **argv, enum modify_type m) switch (m) { case ADD: if (core_sparse_checkout_cone) - add_patterns_cone_mode(argc, argv, pl); + add_patterns_cone_mode(argc, argv, pl, use_stdin); else - add_patterns_literal(argc, argv, pl); + add_patterns_literal(argc, argv, pl, use_stdin); break; case REPLACE: - add_patterns_from_input(pl, argc, argv); + add_patterns_from_input(pl, argc, argv, use_stdin); break; } @@ -659,41 +678,124 @@ static int modify_pattern_list(int argc, const char **argv, enum modify_type m) return result; } -static int sparse_checkout_set(int argc, const char **argv, const char *prefix, - enum modify_type m) +static char const * const builtin_sparse_checkout_add_usage[] = { + N_("git sparse-checkout add (--stdin | <patterns>)"), + NULL +}; + +static struct sparse_checkout_add_opts { + int use_stdin; +} add_opts; + +static int sparse_checkout_add(int argc, const char **argv, const char *prefix) { - static struct option builtin_sparse_checkout_set_options[] = { - OPT_BOOL(0, "stdin", &set_opts.use_stdin, + static struct option builtin_sparse_checkout_add_options[] = { + OPT_BOOL(0, "stdin", &add_opts.use_stdin, N_("read patterns from standard in")), OPT_END(), }; + if (!core_apply_sparse_checkout) + die(_("no sparse-checkout to add to")); + repo_read_index(the_repository); argc = parse_options(argc, argv, prefix, + builtin_sparse_checkout_add_options, + builtin_sparse_checkout_add_usage, + PARSE_OPT_KEEP_UNKNOWN); + + return modify_pattern_list(argc, argv, add_opts.use_stdin, ADD); +} + +static char const * const builtin_sparse_checkout_set_usage[] = { + N_("git sparse-checkout set [--[no-]cone] [--[no-]sparse-index] (--stdin | <patterns>)"), + NULL +}; + +static struct sparse_checkout_set_opts { + int cone_mode; + int sparse_index; + int use_stdin; +} set_opts; + +static int sparse_checkout_set(int argc, const char **argv, const char *prefix) +{ + int default_patterns_nr = 2; + const char *default_patterns[] = {"/*", "!/*/", NULL}; + + static struct option builtin_sparse_checkout_set_options[] = { + OPT_BOOL(0, "cone", &set_opts.cone_mode, + N_("initialize the sparse-checkout in cone mode")), + OPT_BOOL(0, "sparse-index", &set_opts.sparse_index, + N_("toggle the use of a sparse index")), + OPT_BOOL_F(0, "stdin", &set_opts.use_stdin, + N_("read patterns from standard in"), + PARSE_OPT_NONEG), + OPT_END(), + }; + + repo_read_index(the_repository); + + set_opts.cone_mode = -1; + set_opts.sparse_index = -1; + + argc = parse_options(argc, argv, prefix, builtin_sparse_checkout_set_options, builtin_sparse_checkout_set_usage, PARSE_OPT_KEEP_UNKNOWN); - return modify_pattern_list(argc, argv, m); + if (update_modes(&set_opts.cone_mode, &set_opts.sparse_index)) + return 1; + + /* + * Cone mode automatically specifies the toplevel directory. For + * non-cone mode, if nothing is specified, manually select just the + * top-level directory (much as 'init' would do). + */ + if (!core_sparse_checkout_cone && argc == 0) { + argv = default_patterns; + argc = default_patterns_nr; + } + + return modify_pattern_list(argc, argv, set_opts.use_stdin, REPLACE); } static char const * const builtin_sparse_checkout_reapply_usage[] = { - N_("git sparse-checkout reapply"), + N_("git sparse-checkout reapply [--[no-]cone] [--[no-]sparse-index]"), NULL }; +static struct sparse_checkout_reapply_opts { + int cone_mode; + int sparse_index; +} reapply_opts; + static int sparse_checkout_reapply(int argc, const char **argv) { static struct option builtin_sparse_checkout_reapply_options[] = { + OPT_BOOL(0, "cone", &reapply_opts.cone_mode, + N_("initialize the sparse-checkout in cone mode")), + OPT_BOOL(0, "sparse-index", &reapply_opts.sparse_index, + N_("toggle the use of a sparse index")), OPT_END(), }; + if (!core_apply_sparse_checkout) + die(_("must be in a sparse-checkout to reapply sparsity patterns")); + argc = parse_options(argc, argv, NULL, builtin_sparse_checkout_reapply_options, builtin_sparse_checkout_reapply_usage, 0); repo_read_index(the_repository); + + reapply_opts.cone_mode = -1; + reapply_opts.sparse_index = -1; + + if (update_modes(&reapply_opts.cone_mode, &reapply_opts.sparse_index)) + return 1; + return update_working_directory(NULL); } @@ -710,6 +812,17 @@ static int sparse_checkout_disable(int argc, const char **argv) struct pattern_list pl; struct strbuf match_all = STRBUF_INIT; + /* + * We do not exit early if !core_apply_sparse_checkout; due to the + * ability for users to manually muck things up between + * direct editing of .git/info/sparse-checkout + * running read-tree -m u HEAD or update-index --skip-worktree + * direct toggling of config options + * users might end up with an index with SKIP_WORKTREE bit set on + * some files and not know how to undo it. So, here we just + * forcibly return to a dense checkout regardless of initial state. + */ + argc = parse_options(argc, argv, NULL, builtin_sparse_checkout_disable_options, builtin_sparse_checkout_disable_usage, 0); @@ -758,9 +871,9 @@ int cmd_sparse_checkout(int argc, const char **argv, const char *prefix) if (!strcmp(argv[0], "init")) return sparse_checkout_init(argc, argv); if (!strcmp(argv[0], "set")) - return sparse_checkout_set(argc, argv, prefix, REPLACE); + return sparse_checkout_set(argc, argv, prefix); if (!strcmp(argv[0], "add")) - return sparse_checkout_set(argc, argv, prefix, ADD); + return sparse_checkout_add(argc, argv, prefix); if (!strcmp(argv[0], "reapply")) return sparse_checkout_reapply(argc, argv); if (!strcmp(argv[0], "disable")) diff --git a/builtin/stash.c b/builtin/stash.c index 18c812bbe0..86cd0b456e 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -561,18 +561,19 @@ static int do_apply_stash(const char *prefix, struct stash_info *info, if (index) fprintf_ln(stderr, _("Index was not unstashed.")); - return ret; + goto restore_untracked; } if (has_index) { if (reset_tree(&index_tree, 0, 0)) - return -1; + ret = -1; } else { unstage_changes_unless_new(&c_tree); } +restore_untracked: if (info->has_u && restore_untracked(&info->u_tree)) - return error(_("could not restore untracked files from stash")); + ret = error(_("could not restore untracked files from stash")); if (!quiet) { struct child_process cp = CHILD_PROCESS_INIT; @@ -592,7 +593,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info, run_command(&cp); } - return 0; + return ret; } static int apply_stash(int argc, const char **argv, const char *prefix) @@ -1538,8 +1539,14 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q struct child_process cp = CHILD_PROCESS_INIT; cp.git_cmd = 1; + if (startup_info->original_cwd) { + cp.dir = startup_info->original_cwd; + strvec_pushf(&cp.env_array, "%s=%s", + GIT_WORK_TREE_ENVIRONMENT, + the_repository->worktree); + } strvec_pushl(&cp.args, "clean", "--force", - "--quiet", "-d", NULL); + "--quiet", "-d", ":/", NULL); if (include_untracked == INCLUDE_ALL_FILES) strvec_push(&cp.args, "-x"); if (run_command(&cp)) { @@ -1681,6 +1688,7 @@ static int push_stash(int argc, const char **argv, const char *prefix, if (argc) { force_assume = !strcmp(argv[0], "-p"); argc = parse_options(argc, argv, prefix, options, + push_assumed ? git_stash_usage : git_stash_push_usage, PARSE_OPT_KEEP_DASHDASH); } @@ -1700,19 +1708,19 @@ static int push_stash(int argc, const char **argv, const char *prefix, if (pathspec_from_file) { if (patch_mode) - die(_("--pathspec-from-file is incompatible with --patch")); + die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--patch"); if (only_staged) - die(_("--pathspec-from-file is incompatible with --staged")); + die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--staged"); if (ps.nr) - die(_("--pathspec-from-file is incompatible with pathspec arguments")); + die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file"); parse_pathspec_file(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN, prefix, pathspec_from_file, pathspec_file_nul); } else if (pathspec_file_nul) { - die(_("--pathspec-file-nul requires --pathspec-from-file")); + die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file"); } return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode, diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 9b25a508e6..c5d3fc3817 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -1313,7 +1313,7 @@ static int module_summary(int argc, const char **argv, const char *prefix) if (files) { if (cached) - die(_("--cached and --files are mutually exclusive")); + die(_("options '%s' and '%s' cannot be used together"), "--cached", "--files"); diff_cmd = DIFF_FILES; } @@ -2972,7 +2972,7 @@ static int module_set_branch(int argc, const char **argv, const char *prefix) die(_("--branch or --default required")); if (opt_branch && opt_default) - die(_("--branch and --default are mutually exclusive")); + die(_("options '%s' and '%s' cannot be used together"), "--branch", "--default"); if (argc != 1 || !(path = argv[0])) usage_with_options(usage, options); diff --git a/builtin/tag.c b/builtin/tag.c index 41863c5ab7..134b3f1edf 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -483,6 +483,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) OPT_END() }; int ret = 0; + const char *only_in_list = NULL; setup_ref_filter_porcelain_msg(); @@ -522,7 +523,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) finalize_colopts(&colopts, -1); if (cmdmode == 'l' && filter.lines != -1) { if (explicitly_enable_column(colopts)) - die(_("--column and -n are incompatible")); + die(_("options '%s' and '%s' cannot be used together"), "--column", "-n"); colopts = 0; } sorting = ref_sorting_options(&sorting_options); @@ -542,15 +543,19 @@ int cmd_tag(int argc, const char **argv, const char *prefix) goto cleanup; } if (filter.lines != -1) - die(_("-n option is only allowed in list mode")); - if (filter.with_commit) - die(_("--contains option is only allowed in list mode")); - if (filter.no_commit) - die(_("--no-contains option is only allowed in list mode")); - if (filter.points_at.nr) - die(_("--points-at option is only allowed in list mode")); - if (filter.reachable_from || filter.unreachable_from) - die(_("--merged and --no-merged options are only allowed in list mode")); + only_in_list = "-n"; + else if (filter.with_commit) + only_in_list = "--contains"; + else if (filter.no_commit) + only_in_list = "--no-contains"; + else if (filter.points_at.nr) + only_in_list = "--points-at"; + else if (filter.reachable_from) + only_in_list = "--merged"; + else if (filter.unreachable_from) + only_in_list = "--no-merged"; + if (only_in_list) + die(_("the '%s' option is only allowed in list mode"), only_in_list); if (cmdmode == 'd') { ret = delete_tags(argv); goto cleanup; @@ -564,7 +569,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) if (msg.given || msgfile) { if (msg.given && msgfile) - die(_("only one -F or -m option is allowed.")); + die(_("options '%s' and '%s' cannot be used together"), "-F", "-m"); if (msg.given) strbuf_addbuf(&buf, &(msg.buf)); else { diff --git a/builtin/worktree.c b/builtin/worktree.c index a396cfdc64..2838254f7f 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -503,9 +503,9 @@ static int add(int ac, const char **av, const char *prefix) opts.checkout = 1; ac = parse_options(ac, av, prefix, options, worktree_usage, 0); if (!!opts.detach + !!new_branch + !!new_branch_force > 1) - die(_("-b, -B, and --detach are mutually exclusive")); + die(_("options '%s', '%s', and '%s' cannot be used together"), "-b", "-B", "--detach"); if (lock_reason && !keep_locked) - die(_("--reason requires --lock")); + die(_("the option '%s' requires '%s'"), "--reason", "--lock"); if (lock_reason) opts.keep_locked = lock_reason; else if (keep_locked) @@ -699,7 +699,7 @@ static int list(int ac, const char **av, const char *prefix) if (ac) usage_with_options(worktree_usage, options); else if (verbose && porcelain) - die(_("--verbose and --porcelain are mutually exclusive")); + die(_("options '%s' and '%s' cannot be used together"), "--verbose", "--porcelain"); else { struct worktree **worktrees = get_worktrees(); int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i; |