434d036fe4
As a convenience measure, 'bisect bad' or 'bisect good' automatically does 'bisect next' when it knows it can, but the result of that test to see if it can was leaking through as the exit code from the whole thing, which was bad. Noticed by Anton Blanchard. Signed-off-by: Junio C Hamano <junkio@cox.net>
223 lines
5.1 KiB
Bash
Executable File
223 lines
5.1 KiB
Bash
Executable File
#!/bin/sh
|
|
. git-sh-setup || dir "Not a git archive"
|
|
|
|
usage() {
|
|
echo >&2 'usage: git bisect [start|bad|good|next|reset|visualize]
|
|
git bisect start 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.'
|
|
exit 1
|
|
}
|
|
|
|
bisect_autostart() {
|
|
test -d "$GIT_DIR/refs/bisect" || {
|
|
echo >&2 'You need to start by "git bisect start"'
|
|
if test -t 0
|
|
then
|
|
echo >&2 -n 'Do you want me to do it for you [Y/n]? '
|
|
read yesno
|
|
case "$yesno" in
|
|
[Nn]*)
|
|
exit ;;
|
|
esac
|
|
bisect_start
|
|
else
|
|
exit 1
|
|
fi
|
|
}
|
|
}
|
|
|
|
bisect_start() {
|
|
case "$#" in 0) ;; *) usage ;; esac
|
|
#
|
|
# Verify HEAD. If we were bisecting before this, reset to the
|
|
# top-of-line master first!
|
|
#
|
|
head=$(readlink $GIT_DIR/HEAD) || die "Bad HEAD - I need a symlink"
|
|
case "$head" in
|
|
refs/heads/bisect*)
|
|
git checkout master || exit
|
|
;;
|
|
refs/heads/*)
|
|
;;
|
|
*)
|
|
die "Bad HEAD - strange symlink"
|
|
;;
|
|
esac
|
|
|
|
#
|
|
# Get rid of any old bisect state
|
|
#
|
|
rm -f "$GIT_DIR/refs/heads/bisect"
|
|
rm -rf "$GIT_DIR/refs/bisect/"
|
|
mkdir "$GIT_DIR/refs/bisect"
|
|
echo "git-bisect start" >"$GIT_DIR/BISECT_LOG"
|
|
}
|
|
|
|
bisect_bad() {
|
|
bisect_autostart
|
|
case "$#" in
|
|
0)
|
|
rev=$(git-rev-parse --verify HEAD) ;;
|
|
1)
|
|
rev=$(git-rev-parse --verify "$1") ;;
|
|
*)
|
|
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"
|
|
bisect_auto_next
|
|
}
|
|
|
|
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
|
|
do
|
|
rev=$(git-rev-parse --verify "$rev") || 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"
|
|
done
|
|
bisect_auto_next
|
|
}
|
|
|
|
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 ;;
|
|
*)
|
|
true ;;
|
|
esac
|
|
}
|
|
|
|
bisect_auto_next() {
|
|
bisect_next_check && bisect_next || :
|
|
}
|
|
|
|
bisect_next() {
|
|
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") || 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
|
|
fi
|
|
nr=$(eval "git-rev-list $rev $good" | wc -l) || exit
|
|
echo "Bisecting: $nr revisions left to test after this"
|
|
echo "$rev" > "$GIT_DIR/refs/heads/new-bisect"
|
|
git checkout new-bisect || exit
|
|
mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
|
|
ln -sf refs/heads/bisect "$GIT_DIR/HEAD"
|
|
git-show-branch "$rev"
|
|
}
|
|
|
|
bisect_visualize() {
|
|
bisect_next_check fail
|
|
gitk bisect/bad --not `cd "$GIT_DIR/refs" && echo bisect/good-*`
|
|
}
|
|
|
|
bisect_reset() {
|
|
case "$#" in
|
|
0) branch=master ;;
|
|
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
|
|
git checkout "$branch" &&
|
|
rm -fr "$GIT_DIR/refs/bisect"
|
|
rm -f "$GIT_DIR/refs/heads/bisect"
|
|
rm -f "$GIT_DIR/BISECT_LOG"
|
|
}
|
|
|
|
bisect_replay () {
|
|
test -r "$1" || {
|
|
echo >&2 "cannot read $1 for replaying"
|
|
exit 1
|
|
}
|
|
bisect_reset
|
|
while read bisect command rev
|
|
do
|
|
test "$bisect" = "git-bisect" || continue
|
|
case "$command" in
|
|
start)
|
|
bisect_start
|
|
;;
|
|
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"
|
|
;;
|
|
*)
|
|
echo >&2 "?? what are you talking about?"
|
|
exit 1 ;;
|
|
esac
|
|
done <"$1"
|
|
bisect_auto_next
|
|
}
|
|
|
|
case "$#" in
|
|
0)
|
|
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" ;;
|
|
*)
|
|
usage ;;
|
|
esac
|
|
esac
|