stash: take advantage of eval_gettextln
[gitweb.git] / git-bisect.sh
index 97ac600873ebd0dff6310071343403a41a867b8a..b2186a86279e8919214bb205400a05f32a316c3b 100755 (executable)
@@ -9,12 +9,12 @@ git bisect bad [<rev>]
         mark <rev> a known-bad revision.
 git bisect good [<rev>...]
         mark <rev>... known-good revisions.
-git bisect skip [<rev>...]
+git bisect skip [(<rev>|<range>)...]
         mark <rev>... untestable 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 reset [<commit>]
+        finish bisection search and go back to commit.
 git bisect visualize
         show bisect status in gitk.
 git bisect replay <logfile>
@@ -28,27 +28,24 @@ 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"
 
-sq() {
-       @@PERL@@ -e '
-               for (@ARGV) {
-                       s/'\''/'\'\\\\\'\''/g;
-                       print " '\''$_'\''";
-               }
-               print "\n";
-       ' "$@"
-}
-
 bisect_autostart() {
        test -s "$GIT_DIR/BISECT_START" || {
-               echo >&2 'You need to start by "git bisect start"'
+               (
+                       gettext "You need to start by \"git bisect start\"" &&
+                       echo
+               ) >&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]*)
@@ -67,7 +64,7 @@ bisect_start() {
        #
        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.
@@ -77,7 +74,7 @@ bisect_start() {
        then
                # Reset to the rev from where we started.
                start_head=$(cat "$GIT_DIR/BISECT_START")
-               git checkout "$start_head" || exit
+               git checkout "$start_head" -- || exit
        else
                # Get rev from where we start.
                case "$head" in
@@ -86,11 +83,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
@@ -107,7 +104,7 @@ bisect_start() {
        for arg; do
            case "$arg" in --) has_double_dash=1; break ;; esac
        done
-       orig_args=$(sq "$@")
+       orig_args=$(git rev-parse --sq-quote "$@")
        bad_seen=0
        eval=''
        while [ $# -gt 0 ]; do
@@ -120,7 +117,7 @@ bisect_start() {
            *)
                rev=$(git rev-parse -q --verify "$arg^{commit}") || {
                    test $has_double_dash -eq 1 &&
-                       die "'$arg' does not appear to be a valid revision"
+                       die "$(eval_gettext "'\$arg' does not appear to be a valid revision")"
                    break
                }
                case $bad_seen in
@@ -147,7 +144,7 @@ bisect_start() {
        # Write new start state.
        #
        echo "$start_head" >"$GIT_DIR/BISECT_START" &&
-       sq "$@" >"$GIT_DIR/BISECT_NAMES" &&
+       git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
        eval "$eval" &&
        echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
        #
@@ -165,35 +162,67 @@ 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"
        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")
+}
+
+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_skip() {
+        all=''
+       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
+}
+
 bisect_state() {
        bisect_autostart
        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"
-               bisect_write "$state" "$rev" ;;
+                       die "$(gettext "Bad rev input: HEAD")"
+               bisect_write "$state" "$rev"
+               check_expected_revs "$rev" ;;
        2,bad|*,good|*,skip)
                shift
                eval=''
                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" ;;
+               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
@@ -216,25 +245,38 @@ 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.'
+               (
+                       gettext "Warning: bisecting only with a bad commit." &&
+                       echo
+               ) >&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
+                       (
+                               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
+               else
+                       (
+                               gettext "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.)" &&
+                               echo
+                       ) >&2
+               fi
                exit 1 ;;
        esac
 }
@@ -243,132 +285,22 @@ bisect_auto_next() {
        bisect_next_check && bisect_next || :
 }
 
-eval_rev_list() {
-       _eval="$1"
-
-       eval $_eval
-       res=$?
-
-       if [ $res -ne 0 ]; then
-               echo >&2 "'git rev-list --bisect-vars' failed:"
-               echo >&2 "maybe you mistake good and bad revs?"
-               exit $res
-       fi
-
-       return $res
-}
-
-filter_skipped() {
-       _eval="$1"
-       _skip="$2"
-
-       if [ -z "$_skip" ]; then
-               eval_rev_list "$_eval"
-               return
-       fi
-
-       # Let's parse the output of:
-       # "git rev-list --bisect-vars --bisect-all ..."
-       eval_rev_list "$_eval" | while read hash line
-       do
-               case "$VARS,$FOUND,$TRIED,$hash" in
-                       # We display some vars.
-                       1,*,*,*) echo "$hash $line" ;;
-
-                       # Split line.
-                       ,*,*,---*) ;;
-
-                       # We had nothing to search.
-                       ,,,bisect_rev*)
-                               echo "bisect_rev="
-                               VARS=1
-                               ;;
-
-                       # We did not find a good bisect rev.
-                       # This should happen only if the "bad"
-                       # commit is also a "skip" commit.
-                       ,,*,bisect_rev*)
-                               echo "bisect_rev=$TRIED"
-                               VARS=1
-                               ;;
-
-                       # We are searching.
-                       ,,*,*)
-                               TRIED="${TRIED:+$TRIED|}$hash"
-                               case "$_skip" in
-                               *$hash*) ;;
-                               *)
-                                       echo "bisect_rev=$hash"
-                                       echo "bisect_tried=\"$TRIED\""
-                                       FOUND=1
-                                       ;;
-                               esac
-                               ;;
-
-                       # We have already found a rev to be tested.
-                       ,1,*,bisect_rev*) VARS=1 ;;
-                       ,1,*,*) ;;
-
-                       # ???
-                       *) die "filter_skipped error " \
-                           "VARS: '$VARS' " \
-                           "FOUND: '$FOUND' " \
-                           "TRIED: '$TRIED' " \
-                           "hash: '$hash' " \
-                           "line: '$line'"
-                       ;;
-               esac
-       done
-}
-
-exit_if_skipped_commits () {
-       _tried=$1
-       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" | tr '[|]' '[\012]'
-               echo "We cannot bisect more!"
-               exit 2
-       fi
-}
-
 bisect_next() {
        case "$#" in 0) ;; *) usage ;; esac
        bisect_autostart
        bisect_next_check good
 
