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