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,9 +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-{new,bad}=<term> --term-{old,good}=<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" ),
35
+ N_ ("git bisect--helper --bisect-autostart" ),
32
36
NULL
33
37
};
34
38
39
+ struct add_bisect_ref_data {
40
+ struct rev_info * revs ;
41
+ unsigned int object_flags ;
42
+ } ;
43
+
35
44
struct bisect_terms {
36
45
char * term_good ;
37
46
char * term_bad ;
@@ -55,6 +64,8 @@ static void set_terms(struct bisect_terms *terms, const char *bad,
55
64
static const char vocab_bad [] = "bad|new" ;
56
65
static const char vocab_good [] = "good|old" ;
57
66
67
+ static int bisect_autostart (struct bisect_terms * terms );
68
+
58
69
/*
59
70
* Check whether the string `term` belongs to the set of strings
60
71
* included in the variable arguments.
@@ -74,6 +85,52 @@ static int one_of(const char *term, ...)
74
85
return res ;
75
86
}
76
87
88
+ static int write_in_file (const char * path , const char * mode , const char * format , va_list args )
89
+ {
90
+ FILE * fp = NULL ;
91
+ int res = 0 ;
92
+
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 )
97
+ return error_errno (_ ("cannot open file '%s' in mode '%s'" ), path , mode );
98
+ res = vfprintf (fp , format , args );
99
+
100
+ if (res < 0 ) {
101
+ int saved_errno = errno ;
102
+ fclose (fp );
103
+ errno = saved_errno ;
104
+ return error_errno (_ ("could not write to file '%s'" ), path );
105
+ }
106
+
107
+ return fclose (fp );
108
+ }
109
+
110
+ static int write_to_file (const char * path , const char * format , ...)
111
+ {
112
+ int res ;
113
+ va_list args ;
114
+
115
+ va_start (args , format );
116
+ res = write_in_file (path , "w" , format , args );
117
+ va_end (args );
118
+
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
+
77
134
static int check_term_format (const char * term , const char * orig_term )
78
135
{
79
136
int res ;
@@ -104,7 +161,6 @@ static int check_term_format(const char *term, const char *orig_term)
104
161
105
162
static int write_terms (const char * bad , const char * good )
106
163
{
107
- FILE * fp = NULL ;
108
164
int res ;
109
165
110
166
if (!strcmp (bad , good ))
@@ -113,13 +169,9 @@ static int write_terms(const char *bad, const char *good)
113
169
if (check_term_format (bad , "bad" ) || check_term_format (good , "good" ))
114
170
return -1 ;
115
171
116
- fp = fopen (git_path_bisect_terms (), "w" );
117
- if (!fp )
118
- return error_errno (_ ("could not open the file BISECT_TERMS" ));
172
+ res = write_to_file (git_path_bisect_terms (), "%s\n%s\n" , bad , good );
119
173
120
- res = fprintf (fp , "%s\n%s\n" , bad , good );
121
- res |= fclose (fp );
122
- return (res < 0 ) ? -1 : 0 ;
174
+ return res ;
123
175
}
124
176
125
177
static int is_expected_rev (const char * expected_hex )
@@ -421,6 +473,142 @@ static int bisect_append_log_quoted(const char **argv)
421
473
return res ;
422
474
}
423
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
+
424
612
static int bisect_start (struct bisect_terms * terms , const char * * argv , int argc )
425
613
{
426
614
int no_checkout = 0 ;
@@ -623,6 +811,38 @@ static int bisect_start(struct bisect_terms *terms, const char **argv, int argc)
623
811
return res ;
624
812
}
625
813
814
+ static inline int file_is_not_empty (const char * path )
815
+ {
816
+ return !is_empty_or_missing_file (path );
817
+ }
818
+
819
+ static int bisect_autostart (struct bisect_terms * terms )
820
+ {
821
+ int res ;
822
+ const char * yesno ;
823
+
824
+ if (file_is_not_empty (git_path_bisect_start ()))
825
+ return 0 ;
826
+
827
+ fprintf_ln (stderr , _ ("You need to start by \"git bisect "
828
+ "start\"\n" ));
829
+
830
+ if (!isatty (STDIN_FILENO ))
831
+ return -1 ;
832
+
833
+ /*
834
+ * TRANSLATORS: Make sure to include [Y] and [n] in your
835
+ * translation. The program will only accept English input
836
+ * at this point.
837
+ */
838
+ yesno = git_prompt (_ ("Do you want me to do it for you "
839
+ "[Y/n]? " ), PROMPT_ECHO );
840
+ res = tolower (* yesno ) == 'n' ?
841
+ -1 : bisect_start (terms , empty_strvec , 0 );
842
+
843
+ return res ;
844
+ }
845
+
626
846
int cmd_bisect__helper (int argc , const char * * argv , const char * prefix )
627
847
{
628
848
enum {
@@ -635,7 +855,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
635
855
CHECK_AND_SET_TERMS ,
636
856
BISECT_NEXT_CHECK ,
637
857
BISECT_TERMS ,
638
- BISECT_START
858
+ BISECT_START ,
859
+ BISECT_AUTOSTART ,
860
+ BISECT_NEXT ,
861
+ BISECT_AUTO_NEXT
639
862
} cmdmode = 0 ;
640
863
int res = 0 , nolog = 0 ;
641
864
struct option options [] = {
@@ -659,6 +882,12 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
659
882
N_ ("print out the bisect terms" ), BISECT_TERMS ),
660
883
OPT_CMDMODE (0 , "bisect-start" , & cmdmode ,
661
884
N_ ("start the bisect session" ), BISECT_START ),
885
+ OPT_CMDMODE (0 , "bisect-next" , & cmdmode ,
886
+ N_ ("find the next bisection commit" ), BISECT_NEXT ),
887
+ OPT_CMDMODE (0 , "bisect-auto-next" , & cmdmode ,
888
+ N_ ("verify the next bisection state then checkout the next bisection commit" ), BISECT_AUTO_NEXT ),
889
+ OPT_CMDMODE (0 , "bisect-autostart" , & cmdmode ,
890
+ N_ ("start the bisection if it has not yet been started" ), BISECT_AUTOSTART ),
662
891
OPT_BOOL (0 , "no-log" , & nolog ,
663
892
N_ ("no log for BISECT_WRITE" )),
664
893
OPT_END ()
@@ -718,17 +947,35 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
718
947
set_terms (& terms , "bad" , "good" );
719
948
res = bisect_start (& terms , argv , argc );
720
949
break ;
950
+ case BISECT_NEXT :
951
+ if (argc )
952
+ return error (_ ("--bisect-next requires 0 arguments" ));
953
+ get_terms (& terms );
954
+ res = bisect_next (& terms , prefix );
955
+ break ;
956
+ case BISECT_AUTO_NEXT :
957
+ if (argc )
958
+ return error (_ ("--bisect-auto-next requires 0 arguments" ));
959
+ get_terms (& terms );
960
+ res = bisect_auto_next (& terms , prefix );
961
+ break ;
962
+ case BISECT_AUTOSTART :
963
+ if (argc )
964
+ return error (_ ("--bisect-autostart does not accept arguments" ));
965
+ set_terms (& terms , "bad" , "good" );
966
+ res = bisect_autostart (& terms );
967
+ break ;
721
968
default :
722
- return error ( "BUG: unknown subcommand '%d' " , cmdmode );
969
+ BUG ( " unknown subcommand %d " , cmdmode );
723
970
}
724
971
free_terms (& terms );
725
972
726
973
/*
727
974
* Handle early success
728
975
* From check_merge_bases > check_good_are_ancestors_of_bad > bisect_next_all
729
976
*/
730
- if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE )
977
+ if (( res == BISECT_INTERNAL_SUCCESS_MERGE_BASE ) || ( res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND ) )
731
978
res = BISECT_OK ;
732
979
733
- return abs ( res ) ;
980
+ return - res ;
734
981
}
0 commit comments