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