8
8
#include "run-command.h"
9
9
#include "prompt.h"
10
10
#include "quote.h"
11
+ #include "revision.h"
11
12
12
13
static GIT_PATH_FUNC (git_path_bisect_terms , "BISECT_TERMS ")
13
14
static GIT_PATH_FUNC (git_path_bisect_expected_rev , "BISECT_EXPECTED_REV ")
@@ -29,10 +30,17 @@ static const char * const git_bisect_helper_usage[] = {
29
30
N_ ("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]" ),
30
31
N_ ("git bisect--helper --bisect-start [--term-{old,good}=<term> --term-{new,bad}=<term>]"
31
32
" [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]" ),
33
+ N_ ("git bisect--helper --bisect-next" ),
34
+ N_ ("git bisect--helper --bisect-auto-next" ),
32
35
N_ ("git bisect--helper --bisect-autostart" ),
33
36
NULL
34
37
};
35
38
39
+ struct add_bisect_ref_data {
40
+ struct rev_info * revs ;
41
+ unsigned int object_flags ;
42
+ } ;
43
+
36
44
struct bisect_terms {
37
45
char * term_good ;
38
46
char * term_bad ;
@@ -56,6 +64,8 @@ static void set_terms(struct bisect_terms *terms, const char *bad,
56
64
static const char vocab_bad [] = "bad|new" ;
57
65
static const char vocab_good [] = "good|old" ;
58
66
67
+ static int bisect_autostart (struct bisect_terms * terms );
68
+
59
69
/*
60
70
* Check whether the string `term` belongs to the set of strings
61
71
* included in the variable arguments.
@@ -80,7 +90,7 @@ static int write_in_file(const char *path, const char *mode, const char *format,
80
90
FILE * fp = NULL ;
81
91
int res = 0 ;
82
92
83
- if (strcmp (mode , "w" ))
93
+ if (strcmp (mode , "w" ) && strcmp ( mode , "a" ) )
84
94
BUG ("write-in-file does not support '%s' mode" , mode );
85
95
fp = fopen (path , mode );
86
96
if (!fp )
@@ -109,6 +119,18 @@ static int write_to_file(const char *path, const char *format, ...)
109
119
return res ;
110
120
}
111
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
+
112
134
static int check_term_format (const char * term , const char * orig_term )
113
135
{
114
136
int res ;
@@ -451,6 +473,142 @@ static int bisect_append_log_quoted(const char **argv)
451
473
return res ;
452
474
}
453
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
+
454
612
static int bisect_start (struct bisect_terms * terms , const char * * argv , int argc )
455
613
{
456
614
int no_checkout = 0 ;
@@ -700,6 +858,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
700
858
BISECT_TERMS ,
701
859
BISECT_START ,
702
860
BISECT_AUTOSTART ,
861
+ BISECT_NEXT ,
862
+ BISECT_AUTO_NEXT
703
863
} cmdmode = 0 ;
704
864
int res = 0 , nolog = 0 ;
705
865
struct option options [] = {
@@ -723,6 +883,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
723
883
N_ ("print out the bisect terms" ), BISECT_TERMS ),
724
884
OPT_CMDMODE (0 , "bisect-start" , & cmdmode ,
725
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 ),
726
890
OPT_CMDMODE (0 , "bisect-autostart" , & cmdmode ,
727
891
N_ ("start the bisection if it has not yet been started" ), BISECT_AUTOSTART ),
728
892
OPT_BOOL (0 , "no-log" , & nolog ,
@@ -784,6 +948,18 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
784
948
set_terms (& terms , "bad" , "good" );
785
949
res = bisect_start (& terms , argv , argc );
786
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 ;
787
963
case BISECT_AUTOSTART :
788
964
if (argc )
789
965
return error (_ ("--bisect-autostart does not accept arguments" ));
@@ -799,7 +975,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
799
975
* Handle early success
800
976
* From check_merge_bases > check_good_are_ancestors_of_bad > bisect_next_all
801
977
*/
802
- if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE )
978
+ if (( res == BISECT_INTERNAL_SUCCESS_MERGE_BASE ) || ( res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND ) )
803
979
res = BISECT_OK ;
804
980
805
981
return - res ;
0 commit comments