git-bisect.shon commit Merge branch 'maint' (50b68ae)
   1#!/bin/sh
   2
   3USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]'
   4LONG_USAGE='git bisect help
   5        print this long help message.
   6git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
   7        reset bisect state and start bisection.
   8git bisect bad [<rev>]
   9        mark <rev> a known-bad revision.
  10git bisect good [<rev>...]
  11        mark <rev>... known-good revisions.
  12git bisect skip [(<rev>|<range>)...]
  13        mark <rev>... untestable revisions.
  14git bisect next
  15        find next bisection to test and check it out.
  16git bisect reset [<commit>]
  17        finish bisection search and go back to commit.
  18git bisect visualize
  19        show bisect status in gitk.
  20git bisect replay <logfile>
  21        replay bisection log.
  22git bisect log
  23        show bisect log.
  24git bisect run <cmd>...
  25        use <cmd>... to automatically bisect.
  26
  27Please use "git help bisect" to get the full man page.'
  28
  29OPTIONS_SPEC=
  30. git-sh-setup
  31. git-sh-i18n
  32require_work_tree
  33
  34_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
  35_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
  36
  37bisect_autostart() {
  38        test -s "$GIT_DIR/BISECT_START" || {
  39                (
  40                        gettext "You need to start by \"git bisect start\"" &&
  41                        echo
  42                ) >&2
  43                if test -t 0
  44                then
  45                        # TRANSLATORS: Make sure to include [Y] and [n] in your
  46                        # translation. The program will only accept English input
  47                        # at this point.
  48            gettext "Do you want me to do it for you [Y/n]? " >&2
  49                        read yesno
  50                        case "$yesno" in
  51                        [Nn]*)
  52                                exit ;;
  53                        esac
  54                        bisect_start
  55                else
  56                        exit 1
  57                fi
  58        }
  59}
  60
  61bisect_start() {
  62        #
  63        # Verify HEAD.
  64        #
  65        head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
  66        head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
  67        die "$(gettext "Bad HEAD - I need a HEAD")"
  68
  69        #
  70        # Check if we are bisecting.
  71        #
  72        start_head=''
  73        if test -s "$GIT_DIR/BISECT_START"
  74        then
  75                # Reset to the rev from where we started.
  76                start_head=$(cat "$GIT_DIR/BISECT_START")
  77                git checkout "$start_head" -- || exit
  78        else
  79                # Get rev from where we start.
  80                case "$head" in
  81                refs/heads/*|$_x40)
  82                        # This error message should only be triggered by
  83                        # cogito usage, and cogito users should understand
  84                        # it relates to cg-seek.
  85                        [ -s "$GIT_DIR/head-name" ] &&
  86                                die "$(gettext "won't bisect on seeked tree")"
  87                        start_head="${head#refs/heads/}"
  88                        ;;
  89                *)
  90                        die "$(gettext "Bad HEAD - strange symbolic ref")"
  91                        ;;
  92                esac
  93        fi
  94
  95        #
  96        # Get rid of any old bisect state.
  97        #
  98        bisect_clean_state || exit
  99
 100        #
 101        # Check for one bad and then some good revisions.
 102        #
 103        has_double_dash=0
 104        for arg; do
 105            case "$arg" in --) has_double_dash=1; break ;; esac
 106        done
 107        orig_args=$(git rev-parse --sq-quote "$@")
 108        bad_seen=0
 109        eval=''
 110        while [ $# -gt 0 ]; do
 111            arg="$1"
 112            case "$arg" in
 113            --)
 114                shift
 115                break
 116                ;;
 117            *)
 118                rev=$(git rev-parse -q --verify "$arg^{commit}") || {
 119                    test $has_double_dash -eq 1 &&
 120                        die "$(eval_gettext "'\$arg' does not appear to be a valid revision")"
 121                    break
 122                }
 123                case $bad_seen in
 124                0) state='bad' ; bad_seen=1 ;;
 125                *) state='good' ;;
 126                esac
 127                eval="$eval bisect_write '$state' '$rev' 'nolog'; "
 128                shift
 129                ;;
 130            esac
 131        done
 132
 133        #
 134        # Change state.
 135        # In case of mistaken revs or checkout error, or signals received,
 136        # "bisect_auto_next" below may exit or misbehave.
 137        # We have to trap this to be able to clean up using
 138        # "bisect_clean_state".
 139        #
 140        trap 'bisect_clean_state' 0
 141        trap 'exit 255' 1 2 3 15
 142
 143        #
 144        # Write new start state.
 145        #
 146        echo "$start_head" >"$GIT_DIR/BISECT_START" &&
 147        git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
 148        eval "$eval" &&
 149        echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
 150        #
 151        # Check if we can proceed to the next bisect state.
 152        #
 153        bisect_auto_next
 154
 155        trap '-' 0
 156}
 157
 158bisect_write() {
 159        state="$1"
 160        rev="$2"
 161        nolog="$3"
 162        case "$state" in
 163                bad)            tag="$state" ;;
 164                good|skip)      tag="$state"-"$rev" ;;
 165                *)              die "$(eval_gettext "Bad bisect_write argument: \$state")" ;;
 166        esac
 167        git update-ref "refs/bisect/$tag" "$rev" || exit
 168        echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG"
 169        test -n "$nolog" || echo "git bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
 170}
 171
 172is_expected_rev() {
 173        test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
 174        test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
 175}
 176
 177check_expected_revs() {
 178        for _rev in "$@"; do
 179                if ! is_expected_rev "$_rev"; then
 180                        rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
 181                        rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
 182                        return
 183                fi
 184        done
 185}
 186
 187bisect_skip() {
 188        all=''
 189        for arg in "$@"
 190        do
 191            case "$arg" in
 192            *..*)
 193                revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
 194            *)
 195                revs=$(git rev-parse --sq-quote "$arg") ;;
 196            esac
 197            all="$all $revs"
 198        done
 199        eval bisect_state 'skip' $all
 200}
 201
 202bisect_state() {
 203        bisect_autostart
 204        state=$1
 205        case "$#,$state" in
 206        0,*)
 207                die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
 208        1,bad|1,good|1,skip)
 209                rev=$(git rev-parse --verify HEAD) ||
 210                        die "$(gettext "Bad rev input: HEAD")"
 211                bisect_write "$state" "$rev"
 212                check_expected_revs "$rev" ;;
 213        2,bad|*,good|*,skip)
 214                shift
 215                eval=''
 216                for rev in "$@"
 217                do
 218                        sha=$(git rev-parse --verify "$rev^{commit}") ||
 219                                die "$(eval_gettext "Bad rev input: \$rev")"
 220                        eval="$eval bisect_write '$state' '$sha'; "
 221                done
 222                eval "$eval"
 223                check_expected_revs "$@" ;;
 224        *,bad)
 225                die "$(gettext "'git bisect bad' can take only one argument.")" ;;
 226        *)
 227                usage ;;
 228        esac
 229        bisect_auto_next
 230}
 231
 232bisect_next_check() {
 233        missing_good= missing_bad=
 234        git show-ref -q --verify refs/bisect/bad || missing_bad=t
 235        test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
 236
 237        case "$missing_good,$missing_bad,$1" in
 238        ,,*)
 239                : have both good and bad - ok
 240                ;;
 241        *,)
 242                # do not have both but not asked to fail - just report.
 243                false
 244                ;;
 245        t,,good)
 246                # have bad but not good.  we could bisect although
 247                # this is less optimum.
 248                (
 249                        gettext "Warning: bisecting only with a bad commit." &&
 250                        echo
 251                ) >&2
 252                if test -t 0
 253                then
 254                        # TRANSLATORS: Make sure to include [Y] and [n] in your
 255                        # translation. The program will only accept English input
 256                        # at this point.
 257                        gettext "Are you sure [Y/n]? " >&2
 258                        read yesno
 259                        case "$yesno" in [Nn]*) exit 1 ;; esac
 260                fi
 261                : bisect without good...
 262                ;;
 263        *)
 264
 265                if test -s "$GIT_DIR/BISECT_START"
 266                then
 267                        (
 268                                gettext "You need to give me at least one good and one bad revisions.
 269(You can use \"git bisect bad\" and \"git bisect good\" for that.)" &&
 270                                echo
 271                        ) >&2
 272                else
 273                        (
 274                                gettext "You need to start by \"git bisect start\".
 275You then need to give me at least one good and one bad revisions.
 276(You can use \"git bisect bad\" and \"git bisect good\" for that.)" &&
 277                                echo
 278                        ) >&2
 279                fi
 280                exit 1 ;;
 281        esac
 282}
 283
 284bisect_auto_next() {
 285        bisect_next_check && bisect_next || :
 286}
 287
 288bisect_next() {
 289        case "$#" in 0) ;; *) usage ;; esac
 290        bisect_autostart
 291        bisect_next_check good
 292
 293        # Perform all bisection computation, display and checkout
 294        git bisect--helper --next-all
 295        res=$?
 296
 297        # Check if we should exit because bisection is finished
 298        test $res -eq 10 && exit 0
 299
 300        # Check for an error in the bisection process
 301        test $res -ne 0 && exit $res
 302
 303        return 0
 304}
 305
 306bisect_visualize() {
 307        bisect_next_check fail
 308
 309        if test $# = 0
 310        then
 311                if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
 312                   type gitk >/dev/null 2>&1; then
 313                        set gitk
 314                else
 315                        set git log
 316                fi
 317        else
 318                case "$1" in
 319                git*|tig) ;;
 320                -*)     set git log "$@" ;;
 321                *)      set git "$@" ;;
 322                esac
 323        fi
 324
 325        eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
 326}
 327
 328bisect_reset() {
 329        test -s "$GIT_DIR/BISECT_START" || {
 330                gettext "We are not bisecting."; echo
 331                return
 332        }
 333        case "$#" in
 334        0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
 335        1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || {
 336               invalid="$1"
 337               die "$(eval_gettext "'\$invalid' is not a valid commit")"
 338           }
 339           branch="$1" ;;
 340        *)
 341            usage ;;
 342        esac
 343        if git checkout "$branch" -- ; then
 344                bisect_clean_state
 345        else
 346                die "$(eval_gettext "Could not check out original HEAD '\$branch'.
 347Try 'git bisect reset <commit>'.")"
 348        fi
 349}
 350
 351bisect_clean_state() {
 352        # There may be some refs packed during bisection.
 353        git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
 354        while read ref hash
 355        do
 356                git update-ref -d $ref $hash || exit
 357        done
 358        rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
 359        rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
 360        rm -f "$GIT_DIR/BISECT_LOG" &&
 361        rm -f "$GIT_DIR/BISECT_NAMES" &&
 362        rm -f "$GIT_DIR/BISECT_RUN" &&
 363        # Cleanup head-name if it got left by an old version of git-bisect
 364        rm -f "$GIT_DIR/head-name" &&
 365
 366        rm -f "$GIT_DIR/BISECT_START"
 367}
 368
 369bisect_replay () {
 370        file="$1"
 371        test "$#" -eq 1 || die "$(gettext "No logfile given")"
 372        test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
 373        bisect_reset
 374        while read git bisect command rev
 375        do
 376                test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
 377                if test "$git" = "git-bisect"; then
 378                        rev="$command"
 379                        command="$bisect"
 380                fi
 381                case "$command" in
 382                start)
 383                        cmd="bisect_start $rev"
 384                        eval "$cmd" ;;
 385                good|bad|skip)
 386                        bisect_write "$command" "$rev" ;;
 387                *)
 388                        die "$(gettext "?? what are you talking about?")" ;;
 389                esac
 390        done <"$file"
 391        bisect_auto_next
 392}
 393
 394bisect_run () {
 395    bisect_next_check fail
 396
 397    while true
 398    do
 399      command="$@"
 400      eval_gettext "running \$command"; echo
 401      "$@"
 402      res=$?
 403
 404      # Check for really bad run error.
 405      if [ $res -lt 0 -o $res -ge 128 ]; then
 406          (
 407            eval_gettext "bisect run failed:
 408exit code \$res from '\$command' is < 0 or >= 128" &&
 409            echo
 410          ) >&2
 411          exit $res
 412      fi
 413
 414      # Find current state depending on run success or failure.
 415      # A special exit code of 125 means cannot test.
 416      if [ $res -eq 125 ]; then
 417          state='skip'
 418      elif [ $res -gt 0 ]; then
 419          state='bad'
 420      else
 421          state='good'
 422      fi
 423
 424      # We have to use a subshell because "bisect_state" can exit.
 425      ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
 426      res=$?
 427
 428      cat "$GIT_DIR/BISECT_RUN"
 429
 430      if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
 431                > /dev/null; then
 432          (
 433              gettext "bisect run cannot continue any more" &&
 434              echo
 435          ) >&2
 436          exit $res
 437      fi
 438
 439      if [ $res -ne 0 ]; then
 440          (
 441              eval_gettext "bisect run failed:
 442'bisect_state \$state' exited with error code \$res" &&
 443              echo
 444          ) >&2
 445          exit $res
 446      fi
 447
 448      if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
 449          gettext "bisect run success"; echo
 450          exit 0;
 451      fi
 452
 453    done
 454}
 455
 456bisect_log () {
 457        test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
 458        cat "$GIT_DIR/BISECT_LOG"
 459}
 460
 461case "$#" in
 4620)
 463    usage ;;
 464*)
 465    cmd="$1"
 466    shift
 467    case "$cmd" in
 468    help)
 469        git bisect -h ;;
 470    start)
 471        bisect_start "$@" ;;
 472    bad|good)
 473        bisect_state "$cmd" "$@" ;;
 474    skip)
 475        bisect_skip "$@" ;;
 476    next)
 477        # Not sure we want "next" at the UI level anymore.
 478        bisect_next "$@" ;;
 479    visualize|view)
 480        bisect_visualize "$@" ;;
 481    reset)
 482        bisect_reset "$@" ;;
 483    replay)
 484        bisect_replay "$@" ;;
 485    log)
 486        bisect_log ;;
 487    run)
 488        bisect_run "$@" ;;
 489    *)
 490        usage ;;
 491    esac
 492esac