git wrapper: Make while loop more reader-friendly
[git/platforms.git] / git-bisect.sh
blob17a35f6adc79480d0533a4ff98b2817c836a7e78
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 [<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 [<branch>]
17 finish bisection search and go back to branch.
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 require_work_tree
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"
36 sq() {
37 @@PERL@@ -e '
38 for (@ARGV) {
39 s/'\''/'\'\\\\\'\''/g;
40 print " '\''$_'\''";
42 print "\n";
43 ' "$@"
46 bisect_autostart() {
47 test -s "$GIT_DIR/BISECT_START" || {
48 echo >&2 'You need to start by "git bisect start"'
49 if test -t 0
50 then
51 echo >&2 -n 'Do you want me to do it for you [Y/n]? '
52 read yesno
53 case "$yesno" in
54 [Nn]*)
55 exit ;;
56 esac
57 bisect_start
58 else
59 exit 1
64 bisect_start() {
66 # Verify HEAD.
68 head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
69 head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
70 die "Bad HEAD - I need a HEAD"
73 # Check if we are bisecting.
75 start_head=''
76 if test -s "$GIT_DIR/BISECT_START"
77 then
78 # Reset to the rev from where we started.
79 start_head=$(cat "$GIT_DIR/BISECT_START")
80 git checkout "$start_head" || exit
81 else
82 # Get rev from where we start.
83 case "$head" in
84 refs/heads/*|$_x40)
85 # This error message should only be triggered by
86 # cogito usage, and cogito users should understand
87 # it relates to cg-seek.
88 [ -s "$GIT_DIR/head-name" ] &&
89 die "won't bisect on seeked tree"
90 start_head="${head#refs/heads/}"
93 die "Bad HEAD - strange symbolic ref"
95 esac
99 # Get rid of any old bisect state.
101 bisect_clean_state || exit
104 # Check for one bad and then some good revisions.
106 has_double_dash=0
107 for arg; do
108 case "$arg" in --) has_double_dash=1; break ;; esac
109 done
110 orig_args=$(sq "$@")
111 bad_seen=0
112 eval=''
113 while [ $# -gt 0 ]; do
114 arg="$1"
115 case "$arg" in
117 shift
118 break
121 rev=$(git rev-parse -q --verify "$arg^{commit}") || {
122 test $has_double_dash -eq 1 &&
123 die "'$arg' does not appear to be a valid revision"
124 break
126 case $bad_seen in
127 0) state='bad' ; bad_seen=1 ;;
128 *) state='good' ;;
129 esac
130 eval="$eval bisect_write '$state' '$rev' 'nolog'; "
131 shift
133 esac
134 done
137 # Change state.
138 # In case of mistaken revs or checkout error, or signals received,
139 # "bisect_auto_next" below may exit or misbehave.
140 # We have to trap this to be able to clean up using
141 # "bisect_clean_state".
143 trap 'bisect_clean_state' 0
144 trap 'exit 255' 1 2 3 15
147 # Write new start state.
149 echo "$start_head" >"$GIT_DIR/BISECT_START" &&
150 sq "$@" >"$GIT_DIR/BISECT_NAMES" &&
151 eval "$eval" &&
152 echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
154 # Check if we can proceed to the next bisect state.
156 bisect_auto_next
158 trap '-' 0
161 bisect_write() {
162 state="$1"
163 rev="$2"
164 nolog="$3"
165 case "$state" in
166 bad) tag="$state" ;;
167 good|skip) tag="$state"-"$rev" ;;
168 *) die "Bad bisect_write argument: $state" ;;
169 esac
170 git update-ref "refs/bisect/$tag" "$rev" || exit
171 echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG"
172 test -n "$nolog" || echo "git bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
175 is_expected_rev() {
176 test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
177 test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
180 mark_expected_rev() {
181 echo "$1" > "$GIT_DIR/BISECT_EXPECTED_REV"
184 check_expected_revs() {
185 for _rev in "$@"; do
186 if ! is_expected_rev "$_rev"; then
187 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
188 rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
189 return
191 done
194 bisect_skip() {
195 all=''
196 for arg in "$@"
198 case "$arg" in
199 *..*)
200 revs=$(git rev-list "$arg") || die "Bad rev input: $arg" ;;
202 revs=$(sq "$arg") ;;
203 esac
204 all="$all $revs"
205 done
206 eval bisect_state 'skip' $all
209 bisect_state() {
210 bisect_autostart
211 state=$1
212 case "$#,$state" in
213 0,*)
214 die "Please call 'bisect_state' with at least one argument." ;;
215 1,bad|1,good|1,skip)
216 rev=$(git rev-parse --verify HEAD) ||
217 die "Bad rev input: HEAD"
218 bisect_write "$state" "$rev"
219 check_expected_revs "$rev" ;;
220 2,bad|*,good|*,skip)
221 shift
222 eval=''
223 for rev in "$@"
225 sha=$(git rev-parse --verify "$rev^{commit}") ||
226 die "Bad rev input: $rev"
227 eval="$eval bisect_write '$state' '$sha'; "
228 done
229 eval "$eval"
230 check_expected_revs "$@" ;;
231 *,bad)
232 die "'git bisect bad' can take only one argument." ;;
234 usage ;;
235 esac
236 bisect_auto_next
239 bisect_next_check() {
240 missing_good= missing_bad=
241 git show-ref -q --verify refs/bisect/bad || missing_bad=t
242 test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
244 case "$missing_good,$missing_bad,$1" in
245 ,,*)
246 : have both good and bad - ok
249 # do not have both but not asked to fail - just report.
250 false
252 t,,good)
253 # have bad but not good. we could bisect although
254 # this is less optimum.
255 echo >&2 'Warning: bisecting only with a bad commit.'
256 if test -t 0
257 then
258 printf >&2 'Are you sure [Y/n]? '
259 read yesno
260 case "$yesno" in [Nn]*) exit 1 ;; esac
262 : bisect without good...
265 THEN=''
266 test -s "$GIT_DIR/BISECT_START" || {
267 echo >&2 'You need to start by "git bisect start".'
268 THEN='then '
270 echo >&2 'You '$THEN'need to give me at least one good' \
271 'and one bad revisions.'
272 echo >&2 '(You can use "git bisect bad" and' \
273 '"git bisect good" for that.)'
274 exit 1 ;;
275 esac
278 bisect_auto_next() {
279 bisect_next_check && bisect_next || :
282 filter_skipped() {
283 _eval="$1"
284 _skip="$2"
286 if [ -z "$_skip" ]; then
287 eval "$_eval"
288 return
291 # Let's parse the output of:
292 # "git rev-list --bisect-vars --bisect-all ..."
293 eval "$_eval" | while read hash line
295 case "$VARS,$FOUND,$TRIED,$hash" in
296 # We display some vars.
297 1,*,*,*) echo "$hash $line" ;;
299 # Split line.
300 ,*,*,---*) ;;
302 # We had nothing to search.
303 ,,,bisect_rev*)
304 echo "bisect_rev="
305 VARS=1
308 # We did not find a good bisect rev.
309 # This should happen only if the "bad"
310 # commit is also a "skip" commit.
311 ,,*,bisect_rev*)
312 echo "bisect_rev=$TRIED"
313 VARS=1
316 # We are searching.
317 ,,*,*)
318 TRIED="${TRIED:+$TRIED|}$hash"
319 case "$_skip" in
320 *$hash*) ;;
322 echo "bisect_rev=$hash"
323 echo "bisect_tried=\"$TRIED\""
324 FOUND=1
326 esac
329 # We have already found a rev to be tested.
330 ,1,*,bisect_rev*) VARS=1 ;;
331 ,1,*,*) ;;
333 # ???
334 *) die "filter_skipped error " \
335 "VARS: '$VARS' " \
336 "FOUND: '$FOUND' " \
337 "TRIED: '$TRIED' " \
338 "hash: '$hash' " \
339 "line: '$line'"
341 esac
342 done
345 exit_if_skipped_commits () {
346 _tried=$1
347 if expr "$_tried" : ".*[|].*" > /dev/null ; then
348 echo "There are only 'skip'ped commit left to test."
349 echo "The first bad commit could be any of:"
350 echo "$_tried" | tr '[|]' '[\012]'
351 echo "We cannot bisect more!"
352 exit 2
356 bisect_checkout() {
357 _rev="$1"
358 _msg="$2"
359 echo "Bisecting: $_msg"
360 mark_expected_rev "$_rev"
361 git checkout -q "$_rev" || exit
362 git show-branch "$_rev"
365 is_among() {
366 _rev="$1"
367 _list="$2"
368 case "$_list" in *$_rev*) return 0 ;; esac
369 return 1
372 handle_bad_merge_base() {
373 _badmb="$1"
374 _good="$2"
375 if is_expected_rev "$_badmb"; then
376 cat >&2 <<EOF
377 The merge base $_badmb is bad.
378 This means the bug has been fixed between $_badmb and [$_good].
380 exit 3
381 else
382 cat >&2 <<EOF
383 Some good revs are not ancestor of the bad rev.
384 git bisect cannot work properly in this case.
385 Maybe you mistake good and bad revs?
387 exit 1
391 handle_skipped_merge_base() {
392 _mb="$1"
393 _bad="$2"
394 _good="$3"
395 cat >&2 <<EOF
396 Warning: the merge base between $_bad and [$_good] must be skipped.
397 So we cannot be sure the first bad commit is between $_mb and $_bad.
398 We continue anyway.
403 # "check_merge_bases" checks that merge bases are not "bad".
405 # - If one is "good", that's good, we have nothing to do.
406 # - If one is "bad", it means the user assumed something wrong
407 # and we must exit.
408 # - If one is "skipped", we can't know but we should warn.
409 # - If we don't know, we should check it out and ask the user to test.
411 # In the last case we will return 1, and otherwise 0.
413 check_merge_bases() {
414 _bad="$1"
415 _good="$2"
416 _skip="$3"
417 for _mb in $(git merge-base --all $_bad $_good)
419 if is_among "$_mb" "$_good"; then
420 continue
421 elif test "$_mb" = "$_bad"; then
422 handle_bad_merge_base "$_bad" "$_good"
423 elif is_among "$_mb" "$_skip"; then
424 handle_skipped_merge_base "$_mb" "$_bad" "$_good"
425 else
426 bisect_checkout "$_mb" "a merge base must be tested"
427 return 1
429 done
430 return 0
434 # "check_good_are_ancestors_of_bad" checks that all "good" revs are
435 # ancestor of the "bad" rev.
437 # If that's not the case, we need to check the merge bases.
438 # If a merge base must be tested by the user we return 1 and
439 # otherwise 0.
441 check_good_are_ancestors_of_bad() {
442 test -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
443 return
445 _bad="$1"
446 _good=$(echo $2 | sed -e 's/\^//g')
447 _skip="$3"
449 # Bisecting with no good rev is ok
450 test -z "$_good" && return
452 _side=$(git rev-list $_good ^$_bad)
453 if test -n "$_side"; then
454 # Return if a checkout was done
455 check_merge_bases "$_bad" "$_good" "$_skip" || return
458 : > "$GIT_DIR/BISECT_ANCESTORS_OK"
460 return 0
463 bisect_next() {
464 case "$#" in 0) ;; *) usage ;; esac
465 bisect_autostart
466 bisect_next_check good
468 # Get bad, good and skipped revs
469 bad=$(git rev-parse --verify refs/bisect/bad) &&
470 good=$(git for-each-ref --format='^%(objectname)' \
471 "refs/bisect/good-*" | tr '\012' ' ') &&
472 skip=$(git for-each-ref --format='%(objectname)' \
473 "refs/bisect/skip-*" | tr '\012' ' ') || exit
475 # Maybe some merge bases must be tested first
476 check_good_are_ancestors_of_bad "$bad" "$good" "$skip"
477 # Return now if a checkout has already been done
478 test "$?" -eq "1" && return
480 # Get bisection information
481 BISECT_OPT=''
482 test -n "$skip" && BISECT_OPT='--bisect-all'
483 eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" &&
484 eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
485 eval=$(filter_skipped "$eval" "$skip") &&
486 eval "$eval" || exit
488 if [ -z "$bisect_rev" ]; then
489 echo "$bad was both good and bad"
490 exit 1
492 if [ "$bisect_rev" = "$bad" ]; then
493 exit_if_skipped_commits "$bisect_tried"
494 echo "$bisect_rev is first bad commit"
495 git diff-tree --pretty $bisect_rev
496 exit 0
499 # We should exit here only if the "bad"
500 # commit is also a "skip" commit (see above).
501 exit_if_skipped_commits "$bisect_rev"
503 bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this"
506 bisect_visualize() {
507 bisect_next_check fail
509 if test $# = 0
510 then
511 case "${DISPLAY+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in
512 '') set git log ;;
513 set*) set gitk ;;
514 esac
515 else
516 case "$1" in
517 git*|tig) ;;
518 -*) set git log "$@" ;;
519 *) set git "$@" ;;
520 esac
523 not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*")
524 eval '"$@"' refs/bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
527 bisect_reset() {
528 test -s "$GIT_DIR/BISECT_START" || {
529 echo "We are not bisecting."
530 return
532 case "$#" in
533 0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
534 1) git show-ref --verify --quiet -- "refs/heads/$1" ||
535 die "$1 does not seem to be a valid branch"
536 branch="$1" ;;
538 usage ;;
539 esac
540 git checkout "$branch" && bisect_clean_state
543 bisect_clean_state() {
544 # There may be some refs packed during bisection.
545 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
546 while read ref hash
548 git update-ref -d $ref $hash || exit
549 done
550 rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
551 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
552 rm -f "$GIT_DIR/BISECT_LOG" &&
553 rm -f "$GIT_DIR/BISECT_NAMES" &&
554 rm -f "$GIT_DIR/BISECT_RUN" &&
555 # Cleanup head-name if it got left by an old version of git-bisect
556 rm -f "$GIT_DIR/head-name" &&
558 rm -f "$GIT_DIR/BISECT_START"
561 bisect_replay () {
562 test -r "$1" || die "cannot read $1 for replaying"
563 bisect_reset
564 while read git bisect command rev
566 test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
567 if test "$git" = "git-bisect"; then
568 rev="$command"
569 command="$bisect"
571 case "$command" in
572 start)
573 cmd="bisect_start $rev"
574 eval "$cmd" ;;
575 good|bad|skip)
576 bisect_write "$command" "$rev" ;;
578 die "?? what are you talking about?" ;;
579 esac
580 done <"$1"
581 bisect_auto_next
584 bisect_run () {
585 bisect_next_check fail
587 while true
589 echo "running $@"
590 "$@"
591 res=$?
593 # Check for really bad run error.
594 if [ $res -lt 0 -o $res -ge 128 ]; then
595 echo >&2 "bisect run failed:"
596 echo >&2 "exit code $res from '$@' is < 0 or >= 128"
597 exit $res
600 # Find current state depending on run success or failure.
601 # A special exit code of 125 means cannot test.
602 if [ $res -eq 125 ]; then
603 state='skip'
604 elif [ $res -gt 0 ]; then
605 state='bad'
606 else
607 state='good'
610 # We have to use a subshell because "bisect_state" can exit.
611 ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
612 res=$?
614 cat "$GIT_DIR/BISECT_RUN"
616 if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
617 > /dev/null; then
618 echo >&2 "bisect run cannot continue any more"
619 exit $res
622 if [ $res -ne 0 ]; then
623 echo >&2 "bisect run failed:"
624 echo >&2 "'bisect_state $state' exited with error code $res"
625 exit $res
628 if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
629 echo "bisect run success"
630 exit 0;
633 done
637 case "$#" in
639 usage ;;
641 cmd="$1"
642 shift
643 case "$cmd" in
644 help)
645 git bisect -h ;;
646 start)
647 bisect_start "$@" ;;
648 bad|good)
649 bisect_state "$cmd" "$@" ;;
650 skip)
651 bisect_skip "$@" ;;
652 next)
653 # Not sure we want "next" at the UI level anymore.
654 bisect_next "$@" ;;
655 visualize|view)
656 bisect_visualize "$@" ;;
657 reset)
658 bisect_reset "$@" ;;
659 replay)
660 bisect_replay "$@" ;;
661 log)
662 cat "$GIT_DIR/BISECT_LOG" ;;
663 run)
664 bisect_run "$@" ;;
666 usage ;;
667 esac
668 esac