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