bisect: replace hardcoded "bad|good" by variables
[git.git] / git-bisect.sh
blobfcbed2263b7500570891054ebe9af6cfe99f4abf
1 #!/bin/sh
3 USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]'
4 LONG_USAGE='git bisect help
5 print this long help message.
6 git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...]
7 reset bisect state and start bisection.
8 git bisect bad [<rev>]
9 mark <rev> a known-bad revision.
10 git bisect good [<rev>...]
11 mark <rev>... known-good revisions.
12 git bisect skip [(<rev>|<range>)...]
13 mark <rev>... untestable revisions.
14 git bisect next
15 find next bisection to test and check it out.
16 git bisect reset [<commit>]
17 finish bisection search and go back to commit.
18 git bisect visualize
19 show bisect status in gitk.
20 git bisect replay <logfile>
21 replay bisection log.
22 git bisect log
23 show bisect log.
24 git bisect run <cmd>...
25 use <cmd>... to automatically bisect.
27 Please use "git help bisect" to get the full man page.'
29 OPTIONS_SPEC=
30 . git-sh-setup
31 . git-sh-i18n
33 _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
34 _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
35 TERM_BAD=bad
36 TERM_GOOD=good
38 bisect_head()
40 if test -f "$GIT_DIR/BISECT_HEAD"
41 then
42 echo BISECT_HEAD
43 else
44 echo HEAD
48 bisect_autostart() {
49 test -s "$GIT_DIR/BISECT_START" || {
50 gettextln "You need to start by \"git bisect start\"" >&2
51 if test -t 0
52 then
53 # TRANSLATORS: Make sure to include [Y] and [n] in your
54 # translation. The program will only accept English input
55 # at this point.
56 gettext "Do you want me to do it for you [Y/n]? " >&2
57 read yesno
58 case "$yesno" in
59 [Nn]*)
60 exit ;;
61 esac
62 bisect_start
63 else
64 exit 1
69 bisect_start() {
71 # Check for one bad and then some good revisions.
73 has_double_dash=0
74 for arg; do
75 case "$arg" in --) has_double_dash=1; break ;; esac
76 done
77 orig_args=$(git rev-parse --sq-quote "$@")
78 bad_seen=0
79 eval=''
80 if test "z$(git rev-parse --is-bare-repository)" != zfalse
81 then
82 mode=--no-checkout
83 else
84 mode=''
86 while [ $# -gt 0 ]; do
87 arg="$1"
88 case "$arg" in
89 --)
90 shift
91 break
93 --no-checkout)
94 mode=--no-checkout
95 shift ;;
96 --*)
97 die "$(eval_gettext "unrecognised option: '\$arg'")" ;;
99 rev=$(git rev-parse -q --verify "$arg^{commit}") || {
100 test $has_double_dash -eq 1 &&
101 die "$(eval_gettext "'\$arg' does not appear to be a valid revision")"
102 break
104 case $bad_seen in
105 0) state=$TERM_BAD ; bad_seen=1 ;;
106 *) state=$TERM_GOOD ;;
107 esac
108 eval="$eval bisect_write '$state' '$rev' 'nolog' &&"
109 shift
111 esac
112 done
115 # Verify HEAD.
117 head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
118 head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
119 die "$(gettext "Bad HEAD - I need a HEAD")"
122 # Check if we are bisecting.
124 start_head=''
125 if test -s "$GIT_DIR/BISECT_START"
126 then
127 # Reset to the rev from where we started.
128 start_head=$(cat "$GIT_DIR/BISECT_START")
129 if test "z$mode" != "z--no-checkout"
130 then
131 git checkout "$start_head" -- ||
132 die "$(eval_gettext "Checking out '\$start_head' failed. Try 'git bisect reset <valid-branch>'.")"
134 else
135 # Get rev from where we start.
136 case "$head" in
137 refs/heads/*|$_x40)
138 # This error message should only be triggered by
139 # cogito usage, and cogito users should understand
140 # it relates to cg-seek.
141 [ -s "$GIT_DIR/head-name" ] &&
142 die "$(gettext "won't bisect on cg-seek'ed tree")"
143 start_head="${head#refs/heads/}"
146 die "$(gettext "Bad HEAD - strange symbolic ref")"
148 esac
152 # Get rid of any old bisect state.
154 bisect_clean_state || exit
157 # Change state.
158 # In case of mistaken revs or checkout error, or signals received,
159 # "bisect_auto_next" below may exit or misbehave.
160 # We have to trap this to be able to clean up using
161 # "bisect_clean_state".
163 trap 'bisect_clean_state' 0
164 trap 'exit 255' 1 2 3 15
167 # Write new start state.
169 echo "$start_head" >"$GIT_DIR/BISECT_START" && {
170 test "z$mode" != "z--no-checkout" ||
171 git update-ref --no-deref BISECT_HEAD "$start_head"
172 } &&
173 git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
174 eval "$eval true" &&
175 echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
177 # Check if we can proceed to the next bisect state.
179 bisect_auto_next
181 trap '-' 0
184 bisect_write() {
185 state="$1"
186 rev="$2"
187 nolog="$3"
188 case "$state" in
189 "$TERM_BAD")
190 tag="$state" ;;
191 "$TERM_GOOD"|skip)
192 tag="$state"-"$rev" ;;
194 die "$(eval_gettext "Bad bisect_write argument: \$state")" ;;
195 esac
196 git update-ref "refs/bisect/$tag" "$rev" || exit
197 echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG"
198 test -n "$nolog" || echo "git bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
201 is_expected_rev() {
202 test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
203 test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
206 check_expected_revs() {
207 for _rev in "$@"; do
208 if ! is_expected_rev "$_rev"
209 then
210 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
211 rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
212 return
214 done
217 bisect_skip() {
218 all=''
219 for arg in "$@"
221 case "$arg" in
222 *..*)
223 revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
225 revs=$(git rev-parse --sq-quote "$arg") ;;
226 esac
227 all="$all $revs"
228 done
229 eval bisect_state 'skip' $all
232 bisect_state() {
233 bisect_autostart
234 state=$1
235 case "$#,$state" in
236 0,*)
237 die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
238 1,"$TERM_BAD"|1,"$TERM_GOOD"|1,skip)
239 rev=$(git rev-parse --verify $(bisect_head)) ||
240 die "$(gettext "Bad rev input: $(bisect_head)")"
241 bisect_write "$state" "$rev"
242 check_expected_revs "$rev" ;;
243 2,"$TERM_BAD"|*,"$TERM_GOOD"|*,skip)
244 shift
245 hash_list=''
246 for rev in "$@"
248 sha=$(git rev-parse --verify "$rev^{commit}") ||
249 die "$(eval_gettext "Bad rev input: \$rev")"
250 hash_list="$hash_list $sha"
251 done
252 for rev in $hash_list
254 bisect_write "$state" "$rev"
255 done
256 check_expected_revs $hash_list ;;
257 *,"$TERM_BAD")
258 die "$(eval_gettext "'git bisect \$TERM_BAD' can take only one argument.")" ;;
260 usage ;;
261 esac
262 bisect_auto_next
265 bisect_next_check() {
266 missing_good= missing_bad=
267 git show-ref -q --verify refs/bisect/$TERM_BAD || missing_bad=t
268 test -n "$(git for-each-ref "refs/bisect/$TERM_GOOD-*")" || missing_good=t
270 case "$missing_good,$missing_bad,$1" in
271 ,,*)
272 : have both $TERM_GOOD and $TERM_BAD - ok
275 # do not have both but not asked to fail - just report.
276 false
278 t,,"$TERM_GOOD")
279 # have bad but not good. we could bisect although
280 # this is less optimum.
281 eval_gettextln "Warning: bisecting only with a \$TERM_BAD commit." >&2
282 if test -t 0
283 then
284 # TRANSLATORS: Make sure to include [Y] and [n] in your
285 # translation. The program will only accept English input
286 # at this point.
287 gettext "Are you sure [Y/n]? " >&2
288 read yesno
289 case "$yesno" in [Nn]*) exit 1 ;; esac
291 : bisect without $TERM_GOOD...
295 if test -s "$GIT_DIR/BISECT_START"
296 then
297 gettextln "You need to give me at least one good and one bad revision.
298 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
299 else
300 gettextln "You need to start by \"git bisect start\".
301 You then need to give me at least one good and one bad revision.
302 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
304 exit 1 ;;
305 esac
308 bisect_auto_next() {
309 bisect_next_check && bisect_next || :
312 bisect_next() {
313 case "$#" in 0) ;; *) usage ;; esac
314 bisect_autostart
315 bisect_next_check $TERM_GOOD
317 # Perform all bisection computation, display and checkout
318 git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
319 res=$?
321 # Check if we should exit because bisection is finished
322 if test $res -eq 10
323 then
324 bad_rev=$(git show-ref --hash --verify refs/bisect/$TERM_BAD)
325 bad_commit=$(git show-branch $bad_rev)
326 echo "# first $TERM_BAD commit: $bad_commit" >>"$GIT_DIR/BISECT_LOG"
327 exit 0
328 elif test $res -eq 2
329 then
330 echo "# only skipped commits left to test" >>"$GIT_DIR/BISECT_LOG"
331 good_revs=$(git for-each-ref --format="%(objectname)" "refs/bisect/$TERM_GOOD-*")
332 for skipped in $(git rev-list refs/bisect/$TERM_BAD --not $good_revs)
334 skipped_commit=$(git show-branch $skipped)
335 echo "# possible first $TERM_BAD commit: $skipped_commit" >>"$GIT_DIR/BISECT_LOG"
336 done
337 exit $res
340 # Check for an error in the bisection process
341 test $res -ne 0 && exit $res
343 return 0
346 bisect_visualize() {
347 bisect_next_check fail
349 if test $# = 0
350 then
351 if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
352 type gitk >/dev/null 2>&1
353 then
354 set gitk
355 else
356 set git log
358 else
359 case "$1" in
360 git*|tig) ;;
361 -*) set git log "$@" ;;
362 *) set git "$@" ;;
363 esac
366 eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
369 bisect_reset() {
370 test -s "$GIT_DIR/BISECT_START" || {
371 gettextln "We are not bisecting."
372 return
374 case "$#" in
375 0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
376 1) git rev-parse --quiet --verify "$1^{commit}" >/dev/null || {
377 invalid="$1"
378 die "$(eval_gettext "'\$invalid' is not a valid commit")"
380 branch="$1" ;;
382 usage ;;
383 esac
385 if ! test -f "$GIT_DIR/BISECT_HEAD" && ! git checkout "$branch" --
386 then
387 die "$(eval_gettext "Could not check out original HEAD '\$branch'.
388 Try 'git bisect reset <commit>'.")"
390 bisect_clean_state
393 bisect_clean_state() {
394 # There may be some refs packed during bisection.
395 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
396 while read ref hash
398 git update-ref -d $ref $hash || exit
399 done
400 rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
401 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
402 rm -f "$GIT_DIR/BISECT_LOG" &&
403 rm -f "$GIT_DIR/BISECT_NAMES" &&
404 rm -f "$GIT_DIR/BISECT_RUN" &&
405 # Cleanup head-name if it got left by an old version of git-bisect
406 rm -f "$GIT_DIR/head-name" &&
407 git update-ref -d --no-deref BISECT_HEAD &&
408 # clean up BISECT_START last
409 rm -f "$GIT_DIR/BISECT_START"
412 bisect_replay () {
413 file="$1"
414 test "$#" -eq 1 || die "$(gettext "No logfile given")"
415 test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
416 bisect_reset
417 while read git bisect command rev
419 test "$git $bisect" = "git bisect" || test "$git" = "git-bisect" || continue
420 if test "$git" = "git-bisect"
421 then
422 rev="$command"
423 command="$bisect"
425 case "$command" in
426 start)
427 cmd="bisect_start $rev"
428 eval "$cmd" ;;
429 $TERM_GOOD|$TERM_BAD|skip)
430 bisect_write "$command" "$rev" ;;
432 die "$(gettext "?? what are you talking about?")" ;;
433 esac
434 done <"$file"
435 bisect_auto_next
438 bisect_run () {
439 bisect_next_check fail
441 while true
443 command="$@"
444 eval_gettextln "running \$command"
445 "$@"
446 res=$?
448 # Check for really bad run error.
449 if [ $res -lt 0 -o $res -ge 128 ]
450 then
451 eval_gettextln "bisect run failed:
452 exit code \$res from '\$command' is < 0 or >= 128" >&2
453 exit $res
456 # Find current state depending on run success or failure.
457 # A special exit code of 125 means cannot test.
458 if [ $res -eq 125 ]
459 then
460 state='skip'
461 elif [ $res -gt 0 ]
462 then
463 state="$TERM_BAD"
464 else
465 state="$TERM_GOOD"
468 # We have to use a subshell because "bisect_state" can exit.
469 ( bisect_state $state >"$GIT_DIR/BISECT_RUN" )
470 res=$?
472 cat "$GIT_DIR/BISECT_RUN"
474 if sane_grep "first $TERM_BAD commit could be any of" "$GIT_DIR/BISECT_RUN" \
475 >/dev/null
476 then
477 gettextln "bisect run cannot continue any more" >&2
478 exit $res
481 if [ $res -ne 0 ]
482 then
483 eval_gettextln "bisect run failed:
484 'bisect_state \$state' exited with error code \$res" >&2
485 exit $res
488 if sane_grep "is the first $TERM_BAD commit" "$GIT_DIR/BISECT_RUN" >/dev/null
489 then
490 gettextln "bisect run success"
491 exit 0;
494 done
497 bisect_log () {
498 test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
499 cat "$GIT_DIR/BISECT_LOG"
502 case "$#" in
504 usage ;;
506 cmd="$1"
507 shift
508 case "$cmd" in
509 help)
510 git bisect -h ;;
511 start)
512 bisect_start "$@" ;;
513 bad|good)
514 bisect_state "$cmd" "$@" ;;
515 skip)
516 bisect_skip "$@" ;;
517 next)
518 # Not sure we want "next" at the UI level anymore.
519 bisect_next "$@" ;;
520 visualize|view)
521 bisect_visualize "$@" ;;
522 reset)
523 bisect_reset "$@" ;;
524 replay)
525 bisect_replay "$@" ;;
526 log)
527 bisect_log ;;
528 run)
529 bisect_run "$@" ;;
531 usage ;;
532 esac
533 esac