#!/bin/sh
-USAGE='[start|bad|good|next|reset|visualize|replay|log|run]'
-LONG_USAGE='git bisect start [<pathspec>] reset bisect state and start bisection.
-git bisect bad [<rev>] mark <rev> a known-bad revision.
-git bisect good [<rev>...] mark <rev>... known-good revisions.
-git bisect next find next bisection to test and check it out.
-git bisect reset [<branch>] finish bisection search and go back to branch.
-git bisect visualize show bisect status in gitk.
-git bisect replay <logfile> replay bisection log.
-git bisect log show bisect log.
-git bisect run <cmd>... use <cmd>... to automatically bisect.'
+USAGE='[help|start|bad|good|new|old|terms|skip|next|reset|visualize|view|replay|log|run]'
+LONG_USAGE='git bisect help
+ print this long help message.
+git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]
+ [--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...]
+ reset bisect state and start bisection.
+git bisect (bad|new) [<rev>]
+ mark <rev> a known-bad revision/
+ a revision after change in a given property.
+git bisect (good|old) [<rev>...]
+ mark <rev>... known-good revisions/
+ revisions before change in a given property.
+git bisect terms [--term-good | --term-bad]
+ show the terms used for old and new commits (default: bad, good)
+git bisect skip [(<rev>|<range>)...]
+ mark <rev>... untestable revisions.
+git bisect next
+ find next bisection to test and check it out.
+git bisect reset [<commit>]
+ finish bisection search and go back to commit.
+git bisect (visualize|view)
+ show bisect status in gitk.
+git bisect replay <logfile>
+ replay bisection log.
+git bisect log
+ show bisect log.
+git bisect run <cmd>...
+ use <cmd>... to automatically bisect.
+Please use "git help bisect" to get the full man page.'
+
+OPTIONS_SPEC=
. git-sh-setup
-require_work_tree
-
-sq() {
- @@PERL@@ -e '
- for (@ARGV) {
- s/'\''/'\'\\\\\'\''/g;
- print " '\''$_'\''";
- }
- print "\n";
- ' "$@"
+
+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+TERM_BAD=bad
+TERM_GOOD=good
+
+bisect_head()
+{
+ if test -f "$GIT_DIR/BISECT_HEAD"
+ then
+ echo BISECT_HEAD
+ else
+ echo HEAD
+ fi
}
bisect_autostart() {
- test -d "$GIT_DIR/refs/bisect" || {
- echo >&2 'You need to start by "git bisect start"'
+ test -s "$GIT_DIR/BISECT_START" || {
+ gettextln "You need to start by \"git bisect start\"" >&2
if test -t 0
then
- echo >&2 -n 'Do you want me to do it for you [Y/n]? '
+ # TRANSLATORS: Make sure to include [Y] and [n] in your
+ # translation. The program will only accept English input
+ # at this point.
+ gettext "Do you want me to do it for you [Y/n]? " >&2
read yesno
case "$yesno" in
[Nn]*)
}
bisect_start() {
+ git bisect--helper --bisect-start $@ || exit
+
#
- # Verify HEAD. If we were bisecting before this, reset to the
- # top-of-line master first!
+ # Change state.
+ # In case of mistaken revs or checkout error, or signals received,
+ # "bisect_auto_next" below may exit or misbehave.
+ # We have to trap this to be able to clean up using
+ # "bisect_clean_state".
#
- head=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD) ||
- die "Bad HEAD - I need a symbolic ref"
- case "$head" in
- refs/heads/bisect*)
- if [ -s "$GIT_DIR/head-name" ]; then
- branch=`cat "$GIT_DIR/head-name"`
- else
- branch=master
- fi
- git checkout $branch || exit
- ;;
- refs/heads/*)
- [ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree"
- echo "$head" | sed 's#^refs/heads/##' >"$GIT_DIR/head-name"
- ;;
- *)
- die "Bad HEAD - strange symbolic ref"
- ;;
- esac
+ trap 'git bisect--helper --bisect-clean-state' 0
+ trap 'exit 255' 1 2 3 15
#
- # Get rid of any old bisect state
+ # Check if we can proceed to the next bisect state.
#
- rm -f "$GIT_DIR/refs/heads/bisect"
- rm -rf "$GIT_DIR/refs/bisect/"
- mkdir "$GIT_DIR/refs/bisect"
- {
- printf "git-bisect start"
- sq "$@"
- } >"$GIT_DIR/BISECT_LOG"
- sq "$@" >"$GIT_DIR/BISECT_NAMES"
-}
-
-bisect_bad() {
- bisect_autostart
- case "$#" in
- 0)
- rev=$(git-rev-parse --verify HEAD) ;;
- 1)
- rev=$(git-rev-parse --verify "$1^{commit}") ;;
- *)
- usage ;;
- esac || exit
- echo "$rev" >"$GIT_DIR/refs/bisect/bad"
- echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
- echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
+ get_terms
bisect_auto_next
+
+ trap '-' 0
}
-bisect_good() {
- bisect_autostart
- case "$#" in
- 0) revs=$(git-rev-parse --verify HEAD) || exit ;;
- *) revs=$(git-rev-parse --revs-only --no-flags "$@") &&
- test '' != "$revs" || die "Bad rev input: $@" ;;
- esac
- for rev in $revs
+bisect_skip() {
+ all=''
+ for arg in "$@"
do
- rev=$(git-rev-parse --verify "$rev^{commit}") || exit
- echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
- echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
- echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
+ case "$arg" in
+ *..*)
+ revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
+ *)
+ revs=$(git rev-parse --sq-quote "$arg") ;;
+ esac
+ all="$all $revs"
done
- bisect_auto_next
+ eval bisect_state 'skip' $all
}
-bisect_next_check() {
- next_ok=no
- test -f "$GIT_DIR/refs/bisect/bad" &&
- case "$(cd "$GIT_DIR" && echo refs/bisect/good-*)" in
- refs/bisect/good-\*) ;;
- *) next_ok=yes ;;
- esac
- case "$next_ok,$1" in
- no,) false ;;
- no,fail)
- echo >&2 'You need to give me at least one good and one bad revisions.'
- exit 1 ;;
+bisect_state() {
+ bisect_autostart
+ state=$1
+ git bisect--helper --check-and-set-terms $state $TERM_GOOD $TERM_BAD || exit
+ get_terms
+ case "$#,$state" in
+ 0,*)
+ die "Please call 'bisect_state' with at least one argument." ;;
+ 1,"$TERM_BAD"|1,"$TERM_GOOD"|1,skip)
+ bisected_head=$(bisect_head)
+ rev=$(git rev-parse --verify "$bisected_head") ||
+ die "$(eval_gettext "Bad rev input: \$bisected_head")"
+ git bisect--helper --bisect-write "$state" "$rev" "$TERM_GOOD" "$TERM_BAD" || exit
+ git bisect--helper --check-expected-revs "$rev" ;;
+ 2,"$TERM_BAD"|*,"$TERM_GOOD"|*,skip)
+ shift
+ hash_list=''
+ for rev in "$@"
+ do
+ sha=$(git rev-parse --verify "$rev^{commit}") ||
+ die "$(eval_gettext "Bad rev input: \$rev")"
+ hash_list="$hash_list $sha"
+ done
+ for rev in $hash_list
+ do
+ git bisect--helper --bisect-write "$state" "$rev" "$TERM_GOOD" "$TERM_BAD" || exit
+ done
+ git bisect--helper --check-expected-revs $hash_list ;;
+ *,"$TERM_BAD")
+ die "$(eval_gettext "'git bisect \$TERM_BAD' can take only one argument.")" ;;
*)
- true ;;
+ usage ;;
esac
+ bisect_auto_next
}
bisect_auto_next() {
- bisect_next_check && bisect_next || :
+ git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD && bisect_next || :
}
bisect_next() {
- case "$#" in 0) ;; *) usage ;; esac
+ case "$#" in 0) ;; *) usage ;; esac
bisect_autostart
- bisect_next_check fail
- bad=$(git-rev-parse --verify refs/bisect/bad) &&
- good=$(git-rev-parse --sq --revs-only --not \
- $(cd "$GIT_DIR" && ls refs/bisect/good-*)) &&
- rev=$(eval "git-rev-list --bisect $good $bad -- $(cat $GIT_DIR/BISECT_NAMES)") || exit
- if [ -z "$rev" ]; then
- echo "$bad was both good and bad"
- exit 1
- fi
- if [ "$rev" = "$bad" ]; then
- echo "$rev is first bad commit"
- git-diff-tree --pretty $rev
- exit 0
+ git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD $TERM_GOOD|| exit
+
+ # Perform all bisection computation, display and checkout
+ git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
+ res=$?
+
+ # Check if we should exit because bisection is finished
+ if test $res -eq 10
+ then
+ bad_rev=$(git show-ref --hash --verify refs/bisect/$TERM_BAD)
+ bad_commit=$(git show-branch $bad_rev)
+ echo "# first $TERM_BAD commit: $bad_commit" >>"$GIT_DIR/BISECT_LOG"
+ exit 0
+ elif test $res -eq 2
+ then
+ echo "# only skipped commits left to test" >>"$GIT_DIR/BISECT_LOG"
+ good_revs=$(git for-each-ref --format="%(objectname)" "refs/bisect/$TERM_GOOD-*")
+ for skipped in $(git rev-list refs/bisect/$TERM_BAD --not $good_revs)
+ do
+ skipped_commit=$(git show-branch $skipped)
+ echo "# possible first $TERM_BAD commit: $skipped_commit" >>"$GIT_DIR/BISECT_LOG"
+ done
+ exit $res
fi
- nr=$(eval "git-rev-list $rev $good -- $(cat $GIT_DIR/BISECT_NAMES)" | wc -l) || exit
- echo "Bisecting: $nr revisions left to test after this"
- echo "$rev" > "$GIT_DIR/refs/heads/new-bisect"
- git checkout -q new-bisect || exit
- mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
- GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD refs/heads/bisect
- git-show-branch "$rev"
+
+ # Check for an error in the bisection process
+ test $res -ne 0 && exit $res
+
+ return 0
}
bisect_visualize() {
- bisect_next_check fail
- not=`cd "$GIT_DIR/refs" && echo bisect/good-*`
- eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
-}
+ git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit
-bisect_reset() {
- case "$#" in
- 0) if [ -s "$GIT_DIR/head-name" ]; then
- branch=`cat "$GIT_DIR/head-name"`
- else
- branch=master
- fi ;;
- 1) test -f "$GIT_DIR/refs/heads/$1" || {
- echo >&2 "$1 does not seem to be a valid branch"
- exit 1
- }
- branch="$1" ;;
- *)
- usage ;;
- esac
- if git checkout "$branch"; then
- rm -fr "$GIT_DIR/refs/bisect"
- rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
- rm -f "$GIT_DIR/BISECT_LOG"
- rm -f "$GIT_DIR/BISECT_NAMES"
- rm -f "$GIT_DIR/BISECT_RUN"
+ if test $# = 0
+ then
+ if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
+ type gitk >/dev/null 2>&1
+ then
+ set gitk
+ else
+ set git log
+ fi
+ else
+ case "$1" in
+ git*|tig) ;;
+ -*) set git log "$@" ;;
+ *) set git "$@" ;;
+ esac
fi
+
+ eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
}
bisect_replay () {
- test -r "$1" || {
- echo >&2 "cannot read $1 for replaying"
- exit 1
- }
- bisect_reset
- while read bisect command rev
+ file="$1"
+ test "$#" -eq 1 || die "$(gettext "No logfile given")"
+ test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
+ git bisect--helper --bisect-reset || exit
+ while read git bisect command rev
do
- test "$bisect" = "git-bisect" || continue
+ test "$git $bisect" = "git bisect" || test "$git" = "git-bisect" || continue
+ if test "$git" = "git-bisect"
+ then
+ rev="$command"
+ command="$bisect"
+ fi
+ get_terms
+ git bisect--helper --check-and-set-terms "$command" "$TERM_GOOD" "$TERM_BAD" || exit
+ get_terms
case "$command" in
start)
cmd="bisect_start $rev"
- eval "$cmd"
- ;;
- good)
- echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
- echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
- echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
- ;;
- bad)
- echo "$rev" >"$GIT_DIR/refs/bisect/bad"
- echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
- echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
- ;;
+ eval "$cmd" ;;
+ "$TERM_GOOD"|"$TERM_BAD"|skip)
+ git bisect--helper --bisect-write "$command" "$rev" "$TERM_GOOD" "$TERM_BAD" || exit;;
+ terms)
+ git bisect--helper --bisect-terms $rev || exit;;
*)
- echo >&2 "?? what are you talking about?"
- exit 1 ;;
+ die "$(gettext "?? what are you talking about?")" ;;
esac
- done <"$1"
+ done <"$file"
bisect_auto_next
}
bisect_run () {
- while true
- do
- echo "running $@"
- "$@"
- res=$?
-
- # Check for really bad run error.
- if [ $res -lt 0 -o $res -ge 128 ]; then
- echo >&2 "bisect run failed:"
- echo >&2 "exit code $res from '$@' is < 0 or >= 128"
- exit $res
- fi
-
- # Use "bisect_good" or "bisect_bad"
- # depending on run success or failure.
- if [ $res -gt 0 ]; then
- next_bisect='bisect_bad'
- else
- next_bisect='bisect_good'
- fi
-
- # We have to use a subshell because bisect_good or
- # bisect_bad functions can exit.
- ( $next_bisect > "$GIT_DIR/BISECT_RUN" )
- res=$?
-
- cat "$GIT_DIR/BISECT_RUN"
-
- if [ $res -ne 0 ]; then
- echo >&2 "bisect run failed:"
- echo >&2 "$next_bisect exited with error code $res"
- exit $res
- fi
-
- if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
- echo "bisect run success"
- exit 0;
- fi
-
- done
+ git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit
+
+ test -n "$*" || die "$(gettext "bisect run failed: no command provided.")"
+
+ while true
+ do
+ command="$@"
+ eval_gettextln "running \$command"
+ "$@"
+ res=$?
+
+ # Check for really bad run error.
+ if [ $res -lt 0 -o $res -ge 128 ]
+ then
+ eval_gettextln "bisect run failed:
+exit code \$res from '\$command' is < 0 or >= 128" >&2
+ exit $res
+ fi
+
+ # Find current state depending on run success or failure.
+ # A special exit code of 125 means cannot test.
+ if [ $res -eq 125 ]
+ then
+ state='skip'
+ elif [ $res -gt 0 ]
+ then
+ state="$TERM_BAD"
+ else
+ state="$TERM_GOOD"
+ fi
+
+ # We have to use a subshell because "bisect_state" can exit.
+ ( bisect_state $state >"$GIT_DIR/BISECT_RUN" )
+ res=$?
+
+ cat "$GIT_DIR/BISECT_RUN"
+
+ if sane_grep "first $TERM_BAD commit could be any of" "$GIT_DIR/BISECT_RUN" \
+ >/dev/null
+ then
+ gettextln "bisect run cannot continue any more" >&2
+ exit $res
+ fi
+
+ if [ $res -ne 0 ]
+ then
+ eval_gettextln "bisect run failed:
+'bisect_state \$state' exited with error code \$res" >&2
+ exit $res
+ fi
+
+ if sane_grep "is the first $TERM_BAD commit" "$GIT_DIR/BISECT_RUN" >/dev/null
+ then
+ gettextln "bisect run success"
+ exit 0;
+ fi
+
+ done
+}
+
+bisect_log () {
+ test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
+ cat "$GIT_DIR/BISECT_LOG"
}
+get_terms () {
+ if test -s "$GIT_DIR/BISECT_TERMS"
+ then
+ {
+ read TERM_BAD
+ read TERM_GOOD
+ } <"$GIT_DIR/BISECT_TERMS"
+ fi
+}
case "$#" in
0)
- usage ;;
+ usage ;;
*)
- cmd="$1"
- shift
- case "$cmd" in
- start)
- bisect_start "$@" ;;
- bad)
- bisect_bad "$@" ;;
- good)
- bisect_good "$@" ;;
- next)
- # Not sure we want "next" at the UI level anymore.
- bisect_next "$@" ;;
- visualize)
- bisect_visualize "$@" ;;
- reset)
- bisect_reset "$@" ;;
- replay)
- bisect_replay "$@" ;;
- log)
- cat "$GIT_DIR/BISECT_LOG" ;;
- run)
- bisect_run "$@" ;;
- *)
- usage ;;
- esac
+ cmd="$1"
+ get_terms
+ shift
+ case "$cmd" in
+ help)
+ git bisect -h ;;
+ start)
+ bisect_start "$@" ;;
+ bad|good|new|old|"$TERM_BAD"|"$TERM_GOOD")
+ bisect_state "$cmd" "$@" ;;
+ skip)
+ bisect_skip "$@" ;;
+ next)
+ # Not sure we want "next" at the UI level anymore.
+ bisect_next "$@" ;;
+ visualize|view)
+ bisect_visualize "$@" ;;
+ reset)
+ git bisect--helper --bisect-reset "$@" ;;
+ replay)
+ bisect_replay "$@" ;;
+ log)
+ bisect_log ;;
+ run)
+ bisect_run "$@" ;;
+ terms)
+ git bisect--helper --bisect-terms "$@" || exit;;
+ *)
+ usage ;;
+ esac
esac