Merge branch 'maint-1.6.0' into maint-1.6.1
authorJunio C Hamano <gitster@pobox.com>
Fri, 13 Mar 2009 06:36:57 +0000 (23:36 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 13 Mar 2009 06:36:57 +0000 (23:36 -0700)
* maint-1.6.0:
bisect: fix another instance of eval'ed string
bisect: fix quoting TRIED revs when "bad" commit is also "skip"ped
Support "\" in non-wildcard exclusion entries

Conflicts:
git-bisect.sh

1  2 
dir.c
git-bisect.sh
t/t3001-ls-files-others-exclude.sh
t/t6030-bisect-porcelain.sh
diff --combined dir.c
index 0131983dfbc143ce5dae77e067663bb2e7d5f126,04a4b9861e03739df5c048448cd5c0be28b400db..2c18e417d16801cbca38ab1e7b8b5f8615f1e45d
--- 1/dir.c
--- 2/dir.c
+++ b/dir.c
@@@ -52,6 -52,11 +52,6 @@@ int common_prefix(const char **pathspec
        return prefix;
  }
  
 -static inline int special_char(unsigned char c1)
 -{
 -      return !c1 || c1 == '*' || c1 == '[' || c1 == '?' || c1 == '\\';
 -}
 -
  /*
   * Does 'match' matches the given name?
   * A match is found if
@@@ -75,7 -80,7 +75,7 @@@ static int match_one(const char *match
        for (;;) {
                unsigned char c1 = *match;
                unsigned char c2 = *name;
 -              if (special_char(c1))
 +              if (isspecial(c1))
                        break;
                if (c1 != c2)
                        return 0;
@@@ -134,7 -139,7 +134,7 @@@ int match_pathspec(const char **pathspe
  
  static int no_wildcard(const char *string)
  {
-       return string[strcspn(string, "*?[{")] == '\0';
+       return string[strcspn(string, "*?[{\\")] == '\0';
  }
  
  void add_exclude(const char *string, const char *base,
@@@ -382,7 -387,7 +382,7 @@@ static struct dir_entry *dir_entry_new(
        return ent;
  }
  
 -struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
 +static struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
  {
        if (cache_name_exists(pathname, len, ignore_case))
                return NULL;
        return dir->entries[dir->nr++] = dir_entry_new(pathname, len);
  }
  
 -struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len)
 +static struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len)
  {
        if (cache_name_pos(pathname, len) >= 0)
                return NULL;
@@@ -675,12 -680,17 +675,12 @@@ static int cmp_name(const void *p1, con
   */
  static int simple_length(const char *match)
  {
 -      const char special[256] = {
 -              [0] = 1, ['?'] = 1,
 -              ['\\'] = 1, ['*'] = 1,
 -              ['['] = 1
 -      };
        int len = -1;
  
        for (;;) {
                unsigned char c = *match++;
                len++;
 -              if (special[c])
 +              if (isspecial(c))
                        return len;
        }
  }
@@@ -717,12 -727,8 +717,12 @@@ static void free_simplify(struct path_s
  
  int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec)
  {
 -      struct path_simplify *simplify = create_simplify(pathspec);
 +      struct path_simplify *simplify;
 +
 +      if (has_symlink_leading_path(strlen(path), path))
 +              return dir->nr;
  
 +      simplify = create_simplify(pathspec);
        read_directory_recursive(dir, path, base, baselen, 0, simplify);
        free_simplify(simplify);
        qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
diff --combined git-bisect.sh
index 85db4ba40022e3a9e5790879d6d21fa59475b316,b95dbbbbb243069f5e673869b3867cfa4151aff7..10ad340920efb7177df53cb3a209d1a3edd5a039
@@@ -9,7 -9,7 +9,7 @@@ git bisect bad [<rev>
          mark <rev> a known-bad revision.
  git bisect good [<rev>...]
          mark <rev>... known-good revisions.
 -git bisect skip [<rev>...]
 +git bisect skip [(<rev>|<range>)...]
          mark <rev>... untestable revisions.
  git bisect next
          find next bisection to test and check it out.
@@@ -172,40 -172,6 +172,40 @@@ bisect_write() 
        test -n "$nolog" || echo "git bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
  }
  
 +is_expected_rev() {
 +      test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
 +      test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
 +}
 +
 +mark_expected_rev() {
 +      echo "$1" > "$GIT_DIR/BISECT_EXPECTED_REV"
 +}
 +
 +check_expected_revs() {
 +      for _rev in "$@"; do
 +              if ! is_expected_rev "$_rev"; then
 +                      rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
 +                      rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
 +                      return
 +              fi
 +      done
 +}
 +
 +bisect_skip() {
 +        all=''
 +      for arg in "$@"
 +      do
 +          case "$arg" in
 +            *..*)
 +                revs=$(git rev-list "$arg") || die "Bad rev input: $arg" ;;
 +            *)
 +                revs=$(sq "$arg") ;;
 +          esac
 +            all="$all $revs"
 +        done
 +        eval bisect_state 'skip' $all
 +}
 +
  bisect_state() {
        bisect_autostart
        state=$1
        1,bad|1,good|1,skip)
                rev=$(git rev-parse --verify HEAD) ||
                        die "Bad rev input: HEAD"
 -              bisect_write "$state" "$rev" ;;
 +              bisect_write "$state" "$rev"
 +              check_expected_revs "$rev" ;;
        2,bad|*,good|*,skip)
                shift
                eval=''
                                die "Bad rev input: $rev"
                        eval="$eval bisect_write '$state' '$sha'; "
                done
 -              eval "$eval" ;;
 +              eval "$eval"
 +              check_expected_revs "$@" ;;
        *,bad)
                die "'git bisect bad' can take only one argument." ;;
        *)
