Git fork
at reftables-rust 319 lines 9.4 kB view raw
1#define USE_THE_REPOSITORY_VARIABLE 2 3#include "git-compat-util.h" 4#include "builtin.h" 5#include "parse-options.h" 6#include "diff.h" 7#include "environment.h" 8#include "gettext.h" 9#include "revision.h" 10#include "rerere.h" 11#include "sequencer.h" 12#include "branch.h" 13 14/* 15 * This implements the builtins revert and cherry-pick. 16 * 17 * Copyright (c) 2007 Johannes E. Schindelin 18 * 19 * Based on git-revert.sh, which is 20 * 21 * Copyright (c) 2005 Linus Torvalds 22 * Copyright (c) 2005 Junio C Hamano 23 */ 24 25static const char * const revert_usage[] = { 26 N_("git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] <commit>..."), 27 N_("git revert (--continue | --skip | --abort | --quit)"), 28 NULL 29}; 30 31static const char * const cherry_pick_usage[] = { 32 N_("git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n" 33 " [-S[<keyid>]] <commit>..."), 34 N_("git cherry-pick (--continue | --skip | --abort | --quit)"), 35 NULL 36}; 37 38static const char *action_name(const struct replay_opts *opts) 39{ 40 return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick"; 41} 42 43static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts) 44{ 45 return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage; 46} 47 48enum empty_action { 49 EMPTY_COMMIT_UNSPECIFIED = -1, 50 STOP_ON_EMPTY_COMMIT, /* output errors and stop in the middle of a cherry-pick */ 51 DROP_EMPTY_COMMIT, /* skip with a notice message */ 52 KEEP_EMPTY_COMMIT, /* keep recording as empty commits */ 53}; 54 55static int parse_opt_empty(const struct option *opt, const char *arg, int unset) 56{ 57 int *opt_value = opt->value; 58 59 BUG_ON_OPT_NEG(unset); 60 61 if (!strcmp(arg, "stop")) 62 *opt_value = STOP_ON_EMPTY_COMMIT; 63 else if (!strcmp(arg, "drop")) 64 *opt_value = DROP_EMPTY_COMMIT; 65 else if (!strcmp(arg, "keep")) 66 *opt_value = KEEP_EMPTY_COMMIT; 67 else 68 return error(_("invalid value for '%s': '%s'"), "--empty", arg); 69 70 return 0; 71} 72 73static int option_parse_m(const struct option *opt, 74 const char *arg, int unset) 75{ 76 struct replay_opts *replay = opt->value; 77 char *end; 78 79 if (unset) { 80 replay->mainline = 0; 81 return 0; 82 } 83 84 replay->mainline = strtol(arg, &end, 10); 85 if (*end || replay->mainline <= 0) 86 return error(_("option `%s' expects a number greater than zero"), 87 opt->long_name); 88 89 return 0; 90} 91 92LAST_ARG_MUST_BE_NULL 93static void verify_opt_compatible(const char *me, const char *base_opt, ...) 94{ 95 const char *this_opt; 96 va_list ap; 97 98 va_start(ap, base_opt); 99 while ((this_opt = va_arg(ap, const char *))) { 100 if (va_arg(ap, int)) 101 break; 102 } 103 va_end(ap); 104 105 if (this_opt) 106 die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt); 107} 108 109static int run_sequencer(int argc, const char **argv, const char *prefix, 110 struct replay_opts *opts) 111{ 112 const char * const * usage_str = revert_or_cherry_pick_usage(opts); 113 const char *me = action_name(opts); 114 const char *cleanup_arg = NULL; 115 const char sentinel_value = 0; /* value not important */ 116 const char *strategy = &sentinel_value; 117 const char *gpg_sign = &sentinel_value; 118 enum empty_action empty_opt = EMPTY_COMMIT_UNSPECIFIED; 119 int cmd = 0; 120 struct option base_options[] = { 121 OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'), 122 OPT_CMDMODE(0, "continue", &cmd, N_("resume revert or cherry-pick sequence"), 'c'), 123 OPT_CMDMODE(0, "abort", &cmd, N_("cancel revert or cherry-pick sequence"), 'a'), 124 OPT_CMDMODE(0, "skip", &cmd, N_("skip current commit and continue"), 's'), 125 OPT_CLEANUP(&cleanup_arg), 126 OPT_BOOL('n', "no-commit", &opts->no_commit, N_("don't automatically commit")), 127 OPT_BOOL('e', "edit", &opts->edit, N_("edit the commit message")), 128 OPT_NOOP_NOARG('r', NULL), 129 OPT_BOOL('s', "signoff", &opts->signoff, N_("add a Signed-off-by trailer")), 130 OPT_CALLBACK('m', "mainline", opts, N_("parent-number"), 131 N_("select mainline parent"), option_parse_m), 132 OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto), 133 OPT_STRING(0, "strategy", &strategy, N_("strategy"), N_("merge strategy")), 134 OPT_STRVEC('X', "strategy-option", &opts->xopts, N_("option"), 135 N_("option for merge strategy")), 136 { 137 .type = OPTION_STRING, 138 .short_name = 'S', 139 .long_name = "gpg-sign", 140 .value = &gpg_sign, 141 .argh = N_("key-id"), 142 .help = N_("GPG sign commit"), 143 .flags = PARSE_OPT_OPTARG, 144 .defval = (intptr_t) "", 145 }, 146 OPT_END() 147 }; 148 struct option *options = base_options; 149 150 if (opts->action == REPLAY_PICK) { 151 struct option cp_extra[] = { 152 OPT_BOOL('x', NULL, &opts->record_origin, N_("append commit name")), 153 OPT_BOOL(0, "ff", &opts->allow_ff, N_("allow fast-forward")), 154 OPT_BOOL(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")), 155 OPT_BOOL(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")), 156 OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("deprecated: use --empty=keep instead")), 157 OPT_CALLBACK_F(0, "empty", &empty_opt, "(stop|drop|keep)", 158 N_("how to handle commits that become empty"), 159 PARSE_OPT_NONEG, parse_opt_empty), 160 OPT_END(), 161 }; 162 options = parse_options_concat(options, cp_extra); 163 } else if (opts->action == REPLAY_REVERT) { 164 struct option cp_extra[] = { 165 OPT_BOOL(0, "reference", &opts->commit_use_reference, 166 N_("use the 'reference' format to refer to commits")), 167 OPT_END(), 168 }; 169 options = parse_options_concat(options, cp_extra); 170 } 171 172 argc = parse_options(argc, argv, prefix, options, usage_str, 173 PARSE_OPT_KEEP_ARGV0 | 174 PARSE_OPT_KEEP_UNKNOWN_OPT); 175 176 prepare_repo_settings(the_repository); 177 the_repository->settings.command_requires_full_index = 0; 178 179 if (opts->action == REPLAY_PICK) { 180 opts->drop_redundant_commits = (empty_opt == DROP_EMPTY_COMMIT); 181 opts->keep_redundant_commits = opts->keep_redundant_commits || (empty_opt == KEEP_EMPTY_COMMIT); 182 } 183 184 /* implies allow_empty */ 185 if (opts->keep_redundant_commits) 186 opts->allow_empty = 1; 187 188 if (cleanup_arg) { 189 opts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1); 190 opts->explicit_cleanup = 1; 191 } 192 193 /* Check for incompatible command line arguments */ 194 if (cmd) { 195 const char *this_operation; 196 if (cmd == 'q') 197 this_operation = "--quit"; 198 else if (cmd == 'c') 199 this_operation = "--continue"; 200 else if (cmd == 's') 201 this_operation = "--skip"; 202 else { 203 assert(cmd == 'a'); 204 this_operation = "--abort"; 205 } 206 207 verify_opt_compatible(me, this_operation, 208 "--no-commit", opts->no_commit, 209 "--signoff", opts->signoff, 210 "--mainline", opts->mainline, 211 "--strategy", opts->strategy ? 1 : 0, 212 "--strategy-option", opts->xopts.nr ? 1 : 0, 213 "-x", opts->record_origin, 214 "--ff", opts->allow_ff, 215 "--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE, 216 "--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE, 217 "--keep-redundant-commits", opts->keep_redundant_commits, 218 "--empty", empty_opt != EMPTY_COMMIT_UNSPECIFIED, 219 NULL); 220 } 221 222 if (!opts->strategy && opts->default_strategy) { 223 opts->strategy = opts->default_strategy; 224 opts->default_strategy = NULL; 225 } 226 227 if (opts->allow_ff) 228 verify_opt_compatible(me, "--ff", 229 "--signoff", opts->signoff, 230 "--no-commit", opts->no_commit, 231 "-x", opts->record_origin, 232 "--edit", opts->edit > 0, 233 NULL); 234 235 if (cmd) { 236 opts->revs = NULL; 237 } else { 238 struct setup_revision_opt s_r_opt; 239 opts->revs = xmalloc(sizeof(*opts->revs)); 240 repo_init_revisions(the_repository, opts->revs, NULL); 241 opts->revs->no_walk = 1; 242 opts->revs->unsorted_input = 1; 243 if (argc < 2) 244 usage_with_options(usage_str, options); 245 if (!strcmp(argv[1], "-")) 246 argv[1] = "@{-1}"; 247 memset(&s_r_opt, 0, sizeof(s_r_opt)); 248 s_r_opt.assume_dashdash = 1; 249 argc = setup_revisions(argc, argv, opts->revs, &s_r_opt); 250 } 251 252 if (argc > 1) 253 usage_with_options(usage_str, options); 254 255 /* These option values will be free()d */ 256 if (gpg_sign != &sentinel_value) { 257 free(opts->gpg_sign); 258 opts->gpg_sign = xstrdup_or_null(gpg_sign); 259 } 260 if (strategy != &sentinel_value) { 261 free(opts->strategy); 262 opts->strategy = xstrdup_or_null(strategy); 263 } 264 free(options); 265 266 if (cmd == 'q') { 267 int ret = sequencer_remove_state(opts); 268 if (!ret) 269 remove_branch_state(the_repository, 0); 270 return ret; 271 } 272 if (cmd == 'c') 273 return sequencer_continue(the_repository, opts); 274 if (cmd == 'a') 275 return sequencer_rollback(the_repository, opts); 276 if (cmd == 's') 277 return sequencer_skip(the_repository, opts); 278 return sequencer_pick_revisions(the_repository, opts); 279} 280 281int cmd_revert(int argc, 282 const char **argv, 283 const char *prefix, 284 struct repository *repo UNUSED) 285{ 286 struct replay_opts opts = REPLAY_OPTS_INIT; 287 int res; 288 289#ifndef WITH_BREAKING_CHANGES 290 warn_on_auto_comment_char = true; 291#endif /* !WITH_BREAKING_CHANGES */ 292 opts.action = REPLAY_REVERT; 293 sequencer_init_config(&opts); 294 res = run_sequencer(argc, argv, prefix, &opts); 295 if (res < 0) 296 die(_("revert failed")); 297 replay_opts_release(&opts); 298 return res; 299} 300 301int cmd_cherry_pick(int argc, 302const char **argv, 303const char *prefix, 304struct repository *repo UNUSED) 305{ 306 struct replay_opts opts = REPLAY_OPTS_INIT; 307 int res; 308 309#ifndef WITH_BREAKING_CHANGES 310 warn_on_auto_comment_char = true; 311#endif /* !WITH_BREAKING_CHANGES */ 312 opts.action = REPLAY_PICK; 313 sequencer_init_config(&opts); 314 res = run_sequencer(argc, argv, prefix, &opts); 315 if (res < 0) 316 die(_("cherry-pick failed")); 317 replay_opts_release(&opts); 318 return res; 319}