Merge branch 'sg/subtree-signed-commits' into pu
[git] / builtin / bisect--helper.c
1 #include "builtin.h"
2 #include "cache.h"
3 #include "parse-options.h"
4 #include "bisect.h"
5 #include "refs.h"
6 #include "dir.h"
7 #include "argv-array.h"
8 #include "run-command.h"
9 #include "prompt.h"
10 #include "quote.h"
11
12 static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
13 static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
14 static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
15 static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
16 static GIT_PATH_FUNC(git_path_bisect_head, "BISECT_HEAD")
17 static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
18 static GIT_PATH_FUNC(git_path_head_name, "head-name")
19 static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
20
21 static const char * const git_bisect_helper_usage[] = {
22         N_("git bisect--helper --next-all [--no-checkout]"),
23         N_("git bisect--helper --write-terms <bad_term> <good_term>"),
24         N_("git bisect--helper --bisect-clean-state"),
25         N_("git bisect--helper --bisect-reset [<commit>]"),
26         N_("git bisect--helper --bisect-write <state> <revision> <good_term> <bad_term> [<nolog>]"),
27         N_("git bisect--helper --bisect-check-and-set-terms <command> <good_term> <bad_term>"),
28         N_("git bisect--helper --bisect-next-check [<term>] <good_term> <bad_term>"),
29         N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"),
30         N_("git bisect--helper --bisect-start [--term-{old,good}=<term> --term-{new,bad}=<term>]"
31                                               "[--no-checkout] [<bad> [<good>...]] [--] [<paths>...]"),
32         NULL
33 };
34
35 struct bisect_terms {
36         const char *term_good;
37         const char *term_bad;
38 };
39
40 static void free_terms(struct bisect_terms *terms)
41 {
42         if (!terms->term_good)
43                 free((void *) terms->term_good);
44         if (!terms->term_bad)
45                 free((void *) terms->term_bad);
46 }
47
48 static void set_terms(struct bisect_terms *terms, const char *bad,
49                       const char *good)
50 {
51         terms->term_good = xstrdup(good);
52         terms->term_bad = xstrdup(bad);
53 }
54
55 static const char *voc[] = {
56         "bad|new",
57         "good|old"
58 };
59
60 /*
61  * Check whether the string `term` belongs to the set of strings
62  * included in the variable arguments.
63  */
64 LAST_ARG_MUST_BE_NULL
65 static int one_of(const char *term, ...)
66 {
67         int res = 0;
68         va_list matches;
69         const char *match;
70
71         va_start(matches, term);
72         while (!res && (match = va_arg(matches, const char *)))
73                 res = !strcmp(term, match);
74         va_end(matches);
75
76         return res;
77 }
78
79 static int check_term_format(const char *term, const char *orig_term)
80 {
81         int res;
82         char *new_term = xstrfmt("refs/bisect/%s", term);
83
84         res = check_refname_format(new_term, 0);
85         free(new_term);
86
87         if (res)
88                 return error(_("'%s' is not a valid term"), term);
89
90         if (one_of(term, "help", "start", "skip", "next", "reset",
91                         "visualize", "view", "replay", "log", "run", "terms", NULL))
92                 return error(_("can't use the builtin command '%s' as a term"), term);
93
94         /*
95          * In theory, nothing prevents swapping completely good and bad,
96          * but this situation could be confusing and hasn't been tested
97          * enough. Forbid it for now.
98          */
99
100         if ((strcmp(orig_term, "bad") && one_of(term, "bad", "new", NULL)) ||
101                  (strcmp(orig_term, "good") && one_of(term, "good", "old", NULL)))
102                 return error(_("can't change the meaning of the term '%s'"), term);
103
104         return 0;
105 }
106
107 static int write_terms(const char *bad, const char *good)
108 {
109         FILE *fp = NULL;
110         int res;
111
112         if (!strcmp(bad, good))
113                 return error(_("please use two different terms"));
114
115         if (check_term_format(bad, "bad") || check_term_format(good, "good"))
116                 return -1;
117
118         fp = fopen(git_path_bisect_terms(), "w");
119         if (!fp)
120                 return error_errno(_("could not open the file BISECT_TERMS"));
121
122         res = fprintf(fp, "%s\n%s\n", bad, good);
123         res |= fclose(fp);
124         return (res < 0) ? -1 : 0;
125 }
126
127 static int is_expected_rev(const char *expected_hex)
128 {
129         struct strbuf actual_hex = STRBUF_INIT;
130         int res = 0;
131         if (strbuf_read_file(&actual_hex, git_path_bisect_expected_rev(), 0) >= 40) {
132                 strbuf_trim(&actual_hex);
133                 res = !strcmp(actual_hex.buf, expected_hex);
134         }
135         strbuf_release(&actual_hex);
136         return res;
137 }
138
139 static void check_expected_revs(const char **revs, int rev_nr)
140 {
141         int i;
142
143         for (i = 0; i < rev_nr; i++) {
144                 if (!is_expected_rev(revs[i])) {
145                         unlink_or_warn(git_path_bisect_ancestors_ok());
146                         unlink_or_warn(git_path_bisect_expected_rev());
147                 }
148         }
149 }
150
151 static int bisect_reset(const char *commit)
152 {
153         struct strbuf branch = STRBUF_INIT;
154
155         if (!commit) {
156                 if (strbuf_read_file(&branch, git_path_bisect_start(), 0) < 1)
157                         return !printf(_("We are not bisecting.\n"));
158                 strbuf_rtrim(&branch);
159         } else {
160                 struct object_id oid;
161
162                 if (get_oid_commit(commit, &oid))
163                         return error(_("'%s' is not a valid commit"), commit);
164                 strbuf_addstr(&branch, commit);
165         }
166
167         if (!file_exists(git_path_bisect_head())) {
168                 struct argv_array argv = ARGV_ARRAY_INIT;
169
170                 argv_array_pushl(&argv, "checkout", branch.buf, "--", NULL);
171                 if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
172                         error(_("Could not check out original HEAD '%s'. Try "
173                                 "'git bisect reset <commit>'."), branch.buf);
174                         strbuf_release(&branch);
175                         argv_array_clear(&argv);
176                         return -1;
177                 }
178                 argv_array_clear(&argv);
179         }
180
181         strbuf_release(&branch);
182         return bisect_clean_state();
183 }
184
185 static void log_commit(FILE *fp, char *fmt, const char *state,
186                        struct commit *commit)
187 {
188         struct pretty_print_context pp = {0};
189         struct strbuf commit_msg = STRBUF_INIT;
190         char *label = xstrfmt(fmt, state);
191
192         format_commit_message(commit, "%s", &commit_msg, &pp);
193
194         fprintf(fp, "# %s: [%s] %s\n", label, oid_to_hex(&commit->object.oid),
195                 commit_msg.buf);
196
197         strbuf_release(&commit_msg);
198         free(label);
199 }
200
201 static int bisect_write(const char *state, const char *rev,
202                         const struct bisect_terms *terms, int nolog)
203 {
204         struct strbuf tag = STRBUF_INIT;
205         struct object_id oid;
206         struct commit *commit;
207         FILE *fp = NULL;
208         int retval = 0;
209
210         if (!strcmp(state, terms->term_bad)) {
211                 strbuf_addf(&tag, "refs/bisect/%s", state);
212         } else if (one_of(state, terms->term_good, "skip", NULL)) {
213                 strbuf_addf(&tag, "refs/bisect/%s-%s", state, rev);
214         } else {
215                 error(_("Bad bisect_write argument: %s"), state);
216                 goto fail;
217         }
218
219         if (get_oid(rev, &oid)) {
220                 error(_("couldn't get the oid of the rev '%s'"), rev);
221                 goto fail;
222         }
223
224         if (update_ref(NULL, tag.buf, &oid, NULL, 0,
225                        UPDATE_REFS_MSG_ON_ERR))
226                 goto fail;
227
228         fp = fopen(git_path_bisect_log(), "a");
229         if (!fp) {
230                 error_errno(_("couldn't open the file '%s'"), git_path_bisect_log());
231                 goto fail;
232         }
233
234         commit = lookup_commit_reference(&oid);
235         log_commit(fp, "%s", state, commit);
236
237         if (!nolog)
238                 fprintf(fp, "git bisect %s %s\n", state, rev);
239
240         goto finish;
241
242 fail:
243         retval = -1;
244 finish:
245         if (fp)
246                 fclose(fp);
247         strbuf_release(&tag);
248         return retval;
249 }
250
251 static int check_and_set_terms(struct bisect_terms *terms, const char *cmd)
252 {
253         int has_term_file = !is_empty_or_missing_file(git_path_bisect_terms());
254
255         if (one_of(cmd, "skip", "start", "terms", NULL))
256                 return 0;
257
258         if (has_term_file && strcmp(cmd, terms->term_bad) &&
259             strcmp(cmd, terms->term_good))
260                 return error(_("Invalid command: you're currently in a "
261                                 "%s/%s bisect"), terms->term_bad,
262                                 terms->term_good);
263
264         if (!has_term_file) {
265                 if (one_of(cmd, "bad", "good", NULL)) {
266                         free_terms(terms);
267                         set_terms(terms, "bad", "good");
268                         return write_terms(terms->term_bad, terms->term_good);
269                 }
270                 else if (one_of(cmd, "new", "old", NULL)) {
271                         free_terms(terms);
272                         set_terms(terms, "new", "old");
273                         return write_terms(terms->term_bad, terms->term_good);
274                 }
275         }
276
277         return 0;
278 }
279
280 static int mark_good(const char *refname, const struct object_id *oid,
281                      int flag, void *cb_data)
282 {
283         int *m_good = (int *)cb_data;
284         *m_good = 0;
285         return 1;
286 }
287
288 static int bisect_next_check(const struct bisect_terms *terms,
289                              const char *current_term)
290 {
291         int missing_good = 1, missing_bad = 1, retval = 0;
292         const char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad);
293         const char *good_glob = xstrfmt("%s-*", terms->term_good);
294
295         if (ref_exists(bad_ref))
296                 missing_bad = 0;
297
298         for_each_glob_ref_in(mark_good, good_glob, "refs/bisect/",
299                              (void *) &missing_good);
300
301         if (!missing_good && !missing_bad)
302                 goto finish;
303
304         if (!current_term)
305                 goto fail;
306
307         if (missing_good && !missing_bad && current_term &&
308             !strcmp(current_term, terms->term_good)) {
309                 char *yesno;
310                 /*
311                  * have bad (or new) but not good (or old). We could bisect
312                  * although this is less optimum.
313                  */
314                 fprintf(stderr, _("Warning: bisecting only with a %s commit\n"),
315                         terms->term_bad);
316                 if (!isatty(0))
317                         goto finish;
318                 /*
319                  * TRANSLATORS: Make sure to include [Y] and [n] in your
320                  * translation. The program will only accept English input
321                  * at this point.
322                  */
323                 yesno = git_prompt(_("Are you sure [Y/n]? "), PROMPT_ECHO);
324                 if (starts_with(yesno, "N") || starts_with(yesno, "n"))
325                         goto fail;
326
327                 goto finish;
328         }
329         if (!is_empty_or_missing_file(git_path_bisect_start())) {
330                 error(_("You need to give me at least one %s and "
331                         "%s revision. You can use \"git bisect %s\" "
332                         "and \"git bisect %s\" for that.\n"),
333                         voc[0], voc[1], voc[0], voc[1]);
334                 goto fail;
335         } else {
336                 error(_("You need to start by \"git bisect start\". You "
337                         "then need to give me at least one %s and %s "
338                         "revision. You can use \"git bisect %s\" and "
339                         "\"git bisect %s\" for that.\n"),
340                         voc[1], voc[0], voc[1], voc[0]);
341                 goto fail;
342         }
343         goto finish;
344
345 fail:
346         retval = -1;
347 finish:
348         free((void *) good_glob);
349         free((void *) bad_ref);
350         return retval;
351 }
352
353 static int get_terms(struct bisect_terms *terms)
354 {
355         struct strbuf str = STRBUF_INIT;
356         FILE *fp = NULL;
357         int res = 0;
358
359         fp = fopen(git_path_bisect_terms(), "r");
360         if (!fp)
361                 goto fail;
362
363         free_terms(terms);
364         strbuf_getline_lf(&str, fp);
365         terms->term_bad = strbuf_detach(&str, NULL);
366         strbuf_getline_lf(&str, fp);
367         terms->term_good = strbuf_detach(&str, NULL);
368         goto finish;
369
370 fail:
371         res = -1;
372 finish:
373         if (fp)
374                 fclose(fp);
375         strbuf_release(&str);
376         return res;
377 }
378
379 static int bisect_terms(struct bisect_terms *terms, const char **argv, int argc)
380 {
381         int i;
382
383         if (get_terms(terms))
384                 return error(_("no terms defined"));
385
386         if (argc > 1)
387                 return error(_("--bisect-term requires exactly one argument"));
388
389         if (argc == 0)
390                 return !printf(_("Your current terms are %s for the old state\n"
391                                  "and %s for the new state.\n"),
392                                  terms->term_good, terms->term_bad);
393
394         for (i = 0; i < argc; i++) {
395                 if (!strcmp(argv[i], "--term-good"))
396                         printf(_("%s\n"), terms->term_good);
397                 else if (!strcmp(argv[i], "--term-bad"))
398                         printf(_("%s\n"), terms->term_bad);
399                 else
400                         error(_("BUG: invalid argument %s for 'git bisect terms'.\n"
401                                   "Supported options are: "
402                                   "--term-good|--term-old and "
403                                   "--term-bad|--term-new."), argv[i]);
404         }
405
406         return 0;
407 }
408
409 static int bisect_append_log_quoted(const char **argv)
410 {
411         int retval = 0;
412         FILE *fp = fopen(git_path_bisect_log(), "a");
413         struct strbuf orig_args = STRBUF_INIT;
414
415         if (!fp)
416                 return -1;
417
418         if (fprintf(fp, "git bisect start") < 1)
419                 goto fail;
420
421         sq_quote_argv(&orig_args, argv);
422         if (fprintf(fp, "%s\n", orig_args.buf) < 1)
423                 goto fail;
424
425         goto finish;
426
427 fail:
428         retval = -1;
429 finish:
430         fclose(fp);
431         strbuf_release(&orig_args);
432         return retval;
433 }
434
435 static int bisect_start(struct bisect_terms *terms, int no_checkout,
436                         const char **argv, int argc)
437 {
438         int i, has_double_dash = 0, must_write_terms = 0, bad_seen = 0;
439         int flags, pathspec_pos, retval = 0;
440         struct string_list revs = STRING_LIST_INIT_DUP;
441         struct string_list states = STRING_LIST_INIT_DUP;
442         struct strbuf start_head = STRBUF_INIT;
443         struct strbuf bisect_names = STRBUF_INIT;
444         struct object_id head_oid;
445         struct object_id oid;
446         const char *head;
447
448         if (is_bare_repository())
449                 no_checkout = 1;
450
451         /*
452          * Check for one bad and then some good revisions
453          */
454         for (i = 0; i < argc; i++) {
455                 if (!strcmp(argv[i], "--")) {
456                         has_double_dash = 1;
457                         break;
458                 }
459         }
460
461         for (i = 0; i < argc; i++) {
462                 const char *arg = argv[i];
463                 if (!strcmp(argv[i], "--")) {
464                         break;
465                 } else if (!strcmp(arg, "--no-checkout")) {
466                         no_checkout = 1;
467                 } else if (!strcmp(arg, "--term-good") ||
468                          !strcmp(arg, "--term-old")) {
469                         must_write_terms = 1;
470                         free((void *) terms->term_good);
471                         terms->term_good = xstrdup(argv[++i]);
472                 } else if (skip_prefix(arg, "--term-good=", &arg) ||
473                            skip_prefix(arg, "--term-old=", &arg)) {
474                         must_write_terms = 1;
475                         free((void *) terms->term_good);
476                         terms->term_good = xstrdup(arg);
477                 } else if (!strcmp(arg, "--term-bad") ||
478                          !strcmp(arg, "--term-new")) {
479                         must_write_terms = 1;
480                         free((void *) terms->term_bad);
481                         terms->term_bad = xstrdup(argv[++i]);
482                 } else if (skip_prefix(arg, "--term-bad=", &arg) ||
483                            skip_prefix(arg, "--term-new=", &arg)) {
484                         must_write_terms = 1;
485                         free((void *) terms->term_bad);
486                         terms->term_bad = xstrdup(arg);
487                 } else if (starts_with(arg, "--") &&
488                          !one_of(arg, "--term-good", "--term-bad", NULL)) {
489                         return error(_("unrecognised option: '%s'"), arg);
490                 } else {
491                         char *commit_id = xstrfmt("%s^{commit}", arg);
492                         if (get_oid(commit_id, &oid) && has_double_dash)
493                                 die(_("'%s' does not appear to be a valid "
494                                       "revision"), arg);
495
496                         string_list_append(&revs, oid_to_hex(&oid));
497                         free(commit_id);
498                 }
499         }
500         pathspec_pos = i;
501
502         /*
503          * The user ran "git bisect start <sha1> <sha1>", hence did not
504          * explicitly specify the terms, but we are already starting to
505          * set references named with the default terms, and won't be able
506          * to change afterwards.
507          */
508         must_write_terms |= !!revs.nr;
509         for (i = 0; i < revs.nr; i++) {
510                 if (bad_seen) {
511                         string_list_append(&states, terms->term_good);
512                 } else {
513                         bad_seen = 1;
514                         string_list_append(&states, terms->term_bad);
515                 }
516         }
517
518         /*
519          * Verify HEAD
520          */
521         head = resolve_ref_unsafe("HEAD", 0, &head_oid, &flags);
522         if (!head)
523                 if (get_oid("HEAD", &head_oid))
524                         return error(_("Bad HEAD - I need a HEAD"));
525
526         /*
527          * Check if we are bisecting
528          */
529         if (!is_empty_or_missing_file(git_path_bisect_start())) {
530                 /* Reset to the rev from where we started */
531                 strbuf_read_file(&start_head, git_path_bisect_start(), 0);
532                 strbuf_trim(&start_head);
533                 if (!no_checkout) {
534                         struct argv_array argv = ARGV_ARRAY_INIT;
535
536                         argv_array_pushl(&argv, "checkout", start_head.buf,
537                                          "--", NULL);
538                         if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
539                                 error(_("checking out '%s' failed. Try 'git "
540                                         "bisect start <valid-branch>'."),
541                                       start_head.buf);
542                                 goto fail;
543                         }
544                 }
545         } else {
546                 /* Get the rev from where we start. */
547                 if (!get_oid(head, &head_oid) &&
548                     !starts_with(head, "refs/heads/")) {
549                         strbuf_reset(&start_head);
550                         strbuf_addstr(&start_head, sha1_to_hex(head_oid.hash));
551                 } else if (!get_oid(head, &head_oid) &&
552                            skip_prefix(head, "refs/heads/", &head)) {
553                         /*
554                          * This error message should only be triggered by
555                          * cogito usage, and cogito users should understand
556                          * it relates to cg-seek.
557                          */
558                         if (!is_empty_or_missing_file(git_path_head_name()))
559                                 return error(_("won't bisect on cg-seek'ed tree"));
560                         strbuf_addstr(&start_head, head);
561                 } else {
562                         return error(_("Bad HEAD - strange symbolic ref"));
563                 }
564         }
565
566         /*
567          * Get rid of any old bisect state.
568          */
569         if (bisect_clean_state())
570                 return -1;
571
572         /*
573          * In case of mistaken revs or checkout error, or signals received,
574          * "bisect_auto_next" below may exit or misbehave.
575          * We have to trap this to be able to clean up using
576          * "bisect_clean_state".
577          */
578
579         /*
580          * Write new start state
581          */
582         write_file(git_path_bisect_start(), "%s\n", start_head.buf);
583
584         if (no_checkout) {
585                 get_oid(start_head.buf, &oid);
586                 if (update_ref(NULL, "BISECT_HEAD", &oid, NULL, 0,
587                                UPDATE_REFS_MSG_ON_ERR))
588                         goto fail;
589         }
590
591         if (pathspec_pos < argc - 1)
592                 sq_quote_argv(&bisect_names, argv + pathspec_pos);
593         write_file(git_path_bisect_names(), "%s\n", bisect_names.buf);
594
595         for (i = 0; i < states.nr; i++)
596                 if (bisect_write(states.items[i].string,
597                                  revs.items[i].string, terms, 1))
598                         goto fail;
599
600         if (must_write_terms)
601                 if (write_terms(terms->term_bad, terms->term_good))
602                         goto fail;
603
604         retval = bisect_append_log_quoted(argv);
605         if (retval)
606                 goto fail;
607
608         goto finish;
609
610 fail:
611         retval = -1;
612 finish:
613         string_list_clear(&revs, 0);
614         string_list_clear(&states, 0);
615         strbuf_release(&start_head);
616         strbuf_release(&bisect_names);
617         return retval;
618 }
619
620 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
621 {
622         enum {
623                 NEXT_ALL = 1,
624                 WRITE_TERMS,
625                 BISECT_CLEAN_STATE,
626                 CHECK_EXPECTED_REVS,
627                 BISECT_RESET,
628                 BISECT_WRITE,
629                 CHECK_AND_SET_TERMS,
630                 BISECT_NEXT_CHECK,
631                 BISECT_TERMS,
632                 BISECT_START
633         } cmdmode = 0;
634         int no_checkout = 0, res = 0;
635         struct option options[] = {
636                 OPT_CMDMODE(0, "next-all", &cmdmode,
637                          N_("perform 'git bisect next'"), NEXT_ALL),
638                 OPT_CMDMODE(0, "write-terms", &cmdmode,
639                          N_("write the terms to .git/BISECT_TERMS"), WRITE_TERMS),
640                 OPT_CMDMODE(0, "bisect-clean-state", &cmdmode,
641                          N_("cleanup the bisection state"), BISECT_CLEAN_STATE),
642                 OPT_CMDMODE(0, "check-expected-revs", &cmdmode,
643                          N_("check for expected revs"), CHECK_EXPECTED_REVS),
644                 OPT_CMDMODE(0, "bisect-reset", &cmdmode,
645                          N_("reset the bisection state"), BISECT_RESET),
646                 OPT_CMDMODE(0, "bisect-write", &cmdmode,
647                          N_("update the refs according to the bisection state and may write it to BISECT_LOG"), BISECT_WRITE),
648                 OPT_CMDMODE(0, "check-and-set-terms", &cmdmode,
649                          N_("check and set terms in a bisection state"), CHECK_AND_SET_TERMS),
650                 OPT_CMDMODE(0, "bisect-next-check", &cmdmode,
651                          N_("check whether bad or good terms exist"), BISECT_NEXT_CHECK),
652                 OPT_CMDMODE(0, "bisect-terms", &cmdmode,
653                          N_("print out the bisect terms"), BISECT_TERMS),
654                 OPT_CMDMODE(0, "bisect-start", &cmdmode,
655                          N_("start the bisect session"), BISECT_START),
656                 OPT_BOOL(0, "no-checkout", &no_checkout,
657                          N_("update BISECT_HEAD instead of checking out the current commit")),
658                 OPT_END()
659         };
660         struct bisect_terms terms;
661
662         argc = parse_options(argc, argv, prefix, options,
663                              git_bisect_helper_usage,
664                              PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_UNKNOWN);
665
666         if (!cmdmode)
667                 usage_with_options(git_bisect_helper_usage, options);
668
669         switch (cmdmode) {
670         int nolog;
671         case NEXT_ALL:
672                 return bisect_next_all(prefix, no_checkout);
673         case WRITE_TERMS:
674                 if (argc != 2)
675                         return error(_("--write-terms requires two arguments"));
676                 return write_terms(argv[0], argv[1]);
677         case BISECT_CLEAN_STATE:
678                 if (argc != 0)
679                         return error(_("--bisect-clean-state requires no arguments"));
680                 return bisect_clean_state();
681         case CHECK_EXPECTED_REVS:
682                 check_expected_revs(argv, argc);
683                 return 0;
684         case BISECT_RESET:
685                 if (argc > 1)
686                         return error(_("--bisect-reset requires either no argument or a commit"));
687                 return bisect_reset(argc ? argv[0] : NULL);
688         case BISECT_WRITE:
689                 if (argc != 4 && argc != 5)
690                         return error(_("--bisect-write requires either 4 or 5 arguments"));
691                 nolog = (argc == 5) && !strcmp(argv[4], "nolog");
692                 set_terms(&terms, argv[3], argv[2]);
693                 res = bisect_write(argv[0], argv[1], &terms, nolog);
694                 break;
695         case CHECK_AND_SET_TERMS:
696                 if (argc != 3)
697                         return error(_("--check-and-set-terms requires 3 arguments"));
698                 set_terms(&terms, argv[2], argv[1]);
699                 res = check_and_set_terms(&terms, argv[0]);
700                 break;
701         case BISECT_NEXT_CHECK:
702                 if (argc != 2 && argc != 3)
703                         return error(_("--bisect-next-check requires 2 or 3 arguments"));
704                 set_terms(&terms, argv[1], argv[0]);
705                 res = bisect_next_check(&terms, argc == 3 ? argv[2] : NULL);
706                 break;
707         case BISECT_TERMS:
708                 if (argc > 1)
709                         return error(_("--bisect-terms requires 0 or 1 argument"));
710                 res = bisect_terms(&terms, argv, argc);
711                 break;
712         case BISECT_START:
713                 set_terms(&terms, "bad", "good");
714                 res = bisect_start(&terms, no_checkout, argv, argc);
715                 break;
716         default:
717                 return error("BUG: unknown subcommand '%d'", cmdmode);
718         }
719         free_terms(&terms);
720         return res;
721 }