]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'jk/bisect-peel-tag-fix'
authorJunio C Hamano <gitster@pobox.com>
Fri, 19 Mar 2021 22:25:37 +0000 (15:25 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 19 Mar 2021 22:25:37 +0000 (15:25 -0700)
"git bisect" reimplemented more in C during 2.30 timeframe did not
take an annotated tag as a good/bad endpoint well.  This regression
has been corrected.

* jk/bisect-peel-tag-fix:
  bisect: peel annotated tags to commits

1  2 
builtin/bisect--helper.c
t/t6030-bisect-porcelain.sh

diff --combined builtin/bisect--helper.c
index d69e13335d9c79bb2f8bd1b59bca42106e32b378,81569530e4f08fccbcf7eefb66a96edd1b2cec6f..1fdb7d9d1060b46c4e4e357765549b9a893a2130
@@@ -21,15 -21,16 +21,15 @@@ static GIT_PATH_FUNC(git_path_bisect_fi
  
  static const char * const git_bisect_helper_usage[] = {
        N_("git bisect--helper --bisect-reset [<commit>]"),
 -      N_("git bisect--helper --bisect-write [--no-log] <state> <revision> <good_term> <bad_term>"),
 -      N_("git bisect--helper --bisect-check-and-set-terms <command> <good_term> <bad_term>"),
        N_("git bisect--helper --bisect-next-check <good_term> <bad_term> [<term>]"),
        N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"),
        N_("git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]"
                                            " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"),
        N_("git bisect--helper --bisect-next"),
 -      N_("git bisect--helper --bisect-auto-next"),
        N_("git bisect--helper --bisect-state (bad|new) [<rev>]"),
        N_("git bisect--helper --bisect-state (good|old) [<rev>...]"),
 +      N_("git bisect--helper --bisect-replay <filename>"),
 +      N_("git bisect--helper --bisect-skip [(<rev>|<range>)...]"),
        NULL
  };
  
@@@ -874,12 -875,19 +874,19 @@@ static enum bisect_error bisect_state(s
         */
  
        for (; argc; argc--, argv++) {
+               struct commit *commit;
                if (get_oid(*argv, &oid)){
                        error(_("Bad rev input: %s"), *argv);
                        oid_array_clear(&revs);
                        return BISECT_FAILED;
                }
-               oid_array_append(&revs, &oid);
+               commit = lookup_commit_reference(the_repository, &oid);
+               if (!commit)
+                       die(_("Bad rev input (not a commit): %s"), *argv);
+               oid_array_append(&revs, &commit->object.oid);
        }
  
        if (strbuf_read_file(&buf, git_path_bisect_expected_rev(), 0) < the_hash_algo->hexsz ||
        return bisect_auto_next(terms, NULL);
  }
  
 +static enum bisect_error bisect_log(void)
 +{
 +      int fd, status;
 +      const char* filename = git_path_bisect_log();
 +
 +      if (is_empty_or_missing_file(filename))
 +              return error(_("We are not bisecting."));
 +
 +      fd = open(filename, O_RDONLY);
 +      if (fd < 0)
 +              return BISECT_FAILED;
 +
 +      status = copy_fd(fd, STDOUT_FILENO);
 +      close(fd);
 +      return status ? BISECT_FAILED : BISECT_OK;
 +}
 +
 +static int process_replay_line(struct bisect_terms *terms, struct strbuf *line)
 +{
 +      const char *p = line->buf + strspn(line->buf, " \t");
 +      char *word_end, *rev;
 +
 +      if ((!skip_prefix(p, "git bisect", &p) &&
 +      !skip_prefix(p, "git-bisect", &p)) || !isspace(*p))
 +              return 0;
 +      p += strspn(p, " \t");
 +
 +      word_end = (char *)p + strcspn(p, " \t");
 +      rev = word_end + strspn(word_end, " \t");
 +      *word_end = '\0'; /* NUL-terminate the word */
 +
 +      get_terms(terms);
 +      if (check_and_set_terms(terms, p))
 +              return -1;
 +
 +      if (!strcmp(p, "start")) {
 +              struct strvec argv = STRVEC_INIT;
 +              int res;
 +              sq_dequote_to_strvec(rev, &argv);
 +              res = bisect_start(terms, argv.v, argv.nr);
 +              strvec_clear(&argv);
 +              return res;
 +      }
 +
 +      if (one_of(p, terms->term_good,
 +         terms->term_bad, "skip", NULL))
 +              return bisect_write(p, rev, terms, 0);
 +
 +      if (!strcmp(p, "terms")) {
 +              struct strvec argv = STRVEC_INIT;
 +              int res;
 +              sq_dequote_to_strvec(rev, &argv);
 +              res = bisect_terms(terms, argv.nr == 1 ? argv.v[0] : NULL);
 +              strvec_clear(&argv);
 +              return res;
 +      }
 +      error(_("'%s'?? what are you talking about?"), p);
 +
 +      return -1;
 +}
 +
 +static enum bisect_error bisect_replay(struct bisect_terms *terms, const char *filename)
 +{
 +      FILE *fp = NULL;
 +      enum bisect_error res = BISECT_OK;
 +      struct strbuf line = STRBUF_INIT;
 +
 +      if (is_empty_or_missing_file(filename))
 +              return error(_("cannot read file '%s' for replaying"), filename);
 +
 +      if (bisect_reset(NULL))
 +              return BISECT_FAILED;
 +
 +      fp = fopen(filename, "r");
 +      if (!fp)
 +              return BISECT_FAILED;
 +
 +      while ((strbuf_getline(&line, fp) != EOF) && !res)
 +              res = process_replay_line(terms, &line);
 +
 +      strbuf_release(&line);
 +      fclose(fp);
 +
 +      if (res)
 +              return BISECT_FAILED;
 +
 +      return bisect_auto_next(terms, NULL);
 +}
 +
 +static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **argv, int argc)
 +{
 +      int i;
 +      enum bisect_error res;
 +      struct strvec argv_state = STRVEC_INIT;
 +
 +      strvec_push(&argv_state, "skip");
 +
 +      for (i = 0; i < argc; i++) {
 +              const char *dotdot = strstr(argv[i], "..");
 +
 +              if (dotdot) {
 +                      struct rev_info revs;
 +                      struct commit *commit;
 +
 +                      init_revisions(&revs, NULL);
 +                      setup_revisions(2, argv + i - 1, &revs, NULL);
 +
 +                      if (prepare_revision_walk(&revs))
 +                              die(_("revision walk setup failed\n"));
 +                      while ((commit = get_revision(&revs)) != NULL)
 +                              strvec_push(&argv_state,
 +                                              oid_to_hex(&commit->object.oid));
 +
 +                      reset_revision_walk();
 +              } else {
 +                      strvec_push(&argv_state, argv[i]);
 +              }
 +      }
 +      res = bisect_state(terms, argv_state.v, argv_state.nr);
 +
 +      strvec_clear(&argv_state);
 +      return res;
 +}
 +
  int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
  {
        enum {
                BISECT_RESET = 1,
 -              BISECT_WRITE,
 -              CHECK_AND_SET_TERMS,
                BISECT_NEXT_CHECK,
                BISECT_TERMS,
                BISECT_START,
                BISECT_AUTOSTART,
                BISECT_NEXT,
 -              BISECT_AUTO_NEXT,
 -              BISECT_STATE
 +              BISECT_STATE,
 +              BISECT_LOG,
 +              BISECT_REPLAY,
 +              BISECT_SKIP
        } cmdmode = 0;
        int res = 0, nolog = 0;
        struct option options[] = {
                OPT_CMDMODE(0, "bisect-reset", &cmdmode,
                         N_("reset the bisection state"), BISECT_RESET),
 -              OPT_CMDMODE(0, "bisect-write", &cmdmode,
 -                       N_("write out the bisection state in BISECT_LOG"), BISECT_WRITE),
 -              OPT_CMDMODE(0, "check-and-set-terms", &cmdmode,
 -                       N_("check and set terms in a bisection state"), CHECK_AND_SET_TERMS),
                OPT_CMDMODE(0, "bisect-next-check", &cmdmode,
                         N_("check whether bad or good terms exist"), BISECT_NEXT_CHECK),
                OPT_CMDMODE(0, "bisect-terms", &cmdmode,
                         N_("start the bisect session"), BISECT_START),
                OPT_CMDMODE(0, "bisect-next", &cmdmode,
                         N_("find the next bisection commit"), BISECT_NEXT),
 -              OPT_CMDMODE(0, "bisect-auto-next", &cmdmode,
 -                       N_("verify the next bisection state then checkout the next bisection commit"), BISECT_AUTO_NEXT),
                OPT_CMDMODE(0, "bisect-state", &cmdmode,
                         N_("mark the state of ref (or refs)"), BISECT_STATE),
 +              OPT_CMDMODE(0, "bisect-log", &cmdmode,
 +                       N_("list the bisection steps so far"), BISECT_LOG),
 +              OPT_CMDMODE(0, "bisect-replay", &cmdmode,
 +                       N_("replay the bisection process from the given file"), BISECT_REPLAY),
 +              OPT_CMDMODE(0, "bisect-skip", &cmdmode,
 +                       N_("skip some commits for checkout"), BISECT_SKIP),
                OPT_BOOL(0, "no-log", &nolog,
                         N_("no log for BISECT_WRITE")),
                OPT_END()
        case BISECT_RESET:
                if (argc > 1)
                        return error(_("--bisect-reset requires either no argument or a commit"));
 -              return !!bisect_reset(argc ? argv[0] : NULL);
 -      case BISECT_WRITE:
 -              if (argc != 4 && argc != 5)
 -                      return error(_("--bisect-write requires either 4 or 5 arguments"));
 -              set_terms(&terms, argv[3], argv[2]);
 -              res = bisect_write(argv[0], argv[1], &terms, nolog);
 -              break;
 -      case CHECK_AND_SET_TERMS:
 -              if (argc != 3)
 -                      return error(_("--check-and-set-terms requires 3 arguments"));
 -              set_terms(&terms, argv[2], argv[1]);
 -              res = check_and_set_terms(&terms, argv[0]);
 +              res = bisect_reset(argc ? argv[0] : NULL);
                break;
        case BISECT_NEXT_CHECK:
                if (argc != 2 && argc != 3)
                get_terms(&terms);
                res = bisect_next(&terms, prefix);
                break;
 -      case BISECT_AUTO_NEXT:
 -              if (argc)
 -                      return error(_("--bisect-auto-next requires 0 arguments"));
 -              get_terms(&terms);
 -              res = bisect_auto_next(&terms, prefix);
 -              break;
        case BISECT_STATE:
                set_terms(&terms, "bad", "good");
                get_terms(&terms);
                res = bisect_state(&terms, argv, argc);
                break;
 +      case BISECT_LOG:
 +              if (argc)
 +                      return error(_("--bisect-log requires 0 arguments"));
 +              res = bisect_log();
 +              break;
 +      case BISECT_REPLAY:
 +              if (argc != 1)
 +                      return error(_("no logfile given"));
 +              set_terms(&terms, "bad", "good");
 +              res = bisect_replay(&terms, argv[0]);
 +              break;
 +      case BISECT_SKIP:
 +              set_terms(&terms, "bad", "good");
 +              res = bisect_skip(&terms, argv, argc);
 +              break;
        default:
                BUG("unknown subcommand %d", cmdmode);
        }
index 0ba5a91b4e205c75c47a254a8acb9bdfdbf7af5d,9abcee05548d33544100ca966255db361a93ef56..32bb66e1eda44cfa1de67ab7c168bd856fc9d421
@@@ -6,9 -6,6 +6,9 @@@ test_description='Tests git bisect func
  
  exec </dev/null
  
 +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 +
  . ./test-lib.sh
  
  add_line_into_file()
@@@ -92,9 -89,9 +92,9 @@@ test_expect_success 'bisect start witho
        grep bar ".git/BISECT_NAMES"
  '
  
 -test_expect_success 'bisect reset: back in the master branch' '
 +test_expect_success 'bisect reset: back in the main branch' '
        git bisect reset &&
 -      echo "* master" > branch.expect &&
 +      echo "* main" > branch.expect &&
        git branch > branch.output &&
        cmp branch.expect branch.output
  '
@@@ -105,7 -102,7 +105,7 @@@ test_expect_success 'bisect reset: bac
        git bisect good $HASH1 &&
        git bisect bad $HASH3 &&
        git bisect reset &&
 -      echo "  master" > branch.expect &&
 +      echo "  main" > branch.expect &&
        echo "* other" >> branch.expect &&
        git branch > branch.output &&
        cmp branch.expect branch.output
@@@ -351,7 -348,7 +351,7 @@@ test_expect_success 'bisect skip many r
  
  test_expect_success 'bisect starting with a detached HEAD' '
        git bisect reset &&
 -      git checkout master^ &&
 +      git checkout main^ &&
        HEAD=$(git rev-parse --verify HEAD) &&
        git bisect start &&
        test $HEAD = $(cat .git/BISECT_START) &&
@@@ -578,9 -575,9 +578,9 @@@ test_expect_success 'skipping away fro
        test "$para3" = "$PARA_HASH3"
  '
  
 -test_expect_success 'erroring out when using bad path parameters' '
 +test_expect_success 'erroring out when using bad path arguments' '
        test_must_fail git bisect start $PARA_HASH7 $HASH1 -- foobar 2> error.txt &&
 -      test_i18ngrep "bad path parameters" error.txt
 +      test_i18ngrep "bad path arguments" error.txt
  '
  
  test_expect_success 'test bisection on bare repo - --no-checkout specified' '
@@@ -719,7 -716,7 +719,7 @@@ test_expect_success 'bisect: --no-check
  test_expect_success 'bisect: demonstrate identification of damage boundary' "
        git bisect reset &&
        git checkout broken &&
 -      git bisect start broken master --no-checkout &&
 +      git bisect start broken main --no-checkout &&
        test_must_fail git bisect run \"\$SHELL_PATH\" -c '
                GOOD=\$(git for-each-ref \"--format=%(objectname)\" refs/bisect/good-*) &&
                git rev-list --objects BISECT_HEAD --not \$GOOD >tmp.\$\$ &&
@@@ -829,7 -826,7 +829,7 @@@ test_expect_success 'bisect terms need
        test_must_fail git bisect terms 1 2 &&
        test_must_fail git bisect terms 2>actual &&
        echo "error: no terms defined" >expected &&
 -      test_i18ncmp expected actual
 +      test_cmp expected actual
  '
  
  test_expect_success 'bisect terms shows good/bad after start' '
@@@ -903,7 -900,7 +903,7 @@@ test_expect_success 'bisect start --ter
        Your current terms are two for the old state
        and one for the new state.
        EOF
 -      test_i18ncmp expected actual &&
 +      test_cmp expected actual &&
        git bisect terms --term-bad >actual &&
        echo one >expected &&
        test_cmp expected actual &&
@@@ -939,4 -936,16 +939,16 @@@ test_expect_success 'git bisect reset c
        test_path_is_missing ".git/BISECT_START"
  '
  
+ test_expect_success 'bisect handles annotated tags' '
+       test_commit commit-one &&
+       git tag -m foo tag-one &&
+       test_commit commit-two &&
+       git tag -m foo tag-two &&
+       git bisect start &&
+       git bisect good tag-one &&
+       git bisect bad tag-two >output &&
+       bad=$(git rev-parse --verify tag-two^{commit}) &&
+       grep "$bad is the first bad commit" output
+ '
  test_done