Revert "lstat_cache(): print a warning if doing ping-pong between cache types"
[gitweb.git] / git-bisect.sh
index b314d47704c3d72b0d62382296135a46c2d6469e..85db4ba40022e3a9e5790879d6d21fa59475b316 100755 (executable)
@@ -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,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
@@ -181,7 +215,8 @@ bisect_state() {
        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=''
@@ -191,7 +226,8 @@ bisect_state() {
                                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." ;;
        *)
@@ -321,6 +357,7 @@ bisect_checkout() {
        _rev="$1"
        _msg="$2"
        echo "Bisecting: $_msg"
+       mark_expected_rev "$_rev"
        git checkout -q "$_rev" || exit
        git show-branch "$_rev"
 }
@@ -332,18 +369,10 @@ is_among() {
        return 1
 }
 
-is_testing_merge_base() {
-       grep "^testing $1$" "$GIT_DIR/BISECT_MERGE_BASES" >/dev/null 2>&1
-}
-
-mark_testing_merge_base() {
-       echo "testing $1" >> "$GIT_DIR/BISECT_MERGE_BASES"
-}
-
 handle_bad_merge_base() {
        _badmb="$1"
        _good="$2"
-       if is_testing_merge_base "$_badmb"; then
+       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].
@@ -370,6 +399,17 @@ 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"
@@ -383,15 +423,25 @@ check_merge_bases() {
                elif is_among "$_mb" "$_skip"; then
                        handle_skipped_merge_base "$_mb" "$_bad" "$_good"
                else
-                       mark_testing_merge_base "$_mb"
                        bisect_checkout "$_mb" "a merge base must be tested"
-                       checkout_done=1
-                       return
+                       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"
@@ -401,8 +451,13 @@ check_good_are_ancestors_of_bad() {
 
        _side=$(git rev-list $_good ^$_bad)
        if test -n "$_side"; then
-               check_merge_bases "$_bad" "$_good" "$_skip"
+               # Return if a checkout was done
+               check_merge_bases "$_bad" "$_good" "$_skip" || return
        fi
+
+       : > "$GIT_DIR/BISECT_ANCESTORS_OK"
+
+       return 0
 }
 
 bisect_next() {
@@ -415,11 +470,12 @@ bisect_next() {
        good=$(git for-each-ref --format='^%(objectname)' \
                "refs/bisect/good-*" | tr '\012' ' ') &&
        skip=$(git for-each-ref --format='%(objectname)' \
-               "refs/bisect/skip-*" | tr '\012' ' ') &&
+               "refs/bisect/skip-*" | tr '\012' ' ') || exit
 
        # Maybe some merge bases must be tested first
-       check_good_are_ancestors_of_bad "$bad" "$good" "$skip" || exit
-       test "$checkout_done" -eq "1" && checkout_done='' && return
+       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=''
@@ -452,7 +508,7 @@ 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
@@ -491,7 +547,8 @@ bisect_clean_state() {
        do
                git update-ref -d $ref $hash || exit
        done
-       rm -f "$GIT_DIR/BISECT_MERGE_BASES" &&
+       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" &&
@@ -588,8 +645,10 @@ case "$#" in
         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 "$@" ;;