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