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