Code

Merge branch 'cc/maint-1.6.0-bisect-fix'
[git.git] / git-bisect.sh
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 " '\''$_'\''";
41                 }
42                 print "\n";
43         ' "$@"
44 }
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 }
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"
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
98         #
99         # Get rid of any old bisect state.
100         #
101         bisect_clean_state || exit
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
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
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
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
190                 fi
191         done
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
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
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
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
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
289         fi
291         # Let's parse the output of:
292         # "git rev-list --bisect-vars --bisect-all ..."
293         eval "$_eval" | {
294                 VARS= FOUND= TRIED=
295                 while read hash line
296                 do
297                         case "$VARS,$FOUND,$TRIED,$hash" in
298                         1,*,*,*)
299                                 # "bisect_foo=bar" read from rev-list output.
300                                 echo "$hash &&"
301                                 ;;
302                         ,*,*,---*)
303                                 # Separator
304                                 ;;
305                         ,,,bisect_rev*)
306                                 # We had nothing to search.
307                                 echo "bisect_rev= &&"
308                                 VARS=1
309                                 ;;
310                         ,,*,bisect_rev*)
311                                 # We did not find a good bisect rev.
312                                 # This should happen only if the "bad"
313                                 # commit is also a "skip" commit.
314                                 echo "bisect_rev='$TRIED' &&"
315                                 VARS=1
316                                 ;;
317                         ,,*,*)
318                                 # We are searching.
319                                 TRIED="${TRIED:+$TRIED|}$hash"
320                                 case "$_skip" in
321                                 *$hash*) ;;
322                                 *)
323                                         echo "bisect_rev=$hash &&"
324                                         echo "bisect_tried='$TRIED' &&"
325                                         FOUND=1
326                                         ;;
327                                 esac
328                                 ;;
329                         ,1,*,bisect_rev*)
330                                 # We have already found a rev to be tested.
331                                 VARS=1
332                                 ;;
333                         ,1,*,*)
334                                 ;;
335                         *)
336                                 # Unexpected input
337                                 echo "die 'filter_skipped error'"
338                                 die "filter_skipped error " \
339                                     "VARS: '$VARS' " \
340                                     "FOUND: '$FOUND' " \
341                                     "TRIED: '$TRIED' " \
342                                     "hash: '$hash' " \
343                                     "line: '$line'"
344                                 ;;
345                         esac
346                 done
347                 echo ':'
348         }
351 exit_if_skipped_commits () {
352         _tried=$1
353         if expr "$_tried" : ".*[|].*" > /dev/null ; then
354                 echo "There are only 'skip'ped commit left to test."
355                 echo "The first bad commit could be any of:"
356                 echo "$_tried" | tr '[|]' '[\012]'
357                 echo "We cannot bisect more!"
358                 exit 2
359         fi
362 bisect_checkout() {
363         _rev="$1"
364         _msg="$2"
365         echo "Bisecting: $_msg"
366         mark_expected_rev "$_rev"
367         git checkout -q "$_rev" || exit
368         git show-branch "$_rev"
371 is_among() {
372         _rev="$1"
373         _list="$2"
374         case "$_list" in *$_rev*) return 0 ;; esac
375         return 1
378 handle_bad_merge_base() {
379         _badmb="$1"
380         _good="$2"
381         if is_expected_rev "$_badmb"; then
382                 cat >&2 <<EOF
383 The merge base $_badmb is bad.
384 This means the bug has been fixed between $_badmb and [$_good].
385 EOF
386                 exit 3
387         else
388                 cat >&2 <<EOF
389 Some good revs are not ancestor of the bad rev.
390 git bisect cannot work properly in this case.
391 Maybe you mistake good and bad revs?
392 EOF
393                 exit 1
394         fi
397 handle_skipped_merge_base() {
398         _mb="$1"
399         _bad="$2"
400         _good="$3"
401         cat >&2 <<EOF
402 Warning: the merge base between $_bad and [$_good] must be skipped.
403 So we cannot be sure the first bad commit is between $_mb and $_bad.
404 We continue anyway.
405 EOF
409 # "check_merge_bases" checks that merge bases are not "bad".
411 # - If one is "good", that's good, we have nothing to do.
412 # - If one is "bad", it means the user assumed something wrong
413 # and we must exit.
414 # - If one is "skipped", we can't know but we should warn.
415 # - If we don't know, we should check it out and ask the user to test.
417 # In the last case we will return 1, and otherwise 0.
419 check_merge_bases() {
420         _bad="$1"
421         _good="$2"
422         _skip="$3"
423         for _mb in $(git merge-base --all $_bad $_good)
424         do
425                 if is_among "$_mb" "$_good"; then
426                         continue
427                 elif test "$_mb" = "$_bad"; then
428                         handle_bad_merge_base "$_bad" "$_good"
429                 elif is_among "$_mb" "$_skip"; then
430                         handle_skipped_merge_base "$_mb" "$_bad" "$_good"
431                 else
432                         bisect_checkout "$_mb" "a merge base must be tested"
433                         return 1
434                 fi
435         done
436         return 0
440 # "check_good_are_ancestors_of_bad" checks that all "good" revs are
441 # ancestor of the "bad" rev.
443 # If that's not the case, we need to check the merge bases.
444 # If a merge base must be tested by the user we return 1 and
445 # otherwise 0.
447 check_good_are_ancestors_of_bad() {
448         test -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
449                 return
451         _bad="$1"
452         _good=$(echo $2 | sed -e 's/\^//g')
453         _skip="$3"
455         # Bisecting with no good rev is ok
456         test -z "$_good" && return
458         _side=$(git rev-list $_good ^$_bad)
459         if test -n "$_side"; then
460                 # Return if a checkout was done
461                 check_merge_bases "$_bad" "$_good" "$_skip" || return
462         fi
464         : > "$GIT_DIR/BISECT_ANCESTORS_OK"
466         return 0
469 bisect_next() {
470         case "$#" in 0) ;; *) usage ;; esac
471         bisect_autostart
472         bisect_next_check good
474         # Get bad, good and skipped revs
475         bad=$(git rev-parse --verify refs/bisect/bad) &&
476         good=$(git for-each-ref --format='^%(objectname)' \
477                 "refs/bisect/good-*" | tr '\012' ' ') &&
478         skip=$(git for-each-ref --format='%(objectname)' \
479                 "refs/bisect/skip-*" | tr '\012' ' ') || exit
481         # Maybe some merge bases must be tested first
482         check_good_are_ancestors_of_bad "$bad" "$good" "$skip"
483         # Return now if a checkout has already been done
484         test "$?" -eq "1" && return
486         # Get bisection information
487         BISECT_OPT=''
488         test -n "$skip" && BISECT_OPT='--bisect-all'
489         eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" &&
490         eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
491         eval=$(filter_skipped "$eval" "$skip") &&
492         eval "$eval" || exit
494         if [ -z "$bisect_rev" ]; then
495                 echo "$bad was both good and bad"
496                 exit 1
497         fi
498         if [ "$bisect_rev" = "$bad" ]; then
499                 exit_if_skipped_commits "$bisect_tried"
500                 echo "$bisect_rev is first bad commit"
501                 git diff-tree --pretty $bisect_rev
502                 exit 0
503         fi
505         # We should exit here only if the "bad"
506         # commit is also a "skip" commit (see above).
507         exit_if_skipped_commits "$bisect_rev"
509         bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this"
512 bisect_visualize() {
513         bisect_next_check fail
515         if test $# = 0
516         then
517                 case "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in
518                 '')     set git log ;;
519                 set*)   set gitk ;;
520                 esac
521         else
522                 case "$1" in
523                 git*|tig) ;;
524                 -*)     set git log "$@" ;;
525                 *)      set git "$@" ;;
526                 esac
527         fi
529         not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*")
530         eval '"$@"' refs/bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
533 bisect_reset() {
534         test -s "$GIT_DIR/BISECT_START" || {
535                 echo "We are not bisecting."
536                 return
537         }
538         case "$#" in
539         0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
540         1) git show-ref --verify --quiet -- "refs/heads/$1" ||
541                die "$1 does not seem to be a valid branch"
542            branch="$1" ;;
543         *)
544             usage ;;
545         esac
546         git checkout "$branch" && bisect_clean_state
549 bisect_clean_state() {
550         # There may be some refs packed during bisection.
551         git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
552         while read ref hash
553         do
554                 git update-ref -d $ref $hash || exit
555         done
556         rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
557         rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
558         rm -f "$GIT_DIR/BISECT_LOG" &&
559         rm -f "$GIT_DIR/BISECT_NAMES" &&
560         rm -f "$GIT_DIR/BISECT_RUN" &&
561         # Cleanup head-name if it got left by an old version of git-bisect
562         rm -f "$GIT_DIR/head-name" &&
564         rm -f "$GIT_DIR/BISECT_START"
567 bisect_replay () {
568         test -r "$1" || die "cannot read $1 for replaying"
569         bisect_reset
570         while read git bisect command rev
571         do
572                 test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
573                 if test "$git" = "git-bisect"; then
574                         rev="$command"
575                         command="$bisect"
576                 fi
577                 case "$command" in
578                 start)
579                         cmd="bisect_start $rev"
580                         eval "$cmd" ;;
581                 good|bad|skip)
582                         bisect_write "$command" "$rev" ;;
583                 *)
584                         die "?? what are you talking about?" ;;
585                 esac
586         done <"$1"
587         bisect_auto_next
590 bisect_run () {
591     bisect_next_check fail
593     while true
594     do
595       echo "running $@"
596       "$@"
597       res=$?
599       # Check for really bad run error.
600       if [ $res -lt 0 -o $res -ge 128 ]; then
601           echo >&2 "bisect run failed:"
602           echo >&2 "exit code $res from '$@' is < 0 or >= 128"
603           exit $res
604       fi
606       # Find current state depending on run success or failure.
607       # A special exit code of 125 means cannot test.
608       if [ $res -eq 125 ]; then
609           state='skip'
610       elif [ $res -gt 0 ]; then
611           state='bad'
612       else
613           state='good'
614       fi
616       # We have to use a subshell because "bisect_state" can exit.
617       ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
618       res=$?
620       cat "$GIT_DIR/BISECT_RUN"
622       if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
623                 > /dev/null; then
624           echo >&2 "bisect run cannot continue any more"
625           exit $res
626       fi
628       if [ $res -ne 0 ]; then
629           echo >&2 "bisect run failed:"
630           echo >&2 "'bisect_state $state' exited with error code $res"
631           exit $res
632       fi
634       if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
635           echo "bisect run success"
636           exit 0;
637       fi
639     done
643 case "$#" in
644 0)
645     usage ;;
646 *)
647     cmd="$1"
648     shift
649     case "$cmd" in
650     help)
651         git bisect -h ;;
652     start)
653         bisect_start "$@" ;;
654     bad|good)
655         bisect_state "$cmd" "$@" ;;
656     skip)
657         bisect_skip "$@" ;;
658     next)
659         # Not sure we want "next" at the UI level anymore.
660         bisect_next "$@" ;;
661     visualize|view)
662         bisect_visualize "$@" ;;
663     reset)
664         bisect_reset "$@" ;;
665     replay)
666         bisect_replay "$@" ;;
667     log)
668         cat "$GIT_DIR/BISECT_LOG" ;;
669     run)
670         bisect_run "$@" ;;
671     *)
672         usage ;;
673     esac
674 esac