Code

Merge branch 'jc/checkout-from-tree-keep-local-changes' into maint
[git.git] / git-bisect.sh
index b2186a86279e8919214bb205400a05f32a316c3b..2524060475ef369c0b9a4641aa88dc2ad6083da4 100755 (executable)
@@ -2,50 +2,56 @@
 
 USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]'
 LONG_USAGE='git bisect help
 
 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>]
 git bisect bad [<rev>]
-        mark <rev> a known-bad revision.
+       mark <rev> a known-bad revision.
 git bisect good [<rev>...]
 git bisect good [<rev>...]
-        mark <rev>... known-good revisions.
+       mark <rev>... known-good revisions.
 git bisect skip [(<rev>|<range>)...]
 git bisect skip [(<rev>|<range>)...]
-        mark <rev>... untestable revisions.
+       mark <rev>... untestable revisions.
 git bisect next
 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>]
 git bisect reset [<commit>]
-        finish bisection search and go back to commit.
+       finish bisection search and go back to commit.
 git bisect visualize
 git bisect visualize
-        show bisect status in gitk.
+       show bisect status in gitk.
 git bisect replay <logfile>
 git bisect replay <logfile>
-        replay bisection log.
+       replay bisection log.
 git bisect log
 git bisect log
-        show bisect log.
+       show bisect log.
 git bisect run <cmd>...
 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
 . git-sh-i18n
 
 Please use "git help bisect" to get the full man page.'
 
 OPTIONS_SPEC=
 . git-sh-setup
 . git-sh-i18n
-require_work_tree
 
 _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"
 
 
 _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" || {
 bisect_autostart() {
        test -s "$GIT_DIR/BISECT_START" || {
-               (
-                       gettext "You need to start by \"git bisect start\"" &&
-                       echo
-               ) >&2
+               gettextln "You need to start by \"git bisect start\"" >&2
                if test -t 0
                then
                        # TRANSLATORS: Make sure to include [Y] and [n] in your
                        # translation. The program will only accept English input
                        # at this point.
                if test -t 0
                then
                        # 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
+                       gettext "Do you want me to do it for you [Y/n]? " >&2
                        read yesno
                        case "$yesno" in
                        [Nn]*)
                        read yesno
                        case "$yesno" in
                        [Nn]*)
@@ -59,6 +65,50 @@ bisect_autostart() {
 }
 
 bisect_start() {
 }
 
 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.
        #
        #
        # Verify HEAD.
        #
