Code

e1f300bed1b926ca2e800d0c8bcefcc7d39a0071
[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 bisect_checkout() {
283         _rev="$1"
284         _msg="$2"
285         echo "Bisecting: $_msg"
286         mark_expected_rev "$_rev"
287         git checkout -q "$_rev" -- || exit
288         git show-branch "$_rev"
291 is_among() {
292         _rev="$1"
293         _list="$2"
294         case "$_list" in *$_rev*) return 0 ;; esac
295         return 1
298 handle_bad_merge_base() {
299         _badmb="$1"
300         _good="$2"
301         if is_expected_rev "$_badmb"; then
302                 cat >&2 <<EOF
303 The merge base $_badmb is bad.
304 This means the bug has been fixed between $_badmb and [$_good].
305 EOF
306                 exit 3
307         else
308                 cat >&2 <<EOF
309 Some good revs are not ancestor of the bad rev.
310 git bisect cannot work properly in this case.
311 Maybe you mistake good and bad revs?
312 EOF
313                 exit 1
314         fi
317 handle_skipped_merge_base() {
318         _mb="$1"
319         _bad="$2"
320         _good="$3"
321         cat >&2 <<EOF
322 Warning: the merge base between $_bad and [$_good] must be skipped.
323 So we cannot be sure the first bad commit is between $_mb and $_bad.
324 We continue anyway.
325 EOF
329 # "check_merge_bases" checks that merge bases are not "bad".
331 # - If one is "good", that's good, we have nothing to do.
332 # - If one is "bad", it means the user assumed something wrong
333 # and we must exit.
334 # - If one is "skipped", we can't know but we should warn.
335 # - If we don't know, we should check it out and ask the user to test.
337 # In the last case we will return 1, and otherwise 0.
339 check_merge_bases() {
340         _bad="$1"
341         _good="$2"
342         _skip="$3"
343         for _mb in $(git merge-base --all $_bad $_good)
344         do
345                 if is_among "$_mb" "$_good"; then
346                         continue
347                 elif test "$_mb" = "$_bad"; then
348                         handle_bad_merge_base "$_bad" "$_good"
349                 elif is_among "$_mb" "$_skip"; then
350                         handle_skipped_merge_base "$_mb" "$_bad" "$_good"
351                 else
352                         bisect_checkout "$_mb" "a merge base must be tested"
353                         return 1
354                 fi
355         done
356         return 0
360 # "check_good_are_ancestors_of_bad" checks that all "good" revs are
361 # ancestor of the "bad" rev.
363 # If that's not the case, we need to check the merge bases.
364 # If a merge base must be tested by the user we return 1 and
365 # otherwise 0.
367 check_good_are_ancestors_of_bad() {
368         test -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
369                 return
371         _bad="$1"
372         _good=$(echo $2 | sed -e 's/\^//g')
373         _skip="$3"
375         # Bisecting with no good rev is ok
376         test -z "$_good" && return
378         _side=$(git rev-list $_good ^$_bad)
379         if test -n "$_side"; then
380                 # Return if a checkout was done
381                 check_merge_bases "$_bad" "$_good" "$_skip" || return
382         fi
384         : > "$GIT_DIR/BISECT_ANCESTORS_OK"
386         return 0
389 bisect_next() {
390         case "$#" in 0) ;; *) usage ;; esac
391         bisect_autostart
392         bisect_next_check good
394         # Get bad, good and skipped revs
395         bad=$(git rev-parse --verify refs/bisect/bad) &&
396         good=$(git for-each-ref --format='^%(objectname)' \
397                 "refs/bisect/good-*" | tr '\012' ' ') &&
398         skip=$(git for-each-ref --format='%(objectname)' \
399                 "refs/bisect/skip-*" | tr '\012' ' ') || exit
401         # Maybe some merge bases must be tested first
402         check_good_are_ancestors_of_bad "$bad" "$good" "$skip"
403         # Return now if a checkout has already been done
404         test "$?" -eq "1" && return
406         # Perform bisection computation, display and checkout
407         git bisect--helper --next-exit
408         res=$?
410         # Check if we should exit because bisection is finished
411         test $res -eq 10 && exit 0
413         # Check for an error in the bisection process
414         test $res -ne 0 && exit $res
416         return 0
419 bisect_visualize() {
420         bisect_next_check fail
422         if test $# = 0
423         then
424                 case "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in
425                 '')     set git log ;;
426                 set*)   set gitk ;;
427                 esac
428         else
429                 case "$1" in
430                 git*|tig) ;;
431                 -*)     set git log "$@" ;;
432                 *)      set git "$@" ;;
433                 esac
434         fi
436         not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*")
437         eval '"$@"' refs/bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
440 bisect_reset() {
441         test -s "$GIT_DIR/BISECT_START" || {
442                 echo "We are not bisecting."
443                 return
444         }
445         case "$#" in
446         0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
447         1) git show-ref --verify --quiet -- "refs/heads/$1" ||
448                die "$1 does not seem to be a valid branch"
449            branch="$1" ;;
450         *)
451             usage ;;
452         esac
453         git checkout "$branch" -- && bisect_clean_state
456 bisect_clean_state() {
457         # There may be some refs packed during bisection.
458         git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
459         while read ref hash
460         do
461                 git update-ref -d $ref $hash || exit
462         done
463         rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
464         rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
465         rm -f "$GIT_DIR/BISECT_LOG" &&
466         rm -f "$GIT_DIR/BISECT_NAMES" &&
467         rm -f "$GIT_DIR/BISECT_RUN" &&
468         # Cleanup head-name if it got left by an old version of git-bisect
469         rm -f "$GIT_DIR/head-name" &&
471         rm -f "$GIT_DIR/BISECT_START"
474 bisect_replay () {
475         test -r "$1" || die "cannot read $1 for replaying"
476         bisect_reset
477         while read git bisect command rev
478         do
479                 test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
480                 if test "$git" = "git-bisect"; then
481                         rev="$command"
482                         command="$bisect"
483                 fi
484                 case "$command" in
485                 start)
486                         cmd="bisect_start $rev"
487                         eval "$cmd" ;;
488                 good|bad|skip)
489                         bisect_write "$command" "$rev" ;;
490                 *)
491                         die "?? what are you talking about?" ;;
492                 esac
493         done <"$1"
494         bisect_auto_next
497 bisect_run () {
498     bisect_next_check fail
500     while true
501     do
502       echo "running $@"
503       "$@"
504       res=$?
506       # Check for really bad run error.
507       if [ $res -lt 0 -o $res -ge 128 ]; then
508           echo >&2 "bisect run failed:"
509           echo >&2 "exit code $res from '$@' is < 0 or >= 128"
510           exit $res
511       fi
513       # Find current state depending on run success or failure.
514       # A special exit code of 125 means cannot test.
515       if [ $res -eq 125 ]; then
516           state='skip'
517       elif [ $res -gt 0 ]; then
518           state='bad'
519       else
520           state='good'
521       fi
523       # We have to use a subshell because "bisect_state" can exit.
524       ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
525       res=$?
527       cat "$GIT_DIR/BISECT_RUN"
529       if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
530                 > /dev/null; then
531           echo >&2 "bisect run cannot continue any more"
532           exit $res
533       fi
535       if [ $res -ne 0 ]; then
536           echo >&2 "bisect run failed:"
537           echo >&2 "'bisect_state $state' exited with error code $res"
538           exit $res
539       fi
541       if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
542           echo "bisect run success"
543           exit 0;
544       fi
546     done
550 case "$#" in
551 0)
552     usage ;;
553 *)
554     cmd="$1"
555     shift
556     case "$cmd" in
557     help)
558         git bisect -h ;;
559     start)
560         bisect_start "$@" ;;
561     bad|good)
562         bisect_state "$cmd" "$@" ;;
563     skip)
564         bisect_skip "$@" ;;
565     next)
566         # Not sure we want "next" at the UI level anymore.
567         bisect_next "$@" ;;
568     visualize|view)
569         bisect_visualize "$@" ;;
570     reset)
571         bisect_reset "$@" ;;
572     replay)
573         bisect_replay "$@" ;;
574     log)
575         cat "$GIT_DIR/BISECT_LOG" ;;
576     run)
577         bisect_run "$@" ;;
578     *)
579         usage ;;
580     esac
581 esac