Merge branch 'jc/merge-reword'
[git/jrn.git] / git-bisect.sh
blobe0ca3fb853083d0ebdfe11e5eb3fead4723248ad
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
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"
36 bisect_head()
38 if test -f "$GIT_DIR/BISECT_HEAD"
39 then
40 echo BISECT_HEAD
41 else
42 echo HEAD
46 bisect_autostart() {
47 test -s "$GIT_DIR/BISECT_START" || {
49 gettext "You need to start by \"git bisect start\"" &&
50 echo
51 ) >&2
52 if test -t 0
53 then
54 # TRANSLATORS: Make sure to include [Y] and [n] in your
55 # translation. The program will only accept English input
56 # at this point.
57 gettext "Do you want me to do it for you [Y/n]? " >&2
58 read yesno
59 case "$yesno" in
60 [Nn]*)
61 exit ;;
62 esac
63 bisect_start
64 else
65 exit 1
70 bisect_start() {
72 # Check for one bad and then some good revisions.
74 has_double_dash=0
75 for arg; do
76 case "$arg" in --) has_double_dash=1; break ;; esac
77 done
78 orig_args=$(git rev-parse --sq-quote "$@")
79 bad_seen=0
80 eval=''
81 if test "z$(git rev-parse --is-bare-repository)" != zfalse
82 then
83 mode=--no-checkout
84 else
85 mode=''
87 while [ $# -gt 0 ]; do
88 arg="$1"
89 case "$arg" in
90 --)
91 shift
92 break
94 --no-checkout)
95 mode=--no-checkout
96 shift ;;
97 --*)
98 die "$(eval_gettext "unrecognised option: '\$arg'")" ;;
100 rev=$(git rev-parse -q --verify "$arg^{commit}") || {
101 test $has_double_dash -eq 1 &&
102 die "$(eval_gettext "'\$arg' does not appear to be a valid revision")"
103 break
105 case $bad_seen in
106 0) state='bad' ; bad_seen=1 ;;
107 *) state='good' ;;
108 esac
109 eval="$eval bisect_write '$state' '$rev' 'nolog' &&"
110 shift
112 esac
113 done
116 # Verify HEAD.
118 head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
119 head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
120 die "$(gettext "Bad HEAD - I need a HEAD")"
123 # Check if we are bisecting.
125 start_head=''
126 if test -s "$GIT_DIR/BISECT_START"
127 then
128 # Reset to the rev from where we started.
129 start_head=$(cat "$GIT_DIR/BISECT_START")
130 if test "z$mode" != "z--no-checkout"
131 then
132 git checkout "$start_head" --
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" ] &&
142 die "$(gettext "won't bisect on seeked tree")"
143 start_head="${head#refs/heads/}"
146 die "$(gettext "Bad HEAD - strange symbolic ref")"
148 esac
152 # Get rid of any old bisect state.
154 bisect_clean_state || exit
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".
163 trap 'bisect_clean_state' 0
164 trap 'exit 255' 1 2 3 15
167 # Write new start state.
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 } &&
173 git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
174 eval "$eval true" &&
175 echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
177 # Check if we can proceed to the next bisect state.
179 bisect_auto_next
181 trap '-' 0
184 bisect_write() {
185 state="$1"
186 rev="$2"
187 nolog="$3"
188 case "$state" in
189 bad) tag="$state" ;;
190 good|skip) tag="$state"-"$rev" ;;
191 *) die "$(eval_gettext "Bad bisect_write argument: \$state")" ;;
192 esac
193 git update-ref "refs/bisect/$tag" "$rev" || exit
194 echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG"
195 test -n "$nolog" || echo "git bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
198 is_expected_rev() {
199 test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
200 test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
203 check_expected_revs() {
204 for _rev in "$@"; do
205 if ! is_expected_rev "$_rev"
206 then
207 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
208 rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
209 return
211 done
214 bisect_skip() {
215 all=''
216 for arg in "$@"
218 case "$arg" in
219 *..*)
220 revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
222 revs=$(git rev-parse --sq-quote "$arg") ;;
223 esac
224 all="$all $revs"
225 done
226 eval bisect_state 'skip' $all
229 bisect_state() {
230 bisect_autostart
231 state=$1
232 case "$#,$state" in
233 0,*)
234 die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
235 1,bad|1,good|1,skip)
236 rev=$(git rev-parse --verify $(bisect_head)) ||
237 die "$(gettext "Bad rev input: $(bisect_head)")"
238 bisect_write "$state" "$rev"
239 check_expected_revs "$rev" ;;
240 2,bad|*,good|*,skip)
241 shift
242 eval=''
243 for rev in "$@"
245 sha=$(git rev-parse --verify "$rev^{commit}") ||
246 die "$(eval_gettext "Bad rev input: \$rev")"
247 eval="$eval bisect_write '$state' '$sha'; "
248 done
249 eval "$eval"
250 check_expected_revs "$@" ;;
251 *,bad)
252 die "$(gettext "'git bisect bad' can take only one argument.")" ;;
254 usage ;;
255 esac
256 bisect_auto_next
259 bisect_next_check() {
260 missing_good= missing_bad=
261 git show-ref -q --verify refs/bisect/bad || missing_bad=t
262 test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
264 case "$missing_good,$missing_bad,$1" in
265 ,,*)
266 : have both good and bad - ok
269 # do not have both but not asked to fail - just report.
270 false
272 t,,good)
273 # have bad but not good. we could bisect although
274 # this is less optimum.
276 gettext "Warning: bisecting only with a bad commit." &&
277 echo
278 ) >&2
279 if test -t 0
280 then
281 # TRANSLATORS: Make sure to include [Y] and [n] in your
282 # translation. The program will only accept English input
283 # at this point.
284 gettext "Are you sure [Y/n]? " >&2
285 read yesno
286 case "$yesno" in [Nn]*) exit 1 ;; esac
288 : bisect without good...
292 if test -s "$GIT_DIR/BISECT_START"
293 then
295 gettext "You need to give me at least one good and one bad revisions.
296 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" &&
297 echo
298 ) >&2
299 else
301 gettext "You need to start by \"git bisect start\".
302 You then need to give me at least one good and one bad revisions.
303 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" &&
304 echo
305 ) >&2
307 exit 1 ;;
308 esac
311 bisect_auto_next() {
312 bisect_next_check && bisect_next || :
315 bisect_next() {
316 case "$#" in 0) ;; *) usage ;; esac
317 bisect_autostart
318 bisect_next_check good
320 # Perform all bisection computation, display and checkout
321 git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
322 res=$?
324 # Check if we should exit because bisection is finished
325 test $res -eq 10 && exit 0
327 # Check for an error in the bisection process
328 test $res -ne 0 && exit $res
330 return 0
333 bisect_visualize() {
334 bisect_next_check fail
336 if test $# = 0
337 then
338 if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
339 type gitk >/dev/null 2>&1
340 then
341 set gitk
342 else
343 set git log
345 else
346 case "$1" in
347 git*|tig) ;;
348 -*) set git log "$@" ;;
349 *) set git "$@" ;;
350 esac
353 eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
356 bisect_reset() {
357 test -s "$GIT_DIR/BISECT_START" || {
358 gettext "We are not bisecting."; echo
359 return
361 case "$#" in
362 0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
363 1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || {
364 invalid="$1"
365 die "$(eval_gettext "'\$invalid' is not a valid commit")"
367 branch="$1" ;;
369 usage ;;
370 esac
372 if ! test -f "$GIT_DIR/BISECT_HEAD" && ! git checkout "$branch" --
373 then
374 die "$(eval_gettext "Could not check out original HEAD '\$branch'.
375 Try 'git bisect reset <commit>'.")"
377 bisect_clean_state
380 bisect_clean_state() {
381 # There may be some refs packed during bisection.
382 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
383 while read ref hash
385 git update-ref -d $ref $hash || exit
386 done
387 rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
388 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
389 rm -f "$GIT_DIR/BISECT_LOG" &&
390 rm -f "$GIT_DIR/BISECT_NAMES" &&
391 rm -f "$GIT_DIR/BISECT_RUN" &&
392 # Cleanup head-name if it got left by an old version of git-bisect
393 rm -f "$GIT_DIR/head-name" &&
394 git update-ref -d --no-deref BISECT_HEAD &&
395 # clean up BISECT_START last
396 rm -f "$GIT_DIR/BISECT_START"
399 bisect_replay () {
400 file="$1"
401 test "$#" -eq 1 || die "$(gettext "No logfile given")"
402 test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
403 bisect_reset
404 while read git bisect command rev
406 test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
407 if test "$git" = "git-bisect"
408 then
409 rev="$command"
410 command="$bisect"
412 case "$command" in
413 start)
414 cmd="bisect_start $rev"
415 eval "$cmd" ;;
416 good|bad|skip)
417 bisect_write "$command" "$rev" ;;
419 die "$(gettext "?? what are you talking about?")" ;;
420 esac
421 done <"$file"
422 bisect_auto_next
425 bisect_run () {
426 bisect_next_check fail
428 while true
430 command="$@"
431 eval_gettext "running \$command"; echo
432 "$@"
433 res=$?
435 # Check for really bad run error.
436 if [ $res -lt 0 -o $res -ge 128 ]
437 then
439 eval_gettext "bisect run failed:
440 exit code \$res from '\$command' is < 0 or >= 128" &&
441 echo
442 ) >&2
443 exit $res
446 # Find current state depending on run success or failure.
447 # A special exit code of 125 means cannot test.
448 if [ $res -eq 125 ]
449 then
450 state='skip'
451 elif [ $res -gt 0 ]
452 then
453 state='bad'
454 else
455 state='good'
458 # We have to use a subshell because "bisect_state" can exit.
459 ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
460 res=$?
462 cat "$GIT_DIR/BISECT_RUN"
464 if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
465 > /dev/null
466 then
468 gettext "bisect run cannot continue any more" &&
469 echo
470 ) >&2
471 exit $res
474 if [ $res -ne 0 ]
475 then
477 eval_gettext "bisect run failed:
478 'bisect_state \$state' exited with error code \$res" &&
479 echo
480 ) >&2
481 exit $res
484 if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null
485 then
486 gettext "bisect run success"; echo
487 exit 0;
490 done
493 bisect_log () {
494 test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
495 cat "$GIT_DIR/BISECT_LOG"
498 case "$#" in
500 usage ;;
502 cmd="$1"
503 shift
504 case "$cmd" in
505 help)
506 git bisect -h ;;
507 start)
508 bisect_start "$@" ;;
509 bad|good)
510 bisect_state "$cmd" "$@" ;;
511 skip)
512 bisect_skip "$@" ;;
513 next)
514 # Not sure we want "next" at the UI level anymore.
515 bisect_next "$@" ;;
516 visualize|view)
517 bisect_visualize "$@" ;;
518 reset)
519 bisect_reset "$@" ;;
520 replay)
521 bisect_replay "$@" ;;
522 log)
523 bisect_log ;;
524 run)
525 bisect_run "$@" ;;
527 usage ;;
528 esac
529 esac