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 368 if ! test -f "$GIT_DIR/BISECT_HEAD" && ! git checkout "$branch" -- 369 then 370 die "$(eval_gettext "Could not check out original HEAD '\$branch'. 371Try 'git bisect reset <commit>'.")" 372 fi 373 bisect_clean_state 374} 375 376bisect_clean_state() { 377 # There may be some refs packed during bisection. 378 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* | 379 while read ref hash 380 do 381 git update-ref -d$ref$hash|| exit 382 done 383 rm -f "$GIT_DIR/BISECT_EXPECTED_REV" && 384 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" && 385 rm -f "$GIT_DIR/BISECT_LOG" && 386 rm -f "$GIT_DIR/BISECT_NAMES" && 387 rm -f "$GIT_DIR/BISECT_RUN" && 388 # Cleanup head-name if it got left by an old version of git-bisect 389 rm -f "$GIT_DIR/head-name" && 390 git update-ref -d --no-deref BISECT_HEAD && 391 # clean up BISECT_START last 392 rm -f "$GIT_DIR/BISECT_START" 393} 394 395bisect_replay () { 396 file="$1" 397 test "$#" -eq 1 || die "$(gettext "No logfile given")" 398 test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")" 399 bisect_reset 400 while read git bisect command rev 401 do 402 test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue 403 if test "$git" = "git-bisect" 404 then 405 rev="$command" 406 command="$bisect" 407 fi 408 case "$command" in 409 start) 410 cmd="bisect_start $rev" 411 eval "$cmd" ;; 412 good|bad|skip) 413 bisect_write "$command" "$rev" ;; 414 *) 415 die "$(gettext "?? what are you talking about?")" ;; 416 esac 417 done <"$file" 418 bisect_auto_next 419} 420 421bisect_run () { 422 bisect_next_check fail 423 424 while true 425 do 426 command="$@" 427 eval_gettext "running \$command"; echo 428 "$@" 429 res=$? 430 431 # Check for really bad run error. 432 if [$res-lt 0 -o$res-ge 128 ] 433 then 434 ( 435 eval_gettext "bisect run failed: 436exit code \$res from '\$command' is <0 or >=128" && 437 echo 438 ) >&2 439 exit$res 440 fi 441 442 # Find current state depending on run success or failure. 443 # A special exit code of 125 means cannot test. 444 if [$res-eq 125 ] 445 then 446 state='skip' 447 elif [$res-gt 0 ] 448 then 449 state='bad' 450 else 451 state='good' 452 fi 453 454 # We have to use a subshell because "bisect_state" can exit. 455 ( bisect_state$state> "$GIT_DIR/BISECT_RUN" ) 456 res=$? 457 458 cat "$GIT_DIR/BISECT_RUN" 459 460 if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \ 461 > /dev/null 462 then 463 ( 464 gettext "bisect run cannot continue any more" && 465 echo 466 ) >&2 467 exit$res 468 fi 469 470 if [$res-ne 0 ] 471 then 472 ( 473 eval_gettext "bisect run failed: 474'bisect_state \$state' exited with error code \$res" && 475 echo 476 ) >&2 477 exit$res 478 fi 479 480 if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null 481 then 482 gettext "bisect run success"; echo 483 exit 0; 484 fi 485 486 done 487} 488 489bisect_log () { 490 test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")" 491 cat "$GIT_DIR/BISECT_LOG" 492} 493 494case "$#" in 4950) 496 usage ;; 497*) 498 cmd="$1" 499 shift 500 case "$cmd" in 501 help) 502 git bisect -h ;; 503 start) 504 bisect_start "$@" ;; 505 bad|good) 506 bisect_state "$cmd" "$@" ;; 507 skip) 508 bisect_skip "$@" ;; 509 next) 510 # Not sure we want "next" at the UI level anymore. 511 bisect_next "$@" ;; 512 visualize|view) 513 bisect_visualize "$@" ;; 514 reset) 515 bisect_reset "$@" ;; 516 replay) 517 bisect_replay "$@" ;; 518 log) 519 bisect_log ;; 520 run) 521 bisect_run "$@" ;; 522 *) 523 usage ;; 524 esac 525esac