Code

Merge branch 'maint'
[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 exit_if_skipped_commits () {
283         _tried=$1
284         _bad=$2
285         if test -n "$_tried" ; then
286                 echo "There are only 'skip'ped commit left to test."
287                 echo "The first bad commit could be any of:"
288                 echo "$_tried" | tr '[|]' '[\012]'
289                 test -n "$_bad" && echo "$_bad"
290                 echo "We cannot bisect more!"
291                 exit 2
292         fi
295 bisect_checkout() {
296         _rev="$1"
297         _msg="$2"
298         echo "Bisecting: $_msg"
299         mark_expected_rev "$_rev"
300         git checkout -q "$_rev" -- || exit
301         git show-branch "$_rev"
304 is_among() {
305         _rev="$1"
306         _list="$2"
307         case "$_list" in *$_rev*) return 0 ;; esac
308         return 1
311 handle_bad_merge_base() {
312         _badmb="$1"
313         _good="$2"
314         if is_expected_rev "$_badmb"; then
315                 cat >&2 <<EOF
316 The merge base $_badmb is bad.
317 This means the bug has been fixed between $_badmb and [$_good].
318 EOF
319                 exit 3
320         else
321                 cat >&2 <<EOF
322 Some good revs are not ancestor of the bad rev.
323 git bisect cannot work properly in this case.
324 Maybe you mistake good and bad revs?
325 EOF
326                 exit 1
327         fi
330 handle_skipped_merge_base() {
331         _mb="$1"
332         _bad="$2"
333         _good="$3"
334         cat >&2 <<EOF
335 Warning: the merge base between $_bad and [$_good] must be skipped.
336 So we cannot be sure the first bad commit is between $_mb and $_bad.
337 We continue anyway.
338 EOF
342 # "check_merge_bases" checks that merge bases are not "bad".
344 # - If one is "good", that's good, we have nothing to do.
345 # - If one is "bad", it means the user assumed something wrong
346 # and we must exit.
347 # - If one is "skipped", we can't know but we should warn.
348 # - If we don't know, we should check it out and ask the user to test.
350 # In the last case we will return 1, and otherwise 0.
352 check_merge_bases() {
353         _bad="$1"
354         _good="$2"
355         _skip="$3"
356         for _mb in $(git merge-base --all $_bad $_good)
357         do
358                 if is_among "$_mb" "$_good"; then
359                         continue
360                 elif test "$_mb" = "$_bad"; then
361                         handle_bad_merge_base "$_bad" "$_good"
362                 elif is_among "$_mb" "$_skip"; then
363                         handle_skipped_merge_base "$_mb" "$_bad" "$_good"
364                 else
365                         bisect_checkout "$_mb" "a merge base must be tested"
366                         return 1
367                 fi
368         done
369         return 0
373 # "check_good_are_ancestors_of_bad" checks that all "good" revs are
374 # ancestor of the "bad" rev.
376 # If that's not the case, we need to check the merge bases.
377 # If a merge base must be tested by the user we return 1 and
378 # otherwise 0.
380 check_good_are_ancestors_of_bad() {
381         test -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
382                 return
384         _bad="$1"
385         _good=$(echo $2 | sed -e 's/\^//g')
386         _skip="$3"
388         # Bisecting with no good rev is ok
389         test -z "$_good" && return
391         _side=$(git rev-list $_good ^$_bad)
392         if test -n "$_side"; then
393                 # Return if a checkout was done
394                 check_merge_bases "$_bad" "$_good" "$_skip" || return
395         fi
397         : > "$GIT_DIR/BISECT_ANCESTORS_OK"
399         return 0
402 bisect_next() {
403         case "$#" in 0) ;; *) usage ;; esac
404         bisect_autostart
405         bisect_next_check good
407         # Get bad, good and skipped revs
408         bad=$(git rev-parse --verify refs/bisect/bad) &&
409         good=$(git for-each-ref --format='^%(objectname)' \
410                 "refs/bisect/good-*" | tr '\012' ' ') &&
411         skip=$(git for-each-ref --format='%(objectname)' \
412                 "refs/bisect/skip-*" | tr '\012' ' ') || exit
414         # Maybe some merge bases must be tested first
415         check_good_are_ancestors_of_bad "$bad" "$good" "$skip"
416         # Return now if a checkout has already been done
417         test "$?" -eq "1" && return
419         # Get bisection information
420         eval=$(eval "git bisect--helper --next-vars") &&
421         eval "$eval" || exit
423         if [ -z "$bisect_rev" ]; then
424                 # We should exit here only if the "bad"
425                 # commit is also a "skip" commit (see above).
426                 exit_if_skipped_commits "$bisect_tried"
427                 echo "$bad was both good and bad"
428                 exit 1
429         fi
430         if [ "$bisect_rev" = "$bad" ]; then
431                 exit_if_skipped_commits "$bisect_tried" "$bad"
432                 echo "$bisect_rev is first bad commit"
433                 git diff-tree --pretty $bisect_rev
434                 exit 0
435         fi
437         bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this (roughly $bisect_steps steps)"
440 bisect_visualize() {
441         bisect_next_check fail
443         if test $# = 0
444         then
445                 case "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in
446                 '')     set git log ;;
447                 set*)   set gitk ;;
448                 esac
449         else
450                 case "$1" in
451                 git*|tig) ;;
452                 -*)     set git log "$@" ;;
453                 *)      set git "$@" ;;
454                 esac
455         fi
457         not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*")
458         eval '"$@"' refs/bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
461 bisect_reset() {
462         test -s "$GIT_DIR/BISECT_START" || {
463                 echo "We are not bisecting."
464                 return
465         }
466         case "$#" in
467         0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
468         1) git show-ref --verify --quiet -- "refs/heads/$1" ||
469                die "$1 does not seem to be a valid branch"
470            branch="$1" ;;
471         *)
472             usage ;;
473         esac
474         git checkout "$branch" -- && bisect_clean_state
477 bisect_clean_state() {
478         # There may be some refs packed during bisection.
479         git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
480         while read ref hash
481         do
482                 git update-ref -d $ref $hash || exit
483         done
484         rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
485         rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
486         rm -f "$GIT_DIR/BISECT_LOG" &&
487         rm -f "$GIT_DIR/BISECT_NAMES" &&
488         rm -f "$GIT_DIR/BISECT_RUN" &&
489         # Cleanup head-name if it got left by an old version of git-bisect
490         rm -f "$GIT_DIR/head-name" &&
492         rm -f "$GIT_DIR/BISECT_START"
495 bisect_replay () {
496         test -r "$1" || die "cannot read $1 for replaying"
497         bisect_reset
498         while read git bisect command rev
499         do
500                 test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
501                 if test "$git" = "git-bisect"; then
502                         rev="$command"
503                         command="$bisect"
504                 fi
505                 case "$command" in
506                 start)
507                         cmd="bisect_start $rev"
508                         eval "$cmd" ;;
509                 good|bad|skip)
510                         bisect_write "$command" "$rev" ;;
511                 *)
512                         die "?? what are you talking about?" ;;
513                 esac
514         done <"$1"
515         bisect_auto_next
518 bisect_run () {
519     bisect_next_check fail
521     while true
522     do
523       echo "running $@"
524       "$@"
525       res=$?
527       # Check for really bad run error.
528       if [ $res -lt 0 -o $res -ge 128 ]; then
529           echo >&2 "bisect run failed:"
530           echo >&2 "exit code $res from '$@' is < 0 or >= 128"
531           exit $res
532       fi
534       # Find current state depending on run success or failure.
535       # A special exit code of 125 means cannot test.
536       if [ $res -eq 125 ]; then
537           state='skip'
538       elif [ $res -gt 0 ]; then
539           state='bad'
540       else
541           state='good'
542       fi
544       # We have to use a subshell because "bisect_state" can exit.
545       ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
546       res=$?
548       cat "$GIT_DIR/BISECT_RUN"
550       if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
551                 > /dev/null; then
552           echo >&2 "bisect run cannot continue any more"
553           exit $res
554       fi
556       if [ $res -ne 0 ]; then
557           echo >&2 "bisect run failed:"
558           echo >&2 "'bisect_state $state' exited with error code $res"
559           exit $res
560       fi
562       if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
563           echo "bisect run success"
564           exit 0;
565       fi
567     done
571 case "$#" in
572 0)
573     usage ;;
574 *)
575     cmd="$1"
576     shift
577     case "$cmd" in
578     help)
579         git bisect -h ;;
580     start)
581         bisect_start "$@" ;;
582     bad|good)
583         bisect_state "$cmd" "$@" ;;
584     skip)
585         bisect_skip "$@" ;;
586     next)
587         # Not sure we want "next" at the UI level anymore.
588         bisect_next "$@" ;;
589     visualize|view)
590         bisect_visualize "$@" ;;
591     reset)
592         bisect_reset "$@" ;;
593     replay)
594         bisect_replay "$@" ;;
595     log)
596         cat "$GIT_DIR/BISECT_LOG" ;;
597     run)
598         bisect_run "$@" ;;
599     *)
600         usage ;;
601     esac
602 esac