@@@ -279,67 -243,94 +279,79 @@@ bisect_auto_next() 
        bisect_next_check && bisect_next || :
  }
  
 -eval_rev_list() {
 -      _eval="$1"
 -
 -      eval $_eval
 -      res=$?
 -
 -      if [ $res -ne 0 ]; then
 -              echo >&2 "'git rev-list --bisect-vars' failed:"
 -              echo >&2 "maybe you mistake good and bad revs?"
 -              exit $res
 -      fi
 -
 -      return $res
 -}
 -
  filter_skipped() {
        _eval="$1"
        _skip="$2"
  
        if [ -z "$_skip" ]; then
-               eval "$_eval"
 -              eval_rev_list "$_eval" | {
++              eval "$_eval" | {
+                       while read line
+                       do
+                               echo "$line &&"
+                       done
+                       echo ':'
+               }
                return
        fi
  
        # Let's parse the output of:
        # "git rev-list --bisect-vars --bisect-all ..."
-       eval "$_eval" | while read hash line
-       do
-               case "$VARS,$FOUND,$TRIED,$hash" in
-                       # We display some vars.
-                       1,*,*,*) echo "$hash $line" ;;
-                       # Split line.
-                       ,*,*,---*) ;;
-                       # We had nothing to search.
 -      eval_rev_list "$_eval" | {
++      eval "$_eval" | {
+               VARS= FOUND= TRIED=
+               while read hash line
+               do
+                       case "$VARS,$FOUND,$TRIED,$hash" in
+                       1,*,*,*)
+                               # "bisect_foo=bar" read from rev-list output.
+                               echo "$hash &&"
+                               ;;
+                       ,*,*,---*)
+                               # Separator
+                               ;;
                        ,,,bisect_rev*)
-                               echo "bisect_rev="
+                               # We had nothing to search.
+                               echo "bisect_rev= &&"
                                VARS=1
                                ;;
-                       # We did not find a good bisect rev.
-                       # This should happen only if the "bad"
-                       # commit is also a "skip" commit.
                        ,,*,bisect_rev*)
-                               echo "bisect_rev=$TRIED"
+                               # We did not find a good bisect rev.
+                               # This should happen only if the "bad"
+                               # commit is also a "skip" commit.
+                               echo "bisect_rev='$TRIED' &&"
                                VARS=1
                                ;;
-                       # We are searching.
                        ,,*,*)
+                               # We are searching.
                                TRIED="${TRIED:+$TRIED|}$hash"
                                case "$_skip" in
                                *$hash*) ;;
                                *)
-                                       echo "bisect_rev=$hash"
-                                       echo "bisect_tried=\"$TRIED\""
+                                       echo "bisect_rev=$hash &&"
+                                       echo "bisect_tried='$TRIED' &&"
                                        FOUND=1
                                        ;;
                                esac
                                ;;
