Documentation: describe how to "bisect skip" a range of commits
[git] / git-bisect.sh
1 #!/bin/sh
2
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.
26
27 Please use "git help bisect" to get the full man page.'
28
29 OPTIONS_SPEC=
30 . git-sh-setup
31 require_work_tree
32
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
36 sq() {
37         @@PERL@@ -e '
38                 for (@ARGV) {
39                         s/'\''/'\'\\\\\'\''/g;
40                         print " '\''$_'\''";
41                 }
42                 print "\n";
43         ' "$@"
44 }
45
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
60                 fi
61         }
62 }
63
64 bisect_start() {
65         #
66         # Verify HEAD.
67         #
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"
71
72         #
73         # Check if we are bisecting.
74         #
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/}"
91                         ;;
92                 *)
93                         die "Bad HEAD - strange symbolic ref"
94                         ;;
95                 esac
96         fi
97
98         #
99         # Get rid of any old bisect state.
100         #
101         bisect_clean_state || exit
102
103         #
104         # Check for one bad and then some good revisions.
105         #
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
116             --)
117                 shift
118                 break
119                 ;;
120             *)
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
125                 }
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
132                 ;;
133             esac
134         done
135
136         #
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".
142         #
143         trap 'bisect_clean_state' 0
144         trap 'exit 255' 1 2 3 15
145
146         #
147         # Write new start state.
148         #
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
153         #
154         # Check if we can proceed to the next bisect state.
155         #
156         bisect_auto_next
157
158         trap '-' 0
159 }
160
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"
173 }
174
175 is_expected_rev() {
176         test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
177         test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
178 }
179
180 mark_expected_rev() {
181         echo "$1" > "$GIT_DIR/BISECT_EXPECTED_REV"
182 }
183
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
190                 fi
191         done
192 }
193
194 bisect_skip() {
195         all=''
196         for arg in "$@"
197         do
198             case "$arg" in
199             *..*)
200                 revs=$(git rev-list "$arg") || die "Bad rev input: $arg" ;;
201             *)
202                 revs=$(sq "$arg") ;;
203             esac
204             all="$all $revs"
205         done
206         eval bisect_state 'skip' $all
207 }
208
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 "$@"
224                 do
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." ;;
233         *)
234                 usage ;;
235         esac
236         bisect_auto_next
237 }
238
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
243
244         case "$missing_good,$missing_bad,$1" in
245         ,,*)
246                 : have both good and bad - ok
247                 ;;
248         *,)
249                 # do not have both but not asked to fail - just report.
250                 false
251                 ;;
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
261                 fi
262                 : bisect without good...
263                 ;;
264         *)
265                 THEN=''
266                 test -s "$GIT_DIR/BISECT_START" || {
267                         echo >&2 'You need to start by "git bisect start".'
268                         THEN='then '
269                 }
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
276 }
277
278 bisect_auto_next() {
279         bisect_next_check && bisect_next || :
280 }
281
282 filter_skipped() {
283         _eval="$1"
284         _skip="$2"
285
286         if [ -z "$_skip" ]; then
287                 eval "$_eval"
288                 return
289         fi
290
291         # Let's parse the output of:
292         # "git rev-list --bisect-vars --bisect-all ..."
293         eval "$_eval" | while read hash line
294         do
295                 case "$VARS,$FOUND,$TRIED,$hash" in
296                         # We display some vars.
297                         1,*,*,*) echo "$hash $line" ;;
298
299                         # Split line.
300                         ,*,*,---*) ;;
301
302                         # We had nothing to search.
303                         ,,,bisect_rev*)
304                                 echo "bisect_rev="
305                                 VARS=1
306                                 ;;
307
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
314                                 ;;
315
316                         # We are searching.
317                         ,,*,*)
318                                 TRIED="${TRIED:+$TRIED|}$hash"
319                                 case "$_skip" in
320                                 *$hash*) ;;
321                                 *)
322                                         echo "bisect_rev=$hash"
323                                         echo "bisect_tried=\"$TRIED\""
324                                         FOUND=1
325                                         ;;
326                                 esac
327                                 ;;
328
329                         # We have already found a rev to be tested.
330                         ,1,*,bisect_rev*) VARS=1 ;;
331                         ,1,*,*) ;;
332
333                         # ???
334                         *) die "filter_skipped error " \
335                             "VARS: '$VARS' " \
336                             "FOUND: '$FOUND' " \
337                             "TRIED: '$TRIED' " \
338                             "hash: '$hash' " \
339                             "line: '$line'"
340                         ;;
341                 esac
342         done
343 }
344
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
353         fi
354 }
355
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"
363 }
364
365 is_among() {
366         _rev="$1"
367         _list="$2"
368         case "$_list" in *$_rev*) return 0 ;; esac
369         return 1
370 }
371
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].
379 EOF
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?
386 EOF
387                 exit 1
388         fi
389 }
390
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.
399 EOF
400 }
401
402 #
403 # "check_merge_bases" checks that merge bases are not "bad".
404 #
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.
410 #
411 # In the last case we will return 1, and otherwise 0.
412 #
413 check_merge_bases() {
414         _bad="$1"
415         _good="$2"
416         _skip="$3"
417         for _mb in $(git merge-base --all $_bad $_good)
418         do
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
428                 fi
429         done
430         return 0
431 }
432
433 #
434 # "check_good_are_ancestors_of_bad" checks that all "good" revs are
435 # ancestor of the "bad" rev.
436 #
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.
440 #
441 check_good_are_ancestors_of_bad() {
442         test -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
443                 return
444
445         _bad="$1"
446         _good=$(echo $2 | sed -e 's/\^//g')
447         _skip="$3"
448
449         # Bisecting with no good rev is ok
450         test -z "$_good" && return
451
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
456         fi
457
458         : > "$GIT_DIR/BISECT_ANCESTORS_OK"
459
460         return 0
461 }
462
463 bisect_next() {
464         case "$#" in 0) ;; *) usage ;; esac
465         bisect_autostart
466         bisect_next_check good
467
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
474
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
479
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
487
488         if [ -z "$bisect_rev" ]; then
489                 echo "$bad was both good and bad"
490                 exit 1
491         fi
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
497         fi
498
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"
502
503         bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this"
504 }
505
506 bisect_visualize() {
507         bisect_next_check fail
508
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
521         fi
522
523         not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*")
524         eval '"$@"' refs/bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
525 }
526
527 bisect_reset() {
528         test -s "$GIT_DIR/BISECT_START" || {
529                 echo "We are not bisecting."
530                 return
531         }
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" ;;
537         *)
538             usage ;;
539         esac
540         git checkout "$branch" && bisect_clean_state
541 }
542
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
547         do
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" &&
557
558         rm -f "$GIT_DIR/BISECT_START"
559 }
560
561 bisect_replay () {
562         test -r "$1" || die "cannot read $1 for replaying"
563         bisect_reset
564         while read git bisect command rev
565         do
566                 test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
567                 if test "$git" = "git-bisect"; then
568                         rev="$command"
569                         command="$bisect"
570                 fi
571                 case "$command" in
572                 start)
573                         cmd="bisect_start $rev"
574                         eval "$cmd" ;;
575                 good|bad|skip)
576                         bisect_write "$command" "$rev" ;;
577                 *)
578                         die "?? what are you talking about?" ;;
579                 esac
580         done <"$1"
581         bisect_auto_next
582 }
583
584 bisect_run () {
585     bisect_next_check fail
586
587     while true
588     do
589       echo "running $@"
590       "$@"
591       res=$?
592
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
598       fi
599
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'
608       fi
609
610       # We have to use a subshell because "bisect_state" can exit.
611       ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
612       res=$?
613
614       cat "$GIT_DIR/BISECT_RUN"
615
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
620       fi
621
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
626       fi
627
628       if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
629           echo "bisect run success"
630           exit 0;
631       fi
632
633     done
634 }
635
636
637 case "$#" in
638 0)
639     usage ;;
640 *)
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 "$@" ;;
665     *)
666         usage ;;
667     esac
668 esac