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