-                       # We have already found a rev to be tested.
-                       ,1,*,bisect_rev*) VARS=1 ;;
-                       ,1,*,*) ;;
-                       # ???
-                       *) die "filter_skipped error " \
-                           "VARS: '$VARS' " \
-                           "FOUND: '$FOUND' " \
-                           "TRIED: '$TRIED' " \
-                           "hash: '$hash' " \
-                           "line: '$line'"
-                       ;;
-               esac
-       done
+                       ,1,*,bisect_rev*)
+                               # We have already found a rev to be tested.
+                               VARS=1
+                               ;;
+                       ,1,*,*)
+                               ;;
+                       *)
+                               # Unexpected input
+                               echo "die 'filter_skipped error'"
+                               die "filter_skipped error " \
+                                   "VARS: '$VARS' " \
+                                   "FOUND: '$FOUND' " \
+                                   "TRIED: '$TRIED' " \
+                                   "hash: '$hash' " \
+                                   "line: '$line'"
+                               ;;
+                       esac
+               done
+               echo ':'
+       }
  }
  
  exit_if_skipped_commits () {
        fi
  }
  
 +bisect_checkout() {
 +      _rev="$1"
 +      _msg="$2"
 +      echo "Bisecting: $_msg"
 +      mark_expected_rev "$_rev"
 +      git checkout -q "$_rev" || exit
 +      git show-branch "$_rev"
 +}
 +
 +is_among() {
 +      _rev="$1"
 +      _list="$2"
 +      case "$_list" in *$_rev*) return 0 ;; esac
 +      return 1
 +}
 +
 +handle_bad_merge_base() {
 +      _badmb="$1"
 +      _good="$2"
 +      if is_expected_rev "$_badmb"; then
 +              cat >&2 <<EOF
 +The merge base $_badmb is bad.
 +This means the bug has been fixed between $_badmb and [$_good].
 +EOF
 +              exit 3
 +      else
 +              cat >&2 <<EOF
 +Some good revs are not ancestor of the bad rev.
 +git bisect cannot work properly in this case.
 +Maybe you mistake good and bad revs?
 +EOF
 +              exit 1
 +      fi
 +}
 +
 +handle_skipped_merge_base() {
 +      _mb="$1"
 +      _bad="$2"
 +      _good="$3"
 +      cat >&2 <<EOF
 +Warning: the merge base between $_bad and [$_good] must be skipped.
 +So we cannot be sure the first bad commit is between $_mb and $_bad.
 +We continue anyway.
 +EOF
 +}
 +
 +#
 +# "check_merge_bases" checks that merge bases are not "bad".
 +#
 +# - If one is "good", that's good, we have nothing to do.
 +# - If one is "bad", it means the user assumed something wrong
 +# and we must exit.
 +# - If one is "skipped", we can't know but we should warn.
 +# - If we don't know, we should check it out and ask the user to test.
 +#
 +# In the last case we will return 1, and otherwise 0.
 +#
 +check_merge_bases() {
 +      _bad="$1"
 +      _good="$2"
 +      _skip="$3"
 +      for _mb in $(git merge-base --all $_bad $_good)
 +      do
 +              if is_among "$_mb" "$_good"; then
 +                      continue
 +              elif test "$_mb" = "$_bad"; then
 +                      handle_bad_merge_base "$_bad" "$_good"
 +              elif is_among "$_mb" "$_skip"; then
 +                      handle_skipped_merge_base "$_mb" "$_bad" "$_good"
 +              else
 +                      bisect_checkout "$_mb" "a merge base must be tested"
 +                      return 1
 +              fi
 +      done
 +      return 0
 +}
 +
 +#
 +# "check_good_are_ancestors_of_bad" checks that all "good" revs are
 +# ancestor of the "bad" rev.
 +#
 +# If that's not the case, we need to check the merge bases.
 +# If a merge base must be tested by the user we return 1 and
 +# otherwise 0.
 +#
 +check_good_are_ancestors_of_bad() {
 +      test -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
 +              return
 +
 +      _bad="$1"
 +      _good=$(echo $2 | sed -e 's/\^//g')
 +      _skip="$3"
 +
 +      # Bisecting with no good rev is ok
 +      test -z "$_good" && return
 +
 +      _side=$(git rev-list $_good ^$_bad)
 +      if test -n "$_side"; then
 +              # Return if a checkout was done
 +              check_merge_bases "$_bad" "$_good" "$_skip" || return
 +      fi
 +
 +      : > "$GIT_DIR/BISECT_ANCESTORS_OK"
 +
 +      return 0
 +}
 +
  bisect_next() {
        case "$#" in 0) ;; *) usage ;; esac
        bisect_autostart
        bisect_next_check good
  
 +      # Get bad, good and skipped revs
 +      bad=$(git rev-parse --verify refs/bisect/bad) &&
 +      good=$(git for-each-ref --format='^%(objectname)' \
 +              "refs/bisect/good-*" | tr '\012' ' ') &&
        skip=$(git for-each-ref --format='%(objectname)' \
                "refs/bisect/skip-*" | tr '\012' ' ') || exit
  
 +      # Maybe some merge bases must be tested first
 +      check_good_are_ancestors_of_bad "$bad" "$good" "$skip"
 +      # Return now if a checkout has already been done
 +      test "$?" -eq "1" && return
 +
 +      # Get bisection information
        BISECT_OPT=''
        test -n "$skip" && BISECT_OPT='--bisect-all'
 -
 -      bad=$(git rev-parse --verify refs/bisect/bad) &&
 -      good=$(git for-each-ref --format='^%(objectname)' \
 -              "refs/bisect/good-*" | tr '\012' ' ') &&
        eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" &&
        eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
        eval=$(filter_skipped "$eval" "$skip") &&
        # commit is also a "skip" commit (see above).
        exit_if_skipped_commits "$bisect_rev"
  
 -      echo "Bisecting: $bisect_nr revisions left to test after this"
 -      git checkout -q "$bisect_rev" || exit
 -      git show-branch "$bisect_rev"
 +      bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this"
  }
  
  bisect_visualize() {
  
        if test $# = 0
        then
 -              case "${DISPLAY+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in
 +              case "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in
                '')     set git log ;;
                set*)   set gitk ;;
                esac
