git-bisect: modernization
[gitweb.git] / git-bisect.sh
index 936b4a4b835e51410ccfbb37f4205accbade4422..c936533883a20f6d4f9879f56c750e4b37383a92 100755 (executable)
@@ -1,15 +1,24 @@
 #!/bin/sh
 
 USAGE='[start|bad|good|next|reset|visualize|replay|log|run]'
-LONG_USAGE='git bisect start [<pathspec>]      reset bisect state and start bisection.
-git bisect bad [<rev>]         mark <rev> a known-bad revision.
-git bisect good [<rev>...]     mark <rev>... known-good revisions.
-git bisect next                        find next bisection to test and check it out.
-git bisect reset [<branch>]    finish bisection search and go back to branch.
-git bisect visualize            show bisect status in gitk.
-git bisect replay <logfile>    replay bisection log.
-git bisect log                 show bisect log.
-git bisect run <cmd>...        use <cmd>... to automatically bisect.'
+LONG_USAGE='git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
+        reset bisect state and start bisection.
+git bisect bad [<rev>]
+        mark <rev> a known-bad revision.
+git bisect good [<rev>...]
+        mark <rev>... known-good revisions.
+git bisect next
+        find next bisection to test and check it out.
+git bisect reset [<branch>]
+        finish bisection search and go back to branch.
+git bisect visualize
+        show bisect status in gitk.
+git bisect replay <logfile>
+        replay bisection log.
+git bisect log
+        show bisect log.
+git bisect run <cmd>...
+        use <cmd>... to automatically bisect.'
 
 . git-sh-setup
 require_work_tree
@@ -70,14 +79,48 @@ bisect_start() {
        #
        # Get rid of any old bisect state
        #
-       rm -f "$GIT_DIR/refs/heads/bisect"
-       rm -rf "$GIT_DIR/refs/bisect/"
+       bisect_clean_state
        mkdir "$GIT_DIR/refs/bisect"
+
+       #
+       # Check for one bad and then some good revisions.
+       #
+       has_double_dash=0
+       for arg; do
+           case "$arg" in --) has_double_dash=1; break ;; esac
+       done
+       orig_args=$(sq "$@")
+       bad_seen=0
+       while [ $# -gt 0 ]; do
+           arg="$1"
+           case "$arg" in
+           --)
+               shift
+               break
+               ;;
+           *)
+               rev=$(git-rev-parse --verify "$arg^{commit}" 2>/dev/null) || {
+                   test $has_double_dash -eq 1 &&
+                       die "'$arg' does not appear to be a valid revision"
+                   break
+               }
+               if [ $bad_seen -eq 0 ]; then
+                   bad_seen=1
+                   bisect_write_bad "$rev"
+               else
+                   bisect_write_good "$rev"
+               fi
+               shift
+               ;;
+           esac
+        done
+
+       sq "$@" >"$GIT_DIR/BISECT_NAMES"
        {
            printf "git-bisect start"
-           sq "$@"
-       } >"$GIT_DIR/BISECT_LOG"
-       sq "$@" >"$GIT_DIR/BISECT_NAMES"
+           echo "$orig_args"
+       } >>"$GIT_DIR/BISECT_LOG"
+       bisect_auto_next
 }
 
 bisect_bad() {
@@ -90,12 +133,17 @@ bisect_bad() {
        *)
                usage ;;
        esac || exit
-       echo "$rev" >"$GIT_DIR/refs/bisect/bad"
-       echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
+       bisect_write_bad "$rev"
        echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
        bisect_auto_next
 }
 
+bisect_write_bad() {
+       rev="$1"
+       echo "$rev" >"$GIT_DIR/refs/bisect/bad"
+       echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
+}
+
 bisect_good() {
        bisect_autostart
         case "$#" in
@@ -106,24 +154,37 @@ bisect_good() {
        for rev in $revs
        do
                rev=$(git-rev-parse --verify "$rev^{commit}") || exit
-               echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
-               echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
+               bisect_write_good "$rev"
                echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
+
        done
        bisect_auto_next
 }
 
+bisect_write_good() {
+       rev="$1"
+       echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
+       echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
+}
+
 bisect_next_check() {
        next_ok=no
-        test -f "$GIT_DIR/refs/bisect/bad" &&
-       case "$(cd "$GIT_DIR" && echo refs/bisect/good-*)" in
-       refs/bisect/good-\*) ;;
-       *) next_ok=yes ;;
-       esac
+       git show-ref -q --verify refs/bisect/bad &&
+       test -n "$(git for-each-ref "refs/bisect/good-*")" &&
+       next_ok=yes
+
        case "$next_ok,$1" in
        no,) false ;;
        no,fail)
-           echo >&2 'You need to give me at least one good and one bad revisions.'
+           THEN=''
+           test -d "$GIT_DIR/refs/bisect" || {
+               echo >&2 'You need to start by "git bisect start".'
+               THEN='then '
+           }
+           echo >&2 'You '$THEN'need to give me at least one good' \
+               'and one bad revisions.'
+           echo >&2 '(You can use "git bisect bad" and' \
+               '"git bisect good" for that.)'
            exit 1 ;;
        *)
            true ;;
@@ -173,7 +234,7 @@ bisect_reset() {
           else
               branch=master
           fi ;;
-       1) test -f "$GIT_DIR/refs/heads/$1" || {
+       1) git-show-ref --verify --quiet -- "refs/heads/$1" || {
               echo >&2 "$1 does not seem to be a valid branch"
               exit 1
           }
@@ -182,14 +243,19 @@ bisect_reset() {
            usage ;;
        esac
        if git checkout "$branch"; then
-               rm -fr "$GIT_DIR/refs/bisect"
-               rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
-               rm -f "$GIT_DIR/BISECT_LOG"
-               rm -f "$GIT_DIR/BISECT_NAMES"
-               rm -f "$GIT_DIR/BISECT_RUN"
+               rm -f "$GIT_DIR/head-name"
+               bisect_clean_state
        fi
 }
 
+bisect_clean_state() {
+       rm -fr "$GIT_DIR/refs/bisect"
+       rm -f "$GIT_DIR/refs/heads/bisect"
+       rm -f "$GIT_DIR/BISECT_LOG"
+       rm -f "$GIT_DIR/BISECT_NAMES"
+       rm -f "$GIT_DIR/BISECT_RUN"
+}
+
 bisect_replay () {
        test -r "$1" || {
                echo >&2 "cannot read $1 for replaying"
@@ -223,6 +289,8 @@ bisect_replay () {
 }
 
 bisect_run () {
+    bisect_next_check fail
+
     while true
     do
       echo "running $@"