commit: allow associating auxiliary info on-demand
[gitweb.git] / git-bisect.sh
index c21e33c8d133af0e9e0ae3edaffd0a5593ff4ac3..99efbe884528ffa730d6c0297e20d9de93d5ad9e 100755 (executable)
@@ -2,43 +2,56 @@
 
 USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]'
 LONG_USAGE='git bisect help
-        print this long help message.
-git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
-        reset bisect state and start bisection.
+       print this long help message.
+git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...]
+       reset bisect state and start bisection.
 git bisect bad [<rev>]
-        mark <rev> a known-bad revision.
+       mark <rev> a known-bad revision.
 git bisect good [<rev>...]
-        mark <rev>... known-good revisions.
+       mark <rev>... known-good revisions.
 git bisect skip [(<rev>|<range>)...]
-        mark <rev>... untestable revisions.
+       mark <rev>... untestable revisions.
 git bisect next
-        find next bisection to test and check it out.
+       find next bisection to test and check it out.
 git bisect reset [<commit>]
-        finish bisection search and go back to commit.
+       finish bisection search and go back to commit.
 git bisect visualize
-        show bisect status in gitk.
+       show bisect status in gitk.
 git bisect replay <logfile>
-        replay bisection log.
+       replay bisection log.
 git bisect log
-        show bisect log.
+       show bisect log.
 git bisect run <cmd>...
-        use <cmd>... to automatically bisect.
+       use <cmd>... to automatically bisect.
 
 Please use "git help bisect" to get the full man page.'
 
 OPTIONS_SPEC=
 . git-sh-setup
-require_work_tree
+. git-sh-i18n
 
 _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
 _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
 
+bisect_head()
+{
+       if test -f "$GIT_DIR/BISECT_HEAD"
+       then
+               echo BISECT_HEAD
+       else
+               echo HEAD
+       fi
+}
+
 bisect_autostart() {
        test -s "$GIT_DIR/BISECT_START" || {
-               echo >&2 'You need to start by "git bisect start"'
+               gettextln "You need to start by \"git bisect start\"" >&2
                if test -t 0
                then
-                       echo >&2 -n 'Do you want me to do it for you [Y/n]? '
+                       # TRANSLATORS: Make sure to include [Y] and [n] in your
+                       # translation. The program will only accept English input
+                       # at this point.
+                       gettext "Do you want me to do it for you [Y/n]? " >&2
                        read yesno
                        case "$yesno" in
                        [Nn]*)
@@ -52,12 +65,56 @@ bisect_autostart() {
 }
 
 bisect_start() {
+       #
+       # 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=$(git rev-parse --sq-quote "$@")
+       bad_seen=0
+       eval=''
+       if test "z$(git rev-parse --is-bare-repository)" != zfalse
+       then
+               mode=--no-checkout
+       else
+               mode=''
+       fi
+       while [ $# -gt 0 ]; do
+               arg="$1"
+               case "$arg" in
+               --)
+                       shift
+                       break
+               ;;
+               --no-checkout)
+                       mode=--no-checkout
+                       shift ;;
+               --*)
+                       die "$(eval_gettext "unrecognised option: '\$arg'")" ;;
+               *)
+                       rev=$(git rev-parse -q --verify "$arg^{commit}") || {
+                               test $has_double_dash -eq 1 &&
+                               die "$(eval_gettext "'\$arg' does not appear to be a valid revision")"
+                               break
+                       }
+                       case $bad_seen in
+                       0) state='bad' ; bad_seen=1 ;;
+                       *) state='good' ;;
+                       esac
+                       eval="$eval bisect_write '$state' '$rev' 'nolog' &&"
+                       shift
+                       ;;
+               esac
+       done
+
        #
        # Verify HEAD.
        #
        head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
        head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
-       die "Bad HEAD - I need a HEAD"
+       die "$(gettext "Bad HEAD - I need a HEAD")"
 
        #
        # Check if we are bisecting.