@@@ -547,8 -427,6 +559,8 @@@ bisect_clean_state() 
        do
                git update-ref -d $ref $hash || exit
        done
 +      rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
 +      rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
        rm -f "$GIT_DIR/BISECT_LOG" &&
        rm -f "$GIT_DIR/BISECT_NAMES" &&
        rm -f "$GIT_DIR/BISECT_RUN" &&
@@@ -645,10 -523,8 +657,10 @@@ case "$#" i
          git bisect -h ;;
      start)
          bisect_start "$@" ;;
 -    bad|good|skip)
 +    bad|good)
          bisect_state "$cmd" "$@" ;;
 +    skip)
 +        bisect_skip "$@" ;;
      next)
          # Not sure we want "next" at the UI level anymore.
          bisect_next "$@" ;;
index 85aef12a113fed8164a085a19ce5c794ab96ff6f,6a1711374579082000e95e39e4f6c0d572d11cd4..c65bca838881938e2f924cfe62b2c303dbd5b1cd
@@@ -19,6 -19,9 +19,9 @@@ d
      >$dir/a.$i
    done
  done
+ >"#ignore1"
+ >"#ignore2"
+ >"#hidden"
  
  cat >expect <<EOF
  a.2
@@@ -42,6 -45,9 +45,9 @@@ three/a.
  EOF
  
  echo '.gitignore
+ \#ignore1
+ \#ignore2*
+ \#hid*n
  output
  expect
  .gitignore
@@@ -79,9 -85,10 +85,10 @@@ test_expect_success 
         >output &&
       test_cmp expect output'
  
- cat > excludes-file << EOF
+ cat > excludes-file <<\EOF
  *.[1-8]
  e*
+ \#*
  EOF
  
  git config core.excludesFile excludes-file
@@@ -140,10 -147,4 +147,10 @@@ test_expect_success 'trailing slash in 
  
  '
  
 +test_expect_success 'negated exclude matches can override previous ones' '
 +
 +      git ls-files --others --exclude="a.*" --exclude="!a.1" >output &&
 +      grep "^a.1" output
 +'
 +
  test_done
index dd7eac84ea191fb797075eca3706498edad68b32,0b81e65aa3897f40f4ef420bae7ed4f7bf4c5675..052a6c90f5a184ddc82f2db1a2907a1b1104166c
@@@ -216,6 -216,31 +216,31 @@@ test_expect_success 'bisect skip: canno
        else
                test $? -eq 2 &&
                grep "first bad commit could be any of" my_bisect_log.txt &&
