1#!/bin/sh 2 3USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]' 4LONG_USAGE='git bisect help 5 print this long help message. 6git bisect start [<bad> [<good>...]] [--] [<pathspec>...] 7 reset bisect state and start bisection. 8git bisect bad [<rev>] 9 mark <rev> a known-bad revision. 10git bisect good [<rev>...] 11 mark <rev>... known-good revisions. 12git bisect skip [(<rev>|<range>)...] 13 mark <rev>... untestable revisions. 14git bisect next 15 find next bisection to test and check it out. 16git bisect reset [<branch>] 17 finish bisection search and go back to branch. 18git bisect visualize 19 show bisect status in gitk. 20git bisect replay <logfile> 21 replay bisection log. 22git bisect log 23 show bisect log. 24git bisect run <cmd>... 25 use <cmd>... to automatically bisect. 26 27Please use "git help bisect" to get the full man page.' 28 29OPTIONS_SPEC= 30. git-sh-setup 31require_work_tree 32 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" 35 36bisect_autostart() { 37test -s"$GIT_DIR/BISECT_START"|| { 38echo>&2'You need to start by "git bisect start"' 39iftest -t0 40then 41echo>&2-n'Do you want me to do it for you [Y/n]? ' 42read yesno 43case"$yesno"in 44[Nn]*) 45exit;; 46esac 47 bisect_start 48else 49exit1 50fi 51} 52} 53 54bisect_start() { 55# 56# Verify HEAD. 57# 58head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD)|| 59head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD)|| 60 die "Bad HEAD - I need a HEAD" 61 62# 63# Check if we are bisecting. 64# 65 start_head='' 66iftest -s"$GIT_DIR/BISECT_START" 67then 68# Reset to the rev from where we started. 69 start_head=$(cat "$GIT_DIR/BISECT_START") 70 git checkout "$start_head"--||exit 71else 72# Get rev from where we start. 73case"$head"in 74 refs/heads/*|$_x40) 75# This error message should only be triggered by 76# cogito usage, and cogito users should understand 77# it relates to cg-seek. 78[-s"$GIT_DIR/head-name"] && 79 die "won't bisect on seeked tree" 80 start_head="${head#refs/heads/}" 81;; 82*) 83 die "Bad HEAD - strange symbolic ref" 84;; 85esac 86fi 87 88# 89# Get rid of any old bisect state. 90# 91 bisect_clean_state ||exit 92 93# 94# Check for one bad and then some good revisions. 95# 96 has_double_dash=0 97for arg;do 98case"$arg"in--) has_double_dash=1;break;;esac 99done 100 orig_args=$(git rev-parse --sq-quote "$@") 101 bad_seen=0 102eval='' 103while[$#-gt0];do 104 arg="$1" 105case"$arg"in 106--) 107shift 108break 109;; 110*) 111rev=$(git rev-parse -q --verify "$arg^{commit}")|| { 112test$has_double_dash-eq1&& 113 die "'$arg' does not appear to be a valid revision" 114break 115} 116case$bad_seenin 1170) state='bad'; bad_seen=1;; 118*) state='good';; 119esac 120eval="$evalbisect_write '$state' '$rev' 'nolog'; " 121shift 122;; 123esac 124done 125 126# 127# Change state. 128# In case of mistaken revs or checkout error, or signals received, 129# "bisect_auto_next" below may exit or misbehave. 130# We have to trap this to be able to clean up using 131# "bisect_clean_state". 132# 133trap'bisect_clean_state'0 134trap'exit 255'1 2 3 15 135 136# 137# Write new start state. 138# 139echo"$start_head">"$GIT_DIR/BISECT_START"&& 140 git rev-parse --sq-quote"$@">"$GIT_DIR/BISECT_NAMES"&& 141eval"$eval"&& 142echo"git bisect start$orig_args">>"$GIT_DIR/BISECT_LOG"||exit 143# 144# Check if we can proceed to the next bisect state. 145# 146 bisect_auto_next 147 148trap'-'0 149} 150 151bisect_write() { 152 state="$1" 153rev="$2" 154 nolog="$3" 155case"$state"in 156 bad) tag="$state";; 157 good|skip) tag="$state"-"$rev";; 158*) die "Bad bisect_write argument:$state";; 159esac 160 git update-ref"refs/bisect/$tag""$rev"||exit 161echo"#$state:$(git show-branch $rev)">>"$GIT_DIR/BISECT_LOG" 162test -n"$nolog"||echo"git bisect$state$rev">>"$GIT_DIR/BISECT_LOG" 163} 164 165is_expected_rev() { 166test -f"$GIT_DIR/BISECT_EXPECTED_REV"&& 167test"$1"=$(cat "$GIT_DIR/BISECT_EXPECTED_REV") 168} 169 170mark_expected_rev() { 171echo"$1">"$GIT_DIR/BISECT_EXPECTED_REV" 172} 173 174check_expected_revs() { 175for _rev in"$@";do 176if! is_expected_rev "$_rev";then 177rm-f"$GIT_DIR/BISECT_ANCESTORS_OK" 178rm-f"$GIT_DIR/BISECT_EXPECTED_REV" 179return 180fi 181done 182} 183 184bisect_skip() { 185 all='' 186for arg in"$@" 187do 188case"$arg"in 189*..*) 190 revs=$(git rev-list "$arg")|| die "Bad rev input:$arg";; 191*) 192 revs=$(git rev-parse --sq-quote "$arg");; 193esac 194 all="$all$revs" 195done 196eval bisect_state 'skip'$all 197} 198 199bisect_state() { 200 bisect_autostart 201 state=$1 202case"$#,$state"in 2030,*) 204 die "Please call 'bisect_state' with at least one argument.";; 2051,bad|1,good|1,skip) 206rev=$(git rev-parse --verify HEAD)|| 207 die "Bad rev input: HEAD" 208 bisect_write "$state""$rev" 209 check_expected_revs "$rev";; 2102,bad|*,good|*,skip) 211shift 212eval='' 213forrevin"$@" 214do 215 sha=$(git rev-parse --verify "$rev^{commit}")|| 216 die "Bad rev input:$rev" 217eval="$evalbisect_write '$state' '$sha'; " 218done 219eval"$eval" 220 check_expected_revs "$@";; 221*,bad) 222 die "'git bisect bad' can take only one argument.";; 223*) 224 usage ;; 225esac 226 bisect_auto_next 227} 228 229bisect_next_check() { 230 missing_good= missing_bad= 231 git show-ref -q --verify refs/bisect/bad || missing_bad=t 232test -n"$(git for-each-ref "refs/bisect/good-*")"|| missing_good=t 233 234case"$missing_good,$missing_bad,$1"in 235,,*) 236: have both good and bad - ok 237;; 238*,) 239# do not have both but not asked to fail - just report. 240 false 241;; 242 t,,good) 243# have bad but not good. we could bisect although 244# this is less optimum. 245echo>&2'Warning: bisecting only with a bad commit.' 246iftest -t0 247then 248printf>&2'Are you sure [Y/n]? ' 249read yesno 250case"$yesno"in[Nn]*)exit1;;esac 251fi 252: bisect without good... 253;; 254*) 255 THEN='' 256test -s"$GIT_DIR/BISECT_START"|| { 257echo>&2'You need to start by "git bisect start".' 258 THEN='then ' 259} 260echo>&2'You '$THEN'need to give me at least one good' \ 261'and one bad revisions.' 262echo>&2'(You can use "git bisect bad" and' \ 263'"git bisect good" for that.)' 264exit1;; 265esac 266} 267 268bisect_auto_next() { 269 bisect_next_check && bisect_next || : 270} 271 272bisect_checkout() { 273 _rev="$1" 274 _msg="$2" 275echo"Bisecting:$_msg" 276 mark_expected_rev "$_rev" 277 git checkout -q"$_rev"--||exit 278 git show-branch"$_rev" 279} 280 281is_among() { 282 _rev="$1" 283 _list="$2" 284case"$_list"in*$_rev*)return0;;esac 285return1 286} 287 288handle_bad_merge_base() { 289 _badmb="$1" 290 _good="$2" 291if is_expected_rev "$_badmb";then 292cat>&2<<EOF 293The merge base$_badmbis bad. 294This means the bug has been fixed between$_badmband [$_good]. 295EOF 296exit3 297else 298cat>&2<<EOF 299Some good revs are not ancestor of the bad rev. 300git bisect cannot work properly in this case. 301Maybe you mistake good and bad revs? 302EOF 303exit1 304fi 305} 306 307handle_skipped_merge_base() { 308 _mb="$1" 309 _bad="$2" 310 _good="$3" 311cat>&2<<EOF 312Warning: the merge base between$_badand [$_good] must be skipped. 313So we cannot be sure the first bad commit is between$_mband$_bad. 314We continue anyway. 315EOF 316} 317 318# 319# "check_merge_bases" checks that merge bases are not "bad". 320# 321# - If one is "good", that's good, we have nothing to do. 322# - If one is "bad", it means the user assumed something wrong 323# and we must exit. 324# - If one is "skipped", we can't know but we should warn. 325# - If we don't know, we should check it out and ask the user to test. 326# 327# In the last case we will return 1, and otherwise 0. 328# 329check_merge_bases() { 330 _bad="$1" 331 _good="$2" 332 _skip="$3" 333for _mb in$(git merge-base --all $_bad $_good) 334do 335if is_among "$_mb""$_good";then 336continue 337eliftest"$_mb"="$_bad";then 338 handle_bad_merge_base "$_bad""$_good" 339elif is_among "$_mb""$_skip";then 340 handle_skipped_merge_base "$_mb""$_bad""$_good" 341else 342 bisect_checkout "$_mb""a merge base must be tested" 343return1 344fi 345done 346return0 347} 348 349# 350# "check_good_are_ancestors_of_bad" checks that all "good" revs are 351# ancestor of the "bad" rev. 352# 353# If that's not the case, we need to check the merge bases. 354# If a merge base must be tested by the user we return 1 and 355# otherwise 0. 356# 357check_good_are_ancestors_of_bad() { 358test -f"$GIT_DIR/BISECT_ANCESTORS_OK"&& 359return 360 361 _bad="$1" 362 _good=$(echo $2 | sed -e 's/\^//g') 363 _skip="$3" 364 365# Bisecting with no good rev is ok 366test -z"$_good"&&return 367 368 _side=$(git rev-list $_good ^$_bad) 369iftest -n"$_side";then 370# Return if a checkout was done 371 check_merge_bases "$_bad""$_good""$_skip"||return 372fi 373 374: >"$GIT_DIR/BISECT_ANCESTORS_OK" 375 376return0 377} 378 379bisect_next() { 380case"$#"in0) ;; *) usage ;;esac 381 bisect_autostart 382 bisect_next_check good 383 384# Get bad, good and skipped revs 385 bad=$(git rev-parse --verify refs/bisect/bad)&& 386 good=$(git for-each-ref --format='^%(objectname)' \ 387 "refs/bisect/good-*" | tr '\012' '') && 388 skip=$(git for-each-ref --format='%(objectname)' \ 389"refs/bisect/skip-*"|tr'\012'' ') ||exit 390 391# Maybe some merge bases must be tested first 392 check_good_are_ancestors_of_bad "$bad""$good""$skip" 393# Return now if a checkout has already been done 394test"$?"-eq"1"&&return 395 396# Perform bisection computation, display and checkout 397 git bisect--helper --next-exit 398 res=$? 399 400# Check if we should exit because bisection is finished 401test$res-eq10&&exit0 402 403# Check for an error in the bisection process 404test$res-ne0&&exit$res 405 406return0 407} 408 409bisect_visualize() { 410 bisect_next_check fail 411 412iftest$#=0 413then 414case"${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}"in 415'')set git log ;; 416set*)set gitk ;; 417esac 418else 419case"$1"in 420 git*|tig) ;; 421-*)set git log "$@";; 422*)set git "$@";; 423esac 424fi 425 426 not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*") 427 eval '"$@"' refs/bisect/bad --not$not--$(cat "$GIT_DIR/BISECT_NAMES") 428} 429 430bisect_reset() { 431 test -s "$GIT_DIR/BISECT_START" || { 432 echo "We are not bisecting." 433 return 434 } 435 case "$#" in 436 0) branch=$(cat "$GIT_DIR/BISECT_START");; 437 1) git show-ref --verify --quiet -- "refs/heads/$1" || 438 die "$1does not seem to be a valid branch" 439 branch="$1" ;; 440 *) 441 usage ;; 442 esac 443 git checkout "$branch" -- && bisect_clean_state 444} 445 446bisect_clean_state() { 447 # There may be some refs packed during bisection. 448 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* | 449 while read ref hash 450 do 451 git update-ref -d$ref$hash|| exit 452 done 453 rm -f "$GIT_DIR/BISECT_EXPECTED_REV" && 454 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" && 455 rm -f "$GIT_DIR/BISECT_LOG" && 456 rm -f "$GIT_DIR/BISECT_NAMES" && 457 rm -f "$GIT_DIR/BISECT_RUN" && 458 # Cleanup head-name if it got left by an old version of git-bisect 459 rm -f "$GIT_DIR/head-name" && 460 461 rm -f "$GIT_DIR/BISECT_START" 462} 463 464bisect_replay () { 465 test -r "$1" || die "cannot read$1for replaying" 466 bisect_reset 467 while read git bisect command rev 468 do 469 test "$git$bisect" = "git bisect" -o "$git" = "git-bisect" || continue 470 if test "$git" = "git-bisect"; then 471 rev="$command" 472 command="$bisect" 473 fi 474 case "$command" in 475 start) 476 cmd="bisect_start$rev" 477 eval "$cmd" ;; 478 good|bad|skip) 479 bisect_write "$command" "$rev" ;; 480 *) 481 die "?? what are you talking about?" ;; 482 esac 483 done <"$1" 484 bisect_auto_next 485} 486 487bisect_run () { 488 bisect_next_check fail 489 490 while true 491 do 492 echo "running $@" 493 "$@" 494 res=$? 495 496 # Check for really bad run error. 497 if [$res-lt 0 -o$res-ge 128 ]; then 498 echo >&2 "bisect run failed:" 499 echo >&2 "exit code$resfrom '$@' is < 0 or >= 128" 500 exit$res 501 fi 502 503 # Find current state depending on run success or failure. 504 # A special exit code of 125 means cannot test. 505 if [$res-eq 125 ]; then 506 state='skip' 507 elif [$res-gt 0 ]; then 508 state='bad' 509 else 510 state='good' 511 fi 512 513 # We have to use a subshell because "bisect_state" can exit. 514 ( bisect_state$state> "$GIT_DIR/BISECT_RUN" ) 515 res=$? 516 517 cat "$GIT_DIR/BISECT_RUN" 518 519 if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \ 520 > /dev/null; then 521 echo >&2 "bisect run cannot continue any more" 522 exit$res 523 fi 524 525 if [$res-ne 0 ]; then 526 echo >&2 "bisect run failed:" 527 echo >&2 "'bisect_state $state' exited with error code$res" 528 exit$res 529 fi 530 531 if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then 532 echo "bisect run success" 533 exit 0; 534 fi 535 536 done 537} 538 539 540case "$#" in 5410) 542 usage ;; 543*) 544 cmd="$1" 545 shift 546 case "$cmd" in 547 help) 548 git bisect -h ;; 549 start) 550 bisect_start "$@" ;; 551 bad|good) 552 bisect_state "$cmd" "$@" ;; 553 skip) 554 bisect_skip "$@" ;; 555 next) 556 # Not sure we want "next" at the UI level anymore. 557 bisect_next "$@" ;; 558 visualize|view) 559 bisect_visualize "$@" ;; 560 reset) 561 bisect_reset "$@" ;; 562 replay) 563 bisect_replay "$@" ;; 564 log) 565 cat "$GIT_DIR/BISECT_LOG" ;; 566 run) 567 bisect_run "$@" ;; 568 *) 569 usage ;; 570 esac 571esac