-       skip=$(git for-each-ref --format='%(objectname)' \
-               "refs/bisect/skip-*" | tr '\012' ' ') || exit
-
-       BISECT_OPT=''
-       test -n "$skip" && BISECT_OPT='--bisect-all'
-
-       bad=$(git rev-parse --verify refs/bisect/bad) &&
-       good=$(git for-each-ref --format='^%(objectname)' \
-               "refs/bisect/good-*" | tr '\012' ' ') &&
-       eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" &&
-       eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
-       eval=$(filter_skipped "$eval" "$skip") &&
-       eval "$eval" || exit
+       # Perform all bisection computation, display and checkout
+       git bisect--helper --next-all
+       res=$?
 
-       if [ -z "$bisect_rev" ]; then
-               echo "$bad was both good and bad"
-               exit 1
-       fi
-       if [ "$bisect_rev" = "$bad" ]; then
-               exit_if_skipped_commits "$bisect_tried"
-               echo "$bisect_rev is first bad commit"
-               git diff-tree --pretty $bisect_rev
-               exit 0
-       fi
+        # Check if we should exit because bisection is finished
+       test $res -eq 10 && exit 0
 
-       # We should exit here only if the "bad"
-       # commit is also a "skip" commit (see above).
-       exit_if_skipped_commits "$bisect_rev"
+       # Check for an error in the bisection process
+       test $res -ne 0 && exit $res
 
-       echo "Bisecting: $bisect_nr revisions left to test after this"
-       git checkout -q "$bisect_rev" || exit
-       git show-branch "$bisect_rev"
+       return 0
 }
 
 bisect_visualize() {
@@ -376,10 +308,12 @@ bisect_visualize() {
 
        if test $# = 0
        then
-               case "${DISPLAY+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) ;;
@@ -388,24 +322,30 @@ bisect_visualize() {
                esac
        fi
 
-       not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*")
-       eval '"$@"' refs/bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
+       eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
 }
 
 bisect_reset() {
        test -s "$GIT_DIR/BISECT_START" || {
-               echo "We are not bisecting."
+               gettext "We are not bisecting."; echo
                return
        }
        case "$#" in
        0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
-       1) git show-ref --verify --quiet -- "refs/heads/$1" ||
-              die "$1 does not seem to be a valid branch"
+       1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || {
+              invalid="$1"
+              die "$(eval_gettext "'\$invalid' is not a valid commit")"
+          }
           branch="$1" ;;
        *)
            usage ;;
        esac
-       git checkout "$branch" && bisect_clean_state
+       if git checkout "$branch" -- ; then
+               bisect_clean_state
+       else
+               die "$(eval_gettext "Could not check out original HEAD '\$branch'.
+Try 'git bisect reset <commit>'.")"
+       fi
 }
 
 bisect_clean_state() {
@@ -415,6 +355,8 @@ bisect_clean_state() {
        do
                git update-ref -d $ref $hash || exit
        done
+       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" &&
@@ -425,7 +367,9 @@ bisect_clean_state() {
 }
 
 bisect_replay () {
-       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
@@ -441,9 +385,9 @@ 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
 }
 
@@ -452,14 +396,18 @@ bisect_run () {
 
     while true
     do
-      echo "running $@"
+      command="$@"
+      eval_gettext "running \$command"; echo
       "$@"
       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"
+         (
+           eval_gettext "bisect run failed:
+exit code \$res from '\$command' is < 0 or >= 128" &&
+           echo
+         ) >&2
          exit $res
       fi
 
@@ -479,26 +427,36 @@ bisect_run () {
 
       cat "$GIT_DIR/BISECT_RUN"
 
-      if grep "first bad commit could be any of" "$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"
+         (
+             gettext "bisect run cannot continue any more" &&
+             echo
+         ) >&2
          exit $res
       fi
 
       if [ $res -ne 0 ]; then
-         echo >&2 "bisect run failed:"
-         echo >&2 "'bisect_state $state' exited with error code $res"
+         (
+             eval_gettext "bisect run failed:
+'bisect_state \$state' exited with error code \$res" &&
+             echo
+         ) >&2
          exit $res
       fi
 
-      if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
-         echo "bisect run success"
+      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_log () {
+       test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
+       cat "$GIT_DIR/BISECT_LOG"
+}
 
 case "$#" in
 0)
@@ -511,8 +469,10 @@ case "$#" in
         git bisect -h ;;
     start)
         bisect_start "$@" ;;
-    bad|good|skip)
+    bad|good)
         bisect_state "$cmd" "$@" ;;
+    skip)
+        bisect_skip "$@" ;;
     next)
         # Not sure we want "next" at the UI level anymore.
         bisect_next "$@" ;;
@@ -523,7 +483,7 @@ case "$#" in
     replay)
        bisect_replay "$@" ;;
     log)
-       cat "$GIT_DIR/BISECT_LOG" ;;
+       bisect_log ;;
     run)
         bisect_run "$@" ;;
     *)