git-bisect.shon commit Merge 1.5.0.7 in (045f575)
   1#!/bin/sh
   2
   3USAGE='[start|bad|good|next|reset|visualize|replay|log|run]'
   4LONG_USAGE='git bisect start [<pathspec>]       reset bisect state and start bisection.
   5git bisect bad [<rev>]          mark <rev> a known-bad revision.
   6git bisect good [<rev>...]      mark <rev>... known-good revisions.
   7git bisect next                 find next bisection to test and check it out.
   8git bisect reset [<branch>]     finish bisection search and go back to branch.
   9git bisect visualize            show bisect status in gitk.
  10git bisect replay <logfile>     replay bisection log.
  11git bisect log                  show bisect log.
  12git bisect run <cmd>...         use <cmd>... to automatically bisect.'
  13
  14. git-sh-setup
  15require_work_tree
  16
  17sq() {
  18        @@PERL@@ -e '
  19                for (@ARGV) {
  20                        s/'\''/'\'\\\\\'\''/g;
  21                        print " '\''$_'\''";
  22                }
  23                print "\n";
  24        ' "$@"
  25}
  26
  27bisect_autostart() {
  28        test -d "$GIT_DIR/refs/bisect" || {
  29                echo >&2 'You need to start by "git bisect start"'
  30                if test -t 0
  31                then
  32                        echo >&2 -n 'Do you want me to do it for you [Y/n]? '
  33                        read yesno
  34                        case "$yesno" in
  35                        [Nn]*)
  36                                exit ;;
  37                        esac
  38                        bisect_start
  39                else
  40                        exit 1
  41                fi
  42        }
  43}
  44
  45bisect_start() {
  46        #
  47        # Verify HEAD. If we were bisecting before this, reset to the
  48        # top-of-line master first!
  49        #
  50        head=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD) ||
  51        die "Bad HEAD - I need a symbolic ref"
  52        case "$head" in
  53        refs/heads/bisect)
  54                if [ -s "$GIT_DIR/head-name" ]; then
  55                    branch=`cat "$GIT_DIR/head-name"`
  56                else
  57                    branch=master
  58                fi
  59                git checkout $branch || exit
  60                ;;
  61        refs/heads/*)
  62                [ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree"
  63                echo "$head" | sed 's#^refs/heads/##' >"$GIT_DIR/head-name"
  64                ;;
  65        *)
  66                die "Bad HEAD - strange symbolic ref"
  67                ;;
  68        esac
  69
  70        #
  71        # Get rid of any old bisect state
  72        #
  73        rm -f "$GIT_DIR/refs/heads/bisect"
  74        rm -rf "$GIT_DIR/refs/bisect/"
  75        mkdir "$GIT_DIR/refs/bisect"
  76        {
  77            printf "git-bisect start"
  78            sq "$@"
  79        } >"$GIT_DIR/BISECT_LOG"
  80        sq "$@" >"$GIT_DIR/BISECT_NAMES"
  81}
  82
  83bisect_bad() {
  84        bisect_autostart
  85        case "$#" in
  86        0)
  87                rev=$(git-rev-parse --verify HEAD) ;;
  88        1)
  89                rev=$(git-rev-parse --verify "$1^{commit}") ;;
  90        *)
  91                usage ;;
  92        esac || exit
  93        echo "$rev" >"$GIT_DIR/refs/bisect/bad"
  94        echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
  95        echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
  96        bisect_auto_next
  97}
  98
  99bisect_good() {
 100        bisect_autostart
 101        case "$#" in
 102        0)    revs=$(git-rev-parse --verify HEAD) || exit ;;
 103        *)    revs=$(git-rev-parse --revs-only --no-flags "$@") &&
 104                test '' != "$revs" || die "Bad rev input: $@" ;;
 105        esac
 106        for rev in $revs
 107        do
 108                rev=$(git-rev-parse --verify "$rev^{commit}") || exit
 109                echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
 110                echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
 111                echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
 112        done
 113        bisect_auto_next
 114}
 115
 116bisect_next_check() {
 117        next_ok=no
 118        test -f "$GIT_DIR/refs/bisect/bad" &&
 119        case "$(cd "$GIT_DIR" && echo refs/bisect/good-*)" in
 120        refs/bisect/good-\*) ;;
 121        *) next_ok=yes ;;
 122        esac
 123        case "$next_ok,$1" in
 124        no,) false ;;
 125        no,fail)
 126            THEN=''
 127            test -d "$GIT_DIR/refs/bisect" || {
 128                echo >&2 'You need to start by "git bisect start".'
 129                THEN='then '
 130            }
 131            echo >&2 'You '$THEN'need to give me at least one good' \
 132                'and one bad revisions.'
 133            echo >&2 '(You can use "git bisect bad" and' \
 134                '"git bisect good" for that.)'
 135            exit 1 ;;
 136        *)
 137            true ;;
 138        esac
 139}
 140
 141bisect_auto_next() {
 142        bisect_next_check && bisect_next || :
 143}
 144
 145bisect_next() {
 146        case "$#" in 0) ;; *) usage ;; esac
 147        bisect_autostart
 148        bisect_next_check fail
 149        bad=$(git-rev-parse --verify refs/bisect/bad) &&
 150        good=$(git-rev-parse --sq --revs-only --not \
 151                $(cd "$GIT_DIR" && ls refs/bisect/good-*)) &&
 152        rev=$(eval "git-rev-list --bisect $good $bad -- $(cat "$GIT_DIR/BISECT_NAMES")") || exit
 153        if [ -z "$rev" ]; then
 154            echo "$bad was both good and bad"
 155            exit 1
 156        fi
 157        if [ "$rev" = "$bad" ]; then
 158            echo "$rev is first bad commit"
 159            git-diff-tree --pretty $rev
 160            exit 0
 161        fi
 162        nr=$(eval "git-rev-list $rev $good -- $(cat $GIT_DIR/BISECT_NAMES)" | wc -l) || exit
 163        echo "Bisecting: $nr revisions left to test after this"
 164        echo "$rev" > "$GIT_DIR/refs/heads/new-bisect"
 165        git checkout -q new-bisect || exit
 166        mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
 167        GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD refs/heads/bisect
 168        git-show-branch "$rev"
 169}
 170
 171bisect_visualize() {
 172        bisect_next_check fail
 173        not=`cd "$GIT_DIR/refs" && echo bisect/good-*`
 174        eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
 175}
 176
 177bisect_reset() {
 178        case "$#" in
 179        0) if [ -s "$GIT_DIR/head-name" ]; then
 180               branch=`cat "$GIT_DIR/head-name"`
 181           else
 182               branch=master
 183           fi ;;
 184        1) git-show-ref --verify --quiet -- "refs/heads/$1" || {
 185               echo >&2 "$1 does not seem to be a valid branch"
 186               exit 1
 187           }
 188           branch="$1" ;;
 189        *)
 190            usage ;;
 191        esac
 192        if git checkout "$branch"; then
 193                rm -fr "$GIT_DIR/refs/bisect"
 194                rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
 195                rm -f "$GIT_DIR/BISECT_LOG"
 196                rm -f "$GIT_DIR/BISECT_NAMES"
 197                rm -f "$GIT_DIR/BISECT_RUN"
 198        fi
 199}
 200
 201bisect_replay () {
 202        test -r "$1" || {
 203                echo >&2 "cannot read $1 for replaying"
 204                exit 1
 205        }
 206        bisect_reset
 207        while read bisect command rev
 208        do
 209                test "$bisect" = "git-bisect" || continue
 210                case "$command" in
 211                start)
 212                        cmd="bisect_start $rev"
 213                        eval "$cmd"
 214                        ;;
 215                good)
 216                        echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
 217                        echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
 218                        echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
 219                        ;;
 220                bad)
 221                        echo "$rev" >"$GIT_DIR/refs/bisect/bad"
 222                        echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
 223                        echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
 224                        ;;
 225                *)
 226                        echo >&2 "?? what are you talking about?"
 227                        exit 1 ;;
 228                esac
 229        done <"$1"
 230        bisect_auto_next
 231}
 232
 233bisect_run () {
 234    bisect_next_check fail
 235
 236    while true
 237    do
 238      echo "running $@"
 239      "$@"
 240      res=$?
 241
 242      # Check for really bad run error.
 243      if [ $res -lt 0 -o $res -ge 128 ]; then
 244          echo >&2 "bisect run failed:"
 245          echo >&2 "exit code $res from '$@' is < 0 or >= 128"
 246          exit $res
 247      fi
 248
 249      # Use "bisect_good" or "bisect_bad"
 250      # depending on run success or failure.
 251      if [ $res -gt 0 ]; then
 252          next_bisect='bisect_bad'
 253      else
 254          next_bisect='bisect_good'
 255      fi
 256
 257      # We have to use a subshell because bisect_good or
 258      # bisect_bad functions can exit.
 259      ( $next_bisect > "$GIT_DIR/BISECT_RUN" )
 260      res=$?
 261
 262      cat "$GIT_DIR/BISECT_RUN"
 263
 264      if [ $res -ne 0 ]; then
 265          echo >&2 "bisect run failed:"
 266          echo >&2 "$next_bisect exited with error code $res"
 267          exit $res
 268      fi
 269
 270      if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
 271          echo "bisect run success"
 272          exit 0;
 273      fi
 274
 275    done
 276}
 277
 278
 279case "$#" in
 2800)
 281    usage ;;
 282*)
 283    cmd="$1"
 284    shift
 285    case "$cmd" in
 286    start)
 287        bisect_start "$@" ;;
 288    bad)
 289        bisect_bad "$@" ;;
 290    good)
 291        bisect_good "$@" ;;
 292    next)
 293        # Not sure we want "next" at the UI level anymore.
 294        bisect_next "$@" ;;
 295    visualize)
 296        bisect_visualize "$@" ;;
 297    reset)
 298        bisect_reset "$@" ;;
 299    replay)
 300        bisect_replay "$@" ;;
 301    log)
 302        cat "$GIT_DIR/BISECT_LOG" ;;
 303    run)
 304        bisect_run "$@" ;;
 305    *)
 306        usage ;;
 307    esac
 308esac