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