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>...] 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 36sq() { 37 @@PERL@@ -e' 38 for (@ARGV) { 39 s/'\''/'\'\\\\\'\''/g; 40 print " '\''$_'\''"; 41 } 42 print "\n"; 43 '"$@" 44} 45 46bisect_autostart() { 47test -s"$GIT_DIR/BISECT_START"|| { 48echo>&2'You need to start by "git bisect start"' 49iftest -t0 50then 51echo>&2-n'Do you want me to do it for you [Y/n]? ' 52read yesno 53case"$yesno"in 54[Nn]*) 55exit;; 56esac 57 bisect_start 58else 59exit1 60fi 61} 62} 63 64bisect_start() { 65# 66# Verify HEAD. 67# 68head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD)|| 69head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD)|| 70 die "Bad HEAD - I need a HEAD" 71 72# 73# Check if we are bisecting. 74# 75 start_head='' 76iftest -s"$GIT_DIR/BISECT_START" 77then 78# Reset to the rev from where we started. 79 start_head=$(cat "$GIT_DIR/BISECT_START") 80 git checkout "$start_head"||exit 81else 82# Get rev from where we start. 83case"$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;; 95esac 96fi 97 98# 99# Get rid of any old bisect state. 100# 101 bisect_clean_state ||exit 102 103# 104# Check for one bad and then some good revisions. 105# 106 has_double_dash=0 107for arg;do 108case"$arg"in--) has_double_dash=1;break;;esac 109done 110 orig_args=$(sq "$@") 111 bad_seen=0 112eval='' 113while[$#-gt0];do 114 arg="$1" 115case"$arg"in 116--) 117shift 118break 119;; 120*) 121rev=$(git rev-parse -q --verify "$arg^{commit}")|| { 122test$has_double_dash-eq1&& 123 die "'$arg' does not appear to be a valid revision" 124break 125} 126case$bad_seenin 1270) state='bad'; bad_seen=1;; 128*) state='good';; 129esac 130eval="$evalbisect_write '$state' '$rev' 'nolog'; " 131shift 132;; 133esac 134done 135 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# 143trap'bisect_clean_state'0 144trap'exit 255'1 2 3 15 145 146# 147# Write new start state. 148# 149echo"$start_head">"$GIT_DIR/BISECT_START"&& 150sq"$@">"$GIT_DIR/BISECT_NAMES"&& 151eval"$eval"&& 152echo"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 157 158trap'-'0 159} 160 161bisect_write() { 162 state="$1" 163rev="$2" 164 nolog="$3" 165case"$state"in 166 bad) tag="$state";; 167 good|skip) tag="$state"-"$rev";; 168*) die "Bad bisect_write argument:$state";; 169esac 170 git update-ref"refs/bisect/$tag""$rev"||exit 171echo"#$state:$(git show-branch $rev)">>"$GIT_DIR/BISECT_LOG" 172test -n"$nolog"||echo"git bisect$state$rev">>"$GIT_DIR/BISECT_LOG" 173} 174 175is_expected_rev() { 176test -f"$GIT_DIR/BISECT_EXPECTED_REV"&& 177test"$1"=$(cat "$GIT_DIR/BISECT_EXPECTED_REV") 178} 179 180mark_expected_rev() { 181echo"$1">"$GIT_DIR/BISECT_EXPECTED_REV" 182} 183 184check_expected_revs() { 185for _rev in"$@";do 186if! is_expected_rev "$_rev";then 187rm-f"$GIT_DIR/BISECT_ANCESTORS_OK" 188rm-f"$GIT_DIR/BISECT_EXPECTED_REV" 189return 190fi 191done 192} 193 194bisect_state() { 195 bisect_autostart 196 state=$1 197case"$#,$state"in 1980,*) 199 die "Please call 'bisect_state' with at least one argument.";; 2001,bad|1,good|1,skip) 201rev=$(git rev-parse --verify HEAD)|| 202 die "Bad rev input: HEAD" 203 bisect_write "$state""$rev" 204 check_expected_revs "$rev";; 2052,bad|*,good|*,skip) 206shift 207eval='' 208forrevin"$@" 209do 210 sha=$(git rev-parse --verify "$rev^{commit}")|| 211 die "Bad rev input:$rev" 212eval="$evalbisect_write '$state' '$sha'; " 213done 214eval"$eval" 215 check_expected_revs "$@";; 216*,bad) 217 die "'git bisect bad' can take only one argument.";; 218*) 219 usage ;; 220esac 221 bisect_auto_next 222} 223 224bisect_next_check() { 225 missing_good= missing_bad= 226 git show-ref -q --verify refs/bisect/bad || missing_bad=t 227test -n"$(git for-each-ref "refs/bisect/good-*")"|| missing_good=t 228 229case"$missing_good,$missing_bad,$1"in 230,,*) 231: have both good and bad - ok 232;; 233*,) 234# do not have both but not asked to fail - just report. 235 false 236;; 237 t,,good) 238# have bad but not good. we could bisect although 239# this is less optimum. 240echo>&2'Warning: bisecting only with a bad commit.' 241iftest -t0 242then 243printf>&2'Are you sure [Y/n]? ' 244read yesno 245case"$yesno"in[Nn]*)exit1;;esac 246fi 247: bisect without good... 248;; 249*) 250 THEN='' 251test -s"$GIT_DIR/BISECT_START"|| { 252echo>&2'You need to start by "git bisect start".' 253 THEN='then ' 254} 255echo>&2'You '$THEN'need to give me at least one good' \ 256'and one bad revisions.' 257echo>&2'(You can use "git bisect bad" and' \ 258'"git bisect good" for that.)' 259exit1;; 260esac 261} 262 263bisect_auto_next() { 264 bisect_next_check && bisect_next || : 265} 266 267filter_skipped() { 268 _eval="$1" 269 _skip="$2" 270 271if[-z"$_skip"];then 272eval"$_eval" 273return 274fi 275 276# Let's parse the output of: 277# "git rev-list --bisect-vars --bisect-all ..." 278eval"$_eval"|whileread hash line 279do 280case"$VARS,$FOUND,$TRIED,$hash"in 281# We display some vars. 2821,*,*,*)echo"$hash$line";; 283 284# Split line. 285,*,*,---*) ;; 286 287# We had nothing to search. 288,,,bisect_rev*) 289echo"bisect_rev=" 290 VARS=1 291;; 292 293# We did not find a good bisect rev. 294# This should happen only if the "bad" 295# commit is also a "skip" commit. 296,,*,bisect_rev*) 297echo"bisect_rev=$TRIED" 298 VARS=1 299;; 300 301# We are searching. 302,,*,*) 303 TRIED="${TRIED:+$TRIED|}$hash" 304case"$_skip"in 305*$hash*) ;; 306*) 307echo"bisect_rev=$hash" 308echo"bisect_tried=\"$TRIED\"" 309 FOUND=1 310;; 311esac 312;; 313 314# We have already found a rev to be tested. 315,1,*,bisect_rev*) VARS=1;; 316,1,*,*) ;; 317 318# ??? 319*) die "filter_skipped error " \ 320"VARS: '$VARS' " \ 321"FOUND: '$FOUND' " \ 322"TRIED: '$TRIED' " \ 323"hash: '$hash' " \ 324"line: '$line'" 325;; 326esac 327done 328} 329 330exit_if_skipped_commits () { 331 _tried=$1 332ifexpr"$_tried":".*[|].*"> /dev/null ;then 333echo"There are only 'skip'ped commit left to test." 334echo"The first bad commit could be any of:" 335echo"$_tried"|tr'[|]''[\012]' 336echo"We cannot bisect more!" 337exit2 338fi 339} 340 341bisect_checkout() { 342 _rev="$1" 343 _msg="$2" 344echo"Bisecting:$_msg" 345 mark_expected_rev "$_rev" 346 git checkout -q"$_rev"||exit 347 git show-branch"$_rev" 348} 349 350is_among() { 351 _rev="$1" 352 _list="$2" 353case"$_list"in*$_rev*)return0;;esac 354return1 355} 356 357handle_bad_merge_base() { 358 _badmb="$1" 359 _good="$2" 360if is_expected_rev "$_badmb";then 361cat>&2<<EOF 362The merge base$_badmbis bad. 363This means the bug has been fixed between$_badmband [$_good]. 364EOF 365exit3 366else 367cat>&2<<EOF 368Some good revs are not ancestor of the bad rev. 369git bisect cannot work properly in this case. 370Maybe you mistake good and bad revs? 371EOF 372exit1 373fi 374} 375 376handle_skipped_merge_base() { 377 _mb="$1" 378 _bad="$2" 379 _good="$3" 380cat>&2<<EOF 381Warning: the merge base between$_badand [$_good] must be skipped. 382So we cannot be sure the first bad commit is between$_mband$_bad. 383We continue anyway. 384EOF 385} 386 387check_merge_bases() { 388 _bad="$1" 389 _good="$2" 390 _skip="$3" 391for _mb in$(git merge-base --all $_bad $_good) 392do 393if is_among "$_mb""$_good";then 394continue 395eliftest"$_mb"="$_bad";then 396 handle_bad_merge_base "$_bad""$_good" 397elif is_among "$_mb""$_skip";then 398 handle_skipped_merge_base "$_mb""$_bad""$_good" 399else 400 bisect_checkout "$_mb""a merge base must be tested" 401 checkout_done=1 402return 403fi 404done 405} 406 407check_good_are_ancestors_of_bad() { 408test -f"$GIT_DIR/BISECT_ANCESTORS_OK"&& 409return 410 411 _bad="$1" 412 _good=$(echo $2 | sed -e 's/\^//g') 413 _skip="$3" 414 415# Bisecting with no good rev is ok 416test -z"$_good"&&return 417 418 _side=$(git rev-list $_good ^$_bad) 419iftest -n"$_side";then 420 check_merge_bases "$_bad""$_good""$_skip"||return 421test"$checkout_done"-eq"1"&&return 422fi 423 424: >"$GIT_DIR/BISECT_ANCESTORS_OK" 425} 426 427bisect_next() { 428case"$#"in0) ;; *) usage ;;esac 429 bisect_autostart 430 bisect_next_check good 431 432# Get bad, good and skipped revs 433 bad=$(git rev-parse --verify refs/bisect/bad)&& 434 good=$(git for-each-ref --format='^%(objectname)' \ 435 "refs/bisect/good-*" | tr '\012' '') && 436 skip=$(git for-each-ref --format='%(objectname)' \ 437"refs/bisect/skip-*"|tr'\012'' ') && 438 439# Maybe some merge bases must be tested first 440 check_good_are_ancestors_of_bad "$bad""$good""$skip"||exit 441test"$checkout_done"-eq"1"&& checkout_done=''&&return 442 443# Get bisection information 444 BISECT_OPT='' 445test -n"$skip"&& BISECT_OPT='--bisect-all' 446eval="git rev-list --bisect-vars$BISECT_OPT$good$bad--"&& 447eval="$eval$(cat "$GIT_DIR/BISECT_NAMES")"&& 448eval=$(filter_skipped "$eval" "$skip")&& 449eval"$eval"||exit 450 451if[-z"$bisect_rev"];then 452echo"$badwas both good and bad" 453exit1 454fi 455if["$bisect_rev"="$bad"];then 456 exit_if_skipped_commits "$bisect_tried" 457echo"$bisect_revis first bad commit" 458 git diff-tree --pretty$bisect_rev 459exit0 460fi 461 462# We should exit here only if the "bad" 463# commit is also a "skip" commit (see above). 464 exit_if_skipped_commits "$bisect_rev" 465 466 bisect_checkout "$bisect_rev""$bisect_nrrevisions left to test after this" 467} 468 469bisect_visualize() { 470 bisect_next_check fail 471 472iftest$#=0 473then 474case"${DISPLAY+set}${MSYSTEM+set}${SECURITYSESSIONID+set}"in 475'')set git log ;; 476set*)set gitk ;; 477esac 478else 479case"$1"in 480 git*|tig) ;; 481-*)set git log "$@";; 482*)set git "$@";; 483esac 484fi 485 486 not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*") 487 eval '"$@"' refs/bisect/bad --not$not--$(cat "$GIT_DIR/BISECT_NAMES") 488} 489 490bisect_reset() { 491 test -s "$GIT_DIR/BISECT_START" || { 492 echo "We are not bisecting." 493 return 494 } 495 case "$#" in 496 0) branch=$(cat "$GIT_DIR/BISECT_START");; 497 1) git show-ref --verify --quiet -- "refs/heads/$1" || 498 die "$1does not seem to be a valid branch" 499 branch="$1" ;; 500 *) 501 usage ;; 502 esac 503 git checkout "$branch" && bisect_clean_state 504} 505 506bisect_clean_state() { 507 # There may be some refs packed during bisection. 508 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* | 509 while read ref hash 510 do 511 git update-ref -d$ref$hash|| exit 512 done 513 rm -f "$GIT_DIR/BISECT_EXPECTED_REV" && 514 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" && 515 rm -f "$GIT_DIR/BISECT_LOG" && 516 rm -f "$GIT_DIR/BISECT_NAMES" && 517 rm -f "$GIT_DIR/BISECT_RUN" && 518 # Cleanup head-name if it got left by an old version of git-bisect 519 rm -f "$GIT_DIR/head-name" && 520 521 rm -f "$GIT_DIR/BISECT_START" 522} 523 524bisect_replay () { 525 test -r "$1" || die "cannot read$1for replaying" 526 bisect_reset 527 while read git bisect command rev 528 do 529 test "$git$bisect" = "git bisect" -o "$git" = "git-bisect" || continue 530 if test "$git" = "git-bisect"; then 531 rev="$command" 532 command="$bisect" 533 fi 534 case "$command" in 535 start) 536 cmd="bisect_start$rev" 537 eval "$cmd" ;; 538 good|bad|skip) 539 bisect_write "$command" "$rev" ;; 540 *) 541 die "?? what are you talking about?" ;; 542 esac 543 done <"$1" 544 bisect_auto_next 545} 546 547bisect_run () { 548 bisect_next_check fail 549 550 while true 551 do 552 echo "running $@" 553 "$@" 554 res=$? 555 556 # Check for really bad run error. 557 if [$res-lt 0 -o$res-ge 128 ]; then 558 echo >&2 "bisect run failed:" 559 echo >&2 "exit code$resfrom '$@' is < 0 or >= 128" 560 exit$res 561 fi 562 563 # Find current state depending on run success or failure. 564 # A special exit code of 125 means cannot test. 565 if [$res-eq 125 ]; then 566 state='skip' 567 elif [$res-gt 0 ]; then 568 state='bad' 569 else 570 state='good' 571 fi 572 573 # We have to use a subshell because "bisect_state" can exit. 574 ( bisect_state$state> "$GIT_DIR/BISECT_RUN" ) 575 res=$? 576 577 cat "$GIT_DIR/BISECT_RUN" 578 579 if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \ 580 > /dev/null; then 581 echo >&2 "bisect run cannot continue any more" 582 exit$res 583 fi 584 585 if [$res-ne 0 ]; then 586 echo >&2 "bisect run failed:" 587 echo >&2 "'bisect_state $state' exited with error code$res" 588 exit$res 589 fi 590 591 if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then 592 echo "bisect run success" 593 exit 0; 594 fi 595 596 done 597} 598 599 600case "$#" in 6010) 602 usage ;; 603*) 604 cmd="$1" 605 shift 606 case "$cmd" in 607 help) 608 git bisect -h ;; 609 start) 610 bisect_start "$@" ;; 611 bad|good|skip) 612 bisect_state "$cmd" "$@" ;; 613 next) 614 # Not sure we want "next" at the UI level anymore. 615 bisect_next "$@" ;; 616 visualize|view) 617 bisect_visualize "$@" ;; 618 reset) 619 bisect_reset "$@" ;; 620 replay) 621 bisect_replay "$@" ;; 622 log) 623 cat "$GIT_DIR/BISECT_LOG" ;; 624 run) 625 bisect_run "$@" ;; 626 *) 627 usage ;; 628 esac 629esac