git-bisect.shon commit Merge branch 'lt/rev-list' into next (6f85a78)
   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 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        git checkout "$branch" &&
 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}
 188
 189bisect_replay () {
 190        test -r "$1" || {
 191                echo >&2 "cannot read $1 for replaying"
 192                exit 1
 193        }
 194        bisect_reset
 195        while read bisect command rev
 196        do
 197                test "$bisect" = "git-bisect" || continue
 198                case "$command" in
 199                start)
 200                        cmd="bisect_start $rev"
 201                        eval "$cmd"
 202                        ;;
 203                good)
 204                        echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
 205                        echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
 206                        echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
 207                        ;;
 208                bad)
 209                        echo "$rev" >"$GIT_DIR/refs/bisect/bad"
 210                        echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
 211                        echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
 212                        ;;
 213                *)
 214                        echo >&2 "?? what are you talking about?"
 215                        exit 1 ;;
 216                esac
 217        done <"$1"
 218        bisect_auto_next
 219}
 220
 221case "$#" in
 2220)
 223    usage ;;
 224*)
 225    cmd="$1"
 226    shift
 227    case "$cmd" in
 228    start)
 229        bisect_start "$@" ;;
 230    bad)
 231        bisect_bad "$@" ;;
 232    good)
 233        bisect_good "$@" ;;
 234    next)
 235        # Not sure we want "next" at the UI level anymore.
 236        bisect_next "$@" ;;
 237    visualize)
 238        bisect_visualize "$@" ;;
 239    reset)
 240        bisect_reset "$@" ;;
 241    replay)
 242        bisect_replay "$@" ;;
 243    log)
 244        cat "$GIT_DIR/BISECT_LOG" ;;
 245    *)
 246        usage ;;
 247    esac
 248esac