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