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>|<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 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 36bisect_autostart() { 37test -s"$GIT_DIR/BISECT_START"|| { 38echo>&2'You need to start by "git bisect start"' 39iftest -t0 40then 41echo>&2-n'Do you want me to do it for you [Y/n]? ' 42read yesno 43case"$yesno"in 44[Nn]*) 45exit;; 46esac 47 bisect_start 48else 49exit1 50fi 51} 52} 53 54bisect_start() { 55# 56# Verify HEAD. 57# 58head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD)|| 59head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD)|| 60 die "Bad HEAD - I need a HEAD" 61 62# 63# Check if we are bisecting. 64# 65 start_head='' 66iftest -s"$GIT_DIR/BISECT_START" 67then 68# Reset to the rev from where we started. 69 start_head=$(cat "$GIT_DIR/BISECT_START") 70 git checkout "$start_head"--||exit 71else 72# Get rev from where we start. 73case"$head"in 74 refs/heads/*|$_x40) 75# This error message should only be triggered by 76# cogito usage, and cogito users should understand 77# it relates to cg-seek. 78[-s"$GIT_DIR/head-name"] && 79 die "won't bisect on seeked tree" 80 start_head="${head#refs/heads/}" 81;; 82*) 83 die "Bad HEAD - strange symbolic ref" 84;; 85esac 86fi 87 88# 89# Get rid of any old bisect state. 90# 91 bisect_clean_state ||exit 92 93# 94# Check for one bad and then some good revisions. 95# 96 has_double_dash=0 97for arg;do 98case"$arg"in--) has_double_dash=1;break;;esac 99done 100 orig_args=$(git rev-parse --sq-quote "$@") 101 bad_seen=0 102eval='' 103while[$#-gt0];do 104 arg="$1" 105case"$arg"in 106--) 107shift 108break 109;; 110*) 111rev=$(git rev-parse -q --verify "$arg^{commit}")|| { 112test$has_double_dash-eq1&& 113 die "'$arg' does not appear to be a valid revision" 114break 115} 116case$bad_seenin 1170) state='bad'; bad_seen=1;; 118*) state='good';; 119esac 120eval="$evalbisect_write '$state' '$rev' 'nolog'; " 121shift 122;; 123esac 124done 125 126# 127# Change state. 128# In case of mistaken revs or checkout error, or signals received, 129# "bisect_auto_next" below may exit or misbehave. 130# We have to trap this to be able to clean up using 131# "bisect_clean_state". 132# 133trap'bisect_clean_state'0 134trap'exit 255'1 2 3 15 135 136# 137# Write new start state. 138# 139echo"$start_head">"$GIT_DIR/BISECT_START"&& 140 git rev-parse --sq-quote"$@">"$GIT_DIR/BISECT_NAMES"&& 141eval"$eval"&& 142echo"git bisect start$orig_args">>"$GIT_DIR/BISECT_LOG"||exit 143# 144# Check if we can proceed to the next bisect state. 145# 146 bisect_auto_next 147 148trap'-'0 149} 150 151bisect_write() { 152 state="$1" 153rev="$2" 154 nolog="$3" 155case"$state"in 156 bad) tag="$state";; 157 good|skip) tag="$state"-"$rev";; 158*) die "Bad bisect_write argument:$state";; 159esac 160 git update-ref"refs/bisect/$tag""$rev"||exit 161echo"#$state:$(git show-branch $rev)">>"$GIT_DIR/BISECT_LOG" 162test -n"$nolog"||echo"git bisect$state$rev">>"$GIT_DIR/BISECT_LOG" 163} 164 165is_expected_rev() { 166test -f"$GIT_DIR/BISECT_EXPECTED_REV"&& 167test"$1"=$(cat "$GIT_DIR/BISECT_EXPECTED_REV") 168} 169 170check_expected_revs() { 171for _rev in"$@";do 172if! is_expected_rev "$_rev";then 173rm-f"$GIT_DIR/BISECT_ANCESTORS_OK" 174rm-f"$GIT_DIR/BISECT_EXPECTED_REV" 175return 176fi 177done 178} 179 180bisect_skip() { 181 all='' 182for arg in"$@" 183do 184case"$arg"in 185*..*) 186 revs=$(git rev-list "$arg")|| die "Bad rev input:$arg";; 187*) 188 revs=$(git rev-parse --sq-quote "$arg");; 189esac 190 all="$all$revs" 191done 192eval bisect_state 'skip'$all 193} 194 195bisect_state() { 196 bisect_autostart 197 state=$1 198case"$#,$state"in 1990,*) 200 die "Please call 'bisect_state' with at least one argument.";; 2011,bad|1,good|1,skip) 202rev=$(git rev-parse --verify HEAD)|| 203 die "Bad rev input: HEAD" 204 bisect_write "$state""$rev" 205 check_expected_revs "$rev";; 2062,bad|*,good|*,skip) 207shift 208eval='' 209forrevin"$@" 210do 211 sha=$(git rev-parse --verify "$rev^{commit}")|| 212 die "Bad rev input:$rev" 213eval="$evalbisect_write '$state' '$sha'; " 214done 215eval"$eval" 216 check_expected_revs "$@";; 217*,bad) 218 die "'git bisect bad' can take only one argument.";; 219*) 220 usage ;; 221esac 222 bisect_auto_next 223} 224 225bisect_next_check() { 226 missing_good= missing_bad= 227 git show-ref -q --verify refs/bisect/bad || missing_bad=t 228test -n"$(git for-each-ref "refs/bisect/good-*")"|| missing_good=t 229 230case"$missing_good,$missing_bad,$1"in 231,,*) 232: have both good and bad - ok 233;; 234*,) 235# do not have both but not asked to fail - just report. 236 false 237;; 238 t,,good) 239# have bad but not good. we could bisect although 240# this is less optimum. 241echo>&2'Warning: bisecting only with a bad commit.' 242iftest -t0 243then 244printf>&2'Are you sure [Y/n]? ' 245read yesno 246case"$yesno"in[Nn]*)exit1;;esac 247fi 248: bisect without good... 249;; 250*) 251 THEN='' 252test -s"$GIT_DIR/BISECT_START"|| { 253echo>&2'You need to start by "git bisect start".' 254 THEN='then ' 255} 256echo>&2'You '$THEN'need to give me at least one good' \ 257'and one bad revisions.' 258echo>&2'(You can use "git bisect bad" and' \ 259'"git bisect good" for that.)' 260exit1;; 261esac 262} 263 264bisect_auto_next() { 265 bisect_next_check && bisect_next || : 266} 267 268bisect_next() { 269case"$#"in0) ;; *) usage ;;esac 270 bisect_autostart 271 bisect_next_check good 272 273# Perform all bisection computation, display and checkout 274 git bisect--helper --next-all 275 res=$? 276 277# Check if we should exit because bisection is finished 278test$res-eq10&&exit0 279 280# Check for an error in the bisection process 281test$res-ne0&&exit$res 282 283return0 284} 285 286bisect_visualize() { 287 bisect_next_check fail 288 289iftest$#=0 290then 291case"${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}"in 292'')set git log ;; 293set*)set gitk ;; 294esac 295else 296case"$1"in 297 git*|tig) ;; 298-*)set git log "$@";; 299*)set git "$@";; 300esac 301fi 302 303 not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*") 304 eval '"$@"' refs/bisect/bad --not$not--$(cat "$GIT_DIR/BISECT_NAMES") 305} 306 307bisect_reset() { 308 test -s "$GIT_DIR/BISECT_START" || { 309 echo "We are not bisecting." 310 return 311 } 312 case "$#" in 313 0) branch=$(cat "$GIT_DIR/BISECT_START");; 314 1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || 315 die "'$1' is not a valid commit" 316 branch="$1" ;; 317 *) 318 usage ;; 319 esac 320 git checkout "$branch" -- && bisect_clean_state 321} 322 323bisect_clean_state() { 324 # There may be some refs packed during bisection. 325 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* | 326 while read ref hash 327 do 328 git update-ref -d$ref$hash|| exit 329 done 330 rm -f "$GIT_DIR/BISECT_EXPECTED_REV" && 331 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" && 332 rm -f "$GIT_DIR/BISECT_LOG" && 333 rm -f "$GIT_DIR/BISECT_NAMES" && 334 rm -f "$GIT_DIR/BISECT_RUN" && 335 # Cleanup head-name if it got left by an old version of git-bisect 336 rm -f "$GIT_DIR/head-name" && 337 338 rm -f "$GIT_DIR/BISECT_START" 339} 340 341bisect_replay () { 342 test -r "$1" || die "cannot read$1for replaying" 343 bisect_reset 344 while read git bisect command rev 345 do 346 test "$git$bisect" = "git bisect" -o "$git" = "git-bisect" || continue 347 if test "$git" = "git-bisect"; then 348 rev="$command" 349 command="$bisect" 350 fi 351 case "$command" in 352 start) 353 cmd="bisect_start$rev" 354 eval "$cmd" ;; 355 good|bad|skip) 356 bisect_write "$command" "$rev" ;; 357 *) 358 die "?? what are you talking about?" ;; 359 esac 360 done <"$1" 361 bisect_auto_next 362} 363 364bisect_run () { 365 bisect_next_check fail 366 367 while true 368 do 369 echo "running $@" 370 "$@" 371 res=$? 372 373 # Check for really bad run error. 374 if [$res-lt 0 -o$res-ge 128 ]; then 375 echo >&2 "bisect run failed:" 376 echo >&2 "exit code$resfrom '$@' is < 0 or >= 128" 377 exit$res 378 fi 379 380 # Find current state depending on run success or failure. 381 # A special exit code of 125 means cannot test. 382 if [$res-eq 125 ]; then 383 state='skip' 384 elif [$res-gt 0 ]; then 385 state='bad' 386 else 387 state='good' 388 fi 389 390 # We have to use a subshell because "bisect_state" can exit. 391 ( bisect_state$state> "$GIT_DIR/BISECT_RUN" ) 392 res=$? 393 394 cat "$GIT_DIR/BISECT_RUN" 395 396 if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \ 397 > /dev/null; then 398 echo >&2 "bisect run cannot continue any more" 399 exit$res 400 fi 401 402 if [$res-ne 0 ]; then 403 echo >&2 "bisect run failed:" 404 echo >&2 "'bisect_state $state' exited with error code$res" 405 exit$res 406 fi 407 408 if grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then 409 echo "bisect run success" 410 exit 0; 411 fi 412 413 done 414} 415 416 417case "$#" in 4180) 419 usage ;; 420*) 421 cmd="$1" 422 shift 423 case "$cmd" in 424 help) 425 git bisect -h ;; 426 start) 427 bisect_start "$@" ;; 428 bad|good) 429 bisect_state "$cmd" "$@" ;; 430 skip) 431 bisect_skip "$@" ;; 432 next) 433 # Not sure we want "next" at the UI level anymore. 434 bisect_next "$@" ;; 435 visualize|view) 436 bisect_visualize "$@" ;; 437 reset) 438 bisect_reset "$@" ;; 439 replay) 440 bisect_replay "$@" ;; 441 log) 442 cat "$GIT_DIR/BISECT_LOG" ;; 443 run) 444 bisect_run "$@" ;; 445 *) 446 usage ;; 447 esac 448esac