+               ! grep $HASH1 my_bisect_log.txt &&
+               ! grep $HASH2 my_bisect_log.txt &&
+               grep $HASH3 my_bisect_log.txt &&
+               grep $HASH4 my_bisect_log.txt &&
+               git bisect reset
+       fi
+ '
+ # $HASH1 is good, $HASH4 is both skipped and bad, we skip $HASH3
+ # and $HASH2 is good,
+ # so we should not be able to tell the first bad commit
+ # among $HASH3 and $HASH4
+ test_expect_success 'bisect skip: with commit both bad and skipped' '
+       git bisect start &&
+       git bisect skip &&
+       git bisect bad &&
+       git bisect good $HASH1 &&
+       git bisect skip &&
+       if git bisect good > my_bisect_log.txt
+       then
+               echo Oops, should have failed.
+               false
+       else
+               test $? -eq 2 &&
+               grep "first bad commit could be any of" my_bisect_log.txt &&
                ! grep $HASH1 my_bisect_log.txt &&
                ! grep $HASH2 my_bisect_log.txt &&
                grep $HASH3 my_bisect_log.txt &&
@@@ -313,25 -338,8 +338,25 @@@ test_expect_success 'bisect run & skip
        grep "$HASH6 is first bad commit" my_bisect_log.txt
  '
  
 -test_expect_success 'bisect starting with a detached HEAD' '
 +test_expect_success 'bisect skip only one range' '
 +      git bisect reset &&
 +      git bisect start $HASH7 $HASH1 &&
 +      git bisect skip $HASH1..$HASH5 &&
 +      test "$HASH6" = "$(git rev-parse --verify HEAD)" &&
 +      test_must_fail git bisect bad > my_bisect_log.txt &&
 +      grep "first bad commit could be any of" my_bisect_log.txt
 +'
  
 +test_expect_success 'bisect skip many ranges' '
 +      git bisect start $HASH7 $HASH1 &&
 +      test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
 +      git bisect skip $HASH2 $HASH2.. ..$HASH5 &&
 +      test "$HASH6" = "$(git rev-parse --verify HEAD)" &&
 +      test_must_fail git bisect bad > my_bisect_log.txt &&
 +      grep "first bad commit could be any of" my_bisect_log.txt
 +'
 +
 +test_expect_success 'bisect starting with a detached HEAD' '
        git bisect reset &&
        git checkout master^ &&
        HEAD=$(git rev-parse --verify HEAD) &&
