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