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 -s"$GIT_DIR/BISECT_START"|| { 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. 67# 68head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) || 69head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) || 70 die "Bad HEAD - I need a HEAD" 71 72# 73# Check if we are bisecting. 74# 75 start_head='' 76iftest -s"$GIT_DIR/BISECT_START" 77then 78# Reset to the rev from where we started. 79 start_head=$(cat"$GIT_DIR/BISECT_START") 80 git checkout "$start_head"||exit 81else 82# Get rev from where we start. 83case"$head"in 84 refs/heads/*|$_x40) 85# This error message should only be triggered by 86# cogito usage, and cogito users should understand 87# it relates to cg-seek. 88[-s"$GIT_DIR/head-name"] && 89 die "won't bisect on seeked tree" 90 start_head="${head#refs/heads/}" 91;; 92*) 93 die "Bad HEAD - strange symbolic ref" 94;; 95esac 96fi 97 98# 99# Get rid of any old bisect state. 100# 101 bisect_clean_state ||exit 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 -q --verify"$arg^{commit}") || { 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# 149echo"$start_head">"$GIT_DIR/BISECT_START"&& 150sq"$@">"$GIT_DIR/BISECT_NAMES"&& 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]? ' 223read yesno 224case"$yesno"in[Nn]*)exit1;;esac 225fi 226: bisect without good... 227;; 228*) 229 THEN='' 230test -s"$GIT_DIR/BISECT_START"|| { 231echo>&2'You need to start by "git bisect start".' 232 THEN='then ' 233} 234echo>&2'You '$THEN'need to give me at least one good' \ 235'and one bad revisions.' 236echo>&2'(You can use "git bisect bad" and' \ 237'"git bisect good" for that.)' 238exit1;; 239esac 240} 241 242bisect_auto_next() { 243 bisect_next_check && bisect_next || : 244} 245 246eval_rev_list() { 247 _eval="$1" 248 249eval$_eval 250 res=$? 251 252if[$res-ne0];then 253echo>&2"'git rev-list --bisect-vars' failed:" 254echo>&2"maybe you mistake good and bad revs?" 255exit$res 256fi 257 258return$res 259} 260 261filter_skipped() { 262 _eval="$1" 263 _skip="$2" 264 265if[-z"$_skip"];then 266 eval_rev_list "$_eval"| { 267whileread line 268do 269echo"$line&&" 270done 271echo':' 272} 273return 274fi 275 276# Let's parse the output of: 277# "git rev-list --bisect-vars --bisect-all ..." 278 eval_rev_list "$_eval"| { 279 VARS= FOUND= TRIED= 280whileread hash line 281do 282case"$VARS,$FOUND,$TRIED,$hash"in 2831,*,*,*) 284# "bisect_foo=bar" read from rev-list output. 285echo"$hash&&" 286;; 287,*,*,---*) 288# Separator 289;; 290,,,bisect_rev*) 291# We had nothing to search. 292echo"bisect_rev= &&" 293 VARS=1 294;; 295,,*,bisect_rev*) 296# We did not find a good bisect rev. 297# This should happen only if the "bad" 298# commit is also a "skip" commit. 299echo"bisect_rev='$TRIED' &&" 300 VARS=1 301;; 302,,*,*) 303# We are searching. 304 TRIED="${TRIED:+$TRIED|}$hash" 305case"$_skip"in 306*$hash*) ;; 307*) 308echo"bisect_rev=$hash&&" 309echo"bisect_tried='$TRIED' &&" 310 FOUND=1 311;; 312esac 313;; 314,1,*,bisect_rev*) 315# We have already found a rev to be tested. 316 VARS=1 317;; 318,1,*,*) 319;; 320*) 321# Unexpected input 322echo"die 'filter_skipped error'" 323 die "filter_skipped error " \ 324"VARS: '$VARS' " \ 325"FOUND: '$FOUND' " \ 326"TRIED: '$TRIED' " \ 327"hash: '$hash' " \ 328"line: '$line'" 329;; 330esac 331done 332echo':' 333} 334} 335 336exit_if_skipped_commits () { 337 _tried=$1 338ifexpr"$_tried":".*[|].*"> /dev/null ;then 339echo"There are only 'skip'ped commit left to test." 340echo"The first bad commit could be any of:" 341echo"$_tried"|tr'[|]''[\012]' 342echo"We cannot bisect more!" 343exit2 344fi 345} 346 347bisect_next() { 348case"$#"in0) ;; *) usage ;;esac 349 bisect_autostart 350 bisect_next_check good 351 352 skip=$(git for-each-ref --format='%(objectname)' \ 353"refs/bisect/skip-*"|tr'\012'' ') ||exit 354 355 BISECT_OPT='' 356test -n"$skip"&& BISECT_OPT='--bisect-all' 357 358 bad=$(git rev-parse --verify refs/bisect/bad) && 359 good=$(git for-each-ref --format='^%(objectname)' \ 360"refs/bisect/good-*"|tr'\012'' ') && 361eval="git rev-list --bisect-vars$BISECT_OPT$good$bad--"&& 362eval="$eval$(cat "$GIT_DIR/BISECT_NAMES")"&& 363eval=$(filter_skipped "$eval""$skip") && 364eval"$eval"||exit 365 366if[-z"$bisect_rev"];then 367echo"$badwas both good and bad" 368exit1 369fi 370if["$bisect_rev"="$bad"];then 371 exit_if_skipped_commits "$bisect_tried" 372echo"$bisect_revis first bad commit" 373 git diff-tree --pretty$bisect_rev 374exit0 375fi 376 377# We should exit here only if the "bad" 378# commit is also a "skip" commit (see above). 379 exit_if_skipped_commits "$bisect_rev" 380 381echo"Bisecting:$bisect_nrrevisions left to test after this" 382 git checkout -q"$bisect_rev"||exit 383 git show-branch "$bisect_rev" 384} 385 386bisect_visualize() { 387 bisect_next_check fail 388 389iftest$#=0 390then 391case"${DISPLAY+set}${MSYSTEM+set}${SECURITYSESSIONID+set}"in 392'')set git log ;; 393set*)set gitk ;; 394esac 395else 396case"$1"in 397 git*|tig) ;; 398-*)set git log "$@";; 399*)set git "$@";; 400esac 401fi 402 403 not=$(git for-each-ref --format='%(refname)'"refs/bisect/good-*") 404eval'"$@"' refs/bisect/bad --not$not-- $(cat"$GIT_DIR/BISECT_NAMES") 405} 406 407bisect_reset() { 408test -s"$GIT_DIR/BISECT_START"|| { 409echo"We are not bisecting." 410return 411} 412case"$#"in 4130) branch=$(cat"$GIT_DIR/BISECT_START") ;; 4141) git show-ref --verify --quiet --"refs/heads/$1"|| 415 die "$1does not seem to be a valid branch" 416 branch="$1";; 417*) 418 usage ;; 419esac 420 git checkout "$branch"&& bisect_clean_state 421} 422 423bisect_clean_state() { 424# There may be some refs packed during bisection. 425 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* | 426whileread ref hash 427do 428 git update-ref -d$ref $hash||exit 429done 430rm-f"$GIT_DIR/BISECT_LOG"&& 431rm-f"$GIT_DIR/BISECT_NAMES"&& 432rm-f"$GIT_DIR/BISECT_RUN"&& 433# Cleanup head-name if it got left by an old version of git-bisect 434rm-f"$GIT_DIR/head-name"&& 435 436rm-f"$GIT_DIR/BISECT_START" 437} 438 439bisect_replay () { 440test -r"$1"|| die "cannot read$1for replaying" 441 bisect_reset 442whileread git bisect commandrev 443do 444test"$git$bisect"="git bisect"-o"$git"="git-bisect"||continue 445iftest"$git"="git-bisect";then 446rev="$command" 447command="$bisect" 448fi 449case"$command"in 450 start) 451 cmd="bisect_start$rev" 452eval"$cmd";; 453 good|bad|skip) 454 bisect_write "$command""$rev";; 455*) 456 die "?? what are you talking about?";; 457esac 458done<"$1" 459 bisect_auto_next 460} 461 462bisect_run () { 463 bisect_next_check fail 464 465while true 466do 467echo"running $@" 468"$@" 469 res=$? 470 471# Check for really bad run error. 472if[$res-lt0-o$res-ge128];then 473echo>&2"bisect run failed:" 474echo>&2"exit code$resfrom '$@' is < 0 or >= 128" 475exit$res 476fi 477 478# Find current state depending on run success or failure. 479# A special exit code of 125 means cannot test. 480if[$res-eq125];then 481 state='skip' 482elif[$res-gt0];then 483 state='bad' 484else 485 state='good' 486fi 487 488# We have to use a subshell because "bisect_state" can exit. 489( bisect_state $state>"$GIT_DIR/BISECT_RUN") 490 res=$? 491 492cat"$GIT_DIR/BISECT_RUN" 493 494ifgrep"first bad commit could be any of""$GIT_DIR/BISECT_RUN" \ 495> /dev/null;then 496echo>&2"bisect run cannot continue any more" 497exit$res 498fi 499 500if[$res-ne0];then 501echo>&2"bisect run failed:" 502echo>&2"'bisect_state$state' exited with error code$res" 503exit$res 504fi 505 506ifgrep"is first bad commit""$GIT_DIR/BISECT_RUN"> /dev/null;then 507echo"bisect run success" 508exit0; 509fi 510 511done 512} 513 514 515case"$#"in 5160) 517 usage ;; 518*) 519 cmd="$1" 520shift 521case"$cmd"in 522help) 523 git bisect -h;; 524 start) 525 bisect_start "$@";; 526 bad|good|skip) 527 bisect_state "$cmd""$@";; 528 next) 529# Not sure we want "next" at the UI level anymore. 530 bisect_next "$@";; 531 visualize|view) 532 bisect_visualize "$@";; 533reset) 534 bisect_reset "$@";; 535 replay) 536 bisect_replay "$@";; 537 log) 538cat"$GIT_DIR/BISECT_LOG";; 539 run) 540 bisect_run "$@";; 541*) 542 usage ;; 543esac 544esac