bisect: move common bisect functionality to "bisect_common"
[git/raj.git] / git-bisect.sh
blob24712ff304af89317793fa4c54d39f4c579bb345
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 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
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].
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?
326 exit 1
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.
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)
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
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
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
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
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
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
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" ;;
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
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
500 test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
501 if test "$git" = "git-bisect"; then
502 rev="$command"
503 command="$bisect"
505 case "$command" in
506 start)
507 cmd="bisect_start $rev"
508 eval "$cmd" ;;
509 good|bad|skip)
510 bisect_write "$command" "$rev" ;;
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
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
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'
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
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
562 if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
563 echo "bisect run success"
564 exit 0;
567 done
571 case "$#" in
573 usage ;;
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 "$@" ;;
600 usage ;;
601 esac
602 esac