Git fork
at reftables-rust 1470 lines 38 kB view raw
1#define USE_THE_REPOSITORY_VARIABLE 2#define DISABLE_SIGN_COMPARE_WARNINGS 3 4#include "builtin.h" 5#include "copy.h" 6#include "environment.h" 7#include "gettext.h" 8#include "hex.h" 9#include "object-name.h" 10#include "parse-options.h" 11#include "bisect.h" 12#include "refs.h" 13#include "strvec.h" 14#include "run-command.h" 15#include "oid-array.h" 16#include "path.h" 17#include "prompt.h" 18#include "quote.h" 19#include "revision.h" 20 21static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS") 22static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK") 23static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START") 24static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG") 25static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES") 26static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT") 27static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN") 28 29#define BUILTIN_GIT_BISECT_START_USAGE \ 30 N_("git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]" \ 31 " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--]" \ 32 " [<pathspec>...]") 33#define BUILTIN_GIT_BISECT_STATE_USAGE \ 34 N_("git bisect (good|bad) [<rev>...]") 35#define BUILTIN_GIT_BISECT_TERMS_USAGE \ 36 "git bisect terms [--term-good | --term-bad]" 37#define BUILTIN_GIT_BISECT_SKIP_USAGE \ 38 N_("git bisect skip [(<rev>|<range>)...]") 39#define BUILTIN_GIT_BISECT_NEXT_USAGE \ 40 "git bisect next" 41#define BUILTIN_GIT_BISECT_RESET_USAGE \ 42 N_("git bisect reset [<commit>]") 43#define BUILTIN_GIT_BISECT_VISUALIZE_USAGE \ 44 "git bisect visualize" 45#define BUILTIN_GIT_BISECT_REPLAY_USAGE \ 46 N_("git bisect replay <logfile>") 47#define BUILTIN_GIT_BISECT_LOG_USAGE \ 48 "git bisect log" 49#define BUILTIN_GIT_BISECT_RUN_USAGE \ 50 N_("git bisect run <cmd> [<arg>...]") 51 52static const char * const git_bisect_usage[] = { 53 BUILTIN_GIT_BISECT_START_USAGE, 54 BUILTIN_GIT_BISECT_STATE_USAGE, 55 BUILTIN_GIT_BISECT_TERMS_USAGE, 56 BUILTIN_GIT_BISECT_SKIP_USAGE, 57 BUILTIN_GIT_BISECT_NEXT_USAGE, 58 BUILTIN_GIT_BISECT_RESET_USAGE, 59 BUILTIN_GIT_BISECT_VISUALIZE_USAGE, 60 BUILTIN_GIT_BISECT_REPLAY_USAGE, 61 BUILTIN_GIT_BISECT_LOG_USAGE, 62 BUILTIN_GIT_BISECT_RUN_USAGE, 63 NULL 64}; 65 66struct add_bisect_ref_data { 67 struct rev_info *revs; 68 unsigned int object_flags; 69}; 70 71struct bisect_terms { 72 char *term_good; 73 char *term_bad; 74}; 75 76static void free_terms(struct bisect_terms *terms) 77{ 78 FREE_AND_NULL(terms->term_good); 79 FREE_AND_NULL(terms->term_bad); 80} 81 82static void set_terms(struct bisect_terms *terms, const char *bad, 83 const char *good) 84{ 85 free((void *)terms->term_good); 86 terms->term_good = xstrdup(good); 87 free((void *)terms->term_bad); 88 terms->term_bad = xstrdup(bad); 89} 90 91static const char vocab_bad[] = "bad|new"; 92static const char vocab_good[] = "good|old"; 93 94static int bisect_autostart(struct bisect_terms *terms); 95 96/* 97 * Check whether the string `term` belongs to the set of strings 98 * included in the variable arguments. 99 */ 100LAST_ARG_MUST_BE_NULL 101static int one_of(const char *term, ...) 102{ 103 int res = 0; 104 va_list matches; 105 const char *match; 106 107 va_start(matches, term); 108 while (!res && (match = va_arg(matches, const char *))) 109 res = !strcmp(term, match); 110 va_end(matches); 111 112 return res; 113} 114 115/* 116 * return code BISECT_INTERNAL_SUCCESS_MERGE_BASE 117 * and BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND are codes 118 * that indicate special success. 119 */ 120 121static int is_bisect_success(enum bisect_error res) 122{ 123 return !res || 124 res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND || 125 res == BISECT_INTERNAL_SUCCESS_MERGE_BASE; 126} 127 128static int write_in_file(const char *path, const char *mode, const char *format, va_list args) 129{ 130 FILE *fp = NULL; 131 int res = 0; 132 133 if (strcmp(mode, "w") && strcmp(mode, "a")) 134 BUG("write-in-file does not support '%s' mode", mode); 135 fp = fopen(path, mode); 136 if (!fp) 137 return error_errno(_("cannot open file '%s' in mode '%s'"), path, mode); 138 res = vfprintf(fp, format, args); 139 140 if (res < 0) { 141 int saved_errno = errno; 142 fclose(fp); 143 errno = saved_errno; 144 return error_errno(_("could not write to file '%s'"), path); 145 } 146 147 return fclose(fp); 148} 149 150__attribute__((format (printf, 2, 3))) 151static int write_to_file(const char *path, const char *format, ...) 152{ 153 int res; 154 va_list args; 155 156 va_start(args, format); 157 res = write_in_file(path, "w", format, args); 158 va_end(args); 159 160 return res; 161} 162 163__attribute__((format (printf, 2, 3))) 164static int append_to_file(const char *path, const char *format, ...) 165{ 166 int res; 167 va_list args; 168 169 va_start(args, format); 170 res = write_in_file(path, "a", format, args); 171 va_end(args); 172 173 return res; 174} 175 176static int print_file_to_stdout(const char *path) 177{ 178 int fd = open(path, O_RDONLY); 179 int ret = 0; 180 181 if (fd < 0) 182 return error_errno(_("cannot open file '%s' for reading"), path); 183 if (copy_fd(fd, 1) < 0) 184 ret = error_errno(_("failed to read '%s'"), path); 185 close(fd); 186 return ret; 187} 188 189static int check_term_format(const char *term, const char *orig_term) 190{ 191 int res; 192 char *new_term = xstrfmt("refs/bisect/%s", term); 193 194 res = check_refname_format(new_term, 0); 195 free(new_term); 196 197 if (res) 198 return error(_("'%s' is not a valid term"), term); 199 200 if (one_of(term, "help", "start", "skip", "next", "reset", 201 "visualize", "view", "replay", "log", "run", "terms", NULL)) 202 return error(_("can't use the builtin command '%s' as a term"), term); 203 204 /* 205 * In theory, nothing prevents swapping completely good and bad, 206 * but this situation could be confusing and hasn't been tested 207 * enough. Forbid it for now. 208 */ 209 210 if ((strcmp(orig_term, "bad") && one_of(term, "bad", "new", NULL)) || 211 (strcmp(orig_term, "good") && one_of(term, "good", "old", NULL))) 212 return error(_("can't change the meaning of the term '%s'"), term); 213 214 return 0; 215} 216 217static int write_terms(const char *bad, const char *good) 218{ 219 int res; 220 221 if (!strcmp(bad, good)) 222 return error(_("please use two different terms")); 223 224 if (check_term_format(bad, "bad") || check_term_format(good, "good")) 225 return -1; 226 227 res = write_to_file(git_path_bisect_terms(), "%s\n%s\n", bad, good); 228 229 return res; 230} 231 232static int bisect_reset(const char *commit) 233{ 234 struct strbuf branch = STRBUF_INIT; 235 236 if (!commit) { 237 if (!strbuf_read_file(&branch, git_path_bisect_start(), 0)) 238 printf(_("We are not bisecting.\n")); 239 else 240 strbuf_rtrim(&branch); 241 } else { 242 struct object_id oid; 243 244 if (repo_get_oid_commit(the_repository, commit, &oid)) 245 return error(_("'%s' is not a valid commit"), commit); 246 strbuf_addstr(&branch, commit); 247 } 248 249 if (branch.len && !refs_ref_exists(get_main_ref_store(the_repository), "BISECT_HEAD")) { 250 struct child_process cmd = CHILD_PROCESS_INIT; 251 252 cmd.git_cmd = 1; 253 strvec_pushl(&cmd.args, "checkout", "--ignore-other-worktrees", 254 branch.buf, "--", NULL); 255 if (run_command(&cmd)) { 256 error(_("could not check out original" 257 " HEAD '%s'. Try 'git bisect" 258 " reset <commit>'."), branch.buf); 259 strbuf_release(&branch); 260 return -1; 261 } 262 } 263 264 strbuf_release(&branch); 265 return bisect_clean_state(); 266} 267 268static void log_commit(FILE *fp, 269 const char *fmt, const char *state, 270 struct commit *commit) 271{ 272 struct pretty_print_context pp = {0}; 273 struct strbuf commit_msg = STRBUF_INIT; 274 char *label = xstrfmt(fmt, state); 275 276 repo_format_commit_message(the_repository, commit, "%s", &commit_msg, 277 &pp); 278 279 fprintf(fp, "# %s: [%s] %s\n", label, oid_to_hex(&commit->object.oid), 280 commit_msg.buf); 281 282 strbuf_release(&commit_msg); 283 free(label); 284} 285 286static int bisect_write(const char *state, const char *rev, 287 const struct bisect_terms *terms, int nolog) 288{ 289 struct strbuf tag = STRBUF_INIT; 290 struct object_id oid; 291 struct commit *commit; 292 FILE *fp = NULL; 293 int res = 0; 294 295 if (!strcmp(state, terms->term_bad)) { 296 strbuf_addf(&tag, "refs/bisect/%s", state); 297 } else if (one_of(state, terms->term_good, "skip", NULL)) { 298 strbuf_addf(&tag, "refs/bisect/%s-%s", state, rev); 299 } else { 300 res = error(_("Bad bisect_write argument: %s"), state); 301 goto finish; 302 } 303 304 if (repo_get_oid(the_repository, rev, &oid)) { 305 res = error(_("couldn't get the oid of the rev '%s'"), rev); 306 goto finish; 307 } 308 309 if (refs_update_ref(get_main_ref_store(the_repository), NULL, tag.buf, &oid, NULL, 0, 310 UPDATE_REFS_MSG_ON_ERR)) { 311 res = -1; 312 goto finish; 313 } 314 315 fp = fopen(git_path_bisect_log(), "a"); 316 if (!fp) { 317 res = error_errno(_("couldn't open the file '%s'"), git_path_bisect_log()); 318 goto finish; 319 } 320 321 commit = lookup_commit_reference(the_repository, &oid); 322 log_commit(fp, "%s", state, commit); 323 324 if (!nolog) 325 fprintf(fp, "git bisect %s %s\n", state, rev); 326 327finish: 328 if (fp) 329 fclose(fp); 330 strbuf_release(&tag); 331 return res; 332} 333 334static int check_and_set_terms(struct bisect_terms *terms, const char *cmd) 335{ 336 int has_term_file = !is_empty_or_missing_file(git_path_bisect_terms()); 337 338 if (one_of(cmd, "skip", "start", "terms", NULL)) 339 return 0; 340 341 if (has_term_file && strcmp(cmd, terms->term_bad) && 342 strcmp(cmd, terms->term_good)) 343 return error(_("Invalid command: you're currently in a " 344 "%s/%s bisect"), terms->term_bad, 345 terms->term_good); 346 347 if (!has_term_file) { 348 if (one_of(cmd, "bad", "good", NULL)) { 349 set_terms(terms, "bad", "good"); 350 return write_terms(terms->term_bad, terms->term_good); 351 } 352 if (one_of(cmd, "new", "old", NULL)) { 353 set_terms(terms, "new", "old"); 354 return write_terms(terms->term_bad, terms->term_good); 355 } 356 } 357 358 return 0; 359} 360 361static int inc_nr(const char *refname UNUSED, 362 const char *referent UNUSED, 363 const struct object_id *oid UNUSED, 364 int flag UNUSED, void *cb_data) 365{ 366 unsigned int *nr = (unsigned int *)cb_data; 367 (*nr)++; 368 return 0; 369} 370 371static const char need_bad_and_good_revision_warning[] = 372 N_("You need to give me at least one %s and %s revision.\n" 373 "You can use \"git bisect %s\" and \"git bisect %s\" for that."); 374 375static const char need_bisect_start_warning[] = 376 N_("You need to start by \"git bisect start\".\n" 377 "You then need to give me at least one %s and %s revision.\n" 378 "You can use \"git bisect %s\" and \"git bisect %s\" for that."); 379 380static int decide_next(const struct bisect_terms *terms, 381 const char *current_term, int missing_good, 382 int missing_bad) 383{ 384 if (!missing_good && !missing_bad) 385 return 0; 386 if (!current_term) 387 return -1; 388 389 if (missing_good && !missing_bad && 390 !strcmp(current_term, terms->term_good)) { 391 char *yesno; 392 /* 393 * have bad (or new) but not good (or old). We could bisect 394 * although this is less optimum. 395 */ 396 warning(_("bisecting only with a %s commit"), terms->term_bad); 397 if (!isatty(0)) 398 return 0; 399 /* 400 * TRANSLATORS: Make sure to include [Y] and [n] in your 401 * translation. The program will only accept English input 402 * at this point. 403 */ 404 yesno = git_prompt(_("Are you sure [Y/n]? "), PROMPT_ECHO); 405 if (starts_with(yesno, "N") || starts_with(yesno, "n")) 406 return -1; 407 return 0; 408 } 409 410 if (!is_empty_or_missing_file(git_path_bisect_start())) 411 return error(_(need_bad_and_good_revision_warning), 412 vocab_bad, vocab_good, vocab_bad, vocab_good); 413 else 414 return error(_(need_bisect_start_warning), 415 vocab_good, vocab_bad, vocab_good, vocab_bad); 416} 417 418static void bisect_status(struct bisect_state *state, 419 const struct bisect_terms *terms) 420{ 421 char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad); 422 char *good_glob = xstrfmt("%s-*", terms->term_good); 423 424 if (refs_ref_exists(get_main_ref_store(the_repository), bad_ref)) 425 state->nr_bad = 1; 426 427 refs_for_each_glob_ref_in(get_main_ref_store(the_repository), inc_nr, 428 good_glob, "refs/bisect/", 429 (void *) &state->nr_good); 430 431 free(good_glob); 432 free(bad_ref); 433} 434 435__attribute__((format (printf, 1, 2))) 436static void bisect_log_printf(const char *fmt, ...) 437{ 438 struct strbuf buf = STRBUF_INIT; 439 va_list ap; 440 441 va_start(ap, fmt); 442 strbuf_vaddf(&buf, fmt, ap); 443 va_end(ap); 444 445 printf("%s", buf.buf); 446 append_to_file(git_path_bisect_log(), "# %s", buf.buf); 447 448 strbuf_release(&buf); 449} 450 451static void bisect_print_status(const struct bisect_terms *terms) 452{ 453 struct bisect_state state = { 0 }; 454 455 bisect_status(&state, terms); 456 457 /* If we had both, we'd already be started, and shouldn't get here. */ 458 if (state.nr_good && state.nr_bad) 459 return; 460 461 if (!state.nr_good && !state.nr_bad) 462 bisect_log_printf(_("status: waiting for both good and bad commits\n")); 463 else if (state.nr_good) 464 bisect_log_printf(Q_("status: waiting for bad commit, %d good commit known\n", 465 "status: waiting for bad commit, %d good commits known\n", 466 state.nr_good), state.nr_good); 467 else 468 bisect_log_printf(_("status: waiting for good commit(s), bad commit known\n")); 469} 470 471static int bisect_next_check(const struct bisect_terms *terms, 472 const char *current_term) 473{ 474 struct bisect_state state = { 0 }; 475 bisect_status(&state, terms); 476 return decide_next(terms, current_term, !state.nr_good, !state.nr_bad); 477} 478 479static int get_terms(struct bisect_terms *terms) 480{ 481 struct strbuf str = STRBUF_INIT; 482 FILE *fp = NULL; 483 int res = 0; 484 485 fp = fopen(git_path_bisect_terms(), "r"); 486 if (!fp) { 487 res = -1; 488 goto finish; 489 } 490 491 free_terms(terms); 492 strbuf_getline_lf(&str, fp); 493 terms->term_bad = strbuf_detach(&str, NULL); 494 strbuf_getline_lf(&str, fp); 495 terms->term_good = strbuf_detach(&str, NULL); 496 497finish: 498 if (fp) 499 fclose(fp); 500 strbuf_release(&str); 501 return res; 502} 503 504static int bisect_terms(struct bisect_terms *terms, const char *option) 505{ 506 if (get_terms(terms)) 507 return error(_("no terms defined")); 508 509 if (!option) { 510 printf(_("Your current terms are %s for the old state\n" 511 "and %s for the new state.\n"), 512 terms->term_good, terms->term_bad); 513 return 0; 514 } 515 if (one_of(option, "--term-good", "--term-old", NULL)) 516 printf("%s\n", terms->term_good); 517 else if (one_of(option, "--term-bad", "--term-new", NULL)) 518 printf("%s\n", terms->term_bad); 519 else 520 return error(_("invalid argument %s for 'git bisect terms'.\n" 521 "Supported options are: " 522 "--term-good|--term-old and " 523 "--term-bad|--term-new."), option); 524 525 return 0; 526} 527 528static int bisect_append_log_quoted(const char **argv) 529{ 530 int res = 0; 531 FILE *fp = fopen(git_path_bisect_log(), "a"); 532 struct strbuf orig_args = STRBUF_INIT; 533 534 if (!fp) 535 return -1; 536 537 if (fprintf(fp, "git bisect start") < 1) { 538 res = -1; 539 goto finish; 540 } 541 542 sq_quote_argv(&orig_args, argv); 543 if (fprintf(fp, "%s\n", orig_args.buf) < 1) 544 res = -1; 545 546finish: 547 fclose(fp); 548 strbuf_release(&orig_args); 549 return res; 550} 551 552static int add_bisect_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid, 553 int flags UNUSED, void *cb) 554{ 555 struct add_bisect_ref_data *data = cb; 556 557 add_pending_oid(data->revs, refname, oid, data->object_flags); 558 559 return 0; 560} 561 562static int prepare_revs(struct bisect_terms *terms, struct rev_info *revs) 563{ 564 int res = 0; 565 struct add_bisect_ref_data cb = { revs }; 566 char *good = xstrfmt("%s-*", terms->term_good); 567 568 /* 569 * We cannot use terms->term_bad directly in 570 * for_each_glob_ref_in() and we have to append a '*' to it, 571 * otherwise for_each_glob_ref_in() will append '/' and '*'. 572 */ 573 char *bad = xstrfmt("%s*", terms->term_bad); 574 575 /* 576 * It is important to reset the flags used by revision walks 577 * as the previous call to bisect_next_all() in turn 578 * sets up a revision walk. 579 */ 580 reset_revision_walk(); 581 repo_init_revisions(the_repository, revs, NULL); 582 setup_revisions(0, NULL, revs, NULL); 583 refs_for_each_glob_ref_in(get_main_ref_store(the_repository), 584 add_bisect_ref, bad, "refs/bisect/", &cb); 585 cb.object_flags = UNINTERESTING; 586 refs_for_each_glob_ref_in(get_main_ref_store(the_repository), 587 add_bisect_ref, good, "refs/bisect/", &cb); 588 if (prepare_revision_walk(revs)) 589 res = error(_("revision walk setup failed")); 590 591 free(good); 592 free(bad); 593 return res; 594} 595 596static int bisect_skipped_commits(struct bisect_terms *terms) 597{ 598 int res; 599 FILE *fp = NULL; 600 struct rev_info revs; 601 struct commit *commit; 602 struct pretty_print_context pp = {0}; 603 struct strbuf commit_name = STRBUF_INIT; 604 605 res = prepare_revs(terms, &revs); 606 if (res) 607 return res; 608 609 fp = fopen(git_path_bisect_log(), "a"); 610 if (!fp) 611 return error_errno(_("could not open '%s' for appending"), 612 git_path_bisect_log()); 613 614 if (fprintf(fp, "# only skipped commits left to test\n") < 0) 615 return error_errno(_("failed to write to '%s'"), git_path_bisect_log()); 616 617 while ((commit = get_revision(&revs)) != NULL) { 618 strbuf_reset(&commit_name); 619 repo_format_commit_message(the_repository, commit, "%s", 620 &commit_name, &pp); 621 fprintf(fp, "# possible first %s commit: [%s] %s\n", 622 terms->term_bad, oid_to_hex(&commit->object.oid), 623 commit_name.buf); 624 } 625 626 /* 627 * Reset the flags used by revision walks in case 628 * there is another revision walk after this one. 629 */ 630 reset_revision_walk(); 631 632 strbuf_release(&commit_name); 633 release_revisions(&revs); 634 fclose(fp); 635 return 0; 636} 637 638static int bisect_successful(struct bisect_terms *terms) 639{ 640 struct object_id oid; 641 struct commit *commit; 642 struct pretty_print_context pp = {0}; 643 struct strbuf commit_name = STRBUF_INIT; 644 char *bad_ref = xstrfmt("refs/bisect/%s",terms->term_bad); 645 int res; 646 647 refs_read_ref(get_main_ref_store(the_repository), bad_ref, &oid); 648 commit = lookup_commit_reference_by_name(bad_ref); 649 repo_format_commit_message(the_repository, commit, "%s", &commit_name, 650 &pp); 651 652 res = append_to_file(git_path_bisect_log(), "# first %s commit: [%s] %s\n", 653 terms->term_bad, oid_to_hex(&commit->object.oid), 654 commit_name.buf); 655 656 strbuf_release(&commit_name); 657 free(bad_ref); 658 return res; 659} 660 661static enum bisect_error bisect_next(struct bisect_terms *terms, const char *prefix) 662{ 663 enum bisect_error res; 664 665 if (bisect_autostart(terms)) 666 return BISECT_FAILED; 667 668 if (bisect_next_check(terms, terms->term_good)) 669 return BISECT_FAILED; 670 671 /* Perform all bisection computation */ 672 res = bisect_next_all(the_repository, prefix); 673 674 if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) { 675 res = bisect_successful(terms); 676 return res ? res : BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND; 677 } else if (res == BISECT_ONLY_SKIPPED_LEFT) { 678 res = bisect_skipped_commits(terms); 679 return res ? res : BISECT_ONLY_SKIPPED_LEFT; 680 } 681 return res; 682} 683 684static enum bisect_error bisect_auto_next(struct bisect_terms *terms, const char *prefix) 685{ 686 if (bisect_next_check(terms, NULL)) { 687 bisect_print_status(terms); 688 return BISECT_OK; 689 } 690 691 return bisect_next(terms, prefix); 692} 693 694static enum bisect_error bisect_start(struct bisect_terms *terms, int argc, 695 const char **argv) 696{ 697 int no_checkout = 0; 698 int first_parent_only = 0; 699 int i, has_double_dash = 0, must_write_terms = 0, bad_seen = 0; 700 int flags, pathspec_pos; 701 enum bisect_error res = BISECT_OK; 702 struct string_list revs = STRING_LIST_INIT_DUP; 703 struct string_list states = STRING_LIST_INIT_DUP; 704 struct strbuf start_head = STRBUF_INIT; 705 struct strbuf bisect_names = STRBUF_INIT; 706 struct object_id head_oid; 707 struct object_id oid; 708 const char *head; 709 710 if (is_bare_repository()) 711 no_checkout = 1; 712 713 /* 714 * Check for one bad and then some good revisions 715 */ 716 for (i = 0; i < argc; i++) { 717 if (!strcmp(argv[i], "--")) { 718 has_double_dash = 1; 719 break; 720 } 721 } 722 723 for (i = 0; i < argc; i++) { 724 const char *arg = argv[i]; 725 if (!strcmp(argv[i], "--")) { 726 break; 727 } else if (!strcmp(arg, "--no-checkout")) { 728 no_checkout = 1; 729 } else if (!strcmp(arg, "--first-parent")) { 730 first_parent_only = 1; 731 } else if (!strcmp(arg, "--term-good") || 732 !strcmp(arg, "--term-old")) { 733 i++; 734 if (argc <= i) 735 return error(_("'' is not a valid term")); 736 must_write_terms = 1; 737 free((void *) terms->term_good); 738 terms->term_good = xstrdup(argv[i]); 739 } else if (skip_prefix(arg, "--term-good=", &arg) || 740 skip_prefix(arg, "--term-old=", &arg)) { 741 must_write_terms = 1; 742 free((void *) terms->term_good); 743 terms->term_good = xstrdup(arg); 744 } else if (!strcmp(arg, "--term-bad") || 745 !strcmp(arg, "--term-new")) { 746 i++; 747 if (argc <= i) 748 return error(_("'' is not a valid term")); 749 must_write_terms = 1; 750 free((void *) terms->term_bad); 751 terms->term_bad = xstrdup(argv[i]); 752 } else if (skip_prefix(arg, "--term-bad=", &arg) || 753 skip_prefix(arg, "--term-new=", &arg)) { 754 must_write_terms = 1; 755 free((void *) terms->term_bad); 756 terms->term_bad = xstrdup(arg); 757 } else if (starts_with(arg, "--")) { 758 return error(_("unrecognized option: '%s'"), arg); 759 } else if (!get_oidf(&oid, "%s^{commit}", arg)) { 760 string_list_append(&revs, oid_to_hex(&oid)); 761 } else if (has_double_dash) { 762 die(_("'%s' does not appear to be a valid " 763 "revision"), arg); 764 } else { 765 break; 766 } 767 } 768 pathspec_pos = i; 769 770 /* 771 * The user ran "git bisect start <sha1> <sha1>", hence did not 772 * explicitly specify the terms, but we are already starting to 773 * set references named with the default terms, and won't be able 774 * to change afterwards. 775 */ 776 if (revs.nr) 777 must_write_terms = 1; 778 for (i = 0; i < revs.nr; i++) { 779 if (bad_seen) { 780 string_list_append(&states, terms->term_good); 781 } else { 782 bad_seen = 1; 783 string_list_append(&states, terms->term_bad); 784 } 785 } 786 787 /* 788 * Verify HEAD 789 */ 790 head = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), 791 "HEAD", 0, &head_oid, &flags); 792 if (!head) 793 if (repo_get_oid(the_repository, "HEAD", &head_oid)) 794 return error(_("bad HEAD - I need a HEAD")); 795 796 /* 797 * Check if we are bisecting 798 */ 799 if (!is_empty_or_missing_file(git_path_bisect_start())) { 800 /* Reset to the rev from where we started */ 801 strbuf_read_file(&start_head, git_path_bisect_start(), 0); 802 strbuf_trim(&start_head); 803 if (!no_checkout) { 804 struct child_process cmd = CHILD_PROCESS_INIT; 805 806 cmd.git_cmd = 1; 807 strvec_pushl(&cmd.args, "checkout", start_head.buf, 808 "--", NULL); 809 if (run_command(&cmd)) { 810 res = error(_("checking out '%s' failed." 811 " Try 'git bisect start " 812 "<valid-branch>'."), 813 start_head.buf); 814 goto finish; 815 } 816 } 817 } else { 818 /* Get the rev from where we start. */ 819 if (!repo_get_oid(the_repository, head, &head_oid) && 820 !starts_with(head, "refs/heads/")) { 821 strbuf_reset(&start_head); 822 strbuf_addstr(&start_head, oid_to_hex(&head_oid)); 823 } else if (!repo_get_oid(the_repository, head, &head_oid) && 824 skip_prefix(head, "refs/heads/", &head)) { 825 strbuf_addstr(&start_head, head); 826 } else { 827 return error(_("bad HEAD - strange symbolic ref")); 828 } 829 } 830 831 /* 832 * Get rid of any old bisect state. 833 */ 834 if (bisect_clean_state()) 835 return BISECT_FAILED; 836 837 /* 838 * Write new start state 839 */ 840 write_file(git_path_bisect_start(), "%s\n", start_head.buf); 841 842 if (first_parent_only) 843 write_file(git_path_bisect_first_parent(), "\n"); 844 845 if (no_checkout) { 846 if (repo_get_oid(the_repository, start_head.buf, &oid) < 0) { 847 res = error(_("invalid ref: '%s'"), start_head.buf); 848 goto finish; 849 } 850 if (refs_update_ref(get_main_ref_store(the_repository), NULL, "BISECT_HEAD", &oid, NULL, 0, 851 UPDATE_REFS_MSG_ON_ERR)) { 852 res = BISECT_FAILED; 853 goto finish; 854 } 855 } 856 857 if (pathspec_pos < argc - 1) 858 sq_quote_argv(&bisect_names, argv + pathspec_pos); 859 write_file(git_path_bisect_names(), "%s\n", bisect_names.buf); 860 861 for (i = 0; i < states.nr; i++) 862 if (bisect_write(states.items[i].string, 863 revs.items[i].string, terms, 1)) { 864 res = BISECT_FAILED; 865 goto finish; 866 } 867 868 if (must_write_terms && write_terms(terms->term_bad, 869 terms->term_good)) { 870 res = BISECT_FAILED; 871 goto finish; 872 } 873 874 res = bisect_append_log_quoted(argv); 875 if (res) 876 res = BISECT_FAILED; 877 878finish: 879 string_list_clear(&revs, 0); 880 string_list_clear(&states, 0); 881 strbuf_release(&start_head); 882 strbuf_release(&bisect_names); 883 if (res) 884 return res; 885 886 res = bisect_auto_next(terms, NULL); 887 if (!is_bisect_success(res)) 888 bisect_clean_state(); 889 return res; 890} 891 892static inline int file_is_not_empty(const char *path) 893{ 894 return !is_empty_or_missing_file(path); 895} 896 897static int bisect_autostart(struct bisect_terms *terms) 898{ 899 int res; 900 const char *yesno; 901 902 if (file_is_not_empty(git_path_bisect_start())) 903 return 0; 904 905 fprintf_ln(stderr, _("You need to start by \"git bisect " 906 "start\"\n")); 907 908 if (!isatty(STDIN_FILENO)) 909 return -1; 910 911 /* 912 * TRANSLATORS: Make sure to include [Y] and [n] in your 913 * translation. The program will only accept English input 914 * at this point. 915 */ 916 yesno = git_prompt(_("Do you want me to do it for you " 917 "[Y/n]? "), PROMPT_ECHO); 918 res = tolower(*yesno) == 'n' ? 919 -1 : bisect_start(terms, 0, empty_strvec); 920 921 return res; 922} 923 924static enum bisect_error bisect_state(struct bisect_terms *terms, int argc, 925 const char **argv) 926{ 927 const char *state; 928 int i, verify_expected = 1; 929 struct object_id oid, expected; 930 struct oid_array revs = OID_ARRAY_INIT; 931 932 if (!argc) 933 return error(_("Please call `--bisect-state` with at least one argument")); 934 935 if (bisect_autostart(terms)) 936 return BISECT_FAILED; 937 938 state = argv[0]; 939 if (check_and_set_terms(terms, state) || 940 !one_of(state, terms->term_good, terms->term_bad, "skip", NULL)) 941 return BISECT_FAILED; 942 943 argv++; 944 argc--; 945 if (argc > 1 && !strcmp(state, terms->term_bad)) 946 return error(_("'git bisect %s' can take only one argument."), terms->term_bad); 947 948 if (argc == 0) { 949 const char *head = "BISECT_HEAD"; 950 enum get_oid_result res_head = repo_get_oid(the_repository, 951 head, &oid); 952 953 if (res_head == MISSING_OBJECT) { 954 head = "HEAD"; 955 res_head = repo_get_oid(the_repository, head, &oid); 956 } 957 958 if (res_head) 959 error(_("Bad rev input: %s"), head); 960 oid_array_append(&revs, &oid); 961 } 962 963 /* 964 * All input revs must be checked before executing bisect_write() 965 * to discard junk revs. 966 */ 967 968 for (; argc; argc--, argv++) { 969 struct commit *commit; 970 971 if (repo_get_oid(the_repository, *argv, &oid)){ 972 error(_("Bad rev input: %s"), *argv); 973 oid_array_clear(&revs); 974 return BISECT_FAILED; 975 } 976 977 commit = lookup_commit_reference(the_repository, &oid); 978 if (!commit) 979 die(_("Bad rev input (not a commit): %s"), *argv); 980 981 oid_array_append(&revs, &commit->object.oid); 982 } 983 984 if (refs_read_ref(get_main_ref_store(the_repository), "BISECT_EXPECTED_REV", &expected)) 985 verify_expected = 0; /* Ignore invalid file contents */ 986 987 for (i = 0; i < revs.nr; i++) { 988 if (bisect_write(state, oid_to_hex(&revs.oid[i]), terms, 0)) { 989 oid_array_clear(&revs); 990 return BISECT_FAILED; 991 } 992 if (verify_expected && !oideq(&revs.oid[i], &expected)) { 993 unlink_or_warn(git_path_bisect_ancestors_ok()); 994 refs_delete_ref(get_main_ref_store(the_repository), 995 NULL, "BISECT_EXPECTED_REV", NULL, 996 REF_NO_DEREF); 997 verify_expected = 0; 998 } 999 } 1000 1001 oid_array_clear(&revs); 1002 return bisect_auto_next(terms, NULL); 1003} 1004 1005static enum bisect_error bisect_log(void) 1006{ 1007 int fd, status; 1008 const char* filename = git_path_bisect_log(); 1009 1010 if (is_empty_or_missing_file(filename)) 1011 return error(_("We are not bisecting.")); 1012 1013 fd = open(filename, O_RDONLY); 1014 if (fd < 0) 1015 return BISECT_FAILED; 1016 1017 status = copy_fd(fd, STDOUT_FILENO); 1018 close(fd); 1019 return status ? BISECT_FAILED : BISECT_OK; 1020} 1021 1022static int process_replay_line(struct bisect_terms *terms, struct strbuf *line) 1023{ 1024 const char *p = line->buf + strspn(line->buf, " \t"); 1025 char *word_end, *rev; 1026 1027 if ((!skip_prefix(p, "git bisect", &p) && 1028 !skip_prefix(p, "git-bisect", &p)) || !isspace(*p)) 1029 return 0; 1030 p += strspn(p, " \t"); 1031 1032 word_end = (char *)p + strcspn(p, " \t"); 1033 rev = word_end + strspn(word_end, " \t"); 1034 *word_end = '\0'; /* NUL-terminate the word */ 1035 1036 get_terms(terms); 1037 if (check_and_set_terms(terms, p)) 1038 return -1; 1039 1040 if (!strcmp(p, "start")) { 1041 struct strvec argv = STRVEC_INIT; 1042 int res; 1043 sq_dequote_to_strvec(rev, &argv); 1044 res = bisect_start(terms, argv.nr, argv.v); 1045 strvec_clear(&argv); 1046 return res; 1047 } 1048 1049 if (one_of(p, terms->term_good, 1050 terms->term_bad, "skip", NULL)) 1051 return bisect_write(p, rev, terms, 0); 1052 1053 if (!strcmp(p, "terms")) { 1054 struct strvec argv = STRVEC_INIT; 1055 int res; 1056 sq_dequote_to_strvec(rev, &argv); 1057 res = bisect_terms(terms, argv.nr == 1 ? argv.v[0] : NULL); 1058 strvec_clear(&argv); 1059 return res; 1060 } 1061 error(_("'%s'?? what are you talking about?"), p); 1062 1063 return -1; 1064} 1065 1066static enum bisect_error bisect_replay(struct bisect_terms *terms, const char *filename) 1067{ 1068 FILE *fp = NULL; 1069 enum bisect_error res = BISECT_OK; 1070 struct strbuf line = STRBUF_INIT; 1071 1072 if (is_empty_or_missing_file(filename)) 1073 return error(_("cannot read file '%s' for replaying"), filename); 1074 1075 if (bisect_reset(NULL)) 1076 return BISECT_FAILED; 1077 1078 fp = fopen(filename, "r"); 1079 if (!fp) 1080 return BISECT_FAILED; 1081 1082 while ((strbuf_getline(&line, fp) != EOF) && !res) 1083 res = process_replay_line(terms, &line); 1084 1085 strbuf_release(&line); 1086 fclose(fp); 1087 1088 if (res) 1089 return BISECT_FAILED; 1090 1091 return bisect_auto_next(terms, NULL); 1092} 1093 1094static enum bisect_error bisect_skip(struct bisect_terms *terms, int argc, 1095 const char **argv) 1096{ 1097 int i; 1098 enum bisect_error res; 1099 struct strvec argv_state = STRVEC_INIT; 1100 1101 strvec_push(&argv_state, "skip"); 1102 1103 for (i = 0; i < argc; i++) { 1104 const char *dotdot = strstr(argv[i], ".."); 1105 1106 if (dotdot) { 1107 struct rev_info revs; 1108 struct commit *commit; 1109 1110 repo_init_revisions(the_repository, &revs, NULL); 1111 setup_revisions(2, argv + i - 1, &revs, NULL); 1112 1113 if (prepare_revision_walk(&revs)) 1114 die(_("revision walk setup failed")); 1115 while ((commit = get_revision(&revs)) != NULL) 1116 strvec_push(&argv_state, 1117 oid_to_hex(&commit->object.oid)); 1118 1119 reset_revision_walk(); 1120 release_revisions(&revs); 1121 } else { 1122 strvec_push(&argv_state, argv[i]); 1123 } 1124 } 1125 res = bisect_state(terms, argv_state.nr, argv_state.v); 1126 1127 strvec_clear(&argv_state); 1128 return res; 1129} 1130 1131static int bisect_visualize(struct bisect_terms *terms, int argc, 1132 const char **argv) 1133{ 1134 struct child_process cmd = CHILD_PROCESS_INIT; 1135 struct strbuf sb = STRBUF_INIT; 1136 1137 if (bisect_next_check(terms, NULL) != 0) 1138 return BISECT_FAILED; 1139 1140 cmd.no_stdin = 1; 1141 if (!argc) { 1142 if ((getenv("DISPLAY") || getenv("SESSIONNAME") || getenv("MSYSTEM") || 1143 getenv("SECURITYSESSIONID")) && exists_in_PATH("gitk")) { 1144 strvec_push(&cmd.args, "gitk"); 1145 } else { 1146 strvec_push(&cmd.args, "log"); 1147 cmd.git_cmd = 1; 1148 } 1149 } else { 1150 if (argv[0][0] == '-') { 1151 strvec_push(&cmd.args, "log"); 1152 cmd.git_cmd = 1; 1153 } else if (strcmp(argv[0], "tig") && !starts_with(argv[0], "git")) 1154 cmd.git_cmd = 1; 1155 1156 strvec_pushv(&cmd.args, argv); 1157 } 1158 1159 strvec_pushl(&cmd.args, "--bisect", "--", NULL); 1160 1161 strbuf_read_file(&sb, git_path_bisect_names(), 0); 1162 sq_dequote_to_strvec(sb.buf, &cmd.args); 1163 strbuf_release(&sb); 1164 1165 return run_command(&cmd); 1166} 1167 1168static int get_first_good(const char *refname UNUSED, 1169 const char *referent UNUSED, 1170 const struct object_id *oid, 1171 int flag UNUSED, void *cb_data) 1172{ 1173 oidcpy(cb_data, oid); 1174 return 1; 1175} 1176 1177static int do_bisect_run(const char *command) 1178{ 1179 struct child_process cmd = CHILD_PROCESS_INIT; 1180 1181 printf(_("running %s\n"), command); 1182 cmd.use_shell = 1; 1183 strvec_push(&cmd.args, command); 1184 return run_command(&cmd); 1185} 1186 1187static int verify_good(const struct bisect_terms *terms, const char *command) 1188{ 1189 int rc; 1190 enum bisect_error res; 1191 struct object_id good_rev; 1192 struct object_id current_rev; 1193 char *good_glob = xstrfmt("%s-*", terms->term_good); 1194 int no_checkout = refs_ref_exists(get_main_ref_store(the_repository), 1195 "BISECT_HEAD"); 1196 1197 refs_for_each_glob_ref_in(get_main_ref_store(the_repository), 1198 get_first_good, good_glob, "refs/bisect/", 1199 &good_rev); 1200 free(good_glob); 1201 1202 if (refs_read_ref(get_main_ref_store(the_repository), no_checkout ? "BISECT_HEAD" : "HEAD", &current_rev)) 1203 return -1; 1204 1205 res = bisect_checkout(&good_rev, no_checkout); 1206 if (res != BISECT_OK) 1207 return -1; 1208 1209 rc = do_bisect_run(command); 1210 1211 res = bisect_checkout(&current_rev, no_checkout); 1212 if (res != BISECT_OK) 1213 return -1; 1214 1215 return rc; 1216} 1217 1218static int bisect_run(struct bisect_terms *terms, int argc, const char **argv) 1219{ 1220 int res = BISECT_OK; 1221 struct strbuf command = STRBUF_INIT; 1222 const char *new_state; 1223 int temporary_stdout_fd, saved_stdout; 1224 int is_first_run = 1; 1225 1226 if (bisect_next_check(terms, NULL)) 1227 return BISECT_FAILED; 1228 1229 if (!argc) { 1230 error(_("bisect run failed: no command provided.")); 1231 return BISECT_FAILED; 1232 } 1233 1234 sq_quote_argv(&command, argv); 1235 strbuf_ltrim(&command); 1236 while (1) { 1237 res = do_bisect_run(command.buf); 1238 1239 /* 1240 * Exit code 126 and 127 can either come from the shell 1241 * if it was unable to execute or even find the script, 1242 * or from the script itself. Check with a known-good 1243 * revision to avoid trashing the bisect run due to a 1244 * missing or non-executable script. 1245 */ 1246 if (is_first_run && (res == 126 || res == 127)) { 1247 int rc = verify_good(terms, command.buf); 1248 is_first_run = 0; 1249 if (rc < 0 || 128 <= rc) { 1250 error(_("unable to verify %s on good" 1251 " revision"), command.buf); 1252 res = BISECT_FAILED; 1253 break; 1254 } 1255 if (rc == res) { 1256 error(_("bogus exit code %d for good revision"), 1257 rc); 1258 res = BISECT_FAILED; 1259 break; 1260 } 1261 } 1262 1263 if (res < 0 || 128 <= res) { 1264 error(_("bisect run failed: exit code %d from" 1265 " %s is < 0 or >= 128"), res, command.buf); 1266 break; 1267 } 1268 1269 if (res == 125) 1270 new_state = "skip"; 1271 else if (!res) 1272 new_state = terms->term_good; 1273 else 1274 new_state = terms->term_bad; 1275 1276 temporary_stdout_fd = open(git_path_bisect_run(), O_CREAT | O_WRONLY | O_TRUNC, 0666); 1277 1278 if (temporary_stdout_fd < 0) { 1279 res = error_errno(_("cannot open file '%s' for writing"), git_path_bisect_run()); 1280 break; 1281 } 1282 1283 fflush(stdout); 1284 saved_stdout = dup(1); 1285 dup2(temporary_stdout_fd, 1); 1286 1287 res = bisect_state(terms, 1, &new_state); 1288 1289 fflush(stdout); 1290 dup2(saved_stdout, 1); 1291 close(saved_stdout); 1292 close(temporary_stdout_fd); 1293 1294 print_file_to_stdout(git_path_bisect_run()); 1295 1296 if (res == BISECT_ONLY_SKIPPED_LEFT) 1297 error(_("bisect run cannot continue any more")); 1298 else if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) { 1299 puts(_("bisect run success")); 1300 res = BISECT_OK; 1301 } else if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) { 1302 puts(_("bisect found first bad commit")); 1303 res = BISECT_OK; 1304 } else if (res) { 1305 error(_("bisect run failed: 'git bisect %s'" 1306 " exited with error code %d"), new_state, res); 1307 } else { 1308 continue; 1309 } 1310 break; 1311 } 1312 1313 strbuf_release(&command); 1314 return res; 1315} 1316 1317static int cmd_bisect__reset(int argc, const char **argv, const char *prefix UNUSED, 1318 struct repository *repo UNUSED) 1319{ 1320 if (argc > 1) 1321 return error(_("'%s' requires either no argument or a commit"), 1322 "git bisect reset"); 1323 return bisect_reset(argc ? argv[0] : NULL); 1324} 1325 1326static int cmd_bisect__terms(int argc, const char **argv, const char *prefix UNUSED, 1327 struct repository *repo UNUSED) 1328{ 1329 int res; 1330 struct bisect_terms terms = { 0 }; 1331 1332 if (argc > 1) 1333 return error(_("'%s' requires 0 or 1 argument"), 1334 "git bisect terms"); 1335 res = bisect_terms(&terms, argc == 1 ? argv[0] : NULL); 1336 free_terms(&terms); 1337 return res; 1338} 1339 1340static int cmd_bisect__start(int argc, const char **argv, const char *prefix UNUSED, 1341 struct repository *repo UNUSED) 1342{ 1343 int res; 1344 struct bisect_terms terms = { 0 }; 1345 1346 set_terms(&terms, "bad", "good"); 1347 res = bisect_start(&terms, argc, argv); 1348 free_terms(&terms); 1349 return res; 1350} 1351 1352static int cmd_bisect__next(int argc, const char **argv UNUSED, const char *prefix, 1353 struct repository *repo UNUSED) 1354{ 1355 int res; 1356 struct bisect_terms terms = { 0 }; 1357 1358 if (argc) 1359 return error(_("'%s' requires 0 arguments"), 1360 "git bisect next"); 1361 get_terms(&terms); 1362 res = bisect_next(&terms, prefix); 1363 free_terms(&terms); 1364 return res; 1365} 1366 1367static int cmd_bisect__log(int argc UNUSED, const char **argv UNUSED, 1368 const char *prefix UNUSED, 1369 struct repository *repo UNUSED) 1370{ 1371 return bisect_log(); 1372} 1373 1374static int cmd_bisect__replay(int argc, const char **argv, const char *prefix UNUSED, 1375 struct repository *repo UNUSED) 1376{ 1377 int res; 1378 struct bisect_terms terms = { 0 }; 1379 1380 if (argc != 1) 1381 return error(_("no logfile given")); 1382 set_terms(&terms, "bad", "good"); 1383 res = bisect_replay(&terms, argv[0]); 1384 free_terms(&terms); 1385 return res; 1386} 1387 1388static int cmd_bisect__skip(int argc, const char **argv, const char *prefix UNUSED, 1389 struct repository *repo UNUSED) 1390{ 1391 int res; 1392 struct bisect_terms terms = { 0 }; 1393 1394 set_terms(&terms, "bad", "good"); 1395 get_terms(&terms); 1396 res = bisect_skip(&terms, argc, argv); 1397 free_terms(&terms); 1398 return res; 1399} 1400 1401static int cmd_bisect__visualize(int argc, const char **argv, const char *prefix UNUSED, 1402 struct repository *repo UNUSED) 1403{ 1404 int res; 1405 struct bisect_terms terms = { 0 }; 1406 1407 get_terms(&terms); 1408 res = bisect_visualize(&terms, argc, argv); 1409 free_terms(&terms); 1410 return res; 1411} 1412 1413static int cmd_bisect__run(int argc, const char **argv, const char *prefix UNUSED, 1414 struct repository *repo UNUSED) 1415{ 1416 int res; 1417 struct bisect_terms terms = { 0 }; 1418 1419 if (!argc) 1420 return error(_("'%s' failed: no command provided."), "git bisect run"); 1421 get_terms(&terms); 1422 res = bisect_run(&terms, argc, argv); 1423 free_terms(&terms); 1424 return res; 1425} 1426 1427int cmd_bisect(int argc, 1428 const char **argv, 1429 const char *prefix, 1430 struct repository *repo) 1431{ 1432 int res = 0; 1433 parse_opt_subcommand_fn *fn = NULL; 1434 struct option options[] = { 1435 OPT_SUBCOMMAND("reset", &fn, cmd_bisect__reset), 1436 OPT_SUBCOMMAND("terms", &fn, cmd_bisect__terms), 1437 OPT_SUBCOMMAND("start", &fn, cmd_bisect__start), 1438 OPT_SUBCOMMAND("next", &fn, cmd_bisect__next), 1439 OPT_SUBCOMMAND("log", &fn, cmd_bisect__log), 1440 OPT_SUBCOMMAND("replay", &fn, cmd_bisect__replay), 1441 OPT_SUBCOMMAND("skip", &fn, cmd_bisect__skip), 1442 OPT_SUBCOMMAND("visualize", &fn, cmd_bisect__visualize), 1443 OPT_SUBCOMMAND("view", &fn, cmd_bisect__visualize), 1444 OPT_SUBCOMMAND("run", &fn, cmd_bisect__run), 1445 OPT_END() 1446 }; 1447 argc = parse_options(argc, argv, prefix, options, git_bisect_usage, 1448 PARSE_OPT_SUBCOMMAND_OPTIONAL); 1449 1450 if (!fn) { 1451 struct bisect_terms terms = { 0 }; 1452 1453 if (!argc) 1454 usage_msg_opt(_("need a command"), git_bisect_usage, options); 1455 1456 set_terms(&terms, "bad", "good"); 1457 get_terms(&terms); 1458 if (check_and_set_terms(&terms, argv[0])) 1459 usage_msg_optf(_("unknown command: '%s'"), git_bisect_usage, 1460 options, argv[0]); 1461 res = bisect_state(&terms, argc, argv); 1462 free_terms(&terms); 1463 } else { 1464 argc--; 1465 argv++; 1466 res = fn(argc, argv, prefix, repo); 1467 } 1468 1469 return is_bisect_success(res) ? 0 : -res; 1470}