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