git-bisect.shon commit Merge branch 'fixes' (d92ddb5)
   1#!/bin/sh
   2. git-sh-setup || dir "Not a git archive"
   3
   4usage() {
   5    echo >&2 'usage: git bisect [start|bad|good|next|reset|visualize]
   6git bisect start                reset bisect state and start bisection.
   7git bisect bad [<rev>]          mark <rev> a known-bad revision.
   8git bisect good [<rev>...]      mark <rev>... known-good revisions.
   9git bisect next                 find next bisection to test and check it out.
  10git bisect reset [<branch>]     finish bisection search and go back to branch.
  11git bisect visualize            show bisect status in gitk.
  12git bisect replay <logfile>     replay bisection log
  13git bisect log                  show bisect log.'
  14    exit 1
  15}
  16
  17bisect_autostart() {
  18        test -d "$GIT_DIR/refs/bisect" || {
  19                echo >&2 'You need to start by "git bisect start"'
  20                if test -t 0
  21                then
  22                        echo >&2 -n 'Do you want me to do it for you [Y/n]? '
  23                        read yesno
  24                        case "$yesno" in
  25                        [Nn]*)
  26                                exit ;;
  27                        esac
  28                        bisect_start
  29                else
  30                        exit 1
  31                fi
  32        }
  33}
  34
  35bisect_start() {
  36        case "$#" in 0) ;; *) usage ;; esac
  37        #
  38        # Verify HEAD. If we were bisecting before this, reset to the
  39        # top-of-line master first!
  40        #
  41        head=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD) ||
  42        die "Bad HEAD - I need a symbolic ref"
  43        case "$head" in
  44        refs/heads/bisect*)
  45                git checkout master || exit
  46                ;;
  47        refs/heads/*)
  48                ;;
  49        *)
  50                die "Bad HEAD - strange symbolic ref"
  51                ;;
  52        esac
  53
  54        #
  55        # Get rid of any old bisect state
  56        #
  57        rm -f "$GIT_DIR/refs/heads/bisect"
  58        rm -rf "$GIT_DIR/refs/bisect/"
  59        mkdir "$GIT_DIR/refs/bisect"
  60        echo "git-bisect start" >"$GIT_DIR/BISECT_LOG"
  61}
  62
  63bisect_bad() {
  64        bisect_autostart
  65        case "$#" in
  66        0)
  67                rev=$(git-rev-parse --verify HEAD) ;;
  68        1)
  69                rev=$(git-rev-parse --verify "$1") ;;
  70        *)
  71                usage ;;
  72        esac || exit
  73        echo "$rev" >"$GIT_DIR/refs/bisect/bad"
  74        echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
  75        echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
  76        bisect_auto_next
  77}
  78
  79bisect_good() {
  80        bisect_autostart
  81        case "$#" in
  82        0)    revs=$(git-rev-parse --verify HEAD) || exit ;;
  83        *)    revs=$(git-rev-parse --revs-only --no-flags "$@") &&
  84                test '' != "$revs" || die "Bad rev input: $@" ;;
  85        esac
  86        for rev in $revs
  87        do
  88                rev=$(git-rev-parse --verify "$rev") || exit
  89                echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
  90                echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
  91                echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
  92        done
  93        bisect_auto_next
  94}
  95
  96bisect_next_check() {
  97        next_ok=no
  98        test -f "$GIT_DIR/refs/bisect/bad" &&
  99        case "$(cd "$GIT_DIR" && echo refs/bisect/good-*)" in
 100        refs/bisect/good-\*) ;;
 101        *) next_ok=yes ;;
 102        esac
 103        case "$next_ok,$1" in
 104        no,) false ;;
 105        no,fail)
 106            echo >&2 'You need to give me at least one good and one bad revisions.'
 107            exit 1 ;;
 108        *)
 109            true ;;
 110        esac
 111}
 112
 113bisect_auto_next() {
 114        bisect_next_check && bisect_next || :
 115}
 116
 117bisect_next() {
 118        case "$#" in 0) ;; *) usage ;; esac
 119        bisect_autostart
 120        bisect_next_check fail
 121        bad=$(git-rev-parse --verify refs/bisect/bad) &&
 122        good=$(git-rev-parse --sq --revs-only --not \
 123                $(cd "$GIT_DIR" && ls refs/bisect/good-*)) &&
 124        rev=$(eval "git-rev-list --bisect $good $bad") || exit
 125        if [ -z "$rev" ]; then
 126            echo "$bad was both good and bad"
 127            exit 1
 128        fi
 129        if [ "$rev" = "$bad" ]; then
 130            echo "$rev is first bad commit"
 131            git-diff-tree --pretty $rev
 132            exit 0
 133        fi
 134        nr=$(eval "git-rev-list $rev $good" | wc -l) || exit
 135        echo "Bisecting: $nr revisions left to test after this"
 136        echo "$rev" > "$GIT_DIR/refs/heads/new-bisect"
 137        git checkout new-bisect || exit
 138        mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
 139        GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD refs/heads/bisect
 140        git-show-branch "$rev"
 141}
 142
 143bisect_visualize() {
 144        bisect_next_check fail
 145        gitk bisect/bad --not `cd "$GIT_DIR/refs" && echo bisect/good-*`
 146}
 147
 148bisect_reset() {
 149        case "$#" in
 150        0) branch=master ;;
 151        1) test -f "$GIT_DIR/refs/heads/$1" || {
 152               echo >&2 "$1 does not seem to be a valid branch"
 153               exit 1
 154           }
 155           branch="$1" ;;
 156        *)
 157            usage ;;
 158        esac
 159        git checkout "$branch" &&
 160        rm -fr "$GIT_DIR/refs/bisect"
 161        rm -f "$GIT_DIR/refs/heads/bisect"
 162        rm -f "$GIT_DIR/BISECT_LOG"
 163}
 164
 165bisect_replay () {
 166        test -r "$1" || {
 167                echo >&2 "cannot read $1 for replaying"
 168                exit 1
 169        }
 170        bisect_reset
 171        while read bisect command rev
 172        do
 173                test "$bisect" = "git-bisect" || continue
 174                case "$command" in
 175                start)
 176                        bisect_start
 177                        ;;
 178                good)
 179                        echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
 180                        echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
 181                        echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
 182                        ;;
 183                bad)
 184                        echo "$rev" >"$GIT_DIR/refs/bisect/bad"
 185                        echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
 186                        echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
 187                        ;;
 188                *)
 189                        echo >&2 "?? what are you talking about?"
 190                        exit 1 ;;
 191                esac
 192        done <"$1"
 193        bisect_auto_next
 194}
 195
 196case "$#" in
 1970)
 198    usage ;;
 199*)
 200    cmd="$1"
 201    shift
 202    case "$cmd" in
 203    start)
 204        bisect_start "$@" ;;
 205    bad)
 206        bisect_bad "$@" ;;
 207    good)
 208        bisect_good "$@" ;;
 209    next)
 210        # Not sure we want "next" at the UI level anymore.
 211        bisect_next "$@" ;;
 212    visualize)
 213        bisect_visualize "$@" ;;
 214    reset)
 215        bisect_reset "$@" ;;
 216    replay)
 217        bisect_replay "$@" ;;
 218    log)
 219        cat "$GIT_DIR/BISECT_LOG" ;;
 220    *)
 221        usage ;;
 222    esac
 223esac