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) 280rev=$(git rev-parse --verify $(bisect_head)) || 281 die "$(gettext "Bad rev input: $(bisect_head)")" 282 bisect_write "$state" "$rev" 283 check_expected_revs "$rev" ;; 284 2,"$TERM_BAD"|*,"$TERM_GOOD"|*,skip) 285 shift 286 hash_list='' 287 for rev in "$@" 288 do 289 sha=$(git rev-parse --verify "$rev^{commit}")|| 290 die "$(eval_gettext "Bad rev input: \$rev")" 291 hash_list="$hash_list $sha" 292 done 293 for rev in$hash_list 294 do 295 bisect_write "$state" "$rev" 296 done 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 ;; 302 esac 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 309 test -n "$(git for-each-ref "refs/bisect/$TERM_GOOD-*")" || missing_good=t 310 311 case "$missing_good,$missing_bad,$1" in 312 ,,*) 313 : have both$TERM_GOODand$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_BAD commit." >&2 323 if test -t 0 324 then 325 # TRANSLATORS: Make sure to include [Y] and [n] in your 326 # translation. The program will only accept English input 327 # at this point. 328 gettext "Are you sure [Y/n]? " >&2 329 read yesno 330 case "$yesno" in [Nn]*) exit 1 ;; esac 331 fi 332 : bisect without$TERM_GOOD... 333 ;; 334 *) 335 bad_syn=$(bisect_voc bad) 336 good_syn=$(bisect_voc good) 337 if test -s "$GIT_DIR/BISECT_START" 338 then 339 340 eval_gettextln "You need to give me at least one \$bad_syn and one \$good_syn revision. 341(You can use \"git bisect \$bad_syn\" and \"git bisect \$good_syn\"for that.)" >&2 342 else 343 eval_gettextln "You need to start by \"git bisect start\". 344You then need to give me at least one \$good_syn and one \$bad_syn revision. 345(You can use \"git bisect \$bad_syn\" and \"git bisect \$good_syn\"for that.)" >&2 346 fi 347 exit 1 ;; 348 esac 349} 350 351bisect_auto_next() { 352 bisect_next_check && bisect_next || : 353} 354 355bisect_next() { 356 case "$#" in 0) ;; *) 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 365 if test$res-eq 10 366 then 367 bad_rev=$(git show-ref --hash --verify refs/bisect/$TERM_BAD) 368 bad_commit=$(git show-branch $bad_rev) 369 echo "# first $TERM_BAD commit: $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