bisect: further style nitpicks
[git/jrn.git] / git-bisect.sh
blob22c4da5a9a77d0124e613303f29adbbb537c60be
1 #!/bin/sh
3 USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]'
4 LONG_USAGE='git bisect help
5 print this long help message.
6 git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...]
7 reset bisect state and start bisection.
8 git bisect bad [<rev>]
9 mark <rev> a known-bad revision.
10 git bisect good [<rev>...]
11 mark <rev>... known-good revisions.
12 git bisect skip [(<rev>|<range>)...]
13 mark <rev>... untestable revisions.
14 git bisect next
15 find next bisection to test and check it out.
16 git bisect reset [<commit>]
17 finish bisection search and go back to commit.
18 git bisect visualize
19 show bisect status in gitk.
20 git bisect replay <logfile>
21 replay bisection log.
22 git bisect log
23 show bisect log.
24 git bisect run <cmd>...
25 use <cmd>... to automatically bisect.
27 Please use "git help bisect" to get the full man page.'
29 OPTIONS_SPEC=
30 . git-sh-setup
31 . git-sh-i18n
32 require_work_tree
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"
37 bisect_head()
39 if test -f "$GIT_DIR/BISECT_HEAD"
40 then
41 echo BISECT_HEAD
42 else
43 echo HEAD
47 bisect_autostart() {
48 test -s "$GIT_DIR/BISECT_START" || {
50 gettext "You need to start by \"git bisect start\"" &&
51 echo
52 ) >&2
53 if test -t 0
54 then
55 # TRANSLATORS: Make sure to include [Y] and [n] in your
56 # translation. The program will only accept English input
57 # at this point.
58 gettext "Do you want me to do it for you [Y/n]? " >&2
59 read yesno
60 case "$yesno" in
61 [Nn]*)
62 exit ;;
63 esac
64 bisect_start
65 else
66 exit 1
71 bisect_start() {
73 # Check for one bad and then some good revisions.
75 has_double_dash=0
76 for arg; do
77 case "$arg" in --) has_double_dash=1; break ;; esac
78 done
79 orig_args=$(git rev-parse --sq-quote "$@")
80 bad_seen=0
81 eval=''
82 mode=''
83 while [ $# -gt 0 ]; do
84 arg="$1"
85 case "$arg" in
86 --)
87 shift
88 break
90 --no-checkout)
91 mode=--no-checkout
92 shift ;;
93 --*)
94 die "$(eval_gettext "unrecognised option: '\$arg'")" ;;
96 rev=$(git rev-parse -q --verify "$arg^{commit}") || {
97 test $has_double_dash -eq 1 &&
98 die "$(eval_gettext "'\$arg' does not appear to be a valid revision")"
99 break
101 case $bad_seen in
102 0) state='bad' ; bad_seen=1 ;;
103 *) state='good' ;;
104 esac
105 eval="$eval bisect_write '$state' '$rev' 'nolog' &&"
106 shift
108 esac
109 done
112 # Verify HEAD.
114 head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
115 head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
116 die "$(gettext "Bad HEAD - I need a HEAD")"
119 # Check if we are bisecting.
121 start_head=''
122 if test -s "$GIT_DIR/BISECT_START"
123 then
124 # Reset to the rev from where we started.
125 start_head=$(cat "$GIT_DIR/BISECT_START")
126 if test "z$mode" != "z--no-checkout"
127 then
128 git checkout "$start_head" --
130 else
131 # Get rev from where we start.
132 case "$head" in
133 refs/heads/*|$_x40)
134 # This error message should only be triggered by
135 # cogito usage, and cogito users should understand
136 # it relates to cg-seek.
137 [ -s "$GIT_DIR/head-name" ] &&
138 die "$(gettext "won't bisect on seeked tree")"
139 start_head="${head#refs/heads/}"
142 die "$(gettext "Bad HEAD - strange symbolic ref")"
144 esac
148 # Get rid of any old bisect state.
150 bisect_clean_state || exit
153 # Change state.
154 # In case of mistaken revs or checkout error, or signals received,
155 # "bisect_auto_next" below may exit or misbehave.
156 # We have to trap this to be able to clean up using
157 # "bisect_clean_state".
159 trap 'bisect_clean_state' 0
160 trap 'exit 255' 1 2 3 15
163 # Write new start state.
165 echo "$start_head" >"$GIT_DIR/BISECT_START" && {
166 test "z$mode" != "z--no-checkout" ||
167 git update-ref --no-deref BISECT_HEAD "$start_head"
168 } &&
169 git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
170 eval "$eval true" &&
171 echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
173 # Check if we can proceed to the next bisect state.
175 bisect_auto_next
177 trap '-' 0
180 bisect_write() {
181 state="$1"
182 rev="$2"
183 nolog="$3"
184 case "$state" in
185 bad) tag="$state" ;;
186 good|skip) tag="$state"-"$rev" ;;
187 *) die "$(eval_gettext "Bad bisect_write argument: \$state")" ;;
188 esac
189 git update-ref "refs/bisect/$tag" "$rev" || exit
190 echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG"
191 test -n "$nolog" || echo "git bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
194 is_expected_rev() {
195 test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
196 test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
199 check_expected_revs() {
200 for _rev in "$@"; do
201 if ! is_expected_rev "$_rev"
202 then
203 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
204 rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
205 return
207 done
210 bisect_skip() {
211 all=''
212 for arg in "$@"
214 case "$arg" in
215 *..*)
216 revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
218 revs=$(git rev-parse --sq-quote "$arg") ;;
219 esac
220 all="$all $revs"
221 done
222 eval bisect_state 'skip' $all
225 bisect_state() {
226 bisect_autostart
227 state=$1
228 case "$#,$state" in
229 0,*)
230 die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
231 1,bad|1,good|1,skip)
232 rev=$(git rev-parse --verify $(bisect_head)) ||
233 die "$(gettext "Bad rev input: $(bisect_head)")"
234 bisect_write "$state" "$rev"
235 check_expected_revs "$rev" ;;
236 2,bad|*,good|*,skip)
237 shift
238 eval=''
239 for rev in "$@"
241 sha=$(git rev-parse --verify "$rev^{commit}") ||
242 die "$(eval_gettext "Bad rev input: \$rev")"
243 eval="$eval bisect_write '$state' '$sha'; "
244 done
245 eval "$eval"
246 check_expected_revs "$@" ;;
247 *,bad)
248 die "$(gettext "'git bisect bad' can take only one argument.")" ;;
250 usage ;;
251 esac
252 bisect_auto_next
255 bisect_next_check() {
256 missing_good= missing_bad=
257 git show-ref -q --verify refs/bisect/bad || missing_bad=t
258 test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
260 case "$missing_good,$missing_bad,$1" in
261 ,,*)
262 : have both good and bad - ok
265 # do not have both but not asked to fail - just report.
266 false
268 t,,good)
269 # have bad but not good. we could bisect although
270 # this is less optimum.
272 gettext "Warning: bisecting only with a bad commit." &&
273 echo
274 ) >&2
275 if test -t 0
276 then
277 # TRANSLATORS: Make sure to include [Y] and [n] in your
278 # translation. The program will only accept English input
279 # at this point.
280 gettext "Are you sure [Y/n]? " >&2
281 read yesno
282 case "$yesno" in [Nn]*) exit 1 ;; esac
284 : bisect without good...
288 if test -s "$GIT_DIR/BISECT_START"
289 then
291 gettext "You need to give me at least one good and one bad revisions.
292 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" &&
293 echo
294 ) >&2
295 else
297 gettext "You need to start by \"git bisect start\".
298 You then need to give me at least one good and one bad revisions.
299 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" &&
300 echo
301 ) >&2
303 exit 1 ;;
304 esac
307 bisect_auto_next() {
308 bisect_next_check && bisect_next || :
311 bisect_next() {
312 case "$#" in 0) ;; *) usage ;; esac
313 bisect_autostart
314 bisect_next_check good
316 # Perform all bisection computation, display and checkout
317 git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
318 res=$?
320 # Check if we should exit because bisection is finished
321 test $res -eq 10 && exit 0
323 # Check for an error in the bisection process
324 test $res -ne 0 && exit $res
326 return 0
329 bisect_visualize() {
330 bisect_next_check fail
332 if test $# = 0
333 then
334 if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
335 type gitk >/dev/null 2>&1
336 then
337 set gitk
338 else
339 set git log
341 else
342 case "$1" in
343 git*|tig) ;;
344 -*) set git log "$@" ;;
345 *) set git "$@" ;;
346 esac
349 eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
352 bisect_reset() {
353 test -s "$GIT_DIR/BISECT_START" || {
354 gettext "We are not bisecting."; echo
355 return
357 case "$#" in
358 0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
359 1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || {
360 invalid="$1"
361 die "$(eval_gettext "'\$invalid' is not a valid commit")"
363 branch="$1" ;;
365 usage ;;
366 esac
368 if ! test -f "$GIT_DIR/BISECT_HEAD" && ! git checkout "$branch" --
369 then
370 die "$(eval_gettext "Could not check out original HEAD '\$branch'.
371 Try 'git bisect reset <commit>'.")"
373 bisect_clean_state
376 bisect_clean_state() {
377 # There may be some refs packed during bisection.
378 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
379 while read ref hash
381 git update-ref -d $ref $hash || exit
382 done
383 rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
384 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
385 rm -f "$GIT_DIR/BISECT_LOG" &&
386 rm -f "$GIT_DIR/BISECT_NAMES" &&
387 rm -f "$GIT_DIR/BISECT_RUN" &&
388 # Cleanup head-name if it got left by an old version of git-bisect
389 rm -f "$GIT_DIR/head-name" &&
390 git update-ref -d --no-deref BISECT_HEAD &&
391 # clean up BISECT_START last
392 rm -f "$GIT_DIR/BISECT_START"
395 bisect_replay () {
396 file="$1"
397 test "$#" -eq 1 || die "$(gettext "No logfile given")"
398 test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
399 bisect_reset
400 while read git bisect command rev
402 test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
403 if test "$git" = "git-bisect"
404 then
405 rev="$command"
406 command="$bisect"
408 case "$command" in
409 start)
410 cmd="bisect_start $rev"
411 eval "$cmd" ;;
412 good|bad|skip)
413 bisect_write "$command" "$rev" ;;
415 die "$(gettext "?? what are you talking about?")" ;;
416 esac
417 done <"$file"
418 bisect_auto_next
421 bisect_run () {
422 bisect_next_check fail
424 while true
426 command="$@"
427 eval_gettext "running \$command"; echo
428 "$@"
429 res=$?
431 # Check for really bad run error.
432 if [ $res -lt 0 -o $res -ge 128 ]
433 then
435 eval_gettext "bisect run failed:
436 exit code \$res from '\$command' is < 0 or >= 128" &&
437 echo
438 ) >&2
439 exit $res
442 # Find current state depending on run success or failure.
443 # A special exit code of 125 means cannot test.
444 if [ $res -eq 125 ]
445 then
446 state='skip'
447 elif [ $res -gt 0 ]
448 then
449 state='bad'
450 else
451 state='good'
454 # We have to use a subshell because "bisect_state" can exit.
455 ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
456 res=$?
458 cat "$GIT_DIR/BISECT_RUN"
460 if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
461 > /dev/null
462 then
464 gettext "bisect run cannot continue any more" &&
465 echo
466 ) >&2
467 exit $res
470 if [ $res -ne 0 ]
471 then
473 eval_gettext "bisect run failed:
474 'bisect_state \$state' exited with error code \$res" &&
475 echo
476 ) >&2
477 exit $res
480 if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null
481 then
482 gettext "bisect run success"; echo
483 exit 0;
486 done
489 bisect_log () {
490 test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
491 cat "$GIT_DIR/BISECT_LOG"
494 case "$#" in
496 usage ;;
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 "$@" ;;
523 usage ;;
524 esac
525 esac