git-bisect.shon commit bash: Support git-bisect and its subcommands. (b2e69f6)
   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                if [ -s "$GIT_DIR/head-name" ]; then
  53                    branch=`cat "$GIT_DIR/head-name"`
  54                else
  55                    branch=master
  56                fi
  57                git checkout $branch || exit
  58                ;;
  59        refs/heads/*)
  60                [ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree"
  61                echo "$head" | sed 's#^refs/heads/##' >"$GIT_DIR/head-name"
  62                ;;
  63        *)
  64                die "Bad HEAD - strange symbolic ref"
  65                ;;
  66        esac
  67
  68        #
  69        # Get rid of any old bisect state
  70        #
  71        rm -f "$GIT_DIR/refs/heads/bisect"
  72        rm -rf "$GIT_DIR/refs/bisect/"
  73        mkdir "$GIT_DIR/refs/bisect"
  74        {
  75            printf "git-bisect start"
  76            sq "$@"
  77        } >"$GIT_DIR/BISECT_LOG"
  78        sq "$@" >"$GIT_DIR/BISECT_NAMES"
  79}
  80
  81bisect_bad() {
  82        bisect_autostart
  83        case "$#" in
  84        0)
  85                rev=$(git-rev-parse --verify HEAD) ;;
  86        1)
  87                rev=$(git-rev-parse --verify "$1") ;;
  88        *)
  89                usage ;;
  90        esac || exit
  91        echo "$rev" >"$GIT_DIR/refs/bisect/bad"
  92        echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
  93        echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
  94        bisect_auto_next
  95}
  96
  97bisect_good() {
  98        bisect_autostart
  99        case "$#" in
 100        0)    revs=$(git-rev-parse --verify HEAD) || exit ;;
 101        *)    revs=$(git-rev-parse --revs-only --no-flags "$@") &&
 102                test '' != "$revs" || die "Bad rev input: $@" ;;
 103        esac
 104        for rev in $revs
 105        do
 106                rev=$(git-rev-parse --verify "$rev") || exit
 107                echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
 108                echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
 109                echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
 110        done
 111        bisect_auto_next
 112}
 113
 114bisect_next_check() {
 115        next_ok=no
 116        test -f "$GIT_DIR/refs/bisect/bad" &&
 117        case "$(cd "$GIT_DIR" && echo refs/bisect/good-*)" in
 118        refs/bisect/good-\*) ;;
 119        *) next_ok=yes ;;
 120        esac
 121        case "$next_ok,$1" in
 122        no,) false ;;
 123        no,fail)
 124            echo >&2 'You need to give me at least one good and one bad revisions.'
 125            exit 1 ;;
 126        *)
 127            true ;;
 128        esac
 129}
 130
 131bisect_auto_next() {
 132        bisect_next_check && bisect_next || :
 133}
 134
 135bisect_next() {
 136        case "$#" in 0) ;; *) usage ;; esac
 137        bisect_autostart
 138        bisect_next_check fail
 139        bad=$(git-rev-parse --verify refs/bisect/bad) &&
 140        good=$(git-rev-parse --sq --revs-only --not \
 141                $(cd "$GIT_DIR" && ls refs/bisect/good-*)) &&
 142        rev=$(eval "git-rev-list --bisect $good $bad -- $(cat $GIT_DIR/BISECT_NAMES)") || exit
 143        if [ -z "$rev" ]; then
 144            echo "$bad was both good and bad"
 145            exit 1
 146        fi
 147        if [ "$rev" = "$bad" ]; then
 148            echo "$rev is first bad commit"
 149            git-diff-tree --pretty $rev
 150            exit 0
 151        fi
 152        nr=$(eval "git-rev-list $rev $good -- $(cat $GIT_DIR/BISECT_NAMES)" | wc -l) || exit
 153        echo "Bisecting: $nr revisions left to test after this"
 154        echo "$rev" > "$GIT_DIR/refs/heads/new-bisect"
 155        git checkout -q new-bisect || exit
 156        mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
 157        GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD refs/heads/bisect
 158        git-show-branch "$rev"
 159}
 160
 161bisect_visualize() {
 162        bisect_next_check fail
 163        not=`cd "$GIT_DIR/refs" && echo bisect/good-*`
 164        eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
 165}
 166
 167bisect_reset() {
 168        case "$#" in
 169        0) if [ -s "$GIT_DIR/head-name" ]; then
 170               branch=`cat "$GIT_DIR/head-name"`
 171           else
 172               branch=master
 173           fi ;;
 174        1) test -f "$GIT_DIR/refs/heads/$1" || {
 175               echo >&2 "$1 does not seem to be a valid branch"
 176               exit 1
 177           }
 178           branch="$1" ;;
 179        *)
 180            usage ;;
 181        esac
 182        if git checkout "$branch"; then
 183                rm -fr "$GIT_DIR/refs/bisect"
 184                rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
 185                rm -f "$GIT_DIR/BISECT_LOG"
 186                rm -f "$GIT_DIR/BISECT_NAMES"
 187        fi
 188}
 189
 190bisect_replay () {
 191        test -r "$1" || {
 192                echo >&2 "cannot read $1 for replaying"
 193                exit 1
 194        }
 195        bisect_reset
 196        while read bisect command rev
 197        do
 198                test "$bisect" = "git-bisect" || continue
 199                case "$command" in
 200                start)
 201                        cmd="bisect_start $rev"
 202                        eval "$cmd"
 203                        ;;
 204                good)
 205                        echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
 206                        echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
 207                        echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
 208                        ;;
 209                bad)
 210                        echo "$rev" >"$GIT_DIR/refs/bisect/bad"
 211                        echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
 212                        echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
 213                        ;;
 214                *)
 215                        echo >&2 "?? what are you talking about?"
 216                        exit 1 ;;
 217                esac
 218        done <"$1"
 219        bisect_auto_next
 220}
 221
 222case "$#" in
 2230)
 224    usage ;;
 225*)
 226    cmd="$1"
 227    shift
 228    case "$cmd" in
 229    start)
 230        bisect_start "$@" ;;
 231    bad)
 232        bisect_bad "$@" ;;
 233    good)
 234        bisect_good "$@" ;;
 235    next)
 236        # Not sure we want "next" at the UI level anymore.
 237        bisect_next "$@" ;;
 238    visualize)
 239        bisect_visualize "$@" ;;
 240    reset)
 241        bisect_reset "$@" ;;
 242    replay)
 243        bisect_replay "$@" ;;
 244    log)
 245        cat "$GIT_DIR/BISECT_LOG" ;;
 246    *)
 247        usage ;;
 248    esac
 249esac