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 [<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>...] 13 mark <rev>... untestable revisions. 14git bisect next 15 find next bisection to test and check it out. 16git bisect reset [<branch>] 17 finish bisection search and go back to branch. 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 31require_work_tree 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" 35 36sq() { 37 @@PERL@@ -e' 38 for (@ARGV) { 39 s/'\''/'\'\\\\\'\''/g; 40 print " '\''$_'\''"; 41 } 42 print "\n"; 43 '"$@" 44} 45 46bisect_autostart() { 47test -f"$GIT_DIR/BISECT_NAMES"|| { 48echo>&2'You need to start by "git bisect start"' 49iftest -t0 50then 51echo>&2-n'Do you want me to do it for you [Y/n]? ' 52read yesno 53case"$yesno"in 54[Nn]*) 55exit;; 56esac 57 bisect_start 58else 59exit1 60fi 61} 62} 63 64bisect_start() { 65# 66# Verify HEAD. If we were bisecting before this, reset to the 67# top-of-line master first! 68# 69head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD)|| 70head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD)|| 71 die "Bad HEAD - I need a HEAD" 72# 73# Check that we either already have BISECT_START, or that the 74# branches bisect, new-bisect don't exist, to not override them. 75# 76test -s"$GIT_DIR/BISECT_START"|| 77if git show-ref --verify -q refs/heads/bisect || 78 git show-ref --verify -q refs/heads/new-bisect;then 79 die 'The branches "bisect" and "new-bisect" must not exist.' 80fi 81 start_head='' 82case"$head"in 83 refs/heads/bisect) 84 start_head=$(cat "$GIT_DIR/BISECT_START") 85 git checkout "$start_head"||exit 86;; 87 refs/heads/*|$_x40) 88# This error message should only be triggered by cogito usage, 89# and cogito users should understand it relates to cg-seek. 90[-s"$GIT_DIR/head-name"] && die "won't bisect on seeked tree" 91 start_head="${head#refs/heads/}" 92;; 93*) 94 die "Bad HEAD - strange symbolic ref" 95;; 96esac 97 98# 99# Get rid of any old bisect state 100# 101 bisect_clean_state 102 103# 104# Check for one bad and then some good revisions. 105# 106 has_double_dash=0 107for arg;do 108case"$arg"in--) has_double_dash=1;break;;esac 109done 110 orig_args=$(sq "$@") 111 bad_seen=0 112eval='' 113while[$#-gt0];do 114 arg="$1" 115case"$arg"in 116--) 117shift 118break 119;; 120*) 121rev=$(git rev-parse --verify "$arg^{commit}" 2>/dev/null)|| { 122test$has_double_dash-eq1&& 123 die "'$arg' does not appear to be a valid revision" 124break 125} 126case$bad_seenin 1270) state='bad'; bad_seen=1;; 128*) state='good';; 129esac 130eval="$evalbisect_write '$state' '$rev' 'nolog'; " 131shift 132;; 133esac 134done 135 136# 137# Change state. 138# In case of mistaken revs or checkout error, or signals received, 139# "bisect_auto_next" below may exit or misbehave. 140# We have to trap this to be able to clean up using 141# "bisect_clean_state". 142# 143trap'bisect_clean_state'0 144trap'exit 255'1 2 3 15 145 146# 147# Write new start state. 148# 149sq"$@">"$GIT_DIR/BISECT_NAMES"&& 150echo"$start_head">"$GIT_DIR/BISECT_START"&& 151eval"$eval"&& 152echo"git-bisect start$orig_args">>"$GIT_DIR/BISECT_LOG"||exit 153# 154# Check if we can proceed to the next bisect state. 155# 156 bisect_auto_next 157 158trap'-'0 159} 160 161bisect_write() { 162 state="$1" 163rev="$2" 164 nolog="$3" 165case"$state"in 166 bad) tag="$state";; 167 good|skip) tag="$state"-"$rev";; 168*) die "Bad bisect_write argument:$state";; 169esac 170 git update-ref"refs/bisect/$tag""$rev"||exit 171echo"#$state:$(git show-branch $rev)">>"$GIT_DIR/BISECT_LOG" 172test -n"$nolog"||echo"git-bisect$state$rev">>"$GIT_DIR/BISECT_LOG" 173} 174 175bisect_state() { 176 bisect_autostart 177 state=$1 178case"$#,$state"in 1790,*) 180 die "Please call 'bisect_state' with at least one argument.";; 1811,bad|1,good|1,skip) 182rev=$(git rev-parse --verify HEAD)|| 183 die "Bad rev input: HEAD" 184 bisect_write "$state""$rev";; 1852,bad|*,good|*,skip) 186shift 187eval='' 188forrevin"$@" 189do 190 sha=$(git rev-parse --verify "$rev^{commit}")|| 191 die "Bad rev input:$rev" 192eval="$evalbisect_write '$state' '$sha'; " 193done 194eval"$eval";; 195*,bad) 196 die "'git bisect bad' can take only one argument.";; 197*) 198 usage ;; 199esac 200 bisect_auto_next 201} 202 203bisect_next_check() { 204 missing_good= missing_bad= 205 git show-ref -q --verify refs/bisect/bad || missing_bad=t 206test -n"$(git for-each-ref "refs/bisect/good-*")"|| missing_good=t 207 208case"$missing_good,$missing_bad,$1"in 209,,*) 210: have both good and bad - ok 211;; 212*,) 213# do not have both but not asked to fail - just report. 214 false 215;; 216 t,,good) 217# have bad but not good. we could bisect although 218# this is less optimum. 219echo>&2'Warning: bisecting only with a bad commit.' 220iftest -t0 221then 222printf>&2'Are you sure [Y/n]? ' 223case"$(read yesno)"in[Nn]*)exit1;;esac 224fi 225: bisect without good... 226;; 227*) 228 THEN='' 229test -f"$GIT_DIR/BISECT_NAMES"|| { 230echo>&2'You need to start by "git bisect start".' 231 THEN='then ' 232} 233echo>&2'You '$THEN'need to give me at least one good' \ 234'and one bad revisions.' 235echo>&2'(You can use "git bisect bad" and' \ 236'"git bisect good" for that.)' 237exit1;; 238esac 239} 240 241bisect_auto_next() { 242 bisect_next_check && bisect_next || : 243} 244 245eval_rev_list() { 246 _eval="$1" 247 248eval$_eval 249 res=$? 250 251if[$res-ne0];then 252echo>&2"'git rev-list --bisect-vars' failed:" 253echo>&2"maybe you mistake good and bad revs?" 254exit$res 255fi 256 257return$res 258} 259 260filter_skipped() { 261 _eval="$1" 262 _skip="$2" 263 264if[-z"$_skip"];then 265 eval_rev_list "$_eval" 266return 267fi 268 269# Let's parse the output of: 270# "git rev-list --bisect-vars --bisect-all ..." 271 eval_rev_list "$_eval"|whileread hash line 272do 273case"$VARS,$FOUND,$TRIED,$hash"in 274# We display some vars. 2751,*,*,*)echo"$hash$line";; 276 277# Split line. 278,*,*,---*) ;; 279 280# We had nothing to search. 281,,,bisect_rev*) 282echo"bisect_rev=" 283 VARS=1 284;; 285 286# We did not find a good bisect rev. 287# This should happen only if the "bad" 288# commit is also a "skip" commit. 289,,*,bisect_rev*) 290echo"bisect_rev=$TRIED" 291 VARS=1 292;; 293 294# We are searching. 295,,*,*) 296 TRIED="${TRIED:+$TRIED|}$hash" 297case"$_skip"in 298*$hash*) ;; 299*) 300echo"bisect_rev=$hash" 301echo"bisect_tried=\"$TRIED\"" 302 FOUND=1 303;; 304esac 305;; 306 307# We have already found a rev to be tested. 308,1,*,bisect_rev*) VARS=1;; 309,1,*,*) ;; 310 311# ??? 312*) die "filter_skipped error " \ 313"VARS: '$VARS' " \ 314"FOUND: '$FOUND' " \ 315"TRIED: '$TRIED' " \ 316"hash: '$hash' " \ 317"line: '$line'" 318;; 319esac 320done 321} 322 323exit_if_skipped_commits () { 324 _tried=$1 325ifexpr"$_tried":".*[|].*"> /dev/null ;then 326echo"There are only 'skip'ped commit left to test." 327echo"The first bad commit could be any of:" 328echo"$_tried"|tr'[|]''[\012]' 329echo"We cannot bisect more!" 330exit2 331fi 332} 333 334bisect_next() { 335case"$#"in0) ;; *) usage ;;esac 336 bisect_autostart 337 bisect_next_check good 338 339 skip=$(git for-each-ref --format='%(objectname)' \ 340 "refs/bisect/skip-*" | tr '\012' '') || exit 341 342 BISECT_OPT='' 343 test -n "$skip" && BISECT_OPT='--bisect-all' 344 345 bad=$(git rev-parse --verify refs/bisect/bad)&& 346 good=$(git for-each-ref --format='^%(objectname)' \ 347"refs/bisect/good-*"|tr'\012'' ') && 348eval="git rev-list --bisect-vars$BISECT_OPT$good$bad--"&& 349eval="$eval$(cat "$GIT_DIR/BISECT_NAMES")"&& 350eval=$(filter_skipped "$eval" "$skip")&& 351eval"$eval"||exit 352 353if[-z"$bisect_rev"];then 354echo"$badwas both good and bad" 355exit1 356fi 357if["$bisect_rev"="$bad"];then 358 exit_if_skipped_commits "$bisect_tried" 359echo"$bisect_revis first bad commit" 360 git diff-tree --pretty$bisect_rev 361exit0 362fi 363 364# We should exit here only if the "bad" 365# commit is also a "skip" commit (see above). 366 exit_if_skipped_commits "$bisect_rev" 367 368echo"Bisecting:$bisect_nrrevisions left to test after this" 369 git branch -D new-bisect2> /dev/null 370 git checkout -q -b new-bisect"$bisect_rev"||exit 371 git branch -M new-bisect bisect 372 git show-branch"$bisect_rev" 373} 374 375bisect_visualize() { 376 bisect_next_check fail 377 378iftest$#=0 379then 380case"${DISPLAY+set}${MSYSTEM+set}${SECURITYSESSIONID+set}"in 381'')set git log ;; 382set*)set gitk ;; 383esac 384else 385case"$1"in 386 git*|tig) ;; 387-*)set git log "$@";; 388*)set git "$@";; 389esac 390fi 391 392 not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*") 393 eval '"$@"' refs/bisect/bad --not$not--$(cat "$GIT_DIR/BISECT_NAMES") 394} 395 396bisect_reset() { 397 test -f "$GIT_DIR/BISECT_NAMES" || { 398 echo "We are not bisecting." 399 return 400 } 401 case "$#" in 402 0) if [ -s "$GIT_DIR/BISECT_START" ]; then 403 branch=`cat "$GIT_DIR/BISECT_START"` 404 else 405 branch=master 406 fi ;; 407 1) git show-ref --verify --quiet -- "refs/heads/$1" || 408 die "$1does not seem to be a valid branch" 409 branch="$1" ;; 410 *) 411 usage ;; 412 esac 413 git checkout "$branch" && bisect_clean_state 414} 415 416bisect_clean_state() { 417 # There may be some refs packed during bisection. 418 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* refs/heads/bisect | 419 while read ref hash 420 do 421 git update-ref -d$ref$hash 422 done 423 rm -f "$GIT_DIR/BISECT_START" 424 rm -f "$GIT_DIR/BISECT_LOG" 425 rm -f "$GIT_DIR/BISECT_NAMES" 426 rm -f "$GIT_DIR/BISECT_RUN" 427 # Cleanup head-name if it got left by an old version of git-bisect 428 rm -f "$GIT_DIR/head-name" 429} 430 431bisect_replay () { 432 test -r "$1" || die "cannot read$1for replaying" 433 bisect_reset 434 while read bisect command rev 435 do 436 test "$bisect" = "git-bisect" || continue 437 case "$command" in 438 start) 439 cmd="bisect_start$rev" 440 eval "$cmd" ;; 441 good|bad|skip) 442 bisect_write "$command" "$rev" ;; 443 *) 444 die "?? what are you talking about?" ;; 445 esac 446 done <"$1" 447 bisect_auto_next 448} 449 450bisect_run () { 451 bisect_next_check fail 452 453 while true 454 do 455 echo "running $@" 456 "$@" 457 res=$? 458 459 # Check for really bad run error. 460 if [$res-lt 0 -o$res-ge 128 ]; then 461 echo >&2 "bisect run failed:" 462 echo >&2 "exit code$resfrom '$@' is < 0 or >= 128" 463 exit$res 464 fi 465 466 # Find current state depending on run success or failure. 467 # A special exit code of 125 means cannot test. 468 if [$res-eq 125 ]; then 469 state='skip' 470 elif [$res-gt 0 ]; then 471 state='bad' 472 else 473 state='good' 474 fi 475 476 # We have to use a subshell because "bisect_state" can exit. 477 ( bisect_state$state> "$GIT_DIR/BISECT_RUN" ) 478 res=$? 479 480 cat "$GIT_DIR/BISECT_RUN" 481 482 if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \ 483 > /dev/null; then 484 echo >&2 "bisect run cannot continue any more" 485 exit$res 486 fi 487 488 if [$res-ne 0 ]; then 489 echo >&2 "bisect run failed:" 490 echo >&2 "'bisect_state $state' exited with error code$res" 491 exit$res 492 fi 493 494 if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then 495 echo "bisect run success" 496 exit 0; 497 fi 498 499 done 500} 501 502 503case "$#" in 5040) 505 usage ;; 506*) 507 cmd="$1" 508 shift 509 case "$cmd" in 510 help) 511 git bisect -h ;; 512 start) 513 bisect_start "$@" ;; 514 bad|good|skip) 515 bisect_state "$cmd" "$@" ;; 516 next) 517 # Not sure we want "next" at the UI level anymore. 518 bisect_next "$@" ;; 519 visualize|view) 520 bisect_visualize "$@" ;; 521 reset) 522 bisect_reset "$@" ;; 523 replay) 524 bisect_replay "$@" ;; 525 log) 526 cat "$GIT_DIR/BISECT_LOG" ;; 527 run) 528 bisect_run "$@" ;; 529 *) 530 usage ;; 531 esac 532esac