Merge branch 'maint'
[alt-git.git] / git-bisect.sh
blobb2186a86279e8919214bb205400a05f32a316c3b
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 [<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_autostart() {
38 test -s "$GIT_DIR/BISECT_START" || {
40 gettext "You need to start by \"git bisect start\"" &&
41 echo
42 ) >&2
43 if test -t 0
44 then
45 # TRANSLATORS: Make sure to include [Y] and [n] in your
46 # translation. The program will only accept English input
47 # at this point.
48 gettext "Do you want me to do it for you [Y/n]? " >&2
49 read yesno
50 case "$yesno" in
51 [Nn]*)
52 exit ;;
53 esac
54 bisect_start
55 else
56 exit 1
61 bisect_start() {
63 # Verify HEAD.
65 head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
66 head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
67 die "$(gettext "Bad HEAD - I need a HEAD")"
70 # Check if we are bisecting.
72 start_head=''
73 if test -s "$GIT_DIR/BISECT_START"
74 then
75 # Reset to the rev from where we started.
76 start_head=$(cat "$GIT_DIR/BISECT_START")
77 git checkout "$start_head" -- || exit
78 else
79 # Get rev from where we start.
80 case "$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/}"
90 die "$(gettext "Bad HEAD - strange symbolic ref")"
92 esac
96 # Get rid of any old bisect state.
98 bisect_clean_state || exit
101 # Check for one bad and then some good revisions.
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
114 shift
115 break
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
123 case $bad_seen in
124 0) state='bad' ; bad_seen=1 ;;
125 *) state='good' ;;
126 esac
127 eval="$eval bisect_write '$state' '$rev' 'nolog'; "
128 shift
130 esac
131 done
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".
140 trap 'bisect_clean_state' 0
141 trap 'exit 255' 1 2 3 15
144 # Write new start state.
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
151 # Check if we can proceed to the next bisect state.
153 bisect_auto_next
155 trap '-' 0
158 bisect_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"
172 is_expected_rev() {
173 test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
174 test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
177 check_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
184 done
187 bisect_skip() {
188 all=''
189 for arg in "$@"
191 case "$arg" in
192 *..*)
193 revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
195 revs=$(git rev-parse --sq-quote "$arg") ;;
196 esac
197 all="$all $revs"
198 done
199 eval bisect_state 'skip' $all
202 bisect_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 "$@"
218 sha=$(git rev-parse --verify "$rev^{commit}") ||
219 die "$(eval_gettext "Bad rev input: \$rev")"
220 eval="$eval bisect_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.")" ;;
227 usage ;;
228 esac
229 bisect_auto_next
232 bisect_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
237 case "$missing_good,$missing_bad,$1" in
238 ,,*)
239 : have both good and bad - ok
242 # do not have both but not asked to fail - just report.
243 false
245 t,,good)
246 # have bad but not good. we could bisect although
247 # this is less optimum.
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
261 : bisect without good...
265 if test -s "$GIT_DIR/BISECT_START"
266 then
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
274 gettext "You need to start by \"git bisect start\".
275 You 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
280 exit 1 ;;
281 esac
284 bisect_auto_next() {
285 bisect_next_check && bisect_next || :
288 bisect_next() {
289 case "$#" in 0) ;; *) usage ;; esac
290 bisect_autostart
291 bisect_next_check good
293 # Perform all bisection computation, display and checkout
294 git bisect--helper --next-all
295 res=$?
297 # Check if we should exit because bisection is finished
298 test $res -eq 10 && exit 0
300 # Check for an error in the bisection process
301 test $res -ne 0 && exit $res
303 return 0
306 bisect_visualize() {
307 bisect_next_check fail
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
317 else
318 case "$1" in
319 git*|tig) ;;
320 -*) set git log "$@" ;;
321 *) set git "$@" ;;
322 esac
325 eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
328 bisect_reset() {
329 test -s "$GIT_DIR/BISECT_START" || {
330 gettext "We are not bisecting."; echo
331 return
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")"
339 branch="$1" ;;
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'.
347 Try 'git bisect reset <commit>'.")"
351 bisect_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
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" &&
366 rm -f "$GIT_DIR/BISECT_START"
369 bisect_replay () {
370 file="$1"
371 test "$#" -eq 1 || die "$(gettext "No logfile given")"
372 test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
373 bisect_reset
374 while read git bisect command rev
376 test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
377 if test "$git" = "git-bisect"; then
378 rev="$command"
379 command="$bisect"
381 case "$command" in
382 start)
383 cmd="bisect_start $rev"
384 eval "$cmd" ;;
385 good|bad|skip)
386 bisect_write "$command" "$rev" ;;
388 die "$(gettext "?? what are you talking about?")" ;;
389 esac
390 done <"$file"
391 bisect_auto_next
394 bisect_run () {
395 bisect_next_check fail
397 while true
399 command="$@"
400 eval_gettext "running \$command"; echo
401 "$@"
402 res=$?
404 # Check for really bad run error.
405 if [ $res -lt 0 -o $res -ge 128 ]; then
407 eval_gettext "bisect run failed:
408 exit code \$res from '\$command' is < 0 or >= 128" &&
409 echo
410 ) >&2
411 exit $res
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'
424 # We have to use a subshell because "bisect_state" can exit.
425 ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
426 res=$?
428 cat "$GIT_DIR/BISECT_RUN"
430 if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
431 > /dev/null; then
433 gettext "bisect run cannot continue any more" &&
434 echo
435 ) >&2
436 exit $res
439 if [ $res -ne 0 ]; then
441 eval_gettext "bisect run failed:
442 'bisect_state \$state' exited with error code \$res" &&
443 echo
444 ) >&2
445 exit $res
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;
453 done
456 bisect_log () {
457 test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
458 cat "$GIT_DIR/BISECT_LOG"
461 case "$#" in
463 usage ;;
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 "$@" ;;
490 usage ;;
491 esac
492 esac