]> asedeno.scripts.mit.edu Git - git.git/blobdiff - git-bisect.sh
Merge git://repo.or.cz/git-gui
[git.git] / git-bisect.sh
index b314d47704c3d72b0d62382296135a46c2d6469e..0d0e278c92c39f17b368eb6ead781a877bc8caed 100755 (executable)
@@ -172,6 +172,25 @@ 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_state() {
        bisect_autostart
        state=$1
@@ -181,7 +200,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 +211,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 +342,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 +354,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 +384,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 +408,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 +436,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 +455,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=''
@@ -491,7 +532,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" &&