@@ -74,7 +124,10 @@ bisect_start() {
        then
                # Reset to the rev from where we started.
                start_head=$(cat "$GIT_DIR/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" --
+               fi
        else
                # Get rev from where we start.
                case "$head" in
        else
                # Get rev from where we start.
                case "$head" in
@@ -97,39 +150,6 @@ bisect_start() {
        #
        bisect_clean_state || exit
 
        #
        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 "$(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
-
        #
        # Change state.
        # In case of mistaken revs or checkout error, or signals received,
        #
        # Change state.
        # In case of mistaken revs or checkout error, or signals received,
@@ -143,9 +163,12 @@ bisect_start() {
        #
        # Write new start state.
        #
        #
        # 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" &&
        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.
        echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
        #
        # Check if we can proceed to the next bisect state.
@@ -176,7 +199,8 @@ is_expected_rev() {
 
 check_expected_revs() {
        for _rev in "$@"; do
 
 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
                        rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
                        rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
                        return
@@ -185,18 +209,18 @@ check_expected_revs() {
 }
 
 bisect_skip() {
 }
 
 bisect_skip() {
-        all=''
+       all=''
        for arg in "$@"
        do
        for arg in "$@"
        do
-           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
+               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() {
 }
 
 bisect_state() {
@@ -206,8 +230,8 @@ bisect_state() {
        0,*)
                die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
        1,bad|1,good|1,skip)
        0,*)
                die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
        1,bad|1,good|1,skip)
-               rev=$(git rev-parse --verify HEAD) ||
-                       die "$(gettext "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)
                bisect_write "$state" "$rev"
                check_expected_revs "$rev" ;;
        2,bad|*,good|*,skip)
@@ -245,10 +269,7 @@ bisect_next_check() {
        t,,good)
                # have bad but not good.  we could bisect although
                # this is less optimum.
        t,,good)
                # have bad but not good.  we could bisect although
                # this is less optimum.
-               (
-                       gettext "Warning: bisecting only with a bad commit." &&
-                       echo
-               ) >&2
+               gettextln "Warning: bisecting only with a bad commit." >&2
                if test -t 0
                then
                        # TRANSLATORS: Make sure to include [Y] and [n] in your
                if test -t 0
                then
                        # TRANSLATORS: Make sure to include [Y] and [n] in your
@@ -264,18 +285,12 @@ bisect_next_check() {
 
                if test -s "$GIT_DIR/BISECT_START"
                then
 
                if test -s "$GIT_DIR/BISECT_START"
                then
-                       (
-                               gettext "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.)" &&
-                               echo
-                       ) >&2
+                       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
                else
-                       (
-                               gettext "You need to start by \"git bisect start\".
+                       gettextln "You need to start by \"git bisect start\".
 You then need to give me at least one good and one bad revisions.
 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.)" &&
-                               echo
-                       ) >&2
+(You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
                fi
                exit 1 ;;
        esac
                fi
                exit 1 ;;
        esac
@@ -291,10 +306,10 @@ bisect_next() {
        bisect_next_check good
 
        # Perform all bisection computation, display and checkout
        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=$?
 
        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
        test $res -eq 10 && exit 0
 
        # Check for an error in the bisection process
@@ -309,7 +324,8 @@ bisect_visualize() {
        if test $# = 0
        then
                if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
        if test $# = 0
        then
                if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
-                  type gitk >/dev/null 2>&1; then
+                       type gitk >/dev/null 2>&1
+               then
                        set gitk
                else
                        set git log
                        set gitk
                else
                        set git log
@@ -327,25 +343,26 @@ bisect_visualize() {
 
 bisect_reset() {
        test -s "$GIT_DIR/BISECT_START" || {
 
 bisect_reset() {
        test -s "$GIT_DIR/BISECT_START" || {
-               gettext "We are not bisecting."; echo
+               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 || {
                return
        }
        case "$#" in
        0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
        1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || {
-              invalid="$1"
-              die "$(eval_gettext "'\$invalid' is not a valid commit")"
-          }
-          branch="$1" ;;
+                       invalid="$1"
+                       die "$(eval_gettext "'\$invalid' is not a valid commit")"
+               }
+               branch="$1" ;;
        *)
        *)
-           usage ;;
+               usage ;;
        esac
        esac
-       if git checkout "$branch" -- ; then
-               bisect_clean_state
-       else
+
+       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
                die "$(eval_gettext "Could not check out original HEAD '\$branch'.
 Try 'git bisect reset <commit>'.")"
        fi
+       bisect_clean_state
 }
 
 bisect_clean_state() {
 }
 
 bisect_clean_state() {
@@ -362,7 +379,8 @@ 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" &&
        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"
 }
 
        rm -f "$GIT_DIR/BISECT_START"
 }
 
@@ -374,7 +392,8 @@ bisect_replay () {
        while read git bisect command rev
        do
                test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
        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
                        rev="$command"
                        command="$bisect"
                fi
@@ -392,65 +411,62 @@ bisect_replay () {
 }
 
 bisect_run () {
 }
 
 bisect_run () {
-    bisect_next_check fail
-
-    while true
-    do
-      command="$@"
-      eval_gettext "running \$command"; echo
-      "$@"
-      res=$?
-
-      # Check for really bad run error.
-      if [ $res -lt 0 -o $res -ge 128 ]; then
-         (
-           eval_gettext "bisect run failed:
-exit code \$res from '\$command' is < 0 or >= 128" &&
-           echo
-         ) >&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
-         (
-             gettext "bisect run cannot continue any more" &&
-             echo
-         ) >&2
-         exit $res
-      fi
-
-      if [ $res -ne 0 ]; then
-         (
-             eval_gettext "bisect run failed:
-'bisect_state \$state' exited with error code \$res" &&
-             echo
-         ) >&2
-         exit $res
-      fi
-
-      if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
-         gettext "bisect run success"; echo
-         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 () {
 }
 
 bisect_log () {
@@ -460,33 +476,33 @@ bisect_log () {
 
 case "$#" in
 0)
 
 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
 esac