bisect: test merge base if good rev is not an ancestor of bad rev
[git/dscho.git] / git-bisect.sh
blobb314d47704c3d72b0d62382296135a46c2d6469e
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>...]
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 bisect_state() {
176 bisect_autostart
177 state=$1
178 case "$#,$state" in
179 0,*)
180 die "Please call 'bisect_state' with at least one argument." ;;
181 1,bad|1,good|1,skip)
182 rev=$(git rev-parse --verify HEAD) ||
183 die "Bad rev input: HEAD"
184 bisect_write "$state" "$rev" ;;
185 2,bad|*,good|*,skip)
186 shift
187 eval=''
188 for rev in "$@"
190 sha=$(git rev-parse --verify "$rev^{commit}") ||
191 die "Bad rev input: $rev"
192 eval="$eval bisect_write '$state' '$sha'; "
193 done
194 eval "$eval" ;;
195 *,bad)
196 die "'git bisect bad' can take only one argument." ;;
198 usage ;;
199 esac
200 bisect_auto_next
203 bisect_next_check() {
204 missing_good= missing_bad=
205 git show-ref -q --verify refs/bisect/bad || missing_bad=t
206 test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
208 case "$missing_good,$missing_bad,$1" in
209 ,,*)
210 : have both good and bad - ok
213 # do not have both but not asked to fail - just report.
214 false
216 t,,good)
217 # have bad but not good. we could bisect although
218 # this is less optimum.
219 echo >&2 'Warning: bisecting only with a bad commit.'
220 if test -t 0
221 then
222 printf >&2 'Are you sure [Y/n]? '
223 read yesno
224 case "$yesno" in [Nn]*) exit 1 ;; esac
226 : bisect without good...
229 THEN=''
230 test -s "$GIT_DIR/BISECT_START" || {
231 echo >&2 'You need to start by "git bisect start".'
232 THEN='then '
234 echo >&2 'You '$THEN'need to give me at least one good' \
235 'and one bad revisions.'
236 echo >&2 '(You can use "git bisect bad" and' \
237 '"git bisect good" for that.)'
238 exit 1 ;;
239 esac
242 bisect_auto_next() {
243 bisect_next_check && bisect_next || :
246 filter_skipped() {
247 _eval="$1"
248 _skip="$2"
250 if [ -z "$_skip" ]; then
251 eval "$_eval"
252 return
255 # Let's parse the output of:
256 # "git rev-list --bisect-vars --bisect-all ..."
257 eval "$_eval" | while read hash line
259 case "$VARS,$FOUND,$TRIED,$hash" in
260 # We display some vars.
261 1,*,*,*) echo "$hash $line" ;;
263 # Split line.
264 ,*,*,---*) ;;
266 # We had nothing to search.
267 ,,,bisect_rev*)
268 echo "bisect_rev="
269 VARS=1
272 # We did not find a good bisect rev.
273 # This should happen only if the "bad"
274 # commit is also a "skip" commit.
275 ,,*,bisect_rev*)
276 echo "bisect_rev=$TRIED"
277 VARS=1
280 # We are searching.
281 ,,*,*)
282 TRIED="${TRIED:+$TRIED|}$hash"
283 case "$_skip" in
284 *$hash*) ;;
286 echo "bisect_rev=$hash"
287 echo "bisect_tried=\"$TRIED\""
288 FOUND=1
290 esac
293 # We have already found a rev to be tested.
294 ,1,*,bisect_rev*) VARS=1 ;;
295 ,1,*,*) ;;
297 # ???
298 *) die "filter_skipped error " \
299 "VARS: '$VARS' " \
300 "FOUND: '$FOUND' " \
301 "TRIED: '$TRIED' " \
302 "hash: '$hash' " \
303 "line: '$line'"
305 esac
306 done
309 exit_if_skipped_commits () {
310 _tried=$1
311 if expr "$_tried" : ".*[|].*" > /dev/null ; then
312 echo "There are only 'skip'ped commit left to test."
313 echo "The first bad commit could be any of:"
314 echo "$_tried" | tr '[|]' '[\012]'
315 echo "We cannot bisect more!"
316 exit 2
320 bisect_checkout() {
321 _rev="$1"
322 _msg="$2"
323 echo "Bisecting: $_msg"
324 git checkout -q "$_rev" || exit
325 git show-branch "$_rev"
328 is_among() {
329 _rev="$1"
330 _list="$2"
331 case "$_list" in *$_rev*) return 0 ;; esac
332 return 1
335 is_testing_merge_base() {
336 grep "^testing $1$" "$GIT_DIR/BISECT_MERGE_BASES" >/dev/null 2>&1
339 mark_testing_merge_base() {
340 echo "testing $1" >> "$GIT_DIR/BISECT_MERGE_BASES"
343 handle_bad_merge_base() {
344 _badmb="$1"
345 _good="$2"
346 if is_testing_merge_base "$_badmb"; then
347 cat >&2 <<EOF
348 The merge base $_badmb is bad.
349 This means the bug has been fixed between $_badmb and [$_good].
351 exit 3
352 else
353 cat >&2 <<EOF
354 Some good revs are not ancestor of the bad rev.
355 git bisect cannot work properly in this case.
356 Maybe you mistake good and bad revs?
358 exit 1
362 handle_skipped_merge_base() {
363 _mb="$1"
364 _bad="$2"
365 _good="$3"
366 cat >&2 <<EOF
367 Warning: the merge base between $_bad and [$_good] must be skipped.
368 So we cannot be sure the first bad commit is between $_mb and $_bad.
369 We continue anyway.
373 check_merge_bases() {
374 _bad="$1"
375 _good="$2"
376 _skip="$3"
377 for _mb in $(git merge-base --all $_bad $_good)
379 if is_among "$_mb" "$_good"; then
380 continue
381 elif test "$_mb" = "$_bad"; then
382 handle_bad_merge_base "$_bad" "$_good"
383 elif is_among "$_mb" "$_skip"; then
384 handle_skipped_merge_base "$_mb" "$_bad" "$_good"
385 else
386 mark_testing_merge_base "$_mb"
387 bisect_checkout "$_mb" "a merge base must be tested"
388 checkout_done=1
389 return
391 done
394 check_good_are_ancestors_of_bad() {
395 _bad="$1"
396 _good=$(echo $2 | sed -e 's/\^//g')
397 _skip="$3"
399 # Bisecting with no good rev is ok
400 test -z "$_good" && return
402 _side=$(git rev-list $_good ^$_bad)
403 if test -n "$_side"; then
404 check_merge_bases "$_bad" "$_good" "$_skip"
408 bisect_next() {
409 case "$#" in 0) ;; *) usage ;; esac
410 bisect_autostart
411 bisect_next_check good
413 # Get bad, good and skipped revs
414 bad=$(git rev-parse --verify refs/bisect/bad) &&
415 good=$(git for-each-ref --format='^%(objectname)' \
416 "refs/bisect/good-*" | tr '\012' ' ') &&
417 skip=$(git for-each-ref --format='%(objectname)' \
418 "refs/bisect/skip-*" | tr '\012' ' ') &&
420 # Maybe some merge bases must be tested first
421 check_good_are_ancestors_of_bad "$bad" "$good" "$skip" || exit
422 test "$checkout_done" -eq "1" && checkout_done='' && return
424 # Get bisection information
425 BISECT_OPT=''
426 test -n "$skip" && BISECT_OPT='--bisect-all'
427 eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" &&
428 eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
429 eval=$(filter_skipped "$eval" "$skip") &&
430 eval "$eval" || exit
432 if [ -z "$bisect_rev" ]; then
433 echo "$bad was both good and bad"
434 exit 1
436 if [ "$bisect_rev" = "$bad" ]; then
437 exit_if_skipped_commits "$bisect_tried"
438 echo "$bisect_rev is first bad commit"
439 git diff-tree --pretty $bisect_rev
440 exit 0
443 # We should exit here only if the "bad"
444 # commit is also a "skip" commit (see above).
445 exit_if_skipped_commits "$bisect_rev"
447 bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this"
450 bisect_visualize() {
451 bisect_next_check fail
453 if test $# = 0
454 then
455 case "${DISPLAY+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in
456 '') set git log ;;
457 set*) set gitk ;;
458 esac
459 else
460 case "$1" in
461 git*|tig) ;;
462 -*) set git log "$@" ;;
463 *) set git "$@" ;;
464 esac
467 not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*")
468 eval '"$@"' refs/bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
471 bisect_reset() {
472 test -s "$GIT_DIR/BISECT_START" || {
473 echo "We are not bisecting."
474 return
476 case "$#" in
477 0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
478 1) git show-ref --verify --quiet -- "refs/heads/$1" ||
479 die "$1 does not seem to be a valid branch"
480 branch="$1" ;;
482 usage ;;
483 esac
484 git checkout "$branch" && bisect_clean_state
487 bisect_clean_state() {
488 # There may be some refs packed during bisection.
489 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
490 while read ref hash
492 git update-ref -d $ref $hash || exit
493 done
494 rm -f "$GIT_DIR/BISECT_MERGE_BASES" &&
495 rm -f "$GIT_DIR/BISECT_LOG" &&
496 rm -f "$GIT_DIR/BISECT_NAMES" &&
497 rm -f "$GIT_DIR/BISECT_RUN" &&
498 # Cleanup head-name if it got left by an old version of git-bisect
499 rm -f "$GIT_DIR/head-name" &&
501 rm -f "$GIT_DIR/BISECT_START"
504 bisect_replay () {
505 test -r "$1" || die "cannot read $1 for replaying"
506 bisect_reset
507 while read git bisect command rev
509 test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
510 if test "$git" = "git-bisect"; then
511 rev="$command"
512 command="$bisect"
514 case "$command" in
515 start)
516 cmd="bisect_start $rev"
517 eval "$cmd" ;;
518 good|bad|skip)
519 bisect_write "$command" "$rev" ;;
521 die "?? what are you talking about?" ;;
522 esac
523 done <"$1"
524 bisect_auto_next
527 bisect_run () {
528 bisect_next_check fail
530 while true
532 echo "running $@"
533 "$@"
534 res=$?
536 # Check for really bad run error.
537 if [ $res -lt 0 -o $res -ge 128 ]; then
538 echo >&2 "bisect run failed:"
539 echo >&2 "exit code $res from '$@' is < 0 or >= 128"
540 exit $res
543 # Find current state depending on run success or failure.
544 # A special exit code of 125 means cannot test.
545 if [ $res -eq 125 ]; then
546 state='skip'
547 elif [ $res -gt 0 ]; then
548 state='bad'
549 else
550 state='good'
553 # We have to use a subshell because "bisect_state" can exit.
554 ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
555 res=$?
557 cat "$GIT_DIR/BISECT_RUN"
559 if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
560 > /dev/null; then
561 echo >&2 "bisect run cannot continue any more"
562 exit $res
565 if [ $res -ne 0 ]; then
566 echo >&2 "bisect run failed:"
567 echo >&2 "'bisect_state $state' exited with error code $res"
568 exit $res
571 if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
572 echo "bisect run success"
573 exit 0;
576 done
580 case "$#" in
582 usage ;;
584 cmd="$1"
585 shift
586 case "$cmd" in
587 help)
588 git bisect -h ;;
589 start)
590 bisect_start "$@" ;;
591 bad|good|skip)
592 bisect_state "$cmd" "$@" ;;
593 next)
594 # Not sure we want "next" at the UI level anymore.
595 bisect_next "$@" ;;
596 visualize|view)
597 bisect_visualize "$@" ;;
598 reset)
599 bisect_reset "$@" ;;
600 replay)
601 bisect_replay "$@" ;;
602 log)
603 cat "$GIT_DIR/BISECT_LOG" ;;
604 run)
605 bisect_run "$@" ;;
607 usage ;;
608 esac
609 esac