diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt index fdcad3d2006c8a..ae36e2fb8d73a9 100644 --- a/Documentation/git-cherry-pick.txt +++ b/Documentation/git-cherry-pick.txt @@ -139,12 +139,25 @@ effect to your index in a row. This option overrides that behavior, allowing commits with empty messages to be cherry picked. +--empty=(drop|keep|ask):: + If a commit being cherry picked duplicates changes already in the + current history, it will become empty. ++ +This option determines how these empty commits are handled: ++ +* `drop`: These empty commits are dropped. ++ +* `keep`: These empty commits are kept. This implies `--allow-empty`. ++ +* `ask`: These empty commits cause `cherry-pick` to stop so the user can examine + the commit. This is the default behavior. ++ +Note that commits which start empty will cause the cherry-pick to fail (unless +`--allow-empty` is specified). ++ + --keep-redundant-commits:: - If a commit being cherry picked duplicates a commit already in the - current history, it will become empty. By default these - redundant commits cause `cherry-pick` to stop so the user can - examine the commit. This option overrides that behavior and - creates an empty commit object. Implies `--allow-empty`. + Deprecated synonym for `--empty=keep`. --strategy=:: Use the given merge strategy. Should only be used once. diff --git a/builtin/revert.c b/builtin/revert.c index e6f9a1ad26721c..55871cbfc281f0 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -1,3 +1,6 @@ +//TODO: How to properly handle? +//Should previous empty rebase code have not used strcasecmp? +#include #include "git-compat-util.h" #include "config.h" #include "builtin.h" @@ -45,6 +48,31 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts) return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage; } +//TODO: Centralize? +enum empty_type { + EMPTY_UNSPECIFIED = -1, + EMPTY_DROP, + EMPTY_KEEP, + EMPTY_ASK +}; + +static int parse_opt_empty(const struct option *opt, const char *arg, int unset) +{ + enum empty_type value = EMPTY_UNSPECIFIED; + + if (unset || !strcasecmp(arg, "ask")) + value = EMPTY_ASK; + else if (!strcasecmp(arg, "drop")) + value = EMPTY_DROP; + else if (!strcasecmp(arg, "keep")) + value = EMPTY_KEEP; + else + return error(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), arg); + + *(enum empty_type *)opt->value = value; + return 0; +} + static int option_parse_m(const struct option *opt, const char *arg, int unset) { @@ -87,6 +115,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix, const char * const * usage_str = revert_or_cherry_pick_usage(opts); const char *me = action_name(opts); const char *cleanup_arg = NULL; + enum empty_type empty_opt = EMPTY_UNSPECIFIED; int cmd = 0; struct option base_options[] = { OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'), @@ -116,7 +145,12 @@ static int run_sequencer(int argc, const char **argv, const char *prefix, OPT_BOOL(0, "ff", &opts->allow_ff, N_("allow fast-forward")), OPT_BOOL(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")), OPT_BOOL(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")), - OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")), + //TODO: anything to do for translation here? + OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("deprecated: use --empty=keep instead")), + //TODO: Same - translation? + OPT_CALLBACK_F(0, "empty", &empty_opt, "(drop|keep|ask)", + N_("how to handle commits that become empty"), + PARSE_OPT_NONEG, parse_opt_empty), OPT_END(), }; options = parse_options_concat(options, cp_extra); @@ -136,9 +170,17 @@ static int run_sequencer(int argc, const char **argv, const char *prefix, prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; - /* implies allow_empty */ - if (opts->keep_redundant_commits) + + if (opts->action == REPLAY_PICK) { + /* + * cherry-pick now uses --empty, and therefore must + * set allow_empty to 1 like rebase does in order to enter the + * advanced handling in sequencer.c -> allow_empty(). + */ opts->allow_empty = 1; + opts->drop_redundant_commits = (empty_opt == EMPTY_DROP); + opts->keep_redundant_commits = (empty_opt == EMPTY_KEEP); + } if (cleanup_arg) { opts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1); diff --git a/sequencer.c b/sequencer.c index d584cac8ed9307..880dcbddbc8550 100644 --- a/sequencer.c +++ b/sequencer.c @@ -2932,6 +2932,9 @@ static int populate_opts_cb(const char *key, const char *value, else if (!strcmp(key, "options.allow-empty-message")) opts->allow_empty_message = git_config_bool_or_int(key, value, ctx->kvi, &error_flag); + else if (!strcmp(key, "options.drop-redundant-commits")) + opts->drop_redundant_commits = + git_config_bool_or_int(key, value, ctx->kvi, &error_flag); else if (!strcmp(key, "options.keep-redundant-commits")) opts->keep_redundant_commits = git_config_bool_or_int(key, value, ctx->kvi, &error_flag); @@ -3476,6 +3479,9 @@ static int save_opts(struct replay_opts *opts) if (opts->allow_empty_message) res |= git_config_set_in_file_gently(opts_file, "options.allow-empty-message", "true"); + if (opts->drop_redundant_commits) + res |= git_config_set_in_file_gently(opts_file, + "options.drop-redundant-commits", "true"); if (opts->keep_redundant_commits) res |= git_config_set_in_file_gently(opts_file, "options.keep-redundant-commits", "true");