f5d9ede9dde988f7b2ce9dc89c1f86d74c30664c
   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"; then
 202                        rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
 203                        rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
 204                        return
 205                fi
 206        done
 207}
 208
 209bisect_skip() {
 210        all=''
 211        for arg in "$@"
 212        do
 213                case "$arg" in
 214                *..*)
 215                        revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
 216                *)
 217                        revs=$(git rev-parse --sq-quote "$arg") ;;
 218                esac
 219                all="$all $revs"
 220        done
 221        eval bisect_state 'skip' $all
 222}
 223
 224bisect_state() {
 225        bisect_autostart
 226        state=$1
 227        case "$#,$state" in
 228        0,*)
 229                die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
 230        1,bad|1,good|1,skip)
 231                rev=$(git rev-parse --verify $(bisect_head)) ||
 232                        die "$(gettext "Bad rev input: $(bisect_head)")"
 233                bisect_write "$state" "$rev"
 234                check_expected_revs "$rev" ;;
 235        2,bad|*,good|*,skip)
 236                shift
 237                eval=''
 238                for rev in "$@"
 239                do
 240                        sha=$(git rev-parse --verify "$rev^{commit}") ||
 241                                die "$(eval_gettext "Bad rev input: \$rev")"
 242                        eval="$eval bisect_write '$state' '$sha'; "
 243                done
 244                eval "$eval"
 245                check_expected_revs "$@" ;;
 246        *,bad)
 247                die "$(gettext "'git bisect bad' can take only one argument.")" ;;
 248        *)
 249                usage ;;
 250        esac
 251        bisect_auto_next
 252}
 253
 254bisect_next_check() {
 255        missing_good= missing_bad=
 256        git show-ref -q --verify refs/bisect/bad || missing_bad=t
 257        test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
 258
 259        case "$missing_good,$missing_bad,$1" in
 260        ,,*)
 261                : have both good and bad - ok
 262                ;;
 263        *,)
 264                # do not have both but not asked to fail - just report.
 265                false
 266                ;;
 267        t,,good)
 268                # have bad but not good.  we could bisect although
 269                # this is less optimum.
 270                (
 271                        gettext "Warning: bisecting only with a bad commit." &&
 272                        echo
 273                ) >&2
 274                if test -t 0
 275                then
 276                        # TRANSLATORS: Make sure to include [Y] and [n] in your
 277                        # translation. The program will only accept English input
 278                        # at this point.
 279                        gettext "Are you sure [Y/n]? " >&2
 280                        read yesno
 281                        case "$yesno" in [Nn]*) exit 1 ;; esac
 282                fi
 283                : bisect without good...
 284                ;;
 285        *)
 286
 287                if test -s "$GIT_DIR/BISECT_START"
 288                then
 289                        (
 290                                gettext "You need to give me at least one good and one bad revisions.
 291(You can use \"git bisect bad\" and \"git bisect good\" for that.)" &&
 292                                echo
 293                        ) >&2
 294                else
 295                        (
 296                                gettext "You need to start by \"git bisect start\".
 297You then need to give me at least one good and one bad revisions.
 298(You can use \"git bisect bad\" and \"git bisect good\" for that.)" &&
 299                                echo
 300                        ) >&2
 301                fi
 302                exit 1 ;;
 303        esac
 304}
 305
 306bisect_auto_next() {
 307        bisect_next_check && bisect_next || :
 308}
 309
 310bisect_next() {
 311        case "$#" in 0) ;; *) usage ;; esac
 312        bisect_autostart
 313        bisect_next_check good
 314
 315        # Perform all bisection computation, display and checkout
 316        git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
 317        res=$?
 318
 319        # Check if we should exit because bisection is finished
 320        test $res -eq 10 && exit 0
 321
 322        # Check for an error in the bisection process
 323        test $res -ne 0 && exit $res
 324
 325        return 0
 326}
 327
 328bisect_visualize() {
 329        bisect_next_check fail
 330
 331        if test $# = 0
 332        then
 333                if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
 334                   type gitk >/dev/null 2>&1; then
 335                        set gitk
 336                else
 337                        set git log
 338                fi
 339        else
 340                case "$1" in
 341                git*|tig) ;;
 342                -*)     set git log "$@" ;;
 343                *)      set git "$@" ;;
 344                esac
 345        fi
 346
 347        eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
 348}
 349
 350bisect_reset() {
 351        test -s "$GIT_DIR/BISECT_START" || {
 352                gettext "We are not bisecting."; echo
 353                return
 354        }
 355        case "$#" in
 356        0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
 357        1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || {
 358                        invalid="$1"
 359                        die "$(eval_gettext "'\$invalid' is not a valid commit")"
 360                }
 361                branch="$1" ;;
 362        *)
 363                usage ;;
 364        esac
 365        if ! test -f "$GIT_DIR/BISECT_HEAD"
 366        then
 367                if ! git checkout "$branch" --
 368                then
 369                        die "$(eval_gettext "Could not check out original HEAD '\$branch'.
 370Try 'git bisect reset <commit>'.")"
 371                fi
 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"; then
 404                        rev="$command"
 405                        command="$bisect"
 406                fi
 407                case "$command" in
 408                start)
 409                        cmd="bisect_start $rev"
 410                        eval "$cmd" ;;
 411                good|bad|skip)
 412                        bisect_write "$command" "$rev" ;;
 413                *)
 414                        die "$(gettext "?? what are you talking about?")" ;;
 415                esac
 416        done <"$file"
 417        bisect_auto_next
 418}
 419
 420bisect_run () {
 421        bisect_next_check fail
 422
 423        while true
 424        do
 425                command="$@"
 426                eval_gettext "running \$command"; echo
 427                "$@"
 428                res=$?
 429
 430                # Check for really bad run error.
 431                if [ $res -lt 0 -o $res -ge 128 ]; then
 432                        (
 433                                eval_gettext "bisect run failed:
 434exit code \$res from '\$command' is < 0 or >= 128" &&
 435                                echo
 436                        ) >&2
 437                        exit $res
 438                fi
 439
 440                # Find current state depending on run success or failure.
 441                # A special exit code of 125 means cannot test.
 442                if [ $res -eq 125 ]; then
 443                        state='skip'
 444                elif [ $res -gt 0 ]; then
 445                        state='bad'
 446                else
 447                        state='good'
 448                fi
 449
 450                # We have to use a subshell because "bisect_state" can exit.
 451                ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
 452                res=$?
 453
 454                cat "$GIT_DIR/BISECT_RUN"
 455
 456                if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
 457                        > /dev/null; then
 458                        (
 459                                gettext "bisect run cannot continue any more" &&
 460                                echo
 461                        ) >&2
 462                        exit $res
 463                fi
 464
 465                if [ $res -ne 0 ]; then
 466                        (
 467                                eval_gettext "bisect run failed:
 468'bisect_state \$state' exited with error code \$res" &&
 469                                echo
 470                        ) >&2
 471                        exit $res
 472                fi
 473
 474                if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
 475                        gettext "bisect run success"; echo
 476                        exit 0;
 477                fi
 478
 479        done
 480}
 481
 482bisect_log () {
 483        test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
 484        cat "$GIT_DIR/BISECT_LOG"
 485}
 486
 487case "$#" in
 4880)
 489        usage ;;
 490*)
 491        cmd="$1"
 492        shift
 493        case "$cmd" in
 494        help)
 495                git bisect -h ;;
 496        start)
 497                bisect_start "$@" ;;
 498        bad|good)
 499                bisect_state "$cmd" "$@" ;;
 500        skip)
 501                bisect_skip "$@" ;;
 502        next)
 503                # Not sure we want "next" at the UI level anymore.
 504                bisect_next "$@" ;;
 505        visualize|view)
 506                bisect_visualize "$@" ;;
 507        reset)
 508                bisect_reset "$@" ;;
 509        replay)
 510                bisect_replay "$@" ;;
 511        log)
 512                bisect_log ;;
 513        run)
 514                bisect_run "$@" ;;
 515        *)
 516                usage ;;
 517        esac
 518esac