1f3c46da83b61b3b8a7fce5084206d32596604f0
   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        if ! test -f "$GIT_DIR/BISECT_HEAD"
 368        then
 369                if ! git checkout "$branch" --
 370                then
 371                        die "$(eval_gettext "Could not check out original HEAD '\$branch'.
 372Try 'git bisect reset <commit>'.")"
 373                fi
 374        fi
 375        bisect_clean_state
 376}
 377
 378bisect_clean_state() {
 379        # There may be some refs packed during bisection.
 380        git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
 381        while read ref hash
 382        do
 383                git update-ref -d $ref $hash || exit
 384        done
 385        rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
 386        rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
 387        rm -f "$GIT_DIR/BISECT_LOG" &&
 388        rm -f "$GIT_DIR/BISECT_NAMES" &&
 389        rm -f "$GIT_DIR/BISECT_RUN" &&
 390        # Cleanup head-name if it got left by an old version of git-bisect
 391        rm -f "$GIT_DIR/head-name" &&
 392        git update-ref -d --no-deref BISECT_HEAD &&
 393        # clean up BISECT_START last
 394        rm -f "$GIT_DIR/BISECT_START"
 395}
 396
 397bisect_replay () {
 398        file="$1"
 399        test "$#" -eq 1 || die "$(gettext "No logfile given")"
 400        test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
 401        bisect_reset
 402        while read git bisect command rev
 403        do
 404                test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
 405                if test "$git" = "git-bisect"
 406                then
 407                        rev="$command"
 408                        command="$bisect"
 409                fi
 410                case "$command" in
 411                start)
 412                        cmd="bisect_start $rev"
 413                        eval "$cmd" ;;
 414                good|bad|skip)
 415                        bisect_write "$command" "$rev" ;;
 416                *)
 417                        die "$(gettext "?? what are you talking about?")" ;;
 418                esac
 419        done <"$file"
 420        bisect_auto_next
 421}
 422
 423bisect_run () {
 424        bisect_next_check fail
 425
 426        while true
 427        do
 428                command="$@"
 429                eval_gettext "running \$command"; echo
 430                "$@"
 431                res=$?
 432
 433                # Check for really bad run error.
 434                if [ $res -lt 0 -o $res -ge 128 ]
 435                then
 436                        (
 437                                eval_gettext "bisect run failed:
 438exit code \$res from '\$command' is < 0 or >= 128" &&
 439                                echo
 440                        ) >&2
 441                        exit $res
 442                fi
 443
 444                # Find current state depending on run success or failure.
 445                # A special exit code of 125 means cannot test.
 446                if [ $res -eq 125 ]
 447                then
 448                        state='skip'
 449                elif [ $res -gt 0 ]
 450                then
 451                        state='bad'
 452                else
 453                        state='good'
 454                fi
 455
 456                # We have to use a subshell because "bisect_state" can exit.
 457                ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
 458                res=$?
 459
 460                cat "$GIT_DIR/BISECT_RUN"
 461
 462                if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
 463                        > /dev/null
 464                then
 465                        (
 466                                gettext "bisect run cannot continue any more" &&
 467                                echo
 468                        ) >&2
 469                        exit $res
 470                fi
 471
 472                if [ $res -ne 0 ]
 473                then
 474                        (
 475                                eval_gettext "bisect run failed:
 476'bisect_state \$state' exited with error code \$res" &&
 477                                echo
 478                        ) >&2
 479                        exit $res
 480                fi
 481
 482                if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null
 483                then
 484                        gettext "bisect run success"; echo
 485                        exit 0;
 486                fi
 487
 488        done
 489}
 490
 491bisect_log () {
 492        test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
 493        cat "$GIT_DIR/BISECT_LOG"
 494}
 495
 496case "$#" in
 4970)
 498        usage ;;
 499*)
 500        cmd="$1"
 501        shift
 502        case "$cmd" in
 503        help)
 504                git bisect -h ;;
 505        start)
 506                bisect_start "$@" ;;
 507        bad|good)
 508                bisect_state "$cmd" "$@" ;;
 509        skip)
 510                bisect_skip "$@" ;;
 511        next)
 512                # Not sure we want "next" at the UI level anymore.
 513                bisect_next "$@" ;;
 514        visualize|view)
 515                bisect_visualize "$@" ;;
 516        reset)
 517                bisect_reset "$@" ;;
 518        replay)
 519                bisect_replay "$@" ;;
 520        log)
 521                bisect_log ;;
 522        run)
 523                bisect_run "$@" ;;
 524        *)
 525                usage ;;
 526        esac
 527esac