@@ -67,7 +124,11 @@ bisect_start() {
        then
                # Reset to the rev from where we started.
                start_head=$(cat "$GIT_DIR/BISECT_START")
-               git checkout "$start_head" -- || exit
+               if test "z$mode" != "z--no-checkout"
+               then
+                       git checkout "$start_head" -- ||
+                       die "$(eval_gettext "Checking out '\$start_head' failed. Try 'git bisect reset <validbranch>'.")"
+               fi
        else
                # Get rev from where we start.
                case "$head" in
@@ -76,11 +137,11 @@ bisect_start() {
                        # cogito usage, and cogito users should understand
                        # it relates to cg-seek.
                        [ -s "$GIT_DIR/head-name" ] &&
-                               die "won't bisect on seeked tree"
+                               die "$(gettext "won't bisect on seeked tree")"
                        start_head="${head#refs/heads/}"
                        ;;
                *)
-                       die "Bad HEAD - strange symbolic ref"
+                       die "$(gettext "Bad HEAD - strange symbolic ref")"
                        ;;
                esac
        fi
@@ -90,39 +151,6 @@ bisect_start() {
        #
        bisect_clean_state || exit
 
-       #
-       # 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=$(git rev-parse --sq-quote "$@")
-       bad_seen=0
-       eval=''
-       while [ $# -gt 0 ]; do
-           arg="$1"
-           case "$arg" in
-           --)
-               shift
-               break
-               ;;
-           *)
-               rev=$(git rev-parse -q --verify "$arg^{commit}") || {
-                   test $has_double_dash -eq 1 &&
-                       die "'$arg' does not appear to be a valid revision"
-                   break
-               }
-               case $bad_seen in
-               0) state='bad' ; bad_seen=1 ;;
-               *) state='good' ;;
-               esac
-               eval="$eval bisect_write '$state' '$rev' 'nolog'; "
-               shift
-               ;;
-           esac
-       done
-
        #
        # Change state.
        # In case of mistaken revs or checkout error, or signals received,
@@ -136,9 +164,12 @@ bisect_start() {
        #
        # Write new start state.
        #
-       echo "$start_head" >"$GIT_DIR/BISECT_START" &&
+       echo "$start_head" >"$GIT_DIR/BISECT_START" && {
+               test "z$mode" != "z--no-checkout" ||
+               git update-ref --no-deref BISECT_HEAD "$start_head"
+       } &&
        git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
-       eval "$eval" &&
+       eval "$eval true" &&
        echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
        #
        # Check if we can proceed to the next bisect state.
@@ -155,7 +186,7 @@ bisect_write() {
        case "$state" in
                bad)            tag="$state" ;;
                good|skip)      tag="$state"-"$rev" ;;
-               *)              die "Bad bisect_write argument: $state" ;;
+               *)              die "$(eval_gettext "Bad bisect_write argument: \$state")" ;;
        esac
        git update-ref "refs/bisect/$tag" "$rev" || exit
        echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG"
@@ -169,7 +200,8 @@ is_expected_rev() {
 
 check_expected_revs() {
        for _rev in "$@"; do
-               if ! is_expected_rev "$_rev"; then
+               if ! is_expected_rev "$_rev"
+               then
                        rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
                        rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
                        return
@@ -178,18 +210,18 @@ check_expected_revs() {
 }
 
 bisect_skip() {
-        all=''
+       all=''
        for arg in "$@"
        do
-           case "$arg" in
-            *..*)
-                revs=$(git rev-list "$arg") || die "Bad rev input: $arg" ;;
-            *)
-                revs=$(git rev-parse --sq-quote "$arg") ;;
-           esac
-            all="$all $revs"
-        done
-        eval bisect_state 'skip' $all
+               case "$arg" in
+               *..*)
+                       revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
+               *)
+                       revs=$(git rev-parse --sq-quote "$arg") ;;
+               esac
+               all="$all $revs"
+       done
+       eval bisect_state 'skip' $all
 }
 
 bisect_state() {
@@ -197,10 +229,10 @@ bisect_state() {
        state=$1
        case "$#,$state" in
        0,*)
-               die "Please call 'bisect_state' with at least one argument." ;;
+               die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
        1,bad|1,good|1,skip)
-               rev=$(git rev-parse --verify HEAD) ||
-                       die "Bad rev input: HEAD"
+               rev=$(git rev-parse --verify $(bisect_head)) ||
+                       die "$(gettext "Bad rev input: $(bisect_head)")"
                bisect_write "$state" "$rev"
                check_expected_revs "$rev" ;;
        2,bad|*,good|*,skip)