@@@ -367,120 -375,6 +392,120 @@@ test_expect_success 'bisect does not cr
        git branch -D bisect
  '
  
 +# This creates a "side" branch to test "siblings" cases.
 +#
 +# H1-H2-H3-H4-H5-H6-H7  <--other
 +#            \
 +#             S5-S6-S7  <--side
 +#
 +test_expect_success 'side branch creation' '
 +      git bisect reset &&
 +      git checkout -b side $HASH4 &&
 +      add_line_into_file "5(side): first line on a side branch" hello2 &&
 +      SIDE_HASH5=$(git rev-parse --verify HEAD) &&
 +      add_line_into_file "6(side): second line on a side branch" hello2 &&
 +      SIDE_HASH6=$(git rev-parse --verify HEAD) &&
 +      add_line_into_file "7(side): third line on a side branch" hello2 &&
 +      SIDE_HASH7=$(git rev-parse --verify HEAD)
 +'
 +
 +test_expect_success 'good merge base when good and bad are siblings' '
 +      git bisect start "$HASH7" "$SIDE_HASH7" > my_bisect_log.txt &&
 +      grep "merge base must be tested" my_bisect_log.txt &&
 +      grep $HASH4 my_bisect_log.txt &&
 +      git bisect good > my_bisect_log.txt &&
 +      test_must_fail grep "merge base must be tested" my_bisect_log.txt &&
 +      grep $HASH6 my_bisect_log.txt &&
 +      git bisect reset
 +'
 +test_expect_success 'skipped merge base when good and bad are siblings' '
 +      git bisect start "$SIDE_HASH7" "$HASH7" > my_bisect_log.txt &&
 +      grep "merge base must be tested" my_bisect_log.txt &&
 +      grep $HASH4 my_bisect_log.txt &&
 +      git bisect skip > my_bisect_log.txt 2>&1 &&
 +      grep "Warning" my_bisect_log.txt &&
 +      grep $SIDE_HASH6 my_bisect_log.txt &&
 +      git bisect reset
 +'
 +
 +test_expect_success 'bad merge base when good and bad are siblings' '
 +      git bisect start "$HASH7" HEAD > my_bisect_log.txt &&
 +      grep "merge base must be tested" my_bisect_log.txt &&
 +      grep $HASH4 my_bisect_log.txt &&
 +      test_must_fail git bisect bad > my_bisect_log.txt 2>&1 &&
 +      grep "merge base $HASH4 is bad" my_bisect_log.txt &&
 +      grep "fixed between $HASH4 and \[$SIDE_HASH7\]" my_bisect_log.txt &&
 +      git bisect reset
 +'
 +
 +# This creates a few more commits (A and B) to test "siblings" cases
 +# when a good and a bad rev have many merge bases.
 +#
 +# We should have the following:
 +#
 +# H1-H2-H3-H4-H5-H6-H7
 +#            \  \     \
 +#             S5-A     \
 +#              \        \
 +#               S6-S7----B
 +#
 +# And there A and B have 2 merge bases (S5 and H5) that should be
 +# reported by "git merge-base --all A B".
 +#
 +test_expect_success 'many merge bases creation' '
 +      git checkout "$SIDE_HASH5" &&
 +      git merge -m "merge HASH5 and SIDE_HASH5" "$HASH5" &&
 +      A_HASH=$(git rev-parse --verify HEAD) &&
 +      git checkout side &&
 +      git merge -m "merge HASH7 and SIDE_HASH7" "$HASH7" &&
 +      B_HASH=$(git rev-parse --verify HEAD) &&
 +      git merge-base --all "$A_HASH" "$B_HASH" > merge_bases.txt &&
 +      test $(wc -l < merge_bases.txt) = "2" &&
 +      grep "$HASH5" merge_bases.txt &&
 +      grep "$SIDE_HASH5" merge_bases.txt
 +'
 +
 +test_expect_success 'good merge bases when good and bad are siblings' '
 +      git bisect start "$B_HASH" "$A_HASH" > my_bisect_log.txt &&
 +      grep "merge base must be tested" my_bisect_log.txt &&
 +      git bisect good > my_bisect_log2.txt &&
 +      grep "merge base must be tested" my_bisect_log2.txt &&
 +      {
 +              {
 +                      grep "$SIDE_HASH5" my_bisect_log.txt &&
 +                      grep "$HASH5" my_bisect_log2.txt
 +              } || {
 +                      grep "$SIDE_HASH5" my_bisect_log2.txt &&
 +                      grep "$HASH5" my_bisect_log.txt
 +              }
 +      } &&
 +      git bisect reset
 +'
 +
 +check_trace() {
 +      grep "$1" "$GIT_TRACE" | grep "\^$2" | grep "$3" >/dev/null
 +}
 +
 +test_expect_success 'optimized merge base checks' '
 +      GIT_TRACE="$(pwd)/trace.log" &&
 +      export GIT_TRACE &&
 +      git bisect start "$HASH7" "$SIDE_HASH7" > my_bisect_log.txt &&
 +      grep "merge base must be tested" my_bisect_log.txt &&
 +      grep "$HASH4" my_bisect_log.txt &&
 +      check_trace "rev-list" "$HASH7" "$SIDE_HASH7" &&
 +      git bisect good > my_bisect_log2.txt &&
 +      test -f ".git/BISECT_ANCESTORS_OK" &&
 +      test "$HASH6" = $(git rev-parse --verify HEAD) &&
 +      : > "$GIT_TRACE" &&
 +      git bisect bad > my_bisect_log3.txt &&
 +      test_must_fail check_trace "rev-list" "$HASH6" "$SIDE_HASH7" &&
 +      git bisect good "$A_HASH" > my_bisect_log4.txt &&
 +      grep "merge base must be tested" my_bisect_log4.txt &&
 +      test_must_fail test -f ".git/BISECT_ANCESTORS_OK" &&
 +      check_trace "rev-list" "$HASH6" "$A_HASH" &&
 +      unset GIT_TRACE
 +'
 +
  #
  #
  test_done