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 branch=`cat "$GIT_DIR/BISECT_START"` 85 git checkout $branch||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 136sq"$@">"$GIT_DIR/BISECT_NAMES" 137test -n"$start_head"&&echo"$start_head">"$GIT_DIR/BISECT_START" 138eval"$eval" 139echo"git-bisect start$orig_args">>"$GIT_DIR/BISECT_LOG" 140 bisect_auto_next 141} 142 143bisect_write() { 144 state="$1" 145rev="$2" 146 nolog="$3" 147case"$state"in 148 bad) tag="$state";; 149 good|skip) tag="$state"-"$rev";; 150*) die "Bad bisect_write argument:$state";; 151esac 152 git update-ref"refs/bisect/$tag""$rev" 153echo"#$state:$(git show-branch $rev)">>"$GIT_DIR/BISECT_LOG" 154test -z"$nolog"&&echo"git-bisect$state$rev">>"$GIT_DIR/BISECT_LOG" 155} 156 157bisect_state() { 158 bisect_autostart 159 state=$1 160case"$#,$state"in 1610,*) 162 die "Please call 'bisect_state' with at least one argument.";; 1631,bad|1,good|1,skip) 164rev=$(git rev-parse --verify HEAD)|| 165 die "Bad rev input: HEAD" 166 bisect_write "$state""$rev";; 1672,bad|*,good|*,skip) 168shift 169eval='' 170forrevin"$@" 171do 172 sha=$(git rev-parse --verify "$rev^{commit}")|| 173 die "Bad rev input:$rev" 174eval="$evalbisect_write '$state' '$sha'; " 175done 176eval"$eval";; 177*,bad) 178 die "'git bisect bad' can take only one argument.";; 179*) 180 usage ;; 181esac 182 bisect_auto_next 183} 184 185bisect_next_check() { 186 missing_good= missing_bad= 187 git show-ref -q --verify refs/bisect/bad || missing_bad=t 188test -n"$(git for-each-ref "refs/bisect/good-*")"|| missing_good=t 189 190case"$missing_good,$missing_bad,$1"in 191,,*) 192: have both good and bad - ok 193;; 194*,) 195# do not have both but not asked to fail - just report. 196 false 197;; 198 t,,good) 199# have bad but not good. we could bisect although 200# this is less optimum. 201echo>&2'Warning: bisecting only with a bad commit.' 202iftest -t0 203then 204printf>&2'Are you sure [Y/n]? ' 205case"$(read yesno)"in[Nn]*)exit1;;esac 206fi 207: bisect without good... 208;; 209*) 210 THEN='' 211test -f"$GIT_DIR/BISECT_NAMES"|| { 212echo>&2'You need to start by "git bisect start".' 213 THEN='then ' 214} 215echo>&2'You '$THEN'need to give me at least one good' \ 216'and one bad revisions.' 217echo>&2'(You can use "git bisect bad" and' \ 218'"git bisect good" for that.)' 219exit1;; 220esac 221} 222 223bisect_auto_next() { 224 bisect_next_check && bisect_next || : 225} 226 227eval_rev_list() { 228 _eval="$1" 229 230eval$_eval 231 res=$? 232 233if[$res-ne0];then 234echo>&2"'git rev-list --bisect-vars' failed:" 235echo>&2"maybe you mistake good and bad revs?" 236exit$res 237fi 238 239return$res 240} 241 242filter_skipped() { 243 _eval="$1" 244 _skip="$2" 245 246if[-z"$_skip"];then 247 eval_rev_list "$_eval" 248return 249fi 250 251# Let's parse the output of: 252# "git rev-list --bisect-vars --bisect-all ..." 253 eval_rev_list "$_eval"|whileread hash line 254do 255case"$VARS,$FOUND,$TRIED,$hash"in 256# We display some vars. 2571,*,*,*)echo"$hash$line";; 258 259# Split line. 260,*,*,---*) ;; 261 262# We had nothing to search. 263,,,bisect_rev*) 264echo"bisect_rev=" 265 VARS=1 266;; 267 268# We did not find a good bisect rev. 269# This should happen only if the "bad" 270# commit is also a "skip" commit. 271,,*,bisect_rev*) 272echo"bisect_rev=$TRIED" 273 VARS=1 274;; 275 276# We are searching. 277,,*,*) 278 TRIED="${TRIED:+$TRIED|}$hash" 279case"$_skip"in 280*$hash*) ;; 281*) 282echo"bisect_rev=$hash" 283echo"bisect_tried=\"$TRIED\"" 284 FOUND=1 285;; 286esac 287;; 288 289# We have already found a rev to be tested. 290,1,*,bisect_rev*) VARS=1;; 291,1,*,*) ;; 292 293# ??? 294*) die "filter_skipped error " \ 295"VARS: '$VARS' " \ 296"FOUND: '$FOUND' " \ 297"TRIED: '$TRIED' " \ 298"hash: '$hash' " \ 299"line: '$line'" 300;; 301esac 302done 303} 304 305exit_if_skipped_commits () { 306 _tried=$1 307ifexpr"$_tried":".*[|].*"> /dev/null ;then 308echo"There are only 'skip'ped commit left to test." 309echo"The first bad commit could be any of:" 310echo"$_tried"|tr'[|]''[\012]' 311echo"We cannot bisect more!" 312exit2 313fi 314} 315 316bisect_next() { 317case"$#"in0) ;; *) usage ;;esac 318 bisect_autostart 319 bisect_next_check good 320 321 skip=$(git for-each-ref --format='%(objectname)' \ 322 "refs/bisect/skip-*" | tr '\012' '') || exit 323 324 BISECT_OPT='' 325 test -n "$skip" && BISECT_OPT='--bisect-all' 326 327 bad=$(git rev-parse --verify refs/bisect/bad)&& 328 good=$(git for-each-ref --format='^%(objectname)' \ 329"refs/bisect/good-*"|tr'\012'' ') && 330eval="git rev-list --bisect-vars$BISECT_OPT$good$bad--"&& 331eval="$eval$(cat "$GIT_DIR/BISECT_NAMES")"&& 332eval=$(filter_skipped "$eval" "$skip")&& 333eval"$eval"||exit 334 335if[-z"$bisect_rev"];then 336echo"$badwas both good and bad" 337exit1 338fi 339if["$bisect_rev"="$bad"];then 340 exit_if_skipped_commits "$bisect_tried" 341echo"$bisect_revis first bad commit" 342 git diff-tree --pretty$bisect_rev 343exit0 344fi 345 346# We should exit here only if the "bad" 347# commit is also a "skip" commit (see above). 348 exit_if_skipped_commits "$bisect_rev" 349 350echo"Bisecting:$bisect_nrrevisions left to test after this" 351 git branch -D new-bisect2> /dev/null 352 git checkout -q -b new-bisect"$bisect_rev"||exit 353 git branch -M new-bisect bisect 354 git show-branch"$bisect_rev" 355} 356 357bisect_visualize() { 358 bisect_next_check fail 359 360iftest$#=0 361then 362case"${DISPLAY+set}${MSYSTEM+set}${SECURITYSESSIONID+set}"in 363'')set git log ;; 364set*)set gitk ;; 365esac 366else 367case"$1"in 368 git*|tig) ;; 369-*)set git log "$@";; 370*)set git "$@";; 371esac 372fi 373 374 not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*") 375 eval '"$@"' refs/bisect/bad --not$not--$(cat "$GIT_DIR/BISECT_NAMES") 376} 377 378bisect_reset() { 379 test -f "$GIT_DIR/BISECT_NAMES" || { 380 echo "We are not bisecting." 381 return 382 } 383 case "$#" in 384 0) if [ -s "$GIT_DIR/BISECT_START" ]; then 385 branch=`cat "$GIT_DIR/BISECT_START"` 386 else 387 branch=master 388 fi ;; 389 1) git show-ref --verify --quiet -- "refs/heads/$1" || 390 die "$1does not seem to be a valid branch" 391 branch="$1" ;; 392 *) 393 usage ;; 394 esac 395 if git checkout "$branch"; then 396 # Cleanup head-name if it got left by an old version of git-bisect 397 rm -f "$GIT_DIR/head-name" 398 rm -f "$GIT_DIR/BISECT_START" 399 bisect_clean_state 400 fi 401} 402 403bisect_clean_state() { 404 # There may be some refs packed during bisection. 405 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* refs/heads/bisect | 406 while read ref hash 407 do 408 git update-ref -d$ref$hash 409 done 410 rm -f "$GIT_DIR/BISECT_LOG" 411 rm -f "$GIT_DIR/BISECT_NAMES" 412 rm -f "$GIT_DIR/BISECT_RUN" 413} 414 415bisect_replay () { 416 test -r "$1" || die "cannot read$1for replaying" 417 bisect_reset 418 while read bisect command rev 419 do 420 test "$bisect" = "git-bisect" || continue 421 case "$command" in 422 start) 423 cmd="bisect_start$rev" 424 eval "$cmd" ;; 425 good|bad|skip) 426 bisect_write "$command" "$rev" ;; 427 *) 428 die "?? what are you talking about?" ;; 429 esac 430 done <"$1" 431 bisect_auto_next 432} 433 434bisect_run () { 435 bisect_next_check fail 436 437 while true 438 do 439 echo "running $@" 440 "$@" 441 res=$? 442 443 # Check for really bad run error. 444 if [$res-lt 0 -o$res-ge 128 ]; then 445 echo >&2 "bisect run failed:" 446 echo >&2 "exit code$resfrom '$@' is < 0 or >= 128" 447 exit$res 448 fi 449 450 # Find current state depending on run success or failure. 451 # A special exit code of 125 means cannot test. 452 if [$res-eq 125 ]; then 453 state='skip' 454 elif [$res-gt 0 ]; then 455 state='bad' 456 else 457 state='good' 458 fi 459 460 # We have to use a subshell because "bisect_state" can exit. 461 ( bisect_state$state> "$GIT_DIR/BISECT_RUN" ) 462 res=$? 463 464 cat "$GIT_DIR/BISECT_RUN" 465 466 if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \ 467 > /dev/null; then 468 echo >&2 "bisect run cannot continue any more" 469 exit$res 470 fi 471 472 if [$res-ne 0 ]; then 473 echo >&2 "bisect run failed:" 474 echo >&2 "'bisect_state $state' exited with error code$res" 475 exit$res 476 fi 477 478 if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then 479 echo "bisect run success" 480 exit 0; 481 fi 482 483 done 484} 485 486 487case "$#" in 4880) 489 usage ;; 490*) 491 cmd="$1" 492 shift 493 case "$cmd" in 494 help) 495 git bisect -h ;; 496 start) 497 bisect_start "$@" ;; 498 bad|good|skip) 499 bisect_state "$cmd" "$@" ;; 500 next) 501 # Not sure we want "next" at the UI level anymore. 502 bisect_next "$@" ;; 503 visualize|view) 504 bisect_visualize "$@" ;; 505 reset) 506 bisect_reset "$@" ;; 507 replay) 508 bisect_replay "$@" ;; 509 log) 510 cat "$GIT_DIR/BISECT_LOG" ;; 511 run) 512 bisect_run "$@" ;; 513 *) 514 usage ;; 515 esac 516esac