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