git-bisect.shon commit Merge branch 'master' of git://repo.or.cz/git/mergetool.git (18acb3e)
   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            echo >&2 'You need to give me at least one good and one bad revisions.'
 127            exit 1 ;;
 128        *)
 129            true ;;
 130        esac
 131}
 132
 133bisect_auto_next() {
 134        bisect_next_check && bisect_next || :
 135}
 136
 137bisect_next() {
 138        case "$#" in 0) ;; *) usage ;; esac
 139        bisect_autostart
 140        bisect_next_check fail
 141        bad=$(git-rev-parse --verify refs/bisect/bad) &&
 142        good=$(git-rev-parse --sq --revs-only --not \
 143                $(cd "$GIT_DIR" && ls refs/bisect/good-*)) &&
 144        rev=$(eval "git-rev-list --bisect $good $bad -- $(cat "$GIT_DIR/BISECT_NAMES")") || exit
 145        if [ -z "$rev" ]; then
 146            echo "$bad was both good and bad"
 147            exit 1
 148        fi
 149        if [ "$rev" = "$bad" ]; then
 150            echo "$rev is first bad commit"
 151            git-diff-tree --pretty $rev
 152            exit 0
 153        fi
 154        nr=$(eval "git-rev-list $rev $good -- $(cat $GIT_DIR/BISECT_NAMES)" | wc -l) || exit
 155        echo "Bisecting: $nr revisions left to test after this"
 156        echo "$rev" > "$GIT_DIR/refs/heads/new-bisect"
 157        git checkout -q new-bisect || exit
 158        mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
 159        GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD refs/heads/bisect
 160        git-show-branch "$rev"
 161}
 162
 163bisect_visualize() {
 164        bisect_next_check fail
 165        not=`cd "$GIT_DIR/refs" && echo bisect/good-*`
 166        eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
 167}
 168
 169bisect_reset() {
 170        case "$#" in
 171        0) if [ -s "$GIT_DIR/head-name" ]; then
 172               branch=`cat "$GIT_DIR/head-name"`
 173           else
 174               branch=master
 175           fi ;;
 176        1) git-show-ref --verify --quiet -- "refs/heads/$1" || {
 177               echo >&2 "$1 does not seem to be a valid branch"
 178               exit 1
 179           }
 180           branch="$1" ;;
 181        *)
 182            usage ;;
 183        esac
 184        if git checkout "$branch"; then
 185                rm -fr "$GIT_DIR/refs/bisect"
 186                rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
 187                rm -f "$GIT_DIR/BISECT_LOG"
 188                rm -f "$GIT_DIR/BISECT_NAMES"
 189                rm -f "$GIT_DIR/BISECT_RUN"
 190        fi
 191}
 192
 193bisect_replay () {
 194        test -r "$1" || {
 195                echo >&2 "cannot read $1 for replaying"
 196                exit 1
 197        }
 198        bisect_reset
 199        while read bisect command rev
 200        do
 201                test "$bisect" = "git-bisect" || continue
 202                case "$command" in
 203                start)
 204                        cmd="bisect_start $rev"
 205                        eval "$cmd"
 206                        ;;
 207                good)
 208                        echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
 209                        echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
 210                        echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
 211                        ;;
 212                bad)
 213                        echo "$rev" >"$GIT_DIR/refs/bisect/bad"
 214                        echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
 215                        echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
 216                        ;;
 217                *)
 218                        echo >&2 "?? what are you talking about?"
 219                        exit 1 ;;
 220                esac
 221        done <"$1"
 222        bisect_auto_next
 223}
 224
 225bisect_run () {
 226    # Check that we have everything to run correctly.
 227    test -d "$GIT_DIR/refs/bisect" || {
 228        echo >&2 'You need to start by "git bisect start".'
 229        echo >&2 'And then by "git bisect bad" and "git bisect good".'
 230        exit 1
 231    }
 232    bisect_next_check fail
 233
 234    while true
 235    do
 236      echo "running $@"
 237      "$@"
 238      res=$?
 239
 240      # Check for really bad run error.
 241      if [ $res -lt 0 -o $res -ge 128 ]; then
 242          echo >&2 "bisect run failed:"
 243          echo >&2 "exit code $res from '$@' is < 0 or >= 128"
 244          exit $res
 245      fi
 246
 247      # Use "bisect_good" or "bisect_bad"
 248      # depending on run success or failure.
 249      if [ $res -gt 0 ]; then
 250          next_bisect='bisect_bad'
 251      else
 252          next_bisect='bisect_good'
 253      fi
 254
 255      # We have to use a subshell because bisect_good or
 256      # bisect_bad functions can exit.
 257      ( $next_bisect > "$GIT_DIR/BISECT_RUN" )
 258      res=$?
 259
 260      cat "$GIT_DIR/BISECT_RUN"
 261
 262      if [ $res -ne 0 ]; then
 263          echo >&2 "bisect run failed:"
 264          echo >&2 "$next_bisect exited with error code $res"
 265          exit $res
 266      fi
 267
 268      if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
 269          echo "bisect run success"
 270          exit 0;
 271      fi
 272
 273    done
 274}
 275
 276
 277case "$#" in
 2780)
 279    usage ;;
 280*)
 281    cmd="$1"
 282    shift
 283    case "$cmd" in
 284    start)
 285        bisect_start "$@" ;;
 286    bad)
 287        bisect_bad "$@" ;;
 288    good)
 289        bisect_good "$@" ;;
 290    next)
 291        # Not sure we want "next" at the UI level anymore.
 292        bisect_next "$@" ;;
 293    visualize)
 294        bisect_visualize "$@" ;;
 295    reset)
 296        bisect_reset "$@" ;;
 297    replay)
 298        bisect_replay "$@" ;;
 299    log)
 300        cat "$GIT_DIR/BISECT_LOG" ;;
 301    run)
 302        bisect_run "$@" ;;
 303    *)
 304        usage ;;
 305    esac
 306esac