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 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_skip() { 195 all='' 196for arg in"$@" 197do 198case"$arg"in 199*..*) 200 revs=$(git rev-list "$arg")|| die "Bad rev input:$arg";; 201*) 202 revs=$(sq "$arg");; 203esac 204 all="$all$revs" 205done 206eval bisect_state 'skip'$all 207} 208 209bisect_state() { 210 bisect_autostart 211 state=$1 212case"$#,$state"in 2130,*) 214 die "Please call 'bisect_state' with at least one argument.";; 2151,bad|1,good|1,skip) 216rev=$(git rev-parse --verify HEAD)|| 217 die "Bad rev input: HEAD" 218 bisect_write "$state""$rev" 219 check_expected_revs "$rev";; 2202,bad|*,good|*,skip) 221shift 222eval='' 223forrevin"$@" 224do 225 sha=$(git rev-parse --verify "$rev^{commit}")|| 226 die "Bad rev input:$rev" 227eval="$evalbisect_write '$state' '$sha'; " 228done 229eval"$eval" 230 check_expected_revs "$@";; 231*,bad) 232 die "'git bisect bad' can take only one argument.";; 233*) 234 usage ;; 235esac 236 bisect_auto_next 237} 238 239bisect_next_check() { 240 missing_good= missing_bad= 241 git show-ref -q --verify refs/bisect/bad || missing_bad=t 242test -n"$(git for-each-ref "refs/bisect/good-*")"|| missing_good=t 243 244case"$missing_good,$missing_bad,$1"in 245,,*) 246: have both good and bad - ok 247;; 248*,) 249# do not have both but not asked to fail - just report. 250 false 251;; 252 t,,good) 253# have bad but not good. we could bisect although 254# this is less optimum. 255echo>&2'Warning: bisecting only with a bad commit.' 256iftest -t0 257then 258printf>&2'Are you sure [Y/n]? ' 259read yesno 260case"$yesno"in[Nn]*)exit1;;esac 261fi 262: bisect without good... 263;; 264*) 265 THEN='' 266test -s"$GIT_DIR/BISECT_START"|| { 267echo>&2'You need to start by "git bisect start".' 268 THEN='then ' 269} 270echo>&2'You '$THEN'need to give me at least one good' \ 271'and one bad revisions.' 272echo>&2'(You can use "git bisect bad" and' \ 273'"git bisect good" for that.)' 274exit1;; 275esac 276} 277 278bisect_auto_next() { 279 bisect_next_check && bisect_next || : 280} 281 282bisect_checkout() { 283 _rev="$1" 284 _msg="$2" 285echo"Bisecting:$_msg" 286 mark_expected_rev "$_rev" 287 git checkout -q"$_rev"--||exit 288 git show-branch"$_rev" 289} 290 291is_among() { 292 _rev="$1" 293 _list="$2" 294case"$_list"in*$_rev*)return0;;esac 295return1 296} 297 298handle_bad_merge_base() { 299 _badmb="$1" 300 _good="$2" 301if is_expected_rev "$_badmb";then 302cat>&2<<EOF 303The merge base$_badmbis bad. 304This means the bug has been fixed between$_badmband [$_good]. 305EOF 306exit3 307else 308cat>&2<<EOF 309Some good revs are not ancestor of the bad rev. 310git bisect cannot work properly in this case. 311Maybe you mistake good and bad revs? 312EOF 313exit1 314fi 315} 316 317handle_skipped_merge_base() { 318 _mb="$1" 319 _bad="$2" 320 _good="$3" 321cat>&2<<EOF 322Warning: the merge base between$_badand [$_good] must be skipped. 323So we cannot be sure the first bad commit is between$_mband$_bad. 324We continue anyway. 325EOF 326} 327 328# 329# "check_merge_bases" checks that merge bases are not "bad". 330# 331# - If one is "good", that's good, we have nothing to do. 332# - If one is "bad", it means the user assumed something wrong 333# and we must exit. 334# - If one is "skipped", we can't know but we should warn. 335# - If we don't know, we should check it out and ask the user to test. 336# 337# In the last case we will return 1, and otherwise 0. 338# 339check_merge_bases() { 340 _bad="$1" 341 _good="$2" 342 _skip="$3" 343for _mb in$(git merge-base --all $_bad $_good) 344do 345if is_among "$_mb""$_good";then 346continue 347eliftest"$_mb"="$_bad";then 348 handle_bad_merge_base "$_bad""$_good" 349elif is_among "$_mb""$_skip";then 350 handle_skipped_merge_base "$_mb""$_bad""$_good" 351else 352 bisect_checkout "$_mb""a merge base must be tested" 353return1 354fi 355done 356return0 357} 358 359# 360# "check_good_are_ancestors_of_bad" checks that all "good" revs are 361# ancestor of the "bad" rev. 362# 363# If that's not the case, we need to check the merge bases. 364# If a merge base must be tested by the user we return 1 and 365# otherwise 0. 366# 367check_good_are_ancestors_of_bad() { 368test -f"$GIT_DIR/BISECT_ANCESTORS_OK"&& 369return 370 371 _bad="$1" 372 _good=$(echo $2 | sed -e 's/\^//g') 373 _skip="$3" 374 375# Bisecting with no good rev is ok 376test -z"$_good"&&return 377 378 _side=$(git rev-list $_good ^$_bad) 379iftest -n"$_side";then 380# Return if a checkout was done 381 check_merge_bases "$_bad""$_good""$_skip"||return 382fi 383 384: >"$GIT_DIR/BISECT_ANCESTORS_OK" 385 386return0 387} 388 389bisect_next() { 390case"$#"in0) ;; *) usage ;;esac 391 bisect_autostart 392 bisect_next_check good 393 394# Get bad, good and skipped revs 395 bad=$(git rev-parse --verify refs/bisect/bad)&& 396 good=$(git for-each-ref --format='^%(objectname)' \ 397 "refs/bisect/good-*" | tr '\012' '') && 398 skip=$(git for-each-ref --format='%(objectname)' \ 399"refs/bisect/skip-*"|tr'\012'' ') ||exit 400 401# Maybe some merge bases must be tested first 402 check_good_are_ancestors_of_bad "$bad""$good""$skip" 403# Return now if a checkout has already been done 404test"$?"-eq"1"&&return 405 406# Perform bisection computation, display and checkout 407 git bisect--helper --next-exit 408 res=$? 409 410# Check if we should exit because bisection is finished 411test$res-eq10&&exit0 412 413# Check for an error in the bisection process 414test$res-ne0&&exit$res 415 416return0 417} 418 419bisect_visualize() { 420 bisect_next_check fail 421 422iftest$#=0 423then 424case"${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}"in 425'')set git log ;; 426set*)set gitk ;; 427esac 428else 429case"$1"in 430 git*|tig) ;; 431-*)set git log "$@";; 432*)set git "$@";; 433esac 434fi 435 436 not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*") 437 eval '"$@"' refs/bisect/bad --not$not--$(cat "$GIT_DIR/BISECT_NAMES") 438} 439 440bisect_reset() { 441 test -s "$GIT_DIR/BISECT_START" || { 442 echo "We are not bisecting." 443 return 444 } 445 case "$#" in 446 0) branch=$(cat "$GIT_DIR/BISECT_START");; 447 1) git show-ref --verify --quiet -- "refs/heads/$1" || 448 die "$1does not seem to be a valid branch" 449 branch="$1" ;; 450 *) 451 usage ;; 452 esac 453 git checkout "$branch" -- && bisect_clean_state 454} 455 456bisect_clean_state() { 457 # There may be some refs packed during bisection. 458 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* | 459 while read ref hash 460 do 461 git update-ref -d$ref$hash|| exit 462 done 463 rm -f "$GIT_DIR/BISECT_EXPECTED_REV" && 464 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" && 465 rm -f "$GIT_DIR/BISECT_LOG" && 466 rm -f "$GIT_DIR/BISECT_NAMES" && 467 rm -f "$GIT_DIR/BISECT_RUN" && 468 # Cleanup head-name if it got left by an old version of git-bisect 469 rm -f "$GIT_DIR/head-name" && 470 471 rm -f "$GIT_DIR/BISECT_START" 472} 473 474bisect_replay () { 475 test -r "$1" || die "cannot read$1for replaying" 476 bisect_reset 477 while read git bisect command rev 478 do 479 test "$git$bisect" = "git bisect" -o "$git" = "git-bisect" || continue 480 if test "$git" = "git-bisect"; then 481 rev="$command" 482 command="$bisect" 483 fi 484 case "$command" in 485 start) 486 cmd="bisect_start$rev" 487 eval "$cmd" ;; 488 good|bad|skip) 489 bisect_write "$command" "$rev" ;; 490 *) 491 die "?? what are you talking about?" ;; 492 esac 493 done <"$1" 494 bisect_auto_next 495} 496 497bisect_run () { 498 bisect_next_check fail 499 500 while true 501 do 502 echo "running $@" 503 "$@" 504 res=$? 505 506 # Check for really bad run error. 507 if [$res-lt 0 -o$res-ge 128 ]; then 508 echo >&2 "bisect run failed:" 509 echo >&2 "exit code$resfrom '$@' is < 0 or >= 128" 510 exit$res 511 fi 512 513 # Find current state depending on run success or failure. 514 # A special exit code of 125 means cannot test. 515 if [$res-eq 125 ]; then 516 state='skip' 517 elif [$res-gt 0 ]; then 518 state='bad' 519 else 520 state='good' 521 fi 522 523 # We have to use a subshell because "bisect_state" can exit. 524 ( bisect_state$state> "$GIT_DIR/BISECT_RUN" ) 525 res=$? 526 527 cat "$GIT_DIR/BISECT_RUN" 528 529 if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \ 530 > /dev/null; then 531 echo >&2 "bisect run cannot continue any more" 532 exit$res 533 fi 534 535 if [$res-ne 0 ]; then 536 echo >&2 "bisect run failed:" 537 echo >&2 "'bisect_state $state' exited with error code$res" 538 exit$res 539 fi 540 541 if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then 542 echo "bisect run success" 543 exit 0; 544 fi 545 546 done 547} 548 549 550case "$#" in 5510) 552 usage ;; 553*) 554 cmd="$1" 555 shift 556 case "$cmd" in 557 help) 558 git bisect -h ;; 559 start) 560 bisect_start "$@" ;; 561 bad|good) 562 bisect_state "$cmd" "$@" ;; 563 skip) 564 bisect_skip "$@" ;; 565 next) 566 # Not sure we want "next" at the UI level anymore. 567 bisect_next "$@" ;; 568 visualize|view) 569 bisect_visualize "$@" ;; 570 reset) 571 bisect_reset "$@" ;; 572 replay) 573 bisect_replay "$@" ;; 574 log) 575 cat "$GIT_DIR/BISECT_LOG" ;; 576 run) 577 bisect_run "$@" ;; 578 *) 579 usage ;; 580 esac 581esac