Merge branch 'pw/am-signoff'
[git] / builtin / revert.c
1 #include "cache.h"
2 #include "config.h"
3 #include "builtin.h"
4 #include "parse-options.h"
5 #include "diff.h"
6 #include "revision.h"
7 #include "rerere.h"
8 #include "dir.h"
9 #include "sequencer.h"
10
11 /*
12  * This implements the builtins revert and cherry-pick.
13  *
14  * Copyright (c) 2007 Johannes E. Schindelin
15  *
16  * Based on git-revert.sh, which is
17  *
18  * Copyright (c) 2005 Linus Torvalds
19  * Copyright (c) 2005 Junio C Hamano
20  */
21
22 static const char * const revert_usage[] = {
23         N_("git revert [<options>] <commit-ish>..."),
24         N_("git revert <subcommand>"),
25         NULL
26 };
27
28 static const char * const cherry_pick_usage[] = {
29         N_("git cherry-pick [<options>] <commit-ish>..."),
30         N_("git cherry-pick <subcommand>"),
31         NULL
32 };
33
34 static const char *action_name(const struct replay_opts *opts)
35 {
36         return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
37 }
38
39 static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
40 {
41         return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;
42 }
43
44 static int option_parse_x(const struct option *opt,
45                           const char *arg, int unset)
46 {
47         struct replay_opts **opts_ptr = opt->value;
48         struct replay_opts *opts = *opts_ptr;
49
50         if (unset)
51                 return 0;
52
53         ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
54         opts->xopts[opts->xopts_nr++] = xstrdup(arg);
55         return 0;
56 }
57
58 static int option_parse_m(const struct option *opt,
59                           const char *arg, int unset)
60 {
61         struct replay_opts *replay = opt->value;
62         char *end;
63
64         if (unset) {
65                 replay->mainline = 0;
66                 return 0;
67         }
68
69         replay->mainline = strtol(arg, &end, 10);
70         if (*end || replay->mainline <= 0)
71                 return opterror(opt, "expects a number greater than zero", 0);
72
73         return 0;
74 }
75
76 LAST_ARG_MUST_BE_NULL
77 static void verify_opt_compatible(const char *me, const char *base_opt, ...)
78 {
79         const char *this_opt;
80         va_list ap;
81
82         va_start(ap, base_opt);
83         while ((this_opt = va_arg(ap, const char *))) {
84                 if (va_arg(ap, int))
85                         break;
86         }
87         va_end(ap);
88
89         if (this_opt)
90                 die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt);
91 }
92
93 static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
94 {
95         const char * const * usage_str = revert_or_cherry_pick_usage(opts);
96         const char *me = action_name(opts);
97         int cmd = 0;
98         struct option base_options[] = {
99                 OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
100                 OPT_CMDMODE(0, "continue", &cmd, N_("resume revert or cherry-pick sequence"), 'c'),
101                 OPT_CMDMODE(0, "abort", &cmd, N_("cancel revert or cherry-pick sequence"), 'a'),
102                 OPT_BOOL('n', "no-commit", &opts->no_commit, N_("don't automatically commit")),
103                 OPT_BOOL('e', "edit", &opts->edit, N_("edit the commit message")),
104                 OPT_NOOP_NOARG('r', NULL),
105                 OPT_BOOL('s', "signoff", &opts->signoff, N_("add Signed-off-by:")),
106                 OPT_CALLBACK('m', "mainline", opts, N_("parent-number"),
107                              N_("select mainline parent"), option_parse_m),
108                 OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),
109                 OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")),
110                 OPT_CALLBACK('X', "strategy-option", &opts, N_("option"),
111                         N_("option for merge strategy"), option_parse_x),
112                 { OPTION_STRING, 'S', "gpg-sign", &opts->gpg_sign, N_("key-id"),
113                   N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
114                 OPT_END()
115         };
116         struct option *options = base_options;
117
118         if (opts->action == REPLAY_PICK) {
119                 struct option cp_extra[] = {
120                         OPT_BOOL('x', NULL, &opts->record_origin, N_("append commit name")),
121                         OPT_BOOL(0, "ff", &opts->allow_ff, N_("allow fast-forward")),
122                         OPT_BOOL(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")),
123                         OPT_BOOL(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")),
124                         OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
125                         OPT_END(),
126                 };
127                 options = parse_options_concat(options, cp_extra);
128         }
129
130         argc = parse_options(argc, argv, NULL, options, usage_str,
131                         PARSE_OPT_KEEP_ARGV0 |
132                         PARSE_OPT_KEEP_UNKNOWN);
133
134         /* implies allow_empty */
135         if (opts->keep_redundant_commits)
136                 opts->allow_empty = 1;
137
138         /* Check for incompatible command line arguments */
139         if (cmd) {
140                 char *this_operation;
141                 if (cmd == 'q')
142                         this_operation = "--quit";
143                 else if (cmd == 'c')
144                         this_operation = "--continue";
145                 else {
146                         assert(cmd == 'a');
147                         this_operation = "--abort";
148                 }
149
150                 verify_opt_compatible(me, this_operation,
151                                 "--no-commit", opts->no_commit,
152                                 "--signoff", opts->signoff,
153                                 "--mainline", opts->mainline,
154                                 "--strategy", opts->strategy ? 1 : 0,
155                                 "--strategy-option", opts->xopts ? 1 : 0,
156                                 "-x", opts->record_origin,
157                                 "--ff", opts->allow_ff,
158                                 "--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
159                                 "--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,
160                                 NULL);
161         }
162
163         if (opts->allow_ff)
164                 verify_opt_compatible(me, "--ff",
165                                 "--signoff", opts->signoff,
166                                 "--no-commit", opts->no_commit,
167                                 "-x", opts->record_origin,
168                                 "--edit", opts->edit,
169                                 NULL);
170
171         if (cmd) {
172                 opts->revs = NULL;
173         } else {
174                 struct setup_revision_opt s_r_opt;
175                 opts->revs = xmalloc(sizeof(*opts->revs));
176                 init_revisions(opts->revs, NULL);
177                 opts->revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED;
178                 if (argc < 2)
179                         usage_with_options(usage_str, options);
180                 if (!strcmp(argv[1], "-"))
181                         argv[1] = "@{-1}";
182                 memset(&s_r_opt, 0, sizeof(s_r_opt));
183                 s_r_opt.assume_dashdash = 1;
184                 argc = setup_revisions(argc, argv, opts->revs, &s_r_opt);
185         }
186
187         if (argc > 1)
188                 usage_with_options(usage_str, options);
189
190         /* These option values will be free()d */
191         opts->gpg_sign = xstrdup_or_null(opts->gpg_sign);
192         opts->strategy = xstrdup_or_null(opts->strategy);
193
194         if (cmd == 'q')
195                 return sequencer_remove_state(opts);
196         if (cmd == 'c')
197                 return sequencer_continue(opts);
198         if (cmd == 'a')
199                 return sequencer_rollback(opts);
200         return sequencer_pick_revisions(opts);
201 }
202
203 int cmd_revert(int argc, const char **argv, const char *prefix)
204 {
205         struct replay_opts opts = REPLAY_OPTS_INIT;
206         int res;
207
208         if (isatty(0))
209                 opts.edit = 1;
210         opts.action = REPLAY_REVERT;
211         git_config(git_default_config, NULL);
212         res = run_sequencer(argc, argv, &opts);
213         if (res < 0)
214                 die(_("revert failed"));
215         return res;
216 }
217
218 int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
219 {
220         struct replay_opts opts = REPLAY_OPTS_INIT;
221         int res;
222
223         opts.action = REPLAY_PICK;
224         git_config(git_default_config, NULL);
225         res = run_sequencer(argc, argv, &opts);
226         if (res < 0)
227                 die(_("cherry-pick failed"));
228         return res;
229 }