git-rebase -i: clean-up error check codepath.
[gitweb.git] / git-bisect.sh
index f8d0099059b2f9bdb27e2bad4f742752ee18c502..5385249890698632dedcaf8dda03d865f66abca9 100755 (executable)
@@ -1,12 +1,14 @@
 #!/bin/sh
 
-USAGE='[start|bad|good|next|reset|visualize|replay|log|run]'
+USAGE='[start|bad|good|skip|next|reset|visualize|replay|log|run]'
 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 skip [<rev>...]
+        mark <rev>... untestable revisions.
 git bisect next
         find next bisection to test and check it out.
 git bisect reset [<branch>]
@@ -17,11 +19,10 @@ git bisect replay <logfile>
         replay bisection log.
 git bisect log
         show bisect log.
-git bisect skip [<rev>...]
-        mark <rev>... untestable revisions.
 git bisect run <cmd>...
         use <cmd>... to automatically bisect.'
 
+OPTIONS_SPEC=
 . git-sh-setup
 require_work_tree
 
@@ -36,7 +37,7 @@ sq() {
 }
 
 bisect_autostart() {
-       test -d "$GIT_DIR/refs/bisect" || {
+       test -f "$GIT_DIR/BISECT_NAMES" || {
                echo >&2 'You need to start by "git bisect start"'
                if test -t 0
                then
@@ -71,7 +72,7 @@ bisect_start() {
                ;;
        refs/heads/*)
                [ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree"
-               echo "$head" | sed 's#^refs/heads/##' >"$GIT_DIR/head-name"
+               echo "${head#refs/heads/}" >"$GIT_DIR/head-name"
                ;;
        *)
                die "Bad HEAD - strange symbolic ref"
@@ -82,7 +83,6 @@ bisect_start() {
        # Get rid of any old bisect state
        #
        bisect_clean_state
-       mkdir "$GIT_DIR/refs/bisect"
 
        #
        # Check for one bad and then some good revisions.
@@ -130,7 +130,7 @@ bisect_write() {
                good|skip)      tag="$state"-"$rev" ;;
                *)              die "Bad bisect_write argument: $state" ;;
        esac
-       echo "$rev" >"$GIT_DIR/refs/bisect/$tag"
+       git update-ref "refs/bisect/$tag" "$rev"
        echo "# $state: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
        test -z "$nolog" && echo "git-bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
 }
@@ -191,7 +191,7 @@ bisect_next_check() {
                ;;
        *)
                THEN=''
-               test -d "$GIT_DIR/refs/bisect" || {
+               test -f "$GIT_DIR/BISECT_NAMES" || {
                        echo >&2 'You need to start by "git bisect start".'
                        THEN='then '
                }
@@ -275,7 +275,7 @@ exit_if_skipped_commits () {
        if expr "$_tried" : ".*[|].*" > /dev/null ; then
                echo "There are only 'skip'ped commit left to test."
                echo "The first bad commit could be any of:"
-               echo "$_tried" | sed -e 's/[|]/\n/g'
+               echo "$_tried" | tr '[|]' '[\012]'
                echo "We cannot bisect more!"
                exit 2
        fi
@@ -316,20 +316,38 @@ bisect_next() {
        exit_if_skipped_commits "$bisect_rev"
 
        echo "Bisecting: $bisect_nr revisions left to test after this"
-       echo "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect"
+       git branch -f new-bisect "$bisect_rev"
        git checkout -q new-bisect || exit
-       mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
-       GIT_DIR="$GIT_DIR" git symbolic-ref HEAD refs/heads/bisect
+       git branch -M new-bisect bisect
        git show-branch "$bisect_rev"
 }
 
 bisect_visualize() {
        bisect_next_check fail
-       not=`cd "$GIT_DIR/refs" && echo bisect/good-*`
-       eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
+
+       if test $# = 0
+       then
+               case "${DISPLAY+set}" in
+               '')     set git log ;;
+               set)    set gitk ;;
+               esac
+       else
+               case "$1" in
+               git*|tig) ;;
+               -*)     set git log "$@" ;;
+               *)      set git "$@" ;;
+               esac
+       fi
+
+       not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*")
+       eval '"$@"' refs/bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
 }
 
 bisect_reset() {
+       test -f "$GIT_DIR/BISECT_NAMES" || {
+               echo "We are not bisecting."
+               return
+       }
        case "$#" in
        0) if [ -s "$GIT_DIR/head-name" ]; then
               branch=`cat "$GIT_DIR/head-name"`
@@ -349,8 +367,12 @@ bisect_reset() {
 }
 
 bisect_clean_state() {
-       rm -fr "$GIT_DIR/refs/bisect"
-       rm -f "$GIT_DIR/refs/heads/bisect"
+       # There may be some refs packed during bisection.
+       git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* refs/heads/bisect |
+       while read ref hash
+       do
+               git update-ref -d $ref $hash
+       done
        rm -f "$GIT_DIR/BISECT_LOG"
        rm -f "$GIT_DIR/BISECT_NAMES"
        rm -f "$GIT_DIR/BISECT_RUN"
@@ -392,7 +414,10 @@ bisect_run () {
       fi
 
       # Find current state depending on run success or failure.
-      if [ $res -gt 0 ]; then
+      # A special exit code of 125 means cannot test.
+      if [ $res -eq 125 ]; then
+         state='skip'
+      elif [ $res -gt 0 ]; then
          state='bad'
       else
          state='good'
@@ -404,6 +429,12 @@ bisect_run () {
 
       cat "$GIT_DIR/BISECT_RUN"
 
+      if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
+               > /dev/null; then
+         echo >&2 "bisect run cannot continue any more"
+         exit $res
+      fi
+
       if [ $res -ne 0 ]; then
          echo >&2 "bisect run failed:"
          echo >&2 "'bisect_state $state' exited with error code $res"
@@ -433,7 +464,7 @@ case "$#" in
     next)
         # Not sure we want "next" at the UI level anymore.
         bisect_next "$@" ;;
-    visualize)
+    visualize|view)
        bisect_visualize "$@" ;;
     reset)
         bisect_reset "$@" ;;