@@ -209,13 +241,13 @@ bisect_state() {
                for rev in "$@"
                do
                        sha=$(git rev-parse --verify "$rev^{commit}") ||
-                               die "Bad rev input: $rev"
+                               die "$(eval_gettext "Bad rev input: \$rev")"
                        eval="$eval bisect_write '$state' '$sha'; "
                done
                eval "$eval"
                check_expected_revs "$@" ;;
        *,bad)
-               die "'git bisect bad' can take only one argument." ;;
+               die "$(gettext "'git bisect bad' can take only one argument.")" ;;
        *)
                usage ;;
        esac
@@ -238,25 +270,29 @@ bisect_next_check() {
        t,,good)
                # have bad but not good.  we could bisect although
                # this is less optimum.
-               echo >&2 'Warning: bisecting only with a bad commit.'
+               gettextln "Warning: bisecting only with a bad commit." >&2
                if test -t 0
                then
-                       printf >&2 'Are you sure [Y/n]? '
+                       # TRANSLATORS: Make sure to include [Y] and [n] in your
+                       # translation. The program will only accept English input
+                       # at this point.
+                       gettext "Are you sure [Y/n]? " >&2
                        read yesno
                        case "$yesno" in [Nn]*) exit 1 ;; esac
                fi
                : bisect without good...
                ;;
        *)
-               THEN=''
-               test -s "$GIT_DIR/BISECT_START" || {
-                       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.)'
+
+               if test -s "$GIT_DIR/BISECT_START"
+               then
+                       gettextln "You need to give me at least one good and one bad revisions.
+(You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
+               else
+                       gettextln "You need to start by \"git bisect start\".
+You then need to give me at least one good and one bad revisions.
+(You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
+               fi
                exit 1 ;;
        esac
 }
@@ -271,10 +307,10 @@ bisect_next() {
        bisect_next_check good
 
        # Perform all bisection computation, display and checkout
-       git bisect--helper --next-all
+       git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
        res=$?
 
-        # Check if we should exit because bisection is finished
+       # Check if we should exit because bisection is finished
        test $res -eq 10 && exit 0
 
        # Check for an error in the bisection process
@@ -288,10 +324,13 @@ bisect_visualize() {
 
        if test $# = 0
        then
-               case "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in
-               '')     set git log ;;
-               set*)   set gitk ;;
-               esac
+               if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
+                       type gitk >/dev/null 2>&1
+               then
+                       set gitk
+               else
+                       set git log
+               fi
        else
                case "$1" in
                git*|tig) ;;
