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