40
};
41
42
enum replay_action { REVERT, CHERRY_PICK };
43
-enum replay_subcommand { REPLAY_NONE, REPLAY_RESET };
43
+enum replay_subcommand { REPLAY_NONE, REPLAY_RESET, REPLAY_CONTINUE };
44
45
struct replay_opts {
46
enum replay_action action;
117
die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt);
118
}
119
120
+static void verify_opt_mutually_compatible(const char *me, ...)
121
+{
122
+ const char *opt1, *opt2;
123
+ va_list ap;
124
+
125
+ va_start(ap, me);
126
+ while ((opt1 = va_arg(ap, const char *))) {
127
+ if (va_arg(ap, int))
128
+ break;
129
+ }
130
+ if (opt1) {
131
+ while ((opt2 = va_arg(ap, const char *))) {
132
+ if (va_arg(ap, int))
133
+ break;
134
+ }
135
+ }
136
+
137
+ if (opt1 && opt2)
138
+ die(_("%s: %s cannot be used with %s"), me, opt1, opt2);
139
+}
140
+
141
static void parse_args(int argc, const char **argv, struct replay_opts *opts)
142
{
143
const char * const * usage_str = revert_or_cherry_pick_usage(opts);
144
const char *me = action_name(opts);
145
int noop;
146
int reset = 0;
147
+ int contin = 0;
148
struct option options[] = {
149
OPT_BOOLEAN(0, "reset", &reset, "forget the current operation"),
150
+ OPT_BOOLEAN(0, "continue", &contin, "continue the current operation"),
151
OPT_BOOLEAN('n', "no-commit", &opts->no_commit, "don't automatically commit"),
152
OPT_BOOLEAN('e', "edit", &opts->edit, "edit the commit message"),
153
{ OPTION_BOOLEAN, 'r', NULL, &noop, NULL, "no-op (backward compatibility)",
177
PARSE_OPT_KEEP_ARGV0 |
178
PARSE_OPT_KEEP_UNKNOWN);
179
180
+ /* Check for incompatible subcommands */
181
+ verify_opt_mutually_compatible(me,
182
+ "--reset", reset,
183
+ "--continue", contin,
184
+ NULL);
185
+
186
/* Set the subcommand */
187
if (reset)
188
opts->subcommand = REPLAY_RESET;
189
+ else if (contin)
190
+ opts->subcommand = REPLAY_CONTINUE;
191
else
192
opts->subcommand = REPLAY_NONE;
193
194
/* Check for incompatible command line arguments */
164
- if (opts->subcommand == REPLAY_RESET) {
165
- verify_opt_compatible(me, "--reset",
195
+ if (opts->subcommand != REPLAY_NONE) {
196
+ char *this_operation;
197
+ if (opts->subcommand == REPLAY_RESET)
198
+ this_operation = "--reset";
199
+ else
200
+ this_operation = "--continue";
201
+
202
+ verify_opt_compatible(me, this_operation,
203
"--no-commit", opts->no_commit,
204
"--signoff", opts->signoff,
205
"--mainline", opts->mainline,
705
return 0;
706
}
707
708
+static struct commit *parse_insn_line(char *start, struct replay_opts *opts)
709
+{
710
+ unsigned char commit_sha1[20];
711
+ char sha1_abbrev[40];
712
+ enum replay_action action;
713
+ int insn_len = 0;
714
+ char *p, *q;
715
+
716
+ if (!prefixcmp(start, "pick ")) {
717
+ action = CHERRY_PICK;
718
+ insn_len = strlen("pick");
719
+ p = start + insn_len + 1;
720
+ } else if (!prefixcmp(start, "revert ")) {
721
+ action = REVERT;
722
+ insn_len = strlen("revert");
723
+ p = start + insn_len + 1;
724
+ } else
725
+ return NULL;
726
+
727
+ q = strchr(p, ' ');
728
+ if (!q)
729
+ return NULL;
730
+ q++;
731
+
732
+ strlcpy(sha1_abbrev, p, q - p);
733
+
734
+ /*
735
+ * Verify that the action matches up with the one in
736
+ * opts; we don't support arbitrary instructions
737
+ */
738
+ if (action != opts->action) {
739
+ const char *action_str;
740
+ action_str = action == REVERT ? "revert" : "cherry-pick";
741
+ error(_("Cannot %s during a %s"), action_str, action_name(opts));
742
+ return NULL;
743
+ }
744
+
745
+ if (get_sha1(sha1_abbrev, commit_sha1) < 0)
746
+ return NULL;
747
+
748
+ return lookup_commit_reference(commit_sha1);
749
+}
750
+
751
+static int parse_insn_buffer(char *buf, struct commit_list **todo_list,
752
+ struct replay_opts *opts)
753
+{
754
+ struct commit_list **next = todo_list;
755
+ struct commit *commit;
756
+ char *p = buf;
757
+ int i;
758
+
759
+ for (i = 1; *p; i++) {
760
+ commit = parse_insn_line(p, opts);
761
+ if (!commit)
762
+ return error(_("Could not parse line %d."), i);
763
+ next = commit_list_append(commit, next);
764
+ p = strchrnul(p, '\n');
765
+ if (*p)
766
+ p++;
767
+ }
768
+ if (!*todo_list)
769
+ return error(_("No commits parsed."));
770
+ return 0;
771
+}
772
+
773
+static void read_populate_todo(struct commit_list **todo_list,
774
+ struct replay_opts *opts)
775
+{
776
+ const char *todo_file = git_path(SEQ_TODO_FILE);
777
+ struct strbuf buf = STRBUF_INIT;
778
+ int fd, res;
779
+
780
+ fd = open(todo_file, O_RDONLY);
781
+ if (fd < 0)
782
+ die_errno(_("Could not open %s."), todo_file);
783
+ if (strbuf_read(&buf, fd, 0) < 0) {
784
+ close(fd);
785
+ strbuf_release(&buf);
786
+ die(_("Could not read %s."), todo_file);
787
+ }
788
+ close(fd);
789
+
790
+ res = parse_insn_buffer(buf.buf, todo_list, opts);
791
+ strbuf_release(&buf);
792
+ if (res)
793
+ die(_("Unusable instruction sheet: %s"), todo_file);
794
+}
795
+
796
+static int populate_opts_cb(const char *key, const char *value, void *data)
797
+{
798
+ struct replay_opts *opts = data;
799
+ int error_flag = 1;
800
+
801
+ if (!value)
802
+ error_flag = 0;
803
+ else if (!strcmp(key, "options.no-commit"))
804
+ opts->no_commit = git_config_bool_or_int(key, value, &error_flag);
805
+ else if (!strcmp(key, "options.edit"))
806
+ opts->edit = git_config_bool_or_int(key, value, &error_flag);
807
+ else if (!strcmp(key, "options.signoff"))
808
+ opts->signoff = git_config_bool_or_int(key, value, &error_flag);
809
+ else if (!strcmp(key, "options.record-origin"))
810
+ opts->record_origin = git_config_bool_or_int(key, value, &error_flag);
811
+ else if (!strcmp(key, "options.allow-ff"))
812
+ opts->allow_ff = git_config_bool_or_int(key, value, &error_flag);
813
+ else if (!strcmp(key, "options.mainline"))
814
+ opts->mainline = git_config_int(key, value);
815
+ else if (!strcmp(key, "options.strategy"))
816
+ git_config_string(&opts->strategy, key, value);
817
+ else if (!strcmp(key, "options.strategy-option")) {
818
+ ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
819
+ opts->xopts[opts->xopts_nr++] = xstrdup(value);
820
+ } else
821
+ return error(_("Invalid key: %s"), key);
822
+
823
+ if (!error_flag)
824
+ return error(_("Invalid value for %s: %s"), key, value);
825
+
826
+ return 0;
827
+}
828
+
829
+static void read_populate_opts(struct replay_opts **opts_ptr)
830
+{
831
+ const char *opts_file = git_path(SEQ_OPTS_FILE);
832
+
833
+ if (!file_exists(opts_file))
834
+ return;
835
+ if (git_config_from_file(populate_opts_cb, opts_file, *opts_ptr) < 0)
836
+ die(_("Malformed options sheet: %s"), opts_file);
837
+}
838
+
839
static void walk_revs_populate_todo(struct commit_list **todo_list,
840
struct replay_opts *opts)
841
{
979
if (opts->subcommand == REPLAY_RESET) {
980
remove_sequencer_state(1);
981
return 0;
982
+ } else if (opts->subcommand == REPLAY_CONTINUE) {
983
+ if (!file_exists(git_path(SEQ_TODO_FILE)))
984
+ goto error;
985
+ read_populate_opts(&opts);
986
+ read_populate_todo(&todo_list, opts);
987
+
988
+ /* Verify that the conflict has been resolved */
989
+ if (!index_differs_from("HEAD", 0))
990
+ todo_list = todo_list->next;
991
} else {
992
/*
993
* Start a new cherry-pick/ revert sequence; but
998
walk_revs_populate_todo(&todo_list, opts);
999
if (create_seq_dir() < 0) {
1000
fatal(_("A cherry-pick or revert is in progress."));
824
- advise(_("Use --reset to forget about it"));
1001
+ advise(_("Use --continue to continue the operation"));
1002
+ advise(_("or --reset to forget about it"));
1003
exit(128);
1004
}
1005
if (get_sha1("HEAD", sha1)) {
1011
save_opts(opts);
1012
}
1013
return pick_commits(todo_list, opts);
1014
+error:
1015
+ die(_("No %s in progress"), action_name(opts));
1016
}
1017
1018
int cmd_revert(int argc, const char **argv, const char *prefix)