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