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