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