revert: Introduce struct to keep command-line options

The current code uses a set of file-scope static variables to tell the cherry-pick/ revert machinery how to replay the changes, and initializes them by parsing the command-line arguments. In later steps in this series, we would like to introduce an API function that calls into this machinery directly and have a way to tell it what to do. Hence, introduce a structure to group these variables, so that the API can take them as a single replay_options parameter. The only exception is the variable "me" -- remove it since it not an independent option, and can be inferred from the action. Unfortunately, this patch introduces a minor regression. Parsing strategy-option violates a C89 rule: Initializers cannot refer to variables whose address is not known at compile time. Currently, this rule is violated by some other parts of Git as well, and it is possible to get GCC to report these instances using the "-std=c89 -pedantic" option. Inspired-by: Christian Couder <chriscool@tuxfamily.org> Mentored-by: Jonathan Nieder <jrnieder@gmail.com> Helped-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Ramkumar Ramachandra committed Aug 4, 2011 at 16:09 UTC 80e1f791883a9b6104a2a1f0149118ade1890f25
1 file changed +113 -88
builtin/revert.c
+113 -88
@@ -35,76 +35,94 @@ static const char * const cherry_pick_usage[] = {
35 NULL
36 };
37
38 -static int edit, record_origin, no_commit, mainline, signoff, allow_ff;
39 -static enum { REVERT, CHERRY_PICK } action;
40 -static int commit_argc;
41 -static const char **commit_argv;
42 -static int allow_rerere_auto;
43 -
44 -static const char *me;
45 -
46 -/* Merge strategy. */
47 -static const char *strategy;
48 -static const char **xopts;
49 -static size_t xopts_nr, xopts_alloc;
38 +enum replay_action { REVERT, CHERRY_PICK };
39 +
40 +struct replay_opts {
41 + enum replay_action action;
42 +
43 + /* Boolean options */
44 + int edit;
45 + int record_origin;
46 + int no_commit;
47 + int signoff;
48 + int allow_ff;
49 + int allow_rerere_auto;
50 +
51 + int mainline;
52 + int commit_argc;
53 + const char **commit_argv;
54 +
55 + /* Merge strategy */
56 + const char *strategy;
57 + const char **xopts;
58 + size_t xopts_nr, xopts_alloc;
59 +};
60
61 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
62
63 +static const char *action_name(const struct replay_opts *opts)
64 +{
65 + return opts->action == REVERT ? "revert" : "cherry-pick";
66 +}
67 +
68 static char *get_encoding(const char *message);
69
55 -static const char * const *revert_or_cherry_pick_usage(void)
70 +static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
71 {
57 - return action == REVERT ? revert_usage : cherry_pick_usage;
72 + return opts->action == REVERT ? revert_usage : cherry_pick_usage;
73 }
74
75 static int option_parse_x(const struct option *opt,
76 const char *arg, int unset)
77 {
78 + struct replay_opts **opts_ptr = opt->value;
79 + struct replay_opts *opts = *opts_ptr;
80 +
81 if (unset)
82 return 0;
83
66 - ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc);
67 - xopts[xopts_nr++] = xstrdup(arg);
84 + ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
85 + opts->xopts[opts->xopts_nr++] = xstrdup(arg);
86 return 0;
87 }
88
71 -static void parse_args(int argc, const char **argv)
89 +static void parse_args(int argc, const char **argv, struct replay_opts *opts)
90 {
73 - const char * const * usage_str = revert_or_cherry_pick_usage();
91 + const char * const * usage_str = revert_or_cherry_pick_usage(opts);
92 int noop;
93 struct option options[] = {
76 - OPT_BOOLEAN('n', "no-commit", &no_commit, "don't automatically commit"),
77 - OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"),
94 + OPT_BOOLEAN('n', "no-commit", &opts->no_commit, "don't automatically commit"),
95 + OPT_BOOLEAN('e', "edit", &opts->edit, "edit the commit message"),
96 { OPTION_BOOLEAN, 'r', NULL, &noop, NULL, "no-op (backward compatibility)",
97 PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 0 },
80 - OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
81 - OPT_INTEGER('m', "mainline", &mainline, "parent number"),
82 - OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
83 - OPT_STRING(0, "strategy", &strategy, "strategy", "merge strategy"),
84 - OPT_CALLBACK('X', "strategy-option", &xopts, "option",
98 + OPT_BOOLEAN('s', "signoff", &opts->signoff, "add Signed-off-by:"),
99 + OPT_INTEGER('m', "mainline", &opts->mainline, "parent number"),
100 + OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),
101 + OPT_STRING(0, "strategy", &opts->strategy, "strategy", "merge strategy"),
102 + OPT_CALLBACK('X', "strategy-option", &opts, "option",
103 "option for merge strategy", option_parse_x),
104 OPT_END(),
105 OPT_END(),
106 OPT_END(),
107 };
108
91 - if (action == CHERRY_PICK) {
109 + if (opts->action == CHERRY_PICK) {
110 struct option cp_extra[] = {
93 - OPT_BOOLEAN('x', NULL, &record_origin, "append commit name"),
94 - OPT_BOOLEAN(0, "ff", &allow_ff, "allow fast-forward"),
111 + OPT_BOOLEAN('x', NULL, &opts->record_origin, "append commit name"),
112 + OPT_BOOLEAN(0, "ff", &opts->allow_ff, "allow fast-forward"),
113 OPT_END(),
114 };
115 if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra))
116 die(_("program error"));
117 }
118
101 - commit_argc = parse_options(argc, argv, NULL, options, usage_str,
102 - PARSE_OPT_KEEP_ARGV0 |
103 - PARSE_OPT_KEEP_UNKNOWN);
104 - if (commit_argc < 2)
119 + opts->commit_argc = parse_options(argc, argv, NULL, options, usage_str,
120 + PARSE_OPT_KEEP_ARGV0 |
121 + PARSE_OPT_KEEP_UNKNOWN);
122 + if (opts->commit_argc < 2)
123 usage_with_options(usage_str, options);
124
107 - commit_argv = argv;
125 + opts->commit_argv = argv;
126 }
127
128 struct commit_message {
@@ -240,20 +258,20 @@ static struct tree *empty_tree(void)
258 return tree;
259 }
260
243 -static NORETURN void die_dirty_index(const char *me)
261 +static NORETURN void die_dirty_index(struct replay_opts *opts)
262 {
263 if (read_cache_unmerged()) {
246 - die_resolve_conflict(me);
264 + die_resolve_conflict(action_name(opts));
265 } else {
266 if (advice_commit_before_merge) {
249 - if (action == REVERT)
267 + if (opts->action == REVERT)
268 die(_("Your local changes would be overwritten by revert.\n"
269 "Please, commit your changes or stash them to proceed."));
270 else
271 die(_("Your local changes would be overwritten by cherry-pick.\n"
272 "Please, commit your changes or stash them to proceed."));
273 } else {
256 - if (action == REVERT)
274 + if (opts->action == REVERT)
275 die(_("Your local changes would be overwritten by revert.\n"));
276 else
277 die(_("Your local changes would be overwritten by cherry-pick.\n"));
@@ -274,7 +292,8 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from)
292
293 static int do_recursive_merge(struct commit *base, struct commit *next,
294 const char *base_label, const char *next_label,
277 - unsigned char *head, struct strbuf *msgbuf)
295 + unsigned char *head, struct strbuf *msgbuf,
296 + struct replay_opts *opts)
297 {
298 struct merge_options o;
299 struct tree *result, *next_tree, *base_tree, *head_tree;
@@ -295,7 +314,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
314 next_tree = next ? next->tree : empty_tree();
315 base_tree = base ? base->tree : empty_tree();
316
298 - for (xopt = xopts; xopt != xopts + xopts_nr; xopt++)
317 + for (xopt = opts->xopts; xopt != opts->xopts + opts->xopts_nr; xopt++)
318 parse_merge_opt(&o, *xopt);
319
320 clean = merge_trees(&o,
@@ -306,7 +325,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
325 (write_cache(index_fd, active_cache, active_nr) ||
326 commit_locked_index(&index_lock)))
327 /* TRANSLATORS: %s will be "revert" or "cherry-pick" */
309 - die(_("%s: Unable to write new index file"), me);
328 + die(_("%s: Unable to write new index file"), action_name(opts));
329 rollback_lock_file(&index_lock);
330
331 if (!clean) {
@@ -335,7 +354,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
354 * If we are revert, or if our cherry-pick results in a hand merge,
355 * we had better say that the current user is responsible for that.
356 */
338 -static int run_git_commit(const char *defmsg)
357 +static int run_git_commit(const char *defmsg, struct replay_opts *opts)
358 {
359 /* 6 is max possible length of our args array including NULL */
360 const char *args[6];
@@ -343,9 +362,9 @@ static int run_git_commit(const char *defmsg)
362
363 args[i++] = "commit";
364 args[i++] = "-n";
346 - if (signoff)
365 + if (opts->signoff)
366 args[i++] = "-s";
348 - if (!edit) {
367 + if (!opts->edit) {
368 args[i++] = "-F";
369 args[i++] = defmsg;
370 }
@@ -354,7 +373,7 @@ static int run_git_commit(const char *defmsg)
373 return run_command_v_opt(args, RUN_GIT_CMD);
374 }
375
357 -static int do_pick_commit(struct commit *commit)
376 +static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
377 {
378 unsigned char head[20];
379 struct commit *base, *next, *parent;
@@ -364,7 +383,7 @@ static int do_pick_commit(struct commit *commit)
383 struct strbuf msgbuf = STRBUF_INIT;
384 int res;
385
367 - if (no_commit) {
386 + if (opts->no_commit) {
387 /*
388 * We do not intend to commit immediately. We just want to
389 * merge the differences in, so let's compute the tree
@@ -377,7 +396,7 @@ static int do_pick_commit(struct commit *commit)
396 if (get_sha1("HEAD", head))
397 die (_("You do not have a valid HEAD"));
398 if (index_differs_from("HEAD", 0))
380 - die_dirty_index(me);
399 + die_dirty_index(opts);
400 }
401 discard_cache();
402
@@ -389,32 +408,32 @@ static int do_pick_commit(struct commit *commit)
408 int cnt;
409 struct commit_list *p;
410
392 - if (!mainline)
411 + if (!opts->mainline)
412 die(_("Commit %s is a merge but no -m option was given."),
413 sha1_to_hex(commit->object.sha1));
414
415 for (cnt = 1, p = commit->parents;
397 - cnt != mainline && p;
416 + cnt != opts->mainline && p;
417 cnt++)
418 p = p->next;
400 - if (cnt != mainline || !p)
419 + if (cnt != opts->mainline || !p)
420 die(_("Commit %s does not have parent %d"),
402 - sha1_to_hex(commit->object.sha1), mainline);
421 + sha1_to_hex(commit->object.sha1), opts->mainline);
422 parent = p->item;
404 - } else if (0 < mainline)
423 + } else if (0 < opts->mainline)
424 die(_("Mainline was specified but commit %s is not a merge."),
425 sha1_to_hex(commit->object.sha1));
426 else
427 parent = commit->parents->item;
428
410 - if (allow_ff && parent && !hashcmp(parent->object.sha1, head))
429 + if (opts->allow_ff && parent && !hashcmp(parent->object.sha1, head))
430 return fast_forward_to(commit->object.sha1, head);
431
432 if (parent && parse_commit(parent) < 0)
433 /* TRANSLATORS: The first %s will be "revert" or
434 "cherry-pick", the second %s a SHA1 */
435 die(_("%s: cannot parse parent commit %s"),
417 - me, sha1_to_hex(parent->object.sha1));
436 + action_name(opts), sha1_to_hex(parent->object.sha1));
437
438 if (get_message(commit, &msg) != 0)
439 die(_("Cannot get commit message for %s"),
@@ -429,7 +448,7 @@ static int do_pick_commit(struct commit *commit)
448
449 defmsg = git_pathdup("MERGE_MSG");
450
432 - if (action == REVERT) {
451 + if (opts->action == REVERT) {
452 base = commit;
453 base_label = msg.label;
454 next = parent;
@@ -463,18 +482,18 @@ static int do_pick_commit(struct commit *commit)
482 strbuf_addstr(&msgbuf, p);
483 }
484
466 - if (record_origin) {
485 + if (opts->record_origin) {
486 strbuf_addstr(&msgbuf, "(cherry picked from commit ");
487 strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
488 strbuf_addstr(&msgbuf, ")\n");
489 }
471 - if (!no_commit)
490 + if (!opts->no_commit)
491 write_cherry_pick_head(commit);
492 }
493
475 - if (!strategy || !strcmp(strategy, "recursive") || action == REVERT) {
494 + if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REVERT) {
495 res = do_recursive_merge(base, next, base_label, next_label,
477 - head, &msgbuf);
496 + head, &msgbuf, opts);
497 write_message(&msgbuf, defmsg);
498 } else {
499 struct commit_list *common = NULL;
@@ -484,23 +503,23 @@ static int do_pick_commit(struct commit *commit)
503
504 commit_list_insert(base, &common);
505 commit_list_insert(next, &remotes);
487 - res = try_merge_command(strategy, xopts_nr, xopts, common,
488 - sha1_to_hex(head), remotes);
506 + res = try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts,
507 + common, sha1_to_hex(head), remotes);
508 free_commit_list(common);
509 free_commit_list(remotes);
510 }
511
512 if (res) {
494 - error(action == REVERT
513 + error(opts->action == REVERT
514 ? _("could not revert %s... %s")
515 : _("could not apply %s... %s"),
516 find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),
517 msg.subject);
518 print_advice();
500 - rerere(allow_rerere_auto);
519 + rerere(opts->allow_rerere_auto);
520 } else {
502 - if (!no_commit)
503 - res = run_git_commit(defmsg);
521 + if (!opts->no_commit)
522 + res = run_git_commit(defmsg, opts);
523 }
524
525 free_message(&msg);
@@ -509,18 +528,18 @@ static int do_pick_commit(struct commit *commit)
528 return res;
529 }
530
512 -static void prepare_revs(struct rev_info *revs)
531 +static void prepare_revs(struct rev_info *revs, struct replay_opts *opts)
532 {
533 int argc;
534
535 init_revisions(revs, NULL);
536 revs->no_walk = 1;
518 - if (action != REVERT)
537 + if (opts->action != REVERT)
538 revs->reverse = 1;
539
521 - argc = setup_revisions(commit_argc, commit_argv, revs, NULL);
540 + argc = setup_revisions(opts->commit_argc, opts->commit_argv, revs, NULL);
541 if (argc > 1)
523 - usage(*revert_or_cherry_pick_usage());
542 + usage(*revert_or_cherry_pick_usage(opts));
543
544 if (prepare_revision_walk(revs))
545 die(_("revision walk setup failed"));
@@ -529,48 +548,48 @@ static void prepare_revs(struct rev_info *revs)
548 die(_("empty commit set passed"));
549 }
550
532 -static void read_and_refresh_cache(const char *me)
551 +static void read_and_refresh_cache(struct replay_opts *opts)
552 {
553 static struct lock_file index_lock;
554 int index_fd = hold_locked_index(&index_lock, 0);
555 if (read_index_preload(&the_index, NULL) < 0)
537 - die(_("git %s: failed to read the index"), me);
556 + die(_("git %s: failed to read the index"), action_name(opts));
557 refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
558 if (the_index.cache_changed) {
559 if (write_index(&the_index, index_fd) ||
560 commit_locked_index(&index_lock))
542 - die(_("git %s: failed to refresh the index"), me);
561 + die(_("git %s: failed to refresh the index"), action_name(opts));
562 }
563 rollback_lock_file(&index_lock);
564 }
565
547 -static int revert_or_cherry_pick(int argc, const char **argv)
566 +static int revert_or_cherry_pick(int argc, const char **argv,
567 + struct replay_opts *opts)
568 {
569 struct rev_info revs;
570 struct commit *commit;
571
572 git_config(git_default_config, NULL);
553 - me = action == REVERT ? "revert" : "cherry-pick";
554 - setenv(GIT_REFLOG_ACTION, me, 0);
555 - parse_args(argc, argv);
573 + setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
574 + parse_args(argc, argv, opts);
575
557 - if (allow_ff) {
558 - if (signoff)
576 + if (opts->allow_ff) {
577 + if (opts->signoff)
578 die(_("cherry-pick --ff cannot be used with --signoff"));
560 - if (no_commit)
579 + if (opts->no_commit)
580 die(_("cherry-pick --ff cannot be used with --no-commit"));
562 - if (record_origin)
581 + if (opts->record_origin)
582 die(_("cherry-pick --ff cannot be used with -x"));
564 - if (edit)
583 + if (opts->edit)
584 die(_("cherry-pick --ff cannot be used with --edit"));
585 }
586
568 - read_and_refresh_cache(me);
587 + read_and_refresh_cache(opts);
588
570 - prepare_revs(&revs);
589 + prepare_revs(&revs, opts);
590
591 while ((commit = get_revision(&revs))) {
573 - int res = do_pick_commit(commit);
592 + int res = do_pick_commit(commit, opts);
593 if (res)
594 return res;
595 }
@@ -580,14 +599,20 @@ static int revert_or_cherry_pick(int argc, const char **argv)
599
600 int cmd_revert(int argc, const char **argv, const char *prefix)
601 {
602 + struct replay_opts opts;
603 +
604 + memset(&opts, 0, sizeof(opts));
605 if (isatty(0))
584 - edit = 1;
585 - action = REVERT;
586 - return revert_or_cherry_pick(argc, argv);
606 + opts.edit = 1;
607 + opts.action = REVERT;
608 + return revert_or_cherry_pick(argc, argv, &opts);
609 }
610
611 int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
612 {
591 - action = CHERRY_PICK;
592 - return revert_or_cherry_pick(argc, argv);
613 + struct replay_opts opts;
614 +
615 + memset(&opts, 0, sizeof(opts));
616 + opts.action = CHERRY_PICK;
617 + return revert_or_cherry_pick(argc, argv, &opts);
618 }