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;; 146esac 147fi 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# 161trap'bisect_clean_state'0 162trap'exit 255'1 2 3 15 163 164# 165# Write new start state. 166# 167echo"$start_head">"$GIT_DIR/BISECT_START"&& { 168test"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"&& 172eval"$evaltrue"&& 173echo"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 179trap'-'0 180} 181 182bisect_write() { 183 state="$1" 184rev="$2" 185 nolog="$3" 186case"$state"in 187 bad) tag="$state";; 188 good|skip) tag="$state"-"$rev";; 189*) die "$(eval_gettext "Bad bisect_write argument: \$state")";; 190esac 191 git update-ref"refs/bisect/$tag""$rev"||exit 192echo"#$state:$(git show-branch $rev)">>"$GIT_DIR/BISECT_LOG" 193test -n"$nolog"||echo"git bisect$state$rev">>"$GIT_DIR/BISECT_LOG" 194} 195 196is_expected_rev() { 197test -f"$GIT_DIR/BISECT_EXPECTED_REV"&& 198test"$1"=$(cat "$GIT_DIR/BISECT_EXPECTED_REV") 199} 200 201check_expected_revs() { 202for _rev in"$@";do 203if! is_expected_rev "$_rev" 204then 205rm-f"$GIT_DIR/BISECT_ANCESTORS_OK" 206rm-f"$GIT_DIR/BISECT_EXPECTED_REV" 207return 208fi 209done 210} 211 212bisect_skip() { 213 all='' 214for arg in"$@" 215do 216case"$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");; 221esac 222 all="$all$revs" 223done 224eval bisect_state 'skip'$all 225} 226 227bisect_state() { 228 bisect_autostart 229 state=$1 230case"$#,$state"in 2310,*) 232 die "$(gettext "Please call 'bisect_state' with at least one argument.")";; 2331,bad|1,good|1,skip) 234rev=$(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="$eval bisect_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 test$res-eq 10 && exit 0 315 316 # Check for an error in the bisection process 317 test$res-ne 0 && exit$res 318 319 return 0 320} 321 322bisect_visualize() { 323 bisect_next_check fail 324 325 if test$#= 0 326 then 327 if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" && 328 type gitk >/dev/null 2>&1 329 then 330 set gitk 331 else 332 set git log 333 fi 334 else 335 case "$1" in 336 git*|tig) ;; 337 -*) set git log "$@" ;; 338 *) set git "$@" ;; 339 esac 340 fi 341 342 eval '"$@"' --bisect --$(cat "$GIT_DIR/BISECT_NAMES") 343} 344 345bisect_reset() { 346 test -s "$GIT_DIR/BISECT_START" || { 347 gettextln "We are not bisecting." 348 return 349 } 350 case "$#" in 351 0) branch=$(cat "$GIT_DIR/BISECT_START");; 352 1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || { 353 invalid="$1" 354 die "$(eval_gettext "'\$invalid' is not a valid commit")" 355 } 356 branch="$1" ;; 357 *) 358 usage ;; 359 esac 360 361 if ! test -f "$GIT_DIR/BISECT_HEAD" && ! git checkout "$branch" -- 362 then 363 die "$(eval_gettext "Could not check out original HEAD '\$branch'. 364Try 'git bisect reset <commit>'.")" 365 fi 366 bisect_clean_state 367} 368 369bisect_clean_state() { 370 # There may be some refs packed during bisection. 371 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* | 372 while read ref hash 373 do 374 git update-ref -d$ref$hash|| exit 375 done 376 rm -f "$GIT_DIR/BISECT_EXPECTED_REV" && 377 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" && 378 rm -f "$GIT_DIR/BISECT_LOG" && 379 rm -f "$GIT_DIR/BISECT_NAMES" && 380 rm -f "$GIT_DIR/BISECT_RUN" && 381 # Cleanup head-name if it got left by an old version of git-bisect 382 rm -f "$GIT_DIR/head-name" && 383 git update-ref -d --no-deref BISECT_HEAD && 384 # clean up BISECT_START last 385 rm -f "$GIT_DIR/BISECT_START" 386} 387 388bisect_replay () { 389 file="$1" 390 test "$#" -eq 1 || die "$(gettext "No logfile given")" 391 test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")" 392 bisect_reset 393 while read git bisect command rev 394 do 395 test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue 396 if test "$git" = "git-bisect" 397 then 398 rev="$command" 399 command="$bisect" 400 fi 401 case "$command" in 402 start) 403 cmd="bisect_start $rev" 404 eval "$cmd" ;; 405 good|bad|skip) 406 bisect_write "$command" "$rev" ;; 407 *) 408 die "$(gettext "?? what are you talking about?")" ;; 409 esac 410 done <"$file" 411 bisect_auto_next 412} 413 414bisect_run () { 415 bisect_next_check fail 416 417 while true 418 do 419 command="$@" 420 eval_gettextln "running \$command" 421 "$@" 422 res=$? 423 424 # Check for really bad run error. 425 if [$res-lt 0 -o$res-ge 128 ] 426 then 427 eval_gettextln "bisect run failed: 428exit code \$res from '\$command' is <0 or >=128" >&2 429 exit$res 430 fi 431 432 # Find current state depending on run success or failure. 433 # A special exit code of 125 means cannot test. 434 if [$res-eq 125 ] 435 then 436 state='skip' 437 elif [$res-gt 0 ] 438 then 439 state='bad' 440 else 441 state='good' 442 fi 443 444 # We have to use a subshell because "bisect_state" can exit. 445 ( bisect_state$state> "$GIT_DIR/BISECT_RUN" ) 446 res=$? 447 448 cat "$GIT_DIR/BISECT_RUN" 449 450 if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \ 451 > /dev/null 452 then 453 gettextln "bisect run cannot continue any more" >&2 454 exit$res 455 fi 456 457 if [$res-ne 0 ] 458 then 459 eval_gettextln "bisect run failed: 460'bisect_state \$state' exited with error code \$res" >&2 461 exit$res 462 fi 463 464 if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null 465 then 466 gettextln "bisect run success" 467 exit 0; 468 fi 469 470 done 471} 472 473bisect_log () { 474 test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")" 475 cat "$GIT_DIR/BISECT_LOG" 476} 477 478case "$#" in 4790) 480 usage ;; 481*) 482 cmd="$1" 483 shift 484 case "$cmd" in 485 help) 486 git bisect -h ;; 487 start) 488 bisect_start "$@" ;; 489 bad|good) 490 bisect_state "$cmd" "$@" ;; 491 skip) 492 bisect_skip "$@" ;; 493 next) 494 # Not sure we want "next" at the UI level anymore. 495 bisect_next "$@" ;; 496 visualize|view) 497 bisect_visualize "$@" ;; 498 reset) 499 bisect_reset "$@" ;; 500 replay) 501 bisect_replay "$@" ;; 502 log) 503 bisect_log ;; 504 run) 505 bisect_run "$@" ;; 506 *) 507 usage ;; 508 esac 509esac