bisect: use the new generic "sha1_pos" function to lookup sha1
[git/kirr.git] / git-bisect.sh
blobe313bdea70d0a765106aa42a17a66f01d3d0f7d8
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 filter_skipped() {
283 _eval="$1"
284 _skip="$2"
286 if [ -z "$_skip" ]; then
287 eval "$_eval" | {
288 while read line
290 echo "$line &&"
291 done
292 echo ':'
294 return
297 # Let's parse the output of:
298 # "git rev-list --bisect-vars --bisect-all ..."
299 eval "$_eval" | {
300 VARS= FOUND= TRIED=
301 while read hash line
303 case "$VARS,$FOUND,$TRIED,$hash" in
304 1,*,*,*)
305 # "bisect_foo=bar" read from rev-list output.
306 echo "$hash &&"
308 ,*,*,---*)
309 # Separator
311 ,,,bisect_rev*)
312 # We had nothing to search.
313 echo "bisect_rev= &&"
314 VARS=1
316 ,,*,bisect_rev*)
317 # We did not find a good bisect rev.
318 # This should happen only if the "bad"
319 # commit is also a "skip" commit.
320 echo "bisect_rev='$TRIED' &&"
321 VARS=1
323 ,,*,*)
324 # We are searching.
325 TRIED="${TRIED:+$TRIED|}$hash"
326 case "$_skip" in
327 *$hash*) ;;
329 echo "bisect_rev=$hash &&"
330 echo "bisect_tried='$TRIED' &&"
331 FOUND=1
333 esac
335 ,1,*,bisect_rev*)
336 # We have already found a rev to be tested.
337 VARS=1
339 ,1,*,*)
342 # Unexpected input
343 echo "die 'filter_skipped error'"
344 die "filter_skipped error " \
345 "VARS: '$VARS' " \
346 "FOUND: '$FOUND' " \
347 "TRIED: '$TRIED' " \
348 "hash: '$hash' " \
349 "line: '$line'"
351 esac
352 done
353 echo ':'
357 exit_if_skipped_commits () {
358 _tried=$1
359 if expr "$_tried" : ".*[|].*" > /dev/null ; then
360 echo "There are only 'skip'ped commit left to test."
361 echo "The first bad commit could be any of:"
362 echo "$_tried" | tr '[|]' '[\012]'
363 echo "We cannot bisect more!"
364 exit 2
368 bisect_checkout() {
369 _rev="$1"
370 _msg="$2"
371 echo "Bisecting: $_msg"
372 mark_expected_rev "$_rev"
373 git checkout -q "$_rev" || exit
374 git show-branch "$_rev"
377 is_among() {
378 _rev="$1"
379 _list="$2"
380 case "$_list" in *$_rev*) return 0 ;; esac
381 return 1
384 handle_bad_merge_base() {
385 _badmb="$1"
386 _good="$2"
387 if is_expected_rev "$_badmb"; then
388 cat >&2 <<EOF
389 The merge base $_badmb is bad.
390 This means the bug has been fixed between $_badmb and [$_good].
392 exit 3
393 else
394 cat >&2 <<EOF
395 Some good revs are not ancestor of the bad rev.
396 git bisect cannot work properly in this case.
397 Maybe you mistake good and bad revs?
399 exit 1
403 handle_skipped_merge_base() {
404 _mb="$1"
405 _bad="$2"
406 _good="$3"
407 cat >&2 <<EOF
408 Warning: the merge base between $_bad and [$_good] must be skipped.
409 So we cannot be sure the first bad commit is between $_mb and $_bad.
410 We continue anyway.
415 # "check_merge_bases" checks that merge bases are not "bad".
417 # - If one is "good", that's good, we have nothing to do.
418 # - If one is "bad", it means the user assumed something wrong
419 # and we must exit.
420 # - If one is "skipped", we can't know but we should warn.
421 # - If we don't know, we should check it out and ask the user to test.
423 # In the last case we will return 1, and otherwise 0.
425 check_merge_bases() {
426 _bad="$1"
427 _good="$2"
428 _skip="$3"
429 for _mb in $(git merge-base --all $_bad $_good)
431 if is_among "$_mb" "$_good"; then
432 continue
433 elif test "$_mb" = "$_bad"; then
434 handle_bad_merge_base "$_bad" "$_good"
435 elif is_among "$_mb" "$_skip"; then
436 handle_skipped_merge_base "$_mb" "$_bad" "$_good"
437 else
438 bisect_checkout "$_mb" "a merge base must be tested"
439 return 1
441 done
442 return 0
446 # "check_good_are_ancestors_of_bad" checks that all "good" revs are
447 # ancestor of the "bad" rev.
449 # If that's not the case, we need to check the merge bases.
450 # If a merge base must be tested by the user we return 1 and
451 # otherwise 0.
453 check_good_are_ancestors_of_bad() {
454 test -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
455 return
457 _bad="$1"
458 _good=$(echo $2 | sed -e 's/\^//g')
459 _skip="$3"
461 # Bisecting with no good rev is ok
462 test -z "$_good" && return
464 _side=$(git rev-list $_good ^$_bad)
465 if test -n "$_side"; then
466 # Return if a checkout was done
467 check_merge_bases "$_bad" "$_good" "$_skip" || return
470 : > "$GIT_DIR/BISECT_ANCESTORS_OK"
472 return 0
475 bisect_next() {
476 case "$#" in 0) ;; *) usage ;; esac
477 bisect_autostart
478 bisect_next_check good
480 # Get bad, good and skipped revs
481 bad=$(git rev-parse --verify refs/bisect/bad) &&
482 good=$(git for-each-ref --format='^%(objectname)' \
483 "refs/bisect/good-*" | tr '\012' ' ') &&
484 skip=$(git for-each-ref --format='%(objectname)' \
485 "refs/bisect/skip-*" | tr '\012' ' ') || exit
487 # Maybe some merge bases must be tested first
488 check_good_are_ancestors_of_bad "$bad" "$good" "$skip"
489 # Return now if a checkout has already been done
490 test "$?" -eq "1" && return
492 # Get bisection information
493 BISECT_OPT=''
494 test -n "$skip" && BISECT_OPT='--bisect-all'
495 eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" &&
496 eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
497 eval=$(filter_skipped "$eval" "$skip") &&
498 eval "$eval" || exit
500 if [ -z "$bisect_rev" ]; then
501 echo "$bad was both good and bad"
502 exit 1
504 if [ "$bisect_rev" = "$bad" ]; then
505 exit_if_skipped_commits "$bisect_tried"
506 echo "$bisect_rev is first bad commit"
507 git diff-tree --pretty $bisect_rev
508 exit 0
511 # We should exit here only if the "bad"
512 # commit is also a "skip" commit (see above).
513 exit_if_skipped_commits "$bisect_rev"
515 bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this (roughly $bisect_steps steps)"
518 bisect_visualize() {
519 bisect_next_check fail
521 if test $# = 0
522 then
523 case "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in
524 '') set git log ;;
525 set*) set gitk ;;
526 esac
527 else
528 case "$1" in
529 git*|tig) ;;
530 -*) set git log "$@" ;;
531 *) set git "$@" ;;
532 esac
535 not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*")
536 eval '"$@"' refs/bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
539 bisect_reset() {
540 test -s "$GIT_DIR/BISECT_START" || {
541 echo "We are not bisecting."
542 return
544 case "$#" in
545 0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
546 1) git show-ref --verify --quiet -- "refs/heads/$1" ||
547 die "$1 does not seem to be a valid branch"
548 branch="$1" ;;
550 usage ;;
551 esac
552 git checkout "$branch" && bisect_clean_state
555 bisect_clean_state() {
556 # There may be some refs packed during bisection.
557 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
558 while read ref hash
560 git update-ref -d $ref $hash || exit
561 done
562 rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
563 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
564 rm -f "$GIT_DIR/BISECT_LOG" &&
565 rm -f "$GIT_DIR/BISECT_NAMES" &&
566 rm -f "$GIT_DIR/BISECT_RUN" &&
567 # Cleanup head-name if it got left by an old version of git-bisect
568 rm -f "$GIT_DIR/head-name" &&
570 rm -f "$GIT_DIR/BISECT_START"
573 bisect_replay () {
574 test -r "$1" || die "cannot read $1 for replaying"
575 bisect_reset
576 while read git bisect command rev
578 test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
579 if test "$git" = "git-bisect"; then
580 rev="$command"
581 command="$bisect"
583 case "$command" in
584 start)
585 cmd="bisect_start $rev"
586 eval "$cmd" ;;
587 good|bad|skip)
588 bisect_write "$command" "$rev" ;;
590 die "?? what are you talking about?" ;;
591 esac
592 done <"$1"
593 bisect_auto_next
596 bisect_run () {
597 bisect_next_check fail
599 while true
601 echo "running $@"
602 "$@"
603 res=$?
605 # Check for really bad run error.
606 if [ $res -lt 0 -o $res -ge 128 ]; then
607 echo >&2 "bisect run failed:"
608 echo >&2 "exit code $res from '$@' is < 0 or >= 128"
609 exit $res
612 # Find current state depending on run success or failure.
613 # A special exit code of 125 means cannot test.
614 if [ $res -eq 125 ]; then
615 state='skip'
616 elif [ $res -gt 0 ]; then
617 state='bad'
618 else
619 state='good'
622 # We have to use a subshell because "bisect_state" can exit.
623 ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
624 res=$?
626 cat "$GIT_DIR/BISECT_RUN"
628 if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
629 > /dev/null; then
630 echo >&2 "bisect run cannot continue any more"
631 exit $res
634 if [ $res -ne 0 ]; then
635 echo >&2 "bisect run failed:"
636 echo >&2 "'bisect_state $state' exited with error code $res"
637 exit $res
640 if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
641 echo "bisect run success"
642 exit 0;
645 done
649 case "$#" in
651 usage ;;
653 cmd="$1"
654 shift
655 case "$cmd" in
656 help)
657 git bisect -h ;;
658 start)
659 bisect_start "$@" ;;
660 bad|good)
661 bisect_state "$cmd" "$@" ;;
662 skip)
663 bisect_skip "$@" ;;
664 next)
665 # Not sure we want "next" at the UI level anymore.
666 bisect_next "$@" ;;
667 visualize|view)
668 bisect_visualize "$@" ;;
669 reset)
670 bisect_reset "$@" ;;
671 replay)
672 bisect_replay "$@" ;;
673 log)
674 cat "$GIT_DIR/BISECT_LOG" ;;
675 run)
676 bisect_run "$@" ;;
678 usage ;;
679 esac
680 esac