bisect: add documentation for --no-checkout option.
[alt-git.git] / git-bisect.sh
blobb9c18dd2071cbfa5a94ab32700f535c0d6acf528
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"; then
202 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
203 rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
204 return
206 done
209 bisect_skip() {
210 all=''
211 for arg in "$@"
213 case "$arg" in
214 *..*)
215 revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
217 revs=$(git rev-parse --sq-quote "$arg") ;;
218 esac
219 all="$all $revs"
220 done
221 eval bisect_state 'skip' $all
224 bisect_state() {
225 bisect_autostart
226 state=$1
227 case "$#,$state" in
228 0,*)
229 die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
230 1,bad|1,good|1,skip)
231 rev=$(git rev-parse --verify $(bisect_head)) ||
232 die "$(gettext "Bad rev input: $(bisect_head)")"
233 bisect_write "$state" "$rev"
234 check_expected_revs "$rev" ;;
235 2,bad|*,good|*,skip)
236 shift
237 eval=''
238 for rev in "$@"
240 sha=$(git rev-parse --verify "$rev^{commit}") ||
241 die "$(eval_gettext "Bad rev input: \$rev")"
242 eval="$eval bisect_write '$state' '$sha'; "
243 done
244 eval "$eval"
245 check_expected_revs "$@" ;;
246 *,bad)
247 die "$(gettext "'git bisect bad' can take only one argument.")" ;;
249 usage ;;
250 esac
251 bisect_auto_next
254 bisect_next_check() {
255 missing_good= missing_bad=
256 git show-ref -q --verify refs/bisect/bad || missing_bad=t
257 test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
259 case "$missing_good,$missing_bad,$1" in
260 ,,*)
261 : have both good and bad - ok
264 # do not have both but not asked to fail - just report.
265 false
267 t,,good)
268 # have bad but not good. we could bisect although
269 # this is less optimum.
271 gettext "Warning: bisecting only with a bad commit." &&
272 echo
273 ) >&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
283 : bisect without good...
287 if test -s "$GIT_DIR/BISECT_START"
288 then
290 gettext "You need to give me at least one good and one bad revisions.
291 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" &&
292 echo
293 ) >&2
294 else
296 gettext "You need to start by \"git bisect start\".
297 You then need to give me at least one good and one bad revisions.
298 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" &&
299 echo
300 ) >&2
302 exit 1 ;;
303 esac
306 bisect_auto_next() {
307 bisect_next_check && bisect_next || :
310 bisect_next() {
311 case "$#" in 0) ;; *) usage ;; esac
312 bisect_autostart
313 bisect_next_check good
315 # Perform all bisection computation, display and checkout
316 git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
317 res=$?
319 # Check if we should exit because bisection is finished
320 test $res -eq 10 && exit 0
322 # Check for an error in the bisection process
323 test $res -ne 0 && exit $res
325 return 0
328 bisect_visualize() {
329 bisect_next_check fail
331 if test $# = 0
332 then
333 if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
334 type gitk >/dev/null 2>&1; then
335 set gitk
336 else
337 set git log
339 else
340 case "$1" in
341 git*|tig) ;;
342 -*) set git log "$@" ;;
343 *) set git "$@" ;;
344 esac
347 eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
350 bisect_reset() {
351 test -s "$GIT_DIR/BISECT_START" || {
352 gettext "We are not bisecting."; echo
353 return
355 case "$#" in
356 0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
357 1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || {
358 invalid="$1"
359 die "$(eval_gettext "'\$invalid' is not a valid commit")"
361 branch="$1" ;;
363 usage ;;
364 esac
365 if ! test -f "$GIT_DIR/BISECT_HEAD"
366 then
367 if ! git checkout "$branch" --
368 then
369 die "$(eval_gettext "Could not check out original HEAD '\$branch'.
370 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"; then
404 rev="$command"
405 command="$bisect"
407 case "$command" in
408 start)
409 cmd="bisect_start $rev"
410 eval "$cmd" ;;
411 good|bad|skip)
412 bisect_write "$command" "$rev" ;;
414 die "$(gettext "?? what are you talking about?")" ;;
415 esac
416 done <"$file"
417 bisect_auto_next
420 bisect_run () {
421 bisect_next_check fail
423 while true
425 command="$@"
426 eval_gettext "running \$command"; echo
427 "$@"
428 res=$?
430 # Check for really bad run error.
431 if [ $res -lt 0 -o $res -ge 128 ]; then
433 eval_gettext "bisect run failed:
434 exit code \$res from '\$command' is < 0 or >= 128" &&
435 echo
436 ) >&2
437 exit $res
440 # Find current state depending on run success or failure.
441 # A special exit code of 125 means cannot test.
442 if [ $res -eq 125 ]; then
443 state='skip'
444 elif [ $res -gt 0 ]; then
445 state='bad'
446 else
447 state='good'
450 # We have to use a subshell because "bisect_state" can exit.
451 ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
452 res=$?
454 cat "$GIT_DIR/BISECT_RUN"
456 if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
457 > /dev/null; then
459 gettext "bisect run cannot continue any more" &&
460 echo
461 ) >&2
462 exit $res
465 if [ $res -ne 0 ]; then
467 eval_gettext "bisect run failed:
468 'bisect_state \$state' exited with error code \$res" &&
469 echo
470 ) >&2
471 exit $res
474 if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
475 gettext "bisect run success"; echo
476 exit 0;
479 done
482 bisect_log () {
483 test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
484 cat "$GIT_DIR/BISECT_LOG"
487 case "$#" in
489 usage ;;
491 cmd="$1"
492 shift
493 case "$cmd" in
494 help)
495 git bisect -h ;;
496 start)
497 bisect_start "$@" ;;
498 bad|good)
499 bisect_state "$cmd" "$@" ;;
500 skip)
501 bisect_skip "$@" ;;
502 next)
503 # Not sure we want "next" at the UI level anymore.
504 bisect_next "$@" ;;
505 visualize|view)
506 bisect_visualize "$@" ;;
507 reset)
508 bisect_reset "$@" ;;
509 replay)
510 bisect_replay "$@" ;;
511 log)
512 bisect_log ;;
513 run)
514 bisect_run "$@" ;;
516 usage ;;
517 esac
518 esac