8
#include "run-command.h"
9
#include "prompt.h"
10
#include "quote.h"
11
+#include "revision.h"
12
13
static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
14
static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
30
N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"),
31
N_("git bisect--helper --bisect-start [--term-{old,good}=<term> --term-{new,bad}=<term>]"
32
" [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"),
33
+ N_("git bisect--helper --bisect-next"),
34
+ N_("git bisect--helper --bisect-auto-next"),
35
N_("git bisect--helper --bisect-autostart"),
36
NULL
37
};
38
39
+struct add_bisect_ref_data {
40
+ struct rev_info *revs;
41
+ unsigned int object_flags;
42
+};
43
+
44
struct bisect_terms {
45
char *term_good;
46
char *term_bad;
64
static const char vocab_bad[] = "bad|new";
65
static const char vocab_good[] = "good|old";
66
67
+static int bisect_autostart(struct bisect_terms *terms);
68
+
69
/*
70
* Check whether the string `term` belongs to the set of strings
71
* included in the variable arguments.
90
FILE *fp = NULL;
91
int res = 0;
92
83
- if (strcmp(mode, "w"))
93
+ if (strcmp(mode, "w") && strcmp(mode, "a"))
94
BUG("write-in-file does not support '%s' mode", mode);
95
fp = fopen(path, mode);
96
if (!fp)
119
return res;
120
}
121
122
+static int append_to_file(const char *path, const char *format, ...)
123
+{
124
+ int res;
125
+ va_list args;
126
+
127
+ va_start(args, format);
128
+ res = write_in_file(path, "a", format, args);
129
+ va_end(args);
130
+
131
+ return res;
132
+}
133
+
134
static int check_term_format(const char *term, const char *orig_term)
135
{
136
int res;
473
return res;
474
}
475
476
+static int add_bisect_ref(const char *refname, const struct object_id *oid,
477
+ int flags, void *cb)
478
+{
479
+ struct add_bisect_ref_data *data = cb;
480
+
481
+ add_pending_oid(data->revs, refname, oid, data->object_flags);
482
+
483
+ return 0;
484
+}
485
+
486
+static int prepare_revs(struct bisect_terms *terms, struct rev_info *revs)
487
+{
488
+ int res = 0;
489
+ struct add_bisect_ref_data cb = { revs };
490
+ char *good = xstrfmt("%s-*", terms->term_good);
491
+
492
+ /*
493
+ * We cannot use terms->term_bad directly in
494
+ * for_each_glob_ref_in() and we have to append a '*' to it,
495
+ * otherwise for_each_glob_ref_in() will append '/' and '*'.
496
+ */
497
+ char *bad = xstrfmt("%s*", terms->term_bad);
498
+
499
+ /*
500
+ * It is important to reset the flags used by revision walks
501
+ * as the previous call to bisect_next_all() in turn
502
+ * sets up a revision walk.
503
+ */
504
+ reset_revision_walk();
505
+ init_revisions(revs, NULL);
506
+ setup_revisions(0, NULL, revs, NULL);
507
+ for_each_glob_ref_in(add_bisect_ref, bad, "refs/bisect/", &cb);
508
+ cb.object_flags = UNINTERESTING;
509
+ for_each_glob_ref_in(add_bisect_ref, good, "refs/bisect/", &cb);
510
+ if (prepare_revision_walk(revs))
511
+ res = error(_("revision walk setup failed\n"));
512
+
513
+ free(good);
514
+ free(bad);
515
+ return res;
516
+}
517
+
518
+static int bisect_skipped_commits(struct bisect_terms *terms)
519
+{
520
+ int res;
521
+ FILE *fp = NULL;
522
+ struct rev_info revs;
523
+ struct commit *commit;
524
+ struct pretty_print_context pp = {0};
525
+ struct strbuf commit_name = STRBUF_INIT;
526
+
527
+ res = prepare_revs(terms, &revs);
528
+ if (res)
529
+ return res;
530
+
531
+ fp = fopen(git_path_bisect_log(), "a");
532
+ if (!fp)
533
+ return error_errno(_("could not open '%s' for appending"),
534
+ git_path_bisect_log());
535
+
536
+ if (fprintf(fp, "# only skipped commits left to test\n") < 0)
537
+ return error_errno(_("failed to write to '%s'"), git_path_bisect_log());
538
+
539
+ while ((commit = get_revision(&revs)) != NULL) {
540
+ strbuf_reset(&commit_name);
541
+ format_commit_message(commit, "%s",
542
+ &commit_name, &pp);
543
+ fprintf(fp, "# possible first %s commit: [%s] %s\n",
544
+ terms->term_bad, oid_to_hex(&commit->object.oid),
545
+ commit_name.buf);
546
+ }
547
+
548
+ /*
549
+ * Reset the flags used by revision walks in case
550
+ * there is another revision walk after this one.
551
+ */
552
+ reset_revision_walk();
553
+
554
+ strbuf_release(&commit_name);
555
+ fclose(fp);
556
+ return 0;
557
+}
558
+
559
+static int bisect_successful(struct bisect_terms *terms)
560
+{
561
+ struct object_id oid;
562
+ struct commit *commit;
563
+ struct pretty_print_context pp = {0};
564
+ struct strbuf commit_name = STRBUF_INIT;
565
+ char *bad_ref = xstrfmt("refs/bisect/%s",terms->term_bad);
566
+ int res;
567
+
568
+ read_ref(bad_ref, &oid);
569
+ commit = lookup_commit_reference_by_name(bad_ref);
570
+ format_commit_message(commit, "%s", &commit_name, &pp);
571
+
572
+ res = append_to_file(git_path_bisect_log(), "# first %s commit: [%s] %s\n",
573
+ terms->term_bad, oid_to_hex(&commit->object.oid),
574
+ commit_name.buf);
575
+
576
+ strbuf_release(&commit_name);
577
+ free(bad_ref);
578
+ return res;
579
+}
580
+
581
+static enum bisect_error bisect_next(struct bisect_terms *terms, const char *prefix)
582
+{
583
+ enum bisect_error res;
584
+
585
+ if (bisect_autostart(terms))
586
+ return BISECT_FAILED;
587
+
588
+ if (bisect_next_check(terms, terms->term_good))
589
+ return BISECT_FAILED;
590
+
591
+ /* Perform all bisection computation */
592
+ res = bisect_next_all(the_repository, prefix);
593
+
594
+ if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {
595
+ res = bisect_successful(terms);
596
+ return res ? res : BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;
597
+ } else if (res == BISECT_ONLY_SKIPPED_LEFT) {
598
+ res = bisect_skipped_commits(terms);
599
+ return res ? res : BISECT_ONLY_SKIPPED_LEFT;
600
+ }
601
+ return res;
602
+}
603
+
604
+static enum bisect_error bisect_auto_next(struct bisect_terms *terms, const char *prefix)
605
+{
606
+ if (bisect_next_check(terms, NULL))
607
+ return BISECT_OK;
608
+
609
+ return bisect_next(terms, prefix);
610
+}
611
+
612
static int bisect_start(struct bisect_terms *terms, const char **argv, int argc)
613
{
614
int no_checkout = 0;
858
BISECT_TERMS,
859
BISECT_START,
860
BISECT_AUTOSTART,
861
+ BISECT_NEXT,
862
+ BISECT_AUTO_NEXT
863
} cmdmode = 0;
864
int res = 0, nolog = 0;
865
struct option options[] = {
883
N_("print out the bisect terms"), BISECT_TERMS),
884
OPT_CMDMODE(0, "bisect-start", &cmdmode,
885
N_("start the bisect session"), BISECT_START),
886
+ OPT_CMDMODE(0, "bisect-next", &cmdmode,
887
+ N_("find the next bisection commit"), BISECT_NEXT),
888
+ OPT_CMDMODE(0, "bisect-auto-next", &cmdmode,
889
+ N_("verify the next bisection state then checkout the next bisection commit"), BISECT_AUTO_NEXT),
890
OPT_CMDMODE(0, "bisect-autostart", &cmdmode,
891
N_("start the bisection if it has not yet been started"), BISECT_AUTOSTART),
892
OPT_BOOL(0, "no-log", &nolog,
948
set_terms(&terms, "bad", "good");
949
res = bisect_start(&terms, argv, argc);
950
break;
951
+ case BISECT_NEXT:
952
+ if (argc)
953
+ return error(_("--bisect-next requires 0 arguments"));
954
+ get_terms(&terms);
955
+ res = bisect_next(&terms, prefix);
956
+ break;
957
+ case BISECT_AUTO_NEXT:
958
+ if (argc)
959
+ return error(_("--bisect-auto-next requires 0 arguments"));
960
+ get_terms(&terms);
961
+ res = bisect_auto_next(&terms, prefix);
962
+ break;
963
case BISECT_AUTOSTART:
964
if (argc)
965
return error(_("--bisect-autostart does not accept arguments"));
975
* Handle early success
976
* From check_merge_bases > check_good_are_ancestors_of_bad > bisect_next_all
977
*/
802
- if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE)
978
+ if ((res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) || (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND))
979
res = BISECT_OK;
980
981
return -res;