@@ -305,23 +344,26 @@ bisect_visualize() {
 
 bisect_reset() {
        test -s "$GIT_DIR/BISECT_START" || {
-               echo "We are not bisecting."
+               gettextln "We are not bisecting."
                return
        }
        case "$#" in
        0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
-       1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null ||
-              die "'$1' is not a valid commit"
-          branch="$1" ;;
+       1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || {
+                       invalid="$1"
+                       die "$(eval_gettext "'\$invalid' is not a valid commit")"
+               }
+               branch="$1" ;;
        *)
-           usage ;;
+               usage ;;
        esac
-       if git checkout "$branch" -- ; then
-               bisect_clean_state
-       else
-               die "Could not check out original HEAD '$branch'." \
-                               "Try 'git bisect reset <commit>'."
+
+       if ! test -f "$GIT_DIR/BISECT_HEAD" && ! git checkout "$branch" --
+       then
+               die "$(eval_gettext "Could not check out original HEAD '\$branch'.
+Try 'git bisect reset <commit>'.")"
        fi
+       bisect_clean_state
 }
 
 bisect_clean_state() {
@@ -338,18 +380,21 @@ bisect_clean_state() {
        rm -f "$GIT_DIR/BISECT_RUN" &&
        # Cleanup head-name if it got left by an old version of git-bisect
        rm -f "$GIT_DIR/head-name" &&
-
+       git update-ref -d --no-deref BISECT_HEAD &&
+       # clean up BISECT_START last
        rm -f "$GIT_DIR/BISECT_START"
 }
 
 bisect_replay () {
-       test "$#" -eq 1 || die "No logfile given"
-       test -r "$1" || die "cannot read $1 for replaying"
+       file="$1"
+       test "$#" -eq 1 || die "$(gettext "No logfile given")"
+       test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
        bisect_reset
        while read git bisect command rev
        do
                test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
-               if test "$git" = "git-bisect"; then
+               if test "$git" = "git-bisect"
+               then
                        rev="$command"
                        command="$bisect"
                fi
@@ -360,98 +405,105 @@ bisect_replay () {
                good|bad|skip)
                        bisect_write "$command" "$rev" ;;
                *)
-                       die "?? what are you talking about?" ;;
+                       die "$(gettext "?? what are you talking about?")" ;;
                esac
-       done <"$1"
+       done <"$file"
        bisect_auto_next
 }
 
 bisect_run () {
-    bisect_next_check fail
-
-    while true
-    do
-      echo "running $@"
-      "$@"
-      res=$?
-
-      # Check for really bad run error.
-      if [ $res -lt 0 -o $res -ge 128 ]; then
-         echo >&2 "bisect run failed:"
-         echo >&2 "exit code $res from '$@' is < 0 or >= 128"
-         exit $res
-      fi
-
-      # Find current state depending on run success or failure.
-      # 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'
-      fi
-
-      # We have to use a subshell because "bisect_state" can exit.
-      ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
-      res=$?
-
-      cat "$GIT_DIR/BISECT_RUN"
-
-      if sane_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"
-         exit $res
-      fi
-
-      if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
-         echo "bisect run success"
-         exit 0;
-      fi
-
-    done
+       bisect_next_check fail
+
+       while true
+       do
+               command="$@"
+               eval_gettextln "running \$command"
+               "$@"
+               res=$?
+
+               # Check for really bad run error.
+               if [ $res -lt 0 -o $res -ge 128 ]
+               then
+                       eval_gettextln "bisect run failed:
+exit code \$res from '\$command' is < 0 or >= 128" >&2
+                       exit $res
+               fi
+
+               # Find current state depending on run success or failure.
+               # 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'
+               fi
+
+               # We have to use a subshell because "bisect_state" can exit.
+               ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
+               res=$?
+
+               cat "$GIT_DIR/BISECT_RUN"
+
+               if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
+                       > /dev/null
+               then
+                       gettextln "bisect run cannot continue any more" >&2
+                       exit $res
+               fi
+
+               if [ $res -ne 0 ]
+               then
+                       eval_gettextln "bisect run failed:
+'bisect_state \$state' exited with error code \$res" >&2
+                       exit $res
+               fi
+
+               if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null
+               then
+                       gettextln "bisect run success"
+                       exit 0;
+               fi
+
+       done
 }
 
 bisect_log () {
-       test -s "$GIT_DIR/BISECT_LOG" || die "We are not bisecting."
+       test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
        cat "$GIT_DIR/BISECT_LOG"
 }
 
 case "$#" in
 0)
-    usage ;;
+       usage ;;
 *)
-    cmd="$1"
-    shift
-    case "$cmd" in
-    help)
-        git bisect -h ;;
-    start)
-        bisect_start "$@" ;;
-    bad|good)
-        bisect_state "$cmd" "$@" ;;
-    skip)
-        bisect_skip "$@" ;;
-    next)
-        # Not sure we want "next" at the UI level anymore.
-        bisect_next "$@" ;;
-    visualize|view)
-       bisect_visualize "$@" ;;
-    reset)
-        bisect_reset "$@" ;;
-    replay)
-       bisect_replay "$@" ;;
-    log)
-       bisect_log ;;
-    run)
-        bisect_run "$@" ;;
-    *)
-        usage ;;
-    esac
+       cmd="$1"
+       shift
+       case "$cmd" in
+       help)
+               git bisect -h ;;
+       start)
+               bisect_start "$@" ;;
+       bad|good)
+               bisect_state "$cmd" "$@" ;;
+       skip)
+               bisect_skip "$@" ;;
+       next)
+               # Not sure we want "next" at the UI level anymore.
+               bisect_next "$@" ;;
+       visualize|view)
+               bisect_visualize "$@" ;;
+       reset)
+               bisect_reset "$@" ;;
+       replay)
+               bisect_replay "$@" ;;
+       log)
+               bisect_log ;;
+       run)
+               bisect_run "$@" ;;
+       *)
+               usage ;;
+       esac
 esac