bisect: replace hardcoded "bad|good" by variables
[git/git.git] / git-bisect.sh
CommitLineData
8cc6a083 1#!/bin/sh
d025524d 2
243a60fb
CC
3USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]'
4LONG_USAGE='git bisect help
6021be86 5 print this long help message.
4796e823 6git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...]
6021be86 7 reset bisect state and start bisection.
38a47fd6 8git bisect bad [<rev>]
6021be86 9 mark <rev> a known-bad revision.
38a47fd6 10git bisect good [<rev>...]
6021be86 11 mark <rev>... known-good revisions.
5413812f 12git bisect skip [(<rev>|<range>)...]
6021be86 13 mark <rev>... untestable revisions.
38a47fd6 14git bisect next
6021be86 15 find next bisection to test and check it out.
6b87ce23 16git bisect reset [<commit>]
6021be86 17 finish bisection search and go back to commit.
38a47fd6 18git bisect visualize
6021be86 19 show bisect status in gitk.
38a47fd6 20git bisect replay <logfile>
6021be86 21 replay bisection log.
38a47fd6 22git bisect log
6021be86 23 show bisect log.
38a47fd6 24git bisect run <cmd>...
6021be86 25 use <cmd>... to automatically bisect.
243a60fb
CC
26
27Please use "git help bisect" to get the full man page.'
d025524d 28
8f321a39 29OPTIONS_SPEC=
ae2b0f15 30. git-sh-setup
dcf9c2e5 31. git-sh-i18n
8cc6a083 32
ce32660e
JS
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"
43f9d9f3
AD
35TERM_BAD=bad
36TERM_GOOD=good
ce32660e 37
4796e823
JS
38bisect_head()
39{
40 if test -f "$GIT_DIR/BISECT_HEAD"
41 then
42 echo BISECT_HEAD
43 else
44 echo HEAD
45 fi
46}
47
8cc6a083 48bisect_autostart() {
823ea121 49 test -s "$GIT_DIR/BISECT_START" || {
3145b1a2 50 gettextln "You need to start by \"git bisect start\"" >&2
8cc6a083
LT
51 if test -t 0
52 then
04de0996
ÆAB
53 # TRANSLATORS: Make sure to include [Y] and [n] in your
54 # translation. The program will only accept English input
55 # at this point.
6021be86 56 gettext "Do you want me to do it for you [Y/n]? " >&2
8cc6a083
LT
57 read yesno
58 case "$yesno" in
59 [Nn]*)
60 exit ;;
61 esac
62 bisect_start
63 else
64 exit 1
65 fi
66 }
67}
68
69bisect_start() {
4764f464
JS
70 #
71 # Check for one bad and then some good revisions.
72 #
73 has_double_dash=0
74 for arg; do
6021be86 75 case "$arg" in --) has_double_dash=1; break ;; esac
4764f464
JS
76 done
77 orig_args=$(git rev-parse --sq-quote "$@")
78 bad_seen=0
79 eval=''
24c51280
JS
80 if test "z$(git rev-parse --is-bare-repository)" != zfalse
81 then
82 mode=--no-checkout
83 else
84 mode=''
85 fi
4764f464 86 while [ $# -gt 0 ]; do
6021be86
JS
87 arg="$1"
88 case "$arg" in
89 --)
90 shift
91 break
4764f464 92 ;;
6021be86
JS
93 --no-checkout)
94 mode=--no-checkout
95 shift ;;
96 --*)
97 die "$(eval_gettext "unrecognised option: '\$arg'")" ;;
98 *)
99 rev=$(git rev-parse -q --verify "$arg^{commit}") || {
43b8ff4b
JH
100 test $has_double_dash -eq 1 &&
101 die "$(eval_gettext "'\$arg' does not appear to be a valid revision")"
102 break
6021be86
JS
103 }
104 case $bad_seen in
43f9d9f3
AD
105 0) state=$TERM_BAD ; bad_seen=1 ;;
106 *) state=$TERM_GOOD ;;
6021be86
JS
107 esac
108 eval="$eval bisect_write '$state' '$rev' 'nolog' &&"
109 shift
110 ;;
4764f464 111 esac
4764f464
JS
112 done
113
8cc6a083 114 #
634f2464 115 # Verify HEAD.
8cc6a083 116 #
48949a18 117 head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
ce32660e 118 head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
9570fc1e 119 die "$(gettext "Bad HEAD - I need a HEAD")"
634f2464 120
ee831f7d 121 #
634f2464 122 # Check if we are bisecting.
ee831f7d 123 #
d3e54c88 124 start_head=''
634f2464
CC
125 if test -s "$GIT_DIR/BISECT_START"
126 then
127 # Reset to the rev from where we started.
9d0cd91c 128 start_head=$(cat "$GIT_DIR/BISECT_START")
4796e823
JS
129 if test "z$mode" != "z--no-checkout"
130 then
1acf1171 131 git checkout "$start_head" -- ||
9c9b4f2f 132 die "$(eval_gettext "Checking out '\$start_head' failed. Try 'git bisect reset <valid-branch>'.")"
4796e823 133 fi
634f2464
CC
134 else
135 # Get rev from where we start.
136 case "$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" ] &&
382d20e3 142 die "$(gettext "won't bisect on cg-seek'ed tree")"
634f2464
CC
143 start_head="${head#refs/heads/}"
144 ;;
145 *)
9570fc1e 146 die "$(gettext "Bad HEAD - strange symbolic ref")"
634f2464
CC
147 ;;
148 esac
149 fi
8cc6a083
LT
150
151 #
634f2464 152 # Get rid of any old bisect state.
8cc6a083 153 #
823ea121 154 bisect_clean_state || exit
38a47fd6 155
ba963de8
CC
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 #
163 trap 'bisect_clean_state' 0
164 trap 'exit 255' 1 2 3 15
165
166 #
167 # Write new start state.
168 #
4796e823
JS
169 echo "$start_head" >"$GIT_DIR/BISECT_START" && {
170 test "z$mode" != "z--no-checkout" ||
171 git update-ref --no-deref BISECT_HEAD "$start_head"
172 } &&
de52f5a8 173 git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
6ba7acff 174 eval "$eval true" &&
6c98c054 175 echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
ba963de8
CC
176 #
177 # Check if we can proceed to the next bisect state.
178 #
38a47fd6 179 bisect_auto_next
ba963de8
CC
180
181 trap '-' 0
8cc6a083
LT
182}
183
55624f9a
CC
184bisect_write() {
185 state="$1"
186 rev="$2"
737c74ee 187 nolog="$3"
55624f9a 188 case "$state" in
43f9d9f3
AD
189 "$TERM_BAD")
190 tag="$state" ;;
191 "$TERM_GOOD"|skip)
192 tag="$state"-"$rev" ;;
193 *)
194 die "$(eval_gettext "Bad bisect_write argument: \$state")" ;;
55624f9a 195 esac
ba963de8 196 git update-ref "refs/bisect/$tag" "$rev" || exit
f454cdc4 197 echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG"
6c98c054 198 test -n "$nolog" || echo "git bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
55624f9a
CC
199}
200
c9c4e2d5
CC
201is_expected_rev() {
202 test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
203 test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
204}
205
c9c4e2d5
CC
206check_expected_revs() {
207 for _rev in "$@"; do
eef12a9a
JS
208 if ! is_expected_rev "$_rev"
209 then
c9c4e2d5
CC
210 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
211 rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
212 return
213 fi
214 done
215}
216
ee2314f5 217bisect_skip() {
6021be86 218 all=''
ee2314f5
CC
219 for arg in "$@"
220 do
6021be86
JS
221 case "$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") ;;
226 esac
227 all="$all $revs"
228 done
229 eval bisect_state 'skip' $all
ee2314f5
CC
230}
231
155fc795 232bisect_state() {
8cc6a083 233 bisect_autostart
155fc795
CC
234 state=$1
235 case "$#,$state" in
236 0,*)
9570fc1e 237 die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
43f9d9f3 238 1,"$TERM_BAD"|1,"$TERM_GOOD"|1,skip)
4796e823
JS
239 rev=$(git rev-parse --verify $(bisect_head)) ||
240 die "$(gettext "Bad rev input: $(bisect_head)")"
c9c4e2d5
CC
241 bisect_write "$state" "$rev"
242 check_expected_revs "$rev" ;;
43f9d9f3 243 2,"$TERM_BAD"|*,"$TERM_GOOD"|*,skip)
155fc795 244 shift
6bc02d56 245 hash_list=''
e3389075 246 for rev in "$@"
155fc795 247 do
a179a303 248 sha=$(git rev-parse --verify "$rev^{commit}") ||
15eaa049 249 die "$(eval_gettext "Bad rev input: \$rev")"
6bc02d56 250 hash_list="$hash_list $sha"
d3e54c88 251 done
6bc02d56
CC
252 for rev in $hash_list
253 do
254 bisect_write "$state" "$rev"
255 done
256 check_expected_revs $hash_list ;;
43f9d9f3
AD
257 *,"$TERM_BAD")
258 die "$(eval_gettext "'git bisect \$TERM_BAD' can take only one argument.")" ;;
cc9f24d0
JH
259 *)
260 usage ;;
8cc6a083 261 esac
97e1c51e
CC
262 bisect_auto_next
263}
264
8cc6a083 265bisect_next_check() {
0a5280a9 266 missing_good= missing_bad=
43f9d9f3
AD
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
6fecf191 269
0a5280a9
JH
270 case "$missing_good,$missing_bad,$1" in
271 ,,*)
43f9d9f3 272 : have both $TERM_GOOD and $TERM_BAD - ok
0a5280a9
JH
273 ;;
274 *,)
275 # do not have both but not asked to fail - just report.
276 false
277 ;;
43f9d9f3 278 t,,"$TERM_GOOD")
0a5280a9
JH
279 # have bad but not good. we could bisect although
280 # this is less optimum.
43f9d9f3 281 eval_gettextln "Warning: bisecting only with a \$TERM_BAD commit." >&2
0a5280a9
JH
282 if test -t 0
283 then
04de0996
ÆAB
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
e5d3afd7
FM
288 read yesno
289 case "$yesno" in [Nn]*) exit 1 ;; esac
0a5280a9 290 fi
43f9d9f3 291 : bisect without $TERM_GOOD...
0a5280a9 292 ;;
8cc6a083 293 *)
be508d3a
ÆAB
294
295 if test -s "$GIT_DIR/BISECT_START"
296 then
ad5fe377 297 gettextln "You need to give me at least one good and one bad revision.
3145b1a2 298(You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
be508d3a 299 else
3145b1a2 300 gettextln "You need to start by \"git bisect start\".
ad5fe377 301You then need to give me at least one good and one bad revision.
3145b1a2 302(You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
be508d3a 303 fi
0a5280a9 304 exit 1 ;;
8cc6a083
LT
305 esac
306}
307
308bisect_auto_next() {
434d036f 309 bisect_next_check && bisect_next || :
8cc6a083
LT
310}
311
312bisect_next() {
8fe26f44 313 case "$#" in 0) ;; *) usage ;; esac
8cc6a083 314 bisect_autostart
43f9d9f3 315 bisect_next_check $TERM_GOOD
0a5280a9 316
0871984d 317 # Perform all bisection computation, display and checkout
4796e823 318 git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
5a1d31c7 319 res=$?
0a5280a9 320
6021be86 321 # Check if we should exit because bisection is finished
a7f8b8ac
TH
322 if test $res -eq 10
323 then
43f9d9f3 324 bad_rev=$(git show-ref --hash --verify refs/bisect/$TERM_BAD)
a7f8b8ac 325 bad_commit=$(git show-branch $bad_rev)
43f9d9f3 326 echo "# first $TERM_BAD commit: $bad_commit" >>"$GIT_DIR/BISECT_LOG"
a7f8b8ac 327 exit 0
f989cac9
TH
328 elif test $res -eq 2
329 then
330 echo "# only skipped commits left to test" >>"$GIT_DIR/BISECT_LOG"
43f9d9f3
AD
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)
f989cac9
TH
333 do
334 skipped_commit=$(git show-branch $skipped)
43f9d9f3 335 echo "# possible first $TERM_BAD commit: $skipped_commit" >>"$GIT_DIR/BISECT_LOG"
f989cac9
TH
336 done
337 exit $res
a7f8b8ac 338 fi
0a5280a9 339
5a1d31c7
CC
340 # Check for an error in the bisection process
341 test $res -ne 0 && exit $res
342
343 return 0
8cc6a083
LT
344}
345
cc9f24d0
JH
346bisect_visualize() {
347 bisect_next_check fail
235997c9
JH
348
349 if test $# = 0
350 then
c4e4644e 351 if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
43b8ff4b 352 type gitk >/dev/null 2>&1
eef12a9a 353 then
c4e4644e
JK
354 set gitk
355 else
356 set git log
357 fi
235997c9
JH
358 else
359 case "$1" in
360 git*|tig) ;;
361 -*) set git log "$@" ;;
362 *) set git "$@" ;;
363 esac
364 fi
365
fc13aa3d 366 eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
cc9f24d0
JH
367}
368
8cc6a083 369bisect_reset() {
823ea121 370 test -s "$GIT_DIR/BISECT_START" || {
3145b1a2 371 gettextln "We are not bisecting."
fce0499f
CC
372 return
373 }
8cc6a083 374 case "$#" in
823ea121 375 0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
305a233c 376 1) git rev-parse --quiet --verify "$1^{commit}" >/dev/null || {
6021be86
JS
377 invalid="$1"
378 die "$(eval_gettext "'\$invalid' is not a valid commit")"
379 }
380 branch="$1" ;;
8fe26f44 381 *)
6021be86 382 usage ;;
8cc6a083 383 esac
43b8ff4b
JH
384
385 if ! test -f "$GIT_DIR/BISECT_HEAD" && ! git checkout "$branch" --
4796e823 386 then
43b8ff4b 387 die "$(eval_gettext "Could not check out original HEAD '\$branch'.
15eaa049 388Try 'git bisect reset <commit>'.")"
3bb8cf88 389 fi
4796e823 390 bisect_clean_state
e204de28
JH
391}
392
38a47fd6 393bisect_clean_state() {
947a604b 394 # There may be some refs packed during bisection.
634f2464 395 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
947a604b
CC
396 while read ref hash
397 do
823ea121 398 git update-ref -d $ref $hash || exit
947a604b 399 done
c9c4e2d5
CC
400 rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
401 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
823ea121
CC
402 rm -f "$GIT_DIR/BISECT_LOG" &&
403 rm -f "$GIT_DIR/BISECT_NAMES" &&
404 rm -f "$GIT_DIR/BISECT_RUN" &&
9d0cd91c 405 # Cleanup head-name if it got left by an old version of git-bisect
823ea121 406 rm -f "$GIT_DIR/head-name" &&
4796e823
JS
407 git update-ref -d --no-deref BISECT_HEAD &&
408 # clean up BISECT_START last
823ea121 409 rm -f "$GIT_DIR/BISECT_START"
38a47fd6
CC
410}
411
e204de28 412bisect_replay () {
55a9fc80
ÆAB
413 file="$1"
414 test "$#" -eq 1 || die "$(gettext "No logfile given")"
415 test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
e204de28 416 bisect_reset
6c98c054 417 while read git bisect command rev
e204de28 418 do
c82af12a 419 test "$git $bisect" = "git bisect" || test "$git" = "git-bisect" || continue
eef12a9a
JS
420 if test "$git" = "git-bisect"
421 then
6c98c054
MV
422 rev="$command"
423 command="$bisect"
424 fi
e204de28
JH
425 case "$command" in
426 start)
e9a45d75 427 cmd="bisect_start $rev"
737c74ee 428 eval "$cmd" ;;
43f9d9f3 429 $TERM_GOOD|$TERM_BAD|skip)
737c74ee 430 bisect_write "$command" "$rev" ;;
e204de28 431 *)
9570fc1e 432 die "$(gettext "?? what are you talking about?")" ;;
e204de28 433 esac
55a9fc80 434 done <"$file"
e204de28 435 bisect_auto_next
8cc6a083
LT
436}
437
a17c4101 438bisect_run () {
6021be86
JS
439 bisect_next_check fail
440
441 while true
442 do
443 command="$@"
3145b1a2 444 eval_gettextln "running \$command"
6021be86
JS
445 "$@"
446 res=$?
447
448 # Check for really bad run error.
eef12a9a
JS
449 if [ $res -lt 0 -o $res -ge 128 ]
450 then
3145b1a2
JS
451 eval_gettextln "bisect run failed:
452exit code \$res from '\$command' is < 0 or >= 128" >&2
6021be86
JS
453 exit $res
454 fi
455
456 # Find current state depending on run success or failure.
457 # A special exit code of 125 means cannot test.
eef12a9a
JS
458 if [ $res -eq 125 ]
459 then
6021be86 460 state='skip'
eef12a9a
JS
461 elif [ $res -gt 0 ]
462 then
43f9d9f3 463 state="$TERM_BAD"
6021be86 464 else
43f9d9f3 465 state="$TERM_GOOD"
6021be86
JS
466 fi
467
468 # We have to use a subshell because "bisect_state" can exit.
305a233c 469 ( bisect_state $state >"$GIT_DIR/BISECT_RUN" )
6021be86
JS
470 res=$?
471
472 cat "$GIT_DIR/BISECT_RUN"
473
43f9d9f3 474 if sane_grep "first $TERM_BAD commit could be any of" "$GIT_DIR/BISECT_RUN" \
305a233c 475 >/dev/null
eef12a9a 476 then
3145b1a2 477 gettextln "bisect run cannot continue any more" >&2
6021be86
JS
478 exit $res
479 fi
480
eef12a9a
JS
481 if [ $res -ne 0 ]
482 then
3145b1a2
JS
483 eval_gettextln "bisect run failed:
484'bisect_state \$state' exited with error code \$res" >&2
6021be86
JS
485 exit $res
486 fi
a17c4101 487
43f9d9f3 488 if sane_grep "is the first $TERM_BAD commit" "$GIT_DIR/BISECT_RUN" >/dev/null
eef12a9a 489 then
3145b1a2 490 gettextln "bisect run success"
6021be86
JS
491 exit 0;
492 fi
a17c4101 493
6021be86 494 done
a17c4101
CC
495}
496
412ff738 497bisect_log () {
9570fc1e 498 test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
412ff738
SG
499 cat "$GIT_DIR/BISECT_LOG"
500}
a17c4101 501
8cc6a083
LT
502case "$#" in
5030)
6021be86 504 usage ;;
8cc6a083 505*)
6021be86
JS
506 cmd="$1"
507 shift
508 case "$cmd" in
509 help)
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 "$@" ;;
522 reset)
523 bisect_reset "$@" ;;
524 replay)
525 bisect_replay "$@" ;;
526 log)
527 bisect_log ;;
528 run)
529 bisect_run "$@" ;;
530 *)
531 usage ;;
532 esac
8cc6a083 533esac