77#include "argv-array.h"
88#include "run-command.h"
99#include "prompt.h"
10+ #include "quote.h"
1011
1112static GIT_PATH_FUNC (git_path_bisect_terms , "BISECT_TERMS ")
1213static GIT_PATH_FUNC (git_path_bisect_expected_rev , "BISECT_EXPECTED_REV ")
1314static GIT_PATH_FUNC (git_path_bisect_ancestors_ok , "BISECT_ANCESTORS_OK ")
1415static GIT_PATH_FUNC (git_path_bisect_start , "BISECT_START ")
1516static GIT_PATH_FUNC (git_path_bisect_head , "BISECT_HEAD ")
1617static GIT_PATH_FUNC (git_path_bisect_log , "BISECT_LOG ")
18+ static GIT_PATH_FUNC (git_path_head_name , "head - name ")
19+ static GIT_PATH_FUNC (git_path_bisect_names , "BISECT_NAMES ")
1720
1821static const char * const git_bisect_helper_usage [] = {
1922 N_ ("git bisect--helper --next-all [--no-checkout]" ),
@@ -24,6 +27,8 @@ static const char * const git_bisect_helper_usage[] = {
2427 N_ ("git bisect--helper --bisect-check-and-set-terms <command> <good_term> <bad_term>" ),
2528 N_ ("git bisect--helper --bisect-next-check <good_term> <bad_term> [<term>]" ),
2629 N_ ("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]" ),
30+ N_ ("git bisect--helper --bisect-start [--term-{old,good}=<term> --term-{new,bad}=<term>]"
31+ "[--no-checkout] [<bad> [<good>...]] [--] [<paths>...]" ),
2732 NULL
2833};
2934
@@ -389,6 +394,219 @@ static int bisect_terms(struct bisect_terms *terms, const char *option)
389394 return 0 ;
390395}
391396
397+ static int bisect_append_log_quoted (const char * * argv )
398+ {
399+ int retval = 0 ;
400+ FILE * fp = fopen (git_path_bisect_log (), "a" );
401+ struct strbuf orig_args = STRBUF_INIT ;
402+
403+ if (!fp )
404+ return -1 ;
405+
406+ if (fprintf (fp , "git bisect start" ) < 1 ) {
407+ retval = -1 ;
408+ goto finish ;
409+ }
410+
411+ sq_quote_argv (& orig_args , argv );
412+ if (fprintf (fp , "%s\n" , orig_args .buf ) < 1 )
413+ retval = -1 ;
414+
415+ finish :
416+ fclose (fp );
417+ strbuf_release (& orig_args );
418+ return retval ;
419+ }
420+
421+ static int bisect_start (struct bisect_terms * terms , int no_checkout ,
422+ const char * * argv , int argc )
423+ {
424+ int i , has_double_dash = 0 , must_write_terms = 0 , bad_seen = 0 ;
425+ int flags , pathspec_pos , retval = 0 ;
426+ struct string_list revs = STRING_LIST_INIT_DUP ;
427+ struct string_list states = STRING_LIST_INIT_DUP ;
428+ struct strbuf start_head = STRBUF_INIT ;
429+ struct strbuf bisect_names = STRBUF_INIT ;
430+ struct object_id head_oid ;
431+ struct object_id oid ;
432+ const char * head ;
433+
434+ if (is_bare_repository ())
435+ no_checkout = 1 ;
436+
437+ /*
438+ * Check for one bad and then some good revisions
439+ */
440+ for (i = 0 ; i < argc ; i ++ ) {
441+ if (!strcmp (argv [i ], "--" )) {
442+ has_double_dash = 1 ;
443+ break ;
444+ }
445+ }
446+
447+ for (i = 0 ; i < argc ; i ++ ) {
448+ const char * arg = argv [i ];
449+ if (!strcmp (argv [i ], "--" )) {
450+ break ;
451+ } else if (!strcmp (arg , "--no-checkout" )) {
452+ no_checkout = 1 ;
453+ } else if (!strcmp (arg , "--term-good" ) ||
454+ !strcmp (arg , "--term-old" )) {
455+ must_write_terms = 1 ;
456+ free ((void * ) terms -> term_good );
457+ terms -> term_good = xstrdup (argv [++ i ]);
458+ } else if (skip_prefix (arg , "--term-good=" , & arg ) ||
459+ skip_prefix (arg , "--term-old=" , & arg )) {
460+ must_write_terms = 1 ;
461+ free ((void * ) terms -> term_good );
462+ terms -> term_good = xstrdup (arg );
463+ } else if (!strcmp (arg , "--term-bad" ) ||
464+ !strcmp (arg , "--term-new" )) {
465+ must_write_terms = 1 ;
466+ free ((void * ) terms -> term_bad );
467+ terms -> term_bad = xstrdup (argv [++ i ]);
468+ } else if (skip_prefix (arg , "--term-bad=" , & arg ) ||
469+ skip_prefix (arg , "--term-new=" , & arg )) {
470+ must_write_terms = 1 ;
471+ free ((void * ) terms -> term_bad );
472+ terms -> term_bad = xstrdup (arg );
473+ } else if (starts_with (arg , "--" ) &&
474+ !one_of (arg , "--term-good" , "--term-bad" , NULL )) {
475+ return error (_ ("unrecognized option: '%s'" ), arg );
476+ } else {
477+ char * commit_id = xstrfmt ("%s^{commit}" , arg );
478+ if (get_oid (commit_id , & oid ) && has_double_dash )
479+ die (_ ("'%s' does not appear to be a valid "
480+ "revision" ), arg );
481+
482+ string_list_append (& revs , oid_to_hex (& oid ));
483+ free (commit_id );
484+ }
485+ }
486+ pathspec_pos = i ;
487+
488+ /*
489+ * The user ran "git bisect start <sha1> <sha1>", hence did not
490+ * explicitly specify the terms, but we are already starting to
491+ * set references named with the default terms, and won't be able
492+ * to change afterwards.
493+ */
494+ if (revs .nr )
495+ must_write_terms = 1 ;
496+ for (i = 0 ; i < revs .nr ; i ++ ) {
497+ if (bad_seen ) {
498+ string_list_append (& states , terms -> term_good );
499+ } else {
500+ bad_seen = 1 ;
501+ string_list_append (& states , terms -> term_bad );
502+ }
503+ }
504+
505+ /*
506+ * Verify HEAD
507+ */
508+ head = resolve_ref_unsafe ("HEAD" , 0 , & head_oid , & flags );
509+ if (!head )
510+ if (get_oid ("HEAD" , & head_oid ))
511+ return error (_ ("bad HEAD - I need a HEAD" ));
512+
513+ /*
514+ * Check if we are bisecting
515+ */
516+ if (!is_empty_or_missing_file (git_path_bisect_start ())) {
517+ /* Reset to the rev from where we started */
518+ strbuf_read_file (& start_head , git_path_bisect_start (), 0 );
519+ strbuf_trim (& start_head );
520+ if (!no_checkout ) {
521+ struct argv_array argv = ARGV_ARRAY_INIT ;
522+
523+ argv_array_pushl (& argv , "checkout" , start_head .buf ,
524+ "--" , NULL );
525+ if (run_command_v_opt (argv .argv , RUN_GIT_CMD )) {
526+ retval = error (_ ("checking out '%s' failed."
527+ " Try 'git bisect start "
528+ "<valid-branch>'." ),
529+ start_head .buf );
530+ goto finish ;
531+ }
532+ }
533+ } else {
534+ /* Get the rev from where we start. */
535+ if (!get_oid (head , & head_oid ) &&
536+ !starts_with (head , "refs/heads/" )) {
537+ strbuf_reset (& start_head );
538+ strbuf_addstr (& start_head , oid_to_hex (& head_oid ));
539+ } else if (!get_oid (head , & head_oid ) &&
540+ skip_prefix (head , "refs/heads/" , & head )) {
541+ /*
542+ * This error message should only be triggered by
543+ * cogito usage, and cogito users should understand
544+ * it relates to cg-seek.
545+ */
546+ if (!is_empty_or_missing_file (git_path_head_name ()))
547+ return error (_ ("won't bisect on cg-seek'ed tree" ));
548+ strbuf_addstr (& start_head , head );
549+ } else {
550+ return error (_ ("bad HEAD - strange symbolic ref" ));
551+ }
552+ }
553+
554+ /*
555+ * Get rid of any old bisect state.
556+ */
557+ if (bisect_clean_state ())
558+ return -1 ;
559+
560+ /*
561+ * In case of mistaken revs or checkout error, or signals received,
562+ * "bisect_auto_next" below may exit or misbehave.
563+ * We have to trap this to be able to clean up using
564+ * "bisect_clean_state".
565+ */
566+
567+ /*
568+ * Write new start state
569+ */
570+ write_file (git_path_bisect_start (), "%s\n" , start_head .buf );
571+
572+ if (no_checkout ) {
573+ get_oid (start_head .buf , & oid );
574+ if (update_ref (NULL , "BISECT_HEAD" , & oid , NULL , 0 ,
575+ UPDATE_REFS_MSG_ON_ERR )) {
576+ retval = -1 ;
577+ goto finish ;
578+ }
579+ }
580+
581+ if (pathspec_pos < argc - 1 )
582+ sq_quote_argv (& bisect_names , argv + pathspec_pos );
583+ write_file (git_path_bisect_names (), "%s\n" , bisect_names .buf );
584+
585+ for (i = 0 ; i < states .nr ; i ++ )
586+ if (bisect_write (states .items [i ].string ,
587+ revs .items [i ].string , terms , 1 )) {
588+ retval = -1 ;
589+ goto finish ;
590+ }
591+
592+ if (must_write_terms && write_terms (terms -> term_bad ,
593+ terms -> term_good )) {
594+ retval = -1 ;
595+ goto finish ;
596+ }
597+
598+ retval = bisect_append_log_quoted (argv );
599+ if (retval )
600+ retval = -1 ;
601+
602+ finish :
603+ string_list_clear (& revs , 0 );
604+ string_list_clear (& states , 0 );
605+ strbuf_release (& start_head );
606+ strbuf_release (& bisect_names );
607+ return retval ;
608+ }
609+
392610int cmd_bisect__helper (int argc , const char * * argv , const char * prefix )
393611{
394612 enum {
@@ -400,7 +618,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
400618 BISECT_WRITE ,
401619 CHECK_AND_SET_TERMS ,
402620 BISECT_NEXT_CHECK ,
403- BISECT_TERMS
621+ BISECT_TERMS ,
622+ BISECT_START
404623 } cmdmode = 0 ;
405624 int no_checkout = 0 , res = 0 , nolog = 0 ;
406625 struct option options [] = {
@@ -422,6 +641,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
422641 N_ ("check whether bad or good terms exist" ), BISECT_NEXT_CHECK ),
423642 OPT_CMDMODE (0 , "bisect-terms" , & cmdmode ,
424643 N_ ("print out the bisect terms" ), BISECT_TERMS ),
644+ OPT_CMDMODE (0 , "bisect-start" , & cmdmode ,
645+ N_ ("start the bisect session" ), BISECT_START ),
425646 OPT_BOOL (0 , "no-checkout" , & no_checkout ,
426647 N_ ("update BISECT_HEAD instead of checking out the current commit" )),
427648 OPT_BOOL (0 , "no-log" , & nolog ,
@@ -431,7 +652,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
431652 struct bisect_terms terms = { .term_good = NULL , .term_bad = NULL };
432653
433654 argc = parse_options (argc , argv , prefix , options ,
434- git_bisect_helper_usage , PARSE_OPT_KEEP_UNKNOWN );
655+ git_bisect_helper_usage ,
656+ PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_UNKNOWN );
435657
436658 if (!cmdmode )
437659 usage_with_options (git_bisect_helper_usage , options );
@@ -477,6 +699,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
477699 return error (_ ("--bisect-terms requires 0 or 1 argument" ));
478700 res = bisect_terms (& terms , argc == 1 ? argv [0 ] : NULL );
479701 break ;
702+ case BISECT_START :
703+ set_terms (& terms , "bad" , "good" );
704+ res = bisect_start (& terms , no_checkout , argv , argc );
705+ break ;
480706 default :
481707 return error ("BUG: unknown subcommand '%d'" , cmdmode );
482708 }
0 commit comments