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