1#!/bin/sh 2 3USAGE='[help|start|bad|good|new|old|terms|skip|next|reset|visualize|replay|log|run]' 4LONG_USAGE='git bisect help 5 print this long help message. 6git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>] 7 [--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...] 8 reset bisect state and start bisection. 9git bisect (bad|new) [<rev>] 10 mark <rev> a known-bad revision/ 11 a revision after change in a given property. 12git bisect (good|old) [<rev>...] 13 mark <rev>... known-good revisions/ 14 revisions before change in a given property. 15git bisect terms [--term-good | --term-bad] 16 show the terms used for old and new commits (default: bad, good) 17git bisect skip [(<rev>|<range>)...] 18 mark <rev>... untestable revisions. 19git bisect next 20 find next bisection to test and check it out. 21git bisect reset [<commit>] 22 finish bisection search and go back to commit. 23git bisect visualize 24 show bisect status in gitk. 25git bisect replay <logfile> 26 replay bisection log. 27git bisect log 28 show bisect log. 29git bisect run <cmd>... 30 use <cmd>... to automatically bisect. 31 32Please use "git help bisect" to get the full man page.' 33 34OPTIONS_SPEC= 35. git-sh-setup 36. git-sh-i18n 37 38_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' 39_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" 40TERM_BAD=bad 41TERM_GOOD=good 42 43bisect_head() 44{ 45iftest -f"$GIT_DIR/BISECT_HEAD" 46then 47echo BISECT_HEAD 48else 49echo HEAD 50fi 51} 52 53bisect_autostart() { 54test -s"$GIT_DIR/BISECT_START"|| { 55 gettextln "You need to start by\"git bisect start\"">&2 56iftest -t0 57then 58# TRANSLATORS: Make sure to include [Y] and [n] in your 59# translation. The program will only accept English input 60# at this point. 61gettext"Do you want me to do it for you [Y/n]? ">&2 62read yesno 63case"$yesno"in 64[Nn]*) 65exit;; 66esac 67 bisect_start 68else 69exit1 70fi 71} 72} 73 74bisect_start() { 75# 76# Check for one bad and then some good revisions. 77# 78 has_double_dash=0 79for arg;do 80case"$arg"in--) has_double_dash=1;break;;esac 81done 82 orig_args=$(git rev-parse --sq-quote "$@") 83 bad_seen=0 84eval='' 85 must_write_terms=0 86 revs='' 87iftest"z$(git rev-parse --is-bare-repository)"!= zfalse 88then 89 mode=--no-checkout 90else 91 mode='' 92fi 93while[$#-gt0];do 94 arg="$1" 95case"$arg"in 96--) 97shift 98break 99;; 100--no-checkout) 101 mode=--no-checkout 102shift;; 103--term-good|--term-old) 104shift 105 must_write_terms=1 106 TERM_GOOD=$1 107shift;; 108--term-good=*|--term-old=*) 109 must_write_terms=1 110 TERM_GOOD=${1#*=} 111shift;; 112--term-bad|--term-new) 113shift 114 must_write_terms=1 115 TERM_BAD=$1 116shift;; 117--term-bad=*|--term-new=*) 118 must_write_terms=1 119 TERM_BAD=${1#*=} 120shift;; 121--*) 122 die "$(eval_gettext "unrecognised option: '\$arg'")";; 123*) 124rev=$(git rev-parse -q --verify "$arg^{commit}")|| { 125test$has_double_dash-eq1&& 126 die "$(eval_gettext "'\$arg' does not appear to be a valid revision")" 127break 128} 129 revs="$revs$rev" 130shift 131;; 132esac 133done 134 135forrevin$revs 136do 137# The user ran "git bisect start <sha1> 138# <sha1>", hence did not explicitly specify 139# the terms, but we are already starting to 140# set references named with the default terms, 141# and won't be able to change afterwards. 142 must_write_terms=1 143 144case$bad_seenin 1450) state=$TERM_BAD; bad_seen=1;; 146*) state=$TERM_GOOD;; 147esac 148eval="$evalbisect_write '$state' '$rev' 'nolog' &&" 149done 150# 151# Verify HEAD. 152# 153head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD)|| 154head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD)|| 155 die "$(gettext "Bad HEAD - I need a HEAD")" 156 157# 158# Check if we are bisecting. 159# 160 start_head='' 161iftest -s"$GIT_DIR/BISECT_START" 162then 163# Reset to the rev from where we started. 164 start_head=$(cat "$GIT_DIR/BISECT_START") 165iftest"z$mode"!="z--no-checkout" 166then 167 git checkout "$start_head"--|| 168 die "$(eval_gettext "Checking out '\$start_head' failed. Try 'git bisect reset <valid-branch>'.")" 169fi 170else 171# Get rev from where we start. 172case"$head"in 173 refs/heads/*|$_x40) 174# This error message should only be triggered by 175# cogito usage, and cogito users should understand 176# it relates to cg-seek. 177[-s"$GIT_DIR/head-name"] && 178 die "$(gettext "won't bisect on cg-seek'ed tree")" 179 start_head="${head#refs/heads/}" 180;; 181*) 182 die "$(gettext "Bad HEAD - strange symbolic ref")" 183;; 184esac 185fi 186 187# 188# Get rid of any old bisect state. 189# 190 bisect_clean_state ||exit 191 192# 193# Change state. 194# In case of mistaken revs or checkout error, or signals received, 195# "bisect_auto_next" below may exit or misbehave. 196# We have to trap this to be able to clean up using 197# "bisect_clean_state". 198# 199trap'bisect_clean_state'0 200trap'exit 255'1 2 3 15 201 202# 203# Write new start state. 204# 205echo"$start_head">"$GIT_DIR/BISECT_START"&& { 206test"z$mode"!="z--no-checkout"|| 207 git update-ref --no-deref BISECT_HEAD "$start_head" 208} && 209 git rev-parse --sq-quote"$@">"$GIT_DIR/BISECT_NAMES"&& 210eval"$evaltrue"&& 211iftest$must_write_terms-eq1 212then 213 write_terms "$TERM_BAD""$TERM_GOOD" 214fi&& 215echo"git bisect start$orig_args">>"$GIT_DIR/BISECT_LOG"||exit 216# 217# Check if we can proceed to the next bisect state. 218# 219 bisect_auto_next 220 221trap'-'0 222} 223 224bisect_write() { 225 state="$1" 226rev="$2" 227 nolog="$3" 228case"$state"in 229"$TERM_BAD") 230 tag="$state";; 231"$TERM_GOOD"|skip) 232 tag="$state"-"$rev";; 233*) 234 die "$(eval_gettext "Bad bisect_write argument: \$state")";; 235esac 236 git update-ref"refs/bisect/$tag""$rev"||exit 237echo"#$state:$(git show-branch $rev)">>"$GIT_DIR/BISECT_LOG" 238test -n"$nolog"||echo"git bisect$state$rev">>"$GIT_DIR/BISECT_LOG" 239} 240 241is_expected_rev() { 242test -f"$GIT_DIR/BISECT_EXPECTED_REV"&& 243test"$1"=$(cat "$GIT_DIR/BISECT_EXPECTED_REV") 244} 245 246check_expected_revs() { 247for _rev in"$@";do 248if! is_expected_rev "$_rev" 249then 250rm-f"$GIT_DIR/BISECT_ANCESTORS_OK" 251rm-f"$GIT_DIR/BISECT_EXPECTED_REV" 252return 253fi 254done 255} 256 257bisect_skip() { 258 all='' 259for arg in"$@" 260do 261case"$arg"in 262*..*) 263 revs=$(git rev-list "$arg")|| die "$(eval_gettext "Bad rev input: \$arg")";; 264*) 265 revs=$(git rev-parse --sq-quote "$arg");; 266esac 267 all="$all$revs" 268done 269eval bisect_state 'skip'$all 270} 271 272bisect_state() { 273 bisect_autostart 274 state=$1 275 check_and_set_terms $state 276case"$#,$state"in 2770,*) 278 die "$(gettext "Please call 'bisect_state' with at least one argument.")";; 2791,"$TERM_BAD"|1,"$TERM_GOOD"|1,skip) 280 bisected_head=$(bisect_head) 281rev=$(git rev-parse --verify "$bisected_head")|| 282 die "$(eval_gettext "Bad rev input: \$bisected_head")" 283 bisect_write "$state""$rev" 284 check_expected_revs "$rev";; 2852,"$TERM_BAD"|*,"$TERM_GOOD"|*,skip) 286shift 287 hash_list='' 288forrevin"$@" 289do 290 sha=$(git rev-parse --verify "$rev^{commit}")|| 291 die "$(eval_gettext "Bad rev input: \$rev")" 292 hash_list="$hash_list$sha" 293done 294forrevin$hash_list 295do 296 bisect_write "$state""$rev" 297done 298 check_expected_revs $hash_list;; 299*,"$TERM_BAD") 300 die "$(eval_gettext "'git bisect \$TERM_BAD' can take only one argument.")";; 301*) 302 usage ;; 303esac 304 bisect_auto_next 305} 306 307bisect_next_check() { 308 missing_good= missing_bad= 309 git show-ref -q --verify refs/bisect/$TERM_BAD|| missing_bad=t 310test -n"$(git for-each-ref "refs/bisect/$TERM_GOOD-*")"|| missing_good=t 311 312case"$missing_good,$missing_bad,$1"in 313,,*) 314: have both $TERM_GOOD and $TERM_BAD- ok 315;; 316*,) 317# do not have both but not asked to fail - just report. 318 false 319;; 320 t,,"$TERM_GOOD") 321# have bad (or new) but not good (or old). we could bisect although 322# this is less optimum. 323 eval_gettextln "Warning: bisecting only with a \$TERM_BADcommit.">&2 324iftest -t0 325then 326# TRANSLATORS: Make sure to include [Y] and [n] in your 327# translation. The program will only accept English input 328# at this point. 329gettext"Are you sure [Y/n]? ">&2 330read yesno 331case"$yesno"in[Nn]*)exit1;;esac 332fi 333: bisect without $TERM_GOOD... 334;; 335*) 336 bad_syn=$(bisect_voc bad) 337 good_syn=$(bisect_voc good) 338iftest -s"$GIT_DIR/BISECT_START" 339then 340 341 eval_gettextln "You need to give me at least one \$bad_synand one \$good_synrevision. 342(You can use\"git bisect \$bad_syn\"and\"git bisect \$good_syn\"for that.)">&2 343else 344 eval_gettextln "You need to start by\"git bisect start\". 345You then need to give me at least one \$good_synand one \$bad_synrevision. 346(You can use\"git bisect \$bad_syn\"and\"git bisect \$good_syn\"for that.)">&2 347fi 348exit1;; 349esac 350} 351 352bisect_auto_next() { 353 bisect_next_check && bisect_next || : 354} 355 356bisect_next() { 357case"$#"in0) ;; *) usage ;;esac 358 bisect_autostart 359 bisect_next_check $TERM_GOOD 360 361# Perform all bisection computation, display and checkout 362 git bisect--helper --next-all$(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout) 363 res=$? 364 365# Check if we should exit because bisection is finished 366iftest$res-eq10 367then 368 bad_rev=$(git show-ref --hash --verify refs/bisect/$TERM_BAD) 369 bad_commit=$(git show-branch $bad_rev) 370echo"# first$TERM_BADcommit:$bad_commit">>"$GIT_DIR/BISECT_LOG" 371exit0 372eliftest$res-eq2 373then 374echo"# only skipped commits left to test">>"$GIT_DIR/BISECT_LOG" 375 good_revs=$(git for-each-ref --format="%(objectname)" "refs/bisect/$TERM_GOOD-*") 376 for skipped in$(git rev-list refs/bisect/$TERM_BAD --not $good_revs) 377 do 378 skipped_commit=$(git show-branch $skipped) 379 echo "# possible first $TERM_BAD commit: $skipped_commit" >>"$GIT_DIR/BISECT_LOG" 380done 381exit$res 382fi 383 384# Check for an error in the bisection process 385test$res-ne0&&exit$res 386 387return0 388} 389 390bisect_visualize() { 391 bisect_next_check fail 392 393iftest$#=0 394then 395iftest -n"${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}"&& 396type gitk >/dev/null 2>&1 397then 398set gitk 399else 400set git log 401fi 402else 403case"$1"in 404 git*|tig) ;; 405-*)set git log "$@";; 406*)set git "$@";; 407esac 408fi 409 410eval'"$@"'--bisect --$(cat "$GIT_DIR/BISECT_NAMES") 411} 412 413bisect_reset() { 414test -s"$GIT_DIR/BISECT_START"|| { 415 gettextln "We are not bisecting." 416return 417} 418case"$#"in 4190) branch=$(cat "$GIT_DIR/BISECT_START");; 4201) git rev-parse --quiet --verify"$1^{commit}">/dev/null || { 421 invalid="$1" 422 die "$(eval_gettext "'\$invalid' is not a valid commit")" 423} 424 branch="$1";; 425*) 426 usage ;; 427esac 428 429if!test -f"$GIT_DIR/BISECT_HEAD"&& ! git checkout "$branch"-- 430then 431 die "$(eval_gettext "Could not check out original HEAD '\$branch'. 432Try 'git bisect reset <commit>'.")" 433fi 434 bisect_clean_state 435} 436 437bisect_clean_state() { 438# There may be some refs packed during bisection. 439 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* | 440whileread ref hash 441do 442 git update-ref -d$ref $hash||exit 443done 444rm-f"$GIT_DIR/BISECT_EXPECTED_REV"&& 445rm-f"$GIT_DIR/BISECT_ANCESTORS_OK"&& 446rm-f"$GIT_DIR/BISECT_LOG"&& 447rm-f"$GIT_DIR/BISECT_NAMES"&& 448rm-f"$GIT_DIR/BISECT_RUN"&& 449rm-f"$GIT_DIR/BISECT_TERMS"&& 450# Cleanup head-name if it got left by an old version of git-bisect 451rm-f"$GIT_DIR/head-name"&& 452 git update-ref -d --no-deref BISECT_HEAD && 453# clean up BISECT_START last 454rm-f"$GIT_DIR/BISECT_START" 455} 456 457bisect_replay () { 458file="$1" 459test"$#"-eq1|| die "$(gettext "No logfile given")" 460test -r"$file"|| die "$(eval_gettext "cannot read \$file for replaying")" 461 bisect_reset 462whileread git bisect commandrev 463do 464test"$git$bisect"="git bisect"||test"$git"="git-bisect"||continue 465iftest"$git"="git-bisect" 466then 467rev="$command" 468command="$bisect" 469fi 470 get_terms 471 check_and_set_terms "$command" 472case"$command"in 473 start) 474 cmd="bisect_start$rev" 475eval"$cmd";; 476"$TERM_GOOD"|"$TERM_BAD"|skip) 477 bisect_write "$command""$rev";; 478 terms) 479 bisect_terms $rev;; 480*) 481 die "$(gettext "?? what are you talking about?")";; 482esac 483done<"$file" 484 bisect_auto_next 485} 486 487bisect_run () { 488 bisect_next_check fail 489 490while true 491do 492command="$@" 493 eval_gettextln "running \$command" 494"$@" 495 res=$? 496 497# Check for really bad run error. 498if[$res-lt0-o$res-ge128] 499then 500 eval_gettextln "bisect run failed: 501exit code \$resfrom '\$command' is < 0 or >= 128">&2 502exit$res 503fi 504 505# Find current state depending on run success or failure. 506# A special exit code of 125 means cannot test. 507if[$res-eq125] 508then 509 state='skip' 510elif[$res-gt0] 511then 512 state="$TERM_BAD" 513else 514 state="$TERM_GOOD" 515fi 516 517# We have to use a subshell because "bisect_state" can exit. 518( bisect_state $state>"$GIT_DIR/BISECT_RUN") 519 res=$? 520 521cat"$GIT_DIR/BISECT_RUN" 522 523if sane_grep "first$TERM_BADcommit could be any of""$GIT_DIR/BISECT_RUN" \ 524>/dev/null 525then 526 gettextln "bisect run cannot continue any more">&2 527exit$res 528fi 529 530if[$res-ne0] 531then 532 eval_gettextln "bisect run failed: 533'bisect_state \$state' exited with error code \$res">&2 534exit$res 535fi 536 537if sane_grep "is the first$TERM_BADcommit""$GIT_DIR/BISECT_RUN">/dev/null 538then 539 gettextln "bisect run success" 540exit0; 541fi 542 543done 544} 545 546bisect_log () { 547test -s"$GIT_DIR/BISECT_LOG"|| die "$(gettext "We are not bisecting.")" 548cat"$GIT_DIR/BISECT_LOG" 549} 550 551get_terms () { 552iftest -s"$GIT_DIR/BISECT_TERMS" 553then 554{ 555read TERM_BAD 556read TERM_GOOD 557} <"$GIT_DIR/BISECT_TERMS" 558fi 559} 560 561write_terms () { 562 TERM_BAD=$1 563 TERM_GOOD=$2 564iftest"$TERM_BAD"="$TERM_GOOD" 565then 566 die "$(gettext "please use two different terms")" 567fi 568 check_term_format "$TERM_BAD" bad 569 check_term_format "$TERM_GOOD" good 570printf'%s\n%s\n'"$TERM_BAD""$TERM_GOOD">"$GIT_DIR/BISECT_TERMS" 571} 572 573check_term_format () { 574 term=$1 575 git check-ref-format refs/bisect/"$term"|| 576 die "$(eval_gettext "'\$term' is not a valid term")" 577case"$term"in 578help|start|terms|skip|next|reset|visualize|replay|log|run) 579 die "$(eval_gettext "can't use the builtin command '\$term' as a term")" 580;; 581 bad|new) 582iftest"$2"!= bad 583then 584# In theory, nothing prevents swapping 585# completely good and bad, but this situation 586# could be confusing and hasn't been tested 587# enough. Forbid it for now. 588 die "$(eval_gettext "can't change the meaning of term '\$term'")" 589fi 590;; 591 good|old) 592iftest"$2"!= good 593then 594 die "$(eval_gettext "can't change the meaning of term '\$term'")" 595fi 596;; 597esac 598} 599 600check_and_set_terms () { 601 cmd="$1" 602case"$cmd"in 603 skip|start|terms) ;; 604*) 605iftest -s"$GIT_DIR/BISECT_TERMS"&&test"$cmd"!="$TERM_BAD"&&test"$cmd"!="$TERM_GOOD" 606then 607 die "$(eval_gettext "Invalid command: you're currently in a \$TERM_BAD/\$TERM_GOOD bisect.")" 608fi 609case"$cmd"in 610 bad|good) 611if!test -s"$GIT_DIR/BISECT_TERMS" 612then 613 write_terms bad good 614fi 615;; 616 new|old) 617if!test -s"$GIT_DIR/BISECT_TERMS" 618then 619 write_terms new old 620fi 621;; 622esac;; 623esac 624} 625 626bisect_voc () { 627case"$1"in 628 bad)echo"bad|new";; 629 good)echo"good|old";; 630esac 631} 632 633bisect_terms () { 634 get_terms 635if!test -s"$GIT_DIR/BISECT_TERMS" 636then 637 die "$(gettext "no terms defined")" 638fi 639case"$#"in 6400) 641 gettextln "Your current terms are$TERM_GOODfor the old state 642and$TERM_BADfor the new state." 643;; 6441) 645 arg=$1 646case"$arg"in 647--term-good|--term-old) 648printf'%s\n'"$TERM_GOOD" 649;; 650--term-bad|--term-new) 651printf'%s\n'"$TERM_BAD" 652;; 653*) 654 die "$(eval_gettext "invalid argument \$argfor'git bisect terms'. 655Supported options are:--term-good|--term-old and --term-bad|--term-new.")" 656;; 657esac 658;; 659*) 660 usage ;; 661esac 662} 663 664case"$#"in 6650) 666 usage ;; 667*) 668 cmd="$1" 669 get_terms 670shift 671case"$cmd"in 672help) 673 git bisect -h;; 674 start) 675 bisect_start "$@";; 676 bad|good|new|old|"$TERM_BAD"|"$TERM_GOOD") 677 bisect_state "$cmd""$@";; 678 skip) 679 bisect_skip "$@";; 680 next) 681# Not sure we want "next" at the UI level anymore. 682 bisect_next "$@";; 683 visualize|view) 684 bisect_visualize "$@";; 685reset) 686 bisect_reset "$@";; 687 replay) 688 bisect_replay "$@";; 689 log) 690 bisect_log ;; 691 run) 692 bisect_run "$@";; 693 terms) 694 bisect_terms "$@";; 695*) 696 usage ;; 697esac 698esac