1#!/bin/sh
23
USAGE='[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-setup
15require_work_tree
1617
sq() {
18@@PERL@@ -e '
19for (@ARGV) {
20s/'\''/'\'\\\\\'\''/g;
21print " '\''$_'\''";
22}
23print "\n";
24' "$@"
25}
2627
bisect_autostart() {
28test -d "$GIT_DIR/refs/bisect" || {
29echo >&2 'You need to start by "git bisect start"'
30if test -t 0
31then
32echo >&2 -n 'Do you want me to do it for you [Y/n]? '
33read yesno
34case "$yesno" in
35[Nn]*)
36exit ;;
37esac
38bisect_start
39else
40exit 1
41fi
42}
43}
4445
bisect_start() {
46#
47# Verify HEAD. If we were bisecting before this, reset to the
48# 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" in
53refs/heads/bisect)
54if [ -s "$GIT_DIR/head-name" ]; then
55branch=`cat "$GIT_DIR/head-name"`
56else
57branch=master
58fi
59git checkout $branch || exit
60;;
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;;
68esac
6970
#
71# Get rid of any old bisect state
72#
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}
8283
bisect_bad() {
84bisect_autostart
85case "$#" in
860)
87rev=$(git-rev-parse --verify HEAD) ;;
881)
89rev=$(git-rev-parse --verify "$1^{commit}") ;;
90*)
91usage ;;
92esac || exit
93echo "$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_next
97}
9899
bisect_good() {
100bisect_autostart
101case "$#" in
1020) revs=$(git-rev-parse --verify HEAD) || exit ;;
103*) revs=$(git-rev-parse --revs-only --no-flags "$@") &&
104test '' != "$revs" || die "Bad rev input: $@" ;;
105esac
106for rev in $revs
107do
108rev=$(git-rev-parse --verify "$rev^{commit}") || exit
109echo "$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"
112done
113bisect_auto_next
114}
115116
bisect_next_check() {
117next_ok=no
118test -f "$GIT_DIR/refs/bisect/bad" &&
119case "$(cd "$GIT_DIR" && echo refs/bisect/good-*)" in
120refs/bisect/good-\*) ;;
121*) next_ok=yes ;;
122esac
123case "$next_ok,$1" in
124no,) false ;;
125no,fail)
126echo >&2 'You need to give me at least one good and one bad revisions.'
127exit 1 ;;
128*)
129true ;;
130esac
131}
132133
bisect_auto_next() {
134bisect_next_check && bisect_next || :
135}
136137
bisect_next() {
138case "$#" in 0) ;; *) usage ;; esac
139bisect_autostart
140bisect_next_check fail
141bad=$(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)") || exit
145if [ -z "$rev" ]; then
146echo "$bad was both good and bad"
147exit 1
148fi
149if [ "$rev" = "$bad" ]; then
150echo "$rev is first bad commit"
151git-diff-tree --pretty $rev
152exit 0
153fi
154nr=$(eval "git-rev-list $rev $good -- $(cat $GIT_DIR/BISECT_NAMES)" | wc -l) || exit
155echo "Bisecting: $nr revisions left to test after this"
156echo "$rev" > "$GIT_DIR/refs/heads/new-bisect"
157git checkout -q new-bisect || exit
158mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
159GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD refs/heads/bisect
160git-show-branch "$rev"
161}
162163
bisect_visualize() {
164bisect_next_check fail
165not=`cd "$GIT_DIR/refs" && echo bisect/good-*`
166eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
167}
168169
bisect_reset() {
170case "$#" in
1710) if [ -s "$GIT_DIR/head-name" ]; then
172branch=`cat "$GIT_DIR/head-name"`
173else
174branch=master
175fi ;;
1761) test -f "$GIT_DIR/refs/heads/$1" || {
177echo >&2 "$1 does not seem to be a valid branch"
178exit 1
179}
180branch="$1" ;;
181*)
182usage ;;
183esac
184if git checkout "$branch"; then
185rm -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"
190fi
191}
192193
bisect_replay () {
194test -r "$1" || {
195echo >&2 "cannot read $1 for replaying"
196exit 1
197}
198bisect_reset
199while read bisect command rev
200do
201test "$bisect" = "git-bisect" || continue
202case "$command" in
203start)
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 ;;
220esac
221done <"$1"
222bisect_auto_next
223}
224225
bisect_run () {
226while true
227do
228echo "running $@"
229"$@"
230res=$?
231232
# Check for really bad run error.
233if [ $res -lt 0 -o $res -ge 128 ]; then
234echo >&2 "bisect run failed:"
235echo >&2 "exit code $res from '$@' is < 0 or >= 128"
236exit $res
237fi
238239
# Use "bisect_good" or "bisect_bad"
240# depending on run success or failure.
241if [ $res -gt 0 ]; then
242next_bisect='bisect_bad'
243else
244next_bisect='bisect_good'
245fi
246247
# We have to use a subshell because bisect_good or
248# bisect_bad functions can exit.
249( $next_bisect > "$GIT_DIR/BISECT_RUN" )
250res=$?
251252
cat "$GIT_DIR/BISECT_RUN"
253254
if [ $res -ne 0 ]; then
255echo >&2 "bisect run failed:"
256echo >&2 "$next_bisect exited with error code $res"
257exit $res
258fi
259260
if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
261echo "bisect run success"
262exit 0;
263fi
264265
done
266}
267268
269
case "$#" in
2700)
271usage ;;
272*)
273cmd="$1"
274shift
275case "$cmd" in
276start)
277bisect_start "$@" ;;
278bad)
279bisect_bad "$@" ;;
280good)
281bisect_good "$@" ;;
282next)
283# Not sure we want "next" at the UI level anymore.
284bisect_next "$@" ;;
285visualize)
286bisect_visualize "$@" ;;
287reset)
288bisect_reset "$@" ;;
289replay)
290bisect_replay "$@" ;;
291log)
292cat "$GIT_DIR/BISECT_LOG" ;;
293run)
294bisect_run "$@" ;;
295*)
296usage ;;
297esac
298esac