1#!/bin/sh23USAGE='[start|bad|good|next|reset|visualize|replay|log|run]'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.12git bisect run <cmd>... use <cmd>... to automatically bisect.'1314. git-sh-setup15require_work_tree1617sq() {18@@PERL@@ -e '19for (@ARGV) {20s/'\''/'\'\\\\\'\''/g;21print " '\''$_'\''";22}23print "\n";24' "$@"25}2627bisect_autostart() {28test -d "$GIT_DIR/refs/bisect" || {29echo >&2 'You need to start by "git bisect start"'30if test -t 031then32echo >&2 -n 'Do you want me to do it for you [Y/n]? '33read yesno34case "$yesno" in35[Nn]*)36exit ;;37esac38bisect_start39else40exit 141fi42}43}4445bisect_start() {46#47# Verify HEAD. If we were bisecting before this, reset to the48# top-of-line master first!49#50head=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD) ||51die "Bad HEAD - I need a symbolic ref"52case "$head" in53refs/heads/bisect)54if [ -s "$GIT_DIR/head-name" ]; then55branch=`cat "$GIT_DIR/head-name"`56else57branch=master58fi59git checkout $branch || exit60;;61refs/heads/*)62[ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree"63echo "$head" | sed 's#^refs/heads/##' >"$GIT_DIR/head-name"64;;65*)66die "Bad HEAD - strange symbolic ref"67;;68esac6970#71# Get rid of any old bisect state72#73rm -f "$GIT_DIR/refs/heads/bisect"74rm -rf "$GIT_DIR/refs/bisect/"75mkdir "$GIT_DIR/refs/bisect"76{77printf "git-bisect start"78sq "$@"79} >"$GIT_DIR/BISECT_LOG"80sq "$@" >"$GIT_DIR/BISECT_NAMES"81}8283bisect_bad() {84bisect_autostart85case "$#" in860)87rev=$(git-rev-parse --verify HEAD) ;;881)89rev=$(git-rev-parse --verify "$1^{commit}") ;;90*)91usage ;;92esac || exit93echo "$rev" >"$GIT_DIR/refs/bisect/bad"94echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"95echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"96bisect_auto_next97}9899bisect_good() {100bisect_autostart101case "$#" in1020) revs=$(git-rev-parse --verify HEAD) || exit ;;103*) revs=$(git-rev-parse --revs-only --no-flags "$@") &&104test '' != "$revs" || die "Bad rev input: $@" ;;105esac106for rev in $revs107do108rev=$(git-rev-parse --verify "$rev^{commit}") || exit109echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"110echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"111echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"112done113bisect_auto_next114}115116bisect_next_check() {117next_ok=no118test -f "$GIT_DIR/refs/bisect/bad" &&119case "$(cd "$GIT_DIR" && echo refs/bisect/good-*)" in120refs/bisect/good-\*) ;;121*) next_ok=yes ;;122esac123case "$next_ok,$1" in124no,) false ;;125no,fail)126echo >&2 'You need to give me at least one good and one bad revisions.'127exit 1 ;;128*)129true ;;130esac131}132133bisect_auto_next() {134bisect_next_check && bisect_next || :135}136137bisect_next() {138case "$#" in 0) ;; *) usage ;; esac139bisect_autostart140bisect_next_check fail141bad=$(git-rev-parse --verify refs/bisect/bad) &&142good=$(git-rev-parse --sq --revs-only --not \143$(cd "$GIT_DIR" && ls refs/bisect/good-*)) &&144rev=$(eval "git-rev-list --bisect $good $bad -- $(cat "$GIT_DIR/BISECT_NAMES")") || exit145if [ -z "$rev" ]; then146echo "$bad was both good and bad"147exit 1148fi149if [ "$rev" = "$bad" ]; then150echo "$rev is first bad commit"151git-diff-tree --pretty $rev152exit 0153fi154nr=$(eval "git-rev-list $rev $good -- $(cat $GIT_DIR/BISECT_NAMES)" | wc -l) || exit155echo "Bisecting: $nr revisions left to test after this"156echo "$rev" > "$GIT_DIR/refs/heads/new-bisect"157git checkout -q new-bisect || exit158mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&159GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD refs/heads/bisect160git-show-branch "$rev"161}162163bisect_visualize() {164bisect_next_check fail165not=`cd "$GIT_DIR/refs" && echo bisect/good-*`166eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")167}168169bisect_reset() {170case "$#" in1710) if [ -s "$GIT_DIR/head-name" ]; then172branch=`cat "$GIT_DIR/head-name"`173else174branch=master175fi ;;1761) git-show-ref --verify --quiet -- "refs/heads/$1" || {177echo >&2 "$1 does not seem to be a valid branch"178exit 1179}180branch="$1" ;;181*)182usage ;;183esac184if git checkout "$branch"; then185rm -fr "$GIT_DIR/refs/bisect"186rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"187rm -f "$GIT_DIR/BISECT_LOG"188rm -f "$GIT_DIR/BISECT_NAMES"189rm -f "$GIT_DIR/BISECT_RUN"190fi191}192193bisect_replay () {194test -r "$1" || {195echo >&2 "cannot read $1 for replaying"196exit 1197}198bisect_reset199while read bisect command rev200do201test "$bisect" = "git-bisect" || continue202case "$command" in203start)204cmd="bisect_start $rev"205eval "$cmd"206;;207good)208echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"209echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"210echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"211;;212bad)213echo "$rev" >"$GIT_DIR/refs/bisect/bad"214echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"215echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"216;;217*)218echo >&2 "?? what are you talking about?"219exit 1 ;;220esac221done <"$1"222bisect_auto_next223}224225bisect_run () {226# Check that we have everything to run correctly.227test -d "$GIT_DIR/refs/bisect" || {228echo >&2 'You need to start by "git bisect start".'229echo >&2 'And then by "git bisect bad" and "git bisect good".'230exit 1231}232bisect_next_check fail233234while true235do236echo "running $@"237"$@"238res=$?239240# Check for really bad run error.241if [ $res -lt 0 -o $res -ge 128 ]; then242echo >&2 "bisect run failed:"243echo >&2 "exit code $res from '$@' is < 0 or >= 128"244exit $res245fi246247# Use "bisect_good" or "bisect_bad"248# depending on run success or failure.249if [ $res -gt 0 ]; then250next_bisect='bisect_bad'251else252next_bisect='bisect_good'253fi254255# We have to use a subshell because bisect_good or256# bisect_bad functions can exit.257( $next_bisect > "$GIT_DIR/BISECT_RUN" )258res=$?259260cat "$GIT_DIR/BISECT_RUN"261262if [ $res -ne 0 ]; then263echo >&2 "bisect run failed:"264echo >&2 "$next_bisect exited with error code $res"265exit $res266fi267268if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then269echo "bisect run success"270exit 0;271fi272273done274}275276277case "$#" in2780)279usage ;;280*)281cmd="$1"282shift283case "$cmd" in284start)285bisect_start "$@" ;;286bad)287bisect_bad "$@" ;;288good)289bisect_good "$@" ;;290next)291# Not sure we want "next" at the UI level anymore.292bisect_next "$@" ;;293visualize)294bisect_visualize "$@" ;;295reset)296bisect_reset "$@" ;;297replay)298bisect_replay "$@" ;;299log)300cat "$GIT_DIR/BISECT_LOG" ;;301run)302bisect_run "$@" ;;303*)304usage ;;305esac306esac