88#include "run-command.h"
99#include "prompt.h"
1010#include "quote.h"
11+ #include "revision.h"
1112
1213static GIT_PATH_FUNC (git_path_bisect_terms , "BISECT_TERMS ")
1314static GIT_PATH_FUNC (git_path_bisect_expected_rev , "BISECT_EXPECTED_REV ")
@@ -29,10 +30,17 @@ static const char * const git_bisect_helper_usage[] = {
2930 N_ ("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]" ),
3031 N_ ("git bisect--helper --bisect-start [--term-{old,good}=<term> --term-{new,bad}=<term>]"
3132 " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]" ),
33+ N_ ("git bisect--helper --bisect-next" ),
34+ N_ ("git bisect--helper --bisect-auto-next" ),
3235 N_ ("git bisect--helper --bisect-autostart" ),
3336 NULL
3437};
3538
39+ struct add_bisect_ref_data {
40+ struct rev_info * revs ;
41+ unsigned int object_flags ;
42+ } ;
43+
3644struct bisect_terms {
3745 char * term_good ;
3846 char * term_bad ;
@@ -56,6 +64,8 @@ static void set_terms(struct bisect_terms *terms, const char *bad,
5664static const char vocab_bad [] = "bad|new" ;
5765static const char vocab_good [] = "good|old" ;
5866
67+ static int bisect_autostart (struct bisect_terms * terms );
68+
5969/*
6070 * Check whether the string `term` belongs to the set of strings
6171 * included in the variable arguments.
@@ -80,7 +90,7 @@ static int write_in_file(const char *path, const char *mode, const char *format,
8090 FILE * fp = NULL ;
8191 int res = 0 ;
8292
83- if (strcmp (mode , "w" ))
93+ if (strcmp (mode , "w" ) && strcmp ( mode , "a" ) )
8494 BUG ("write-in-file does not support '%s' mode" , mode );
8595 fp = fopen (path , mode );
8696 if (!fp )
@@ -109,6 +119,18 @@ static int write_to_file(const char *path, const char *format, ...)
109119 return res ;
110120}
111121
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+
112134static int check_term_format (const char * term , const char * orig_term )
113135{
114136 int res ;
@@ -451,6 +473,142 @@ static int bisect_append_log_quoted(const char **argv)
451473 return res ;
452474}
453475
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+
454612static int bisect_start (struct bisect_terms * terms , const char * * argv , int argc )
455613{
456614 int no_checkout = 0 ;
@@ -700,6 +858,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
700858 BISECT_TERMS ,
701859 BISECT_START ,
702860 BISECT_AUTOSTART ,
861+ BISECT_NEXT ,
862+ BISECT_AUTO_NEXT
703863 } cmdmode = 0 ;
704864 int res = 0 , nolog = 0 ;
705865 struct option options [] = {
@@ -723,6 +883,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
723883 N_ ("print out the bisect terms" ), BISECT_TERMS ),
724884 OPT_CMDMODE (0 , "bisect-start" , & cmdmode ,
725885 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 ),
726890 OPT_CMDMODE (0 , "bisect-autostart" , & cmdmode ,
727891 N_ ("start the bisection if it has not yet been started" ), BISECT_AUTOSTART ),
728892 OPT_BOOL (0 , "no-log" , & nolog ,
@@ -784,6 +948,18 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
784948 set_terms (& terms , "bad" , "good" );
785949 res = bisect_start (& terms , argv , argc );
786950 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 ;
787963 case BISECT_AUTOSTART :
788964 if (argc )
789965 return error (_ ("--bisect-autostart does not accept arguments" ));
@@ -799,7 +975,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
799975 * Handle early success
800976 * From check_merge_bases > check_good_are_ancestors_of_bad > bisect_next_all
801977 */
802- if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE )
978+ if (( res == BISECT_INTERNAL_SUCCESS_MERGE_BASE ) || ( res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND ) )
803979 res = BISECT_OK ;
804980
805981 return - res ;
0 commit comments