Interested in helping open source friends on HP-UX?
[git/mjg.git] / git-bisect.sh
blobae3fec22c48a296cdc1962def9030aa5bb7ce68a
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" || {
48 gettextln "You need to start by \"git bisect start\"" >&2
49 if test -t 0
50 then
51 # TRANSLATORS: Make sure to include [Y] and [n] in your
52 # translation. The program will only accept English input
53 # at this point.
54 gettext "Do you want me to do it for you [Y/n]? " >&2
55 read yesno
56 case "$yesno" in
57 [Nn]*)
58 exit ;;
59 esac
60 bisect_start
61 else
62 exit 1
67 bisect_start() {
69 # Check for one bad and then some good revisions.
71 has_double_dash=0
72 for arg; do
73 case "$arg" in --) has_double_dash=1; break ;; esac
74 done
75 orig_args=$(git rev-parse --sq-quote "$@")
76 bad_seen=0
77 eval=''
78 if test "z$(git rev-parse --is-bare-repository)" != zfalse
79 then
80 mode=--no-checkout
81 else
82 mode=''
84 while [ $# -gt 0 ]; do
85 arg="$1"
86 case "$arg" in
87 --)
88 shift
89 break
91 --no-checkout)
92 mode=--no-checkout
93 shift ;;
94 --*)
95 die "$(eval_gettext "unrecognised option: '\$arg'")" ;;
97 rev=$(git rev-parse -q --verify "$arg^{commit}") || {
98 test $has_double_dash -eq 1 &&
99 die "$(eval_gettext "'\$arg' does not appear to be a valid revision")"
100 break
102 case $bad_seen in
103 0) state='bad' ; bad_seen=1 ;;
104 *) state='good' ;;
105 esac
106 eval="$eval bisect_write '$state' '$rev' 'nolog' &&"
107 shift
109 esac
110 done
113 # Verify HEAD.
115 head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
116 head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
117 die "$(gettext "Bad HEAD - I need a HEAD")"
120 # Check if we are bisecting.
122 start_head=''
123 if test -s "$GIT_DIR/BISECT_START"
124 then
125 # Reset to the rev from where we started.
126 start_head=$(cat "$GIT_DIR/BISECT_START")
127 if test "z$mode" != "z--no-checkout"
128 then
129 git checkout "$start_head" -- ||
130 die "$(eval_gettext "Checking out '\$start_head' failed. Try 'git bisect reset <valid-branch>'.")"
132 else
133 # Get rev from where we start.
134 case "$head" in
135 refs/heads/*|$_x40)
136 # This error message should only be triggered by
137 # cogito usage, and cogito users should understand
138 # it relates to cg-seek.
139 [ -s "$GIT_DIR/head-name" ] &&
140 die "$(gettext "won't bisect on cg-seek'ed tree")"
141 start_head="${head#refs/heads/}"
144 die "$(gettext "Bad HEAD - strange symbolic ref")"
146 esac
150 # Get rid of any old bisect state.
152 bisect_clean_state || exit
155 # Change state.
156 # In case of mistaken revs or checkout error, or signals received,
157 # "bisect_auto_next" below may exit or misbehave.
158 # We have to trap this to be able to clean up using
159 # "bisect_clean_state".
161 trap 'bisect_clean_state' 0
162 trap 'exit 255' 1 2 3 15
165 # Write new start state.
167 echo "$start_head" >"$GIT_DIR/BISECT_START" && {
168 test "z$mode" != "z--no-checkout" ||
169 git update-ref --no-deref BISECT_HEAD "$start_head"
170 } &&
171 git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
172 eval "$eval true" &&
173 echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
175 # Check if we can proceed to the next bisect state.
177 bisect_auto_next
179 trap '-' 0
182 bisect_write() {
183 state="$1"
184 rev="$2"
185 nolog="$3"
186 case "$state" in
187 bad) tag="$state" ;;
188 good|skip) tag="$state"-"$rev" ;;
189 *) die "$(eval_gettext "Bad bisect_write argument: \$state")" ;;
190 esac
191 git update-ref "refs/bisect/$tag" "$rev" || exit
192 echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG"
193 test -n "$nolog" || echo "git bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
196 is_expected_rev() {
197 test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
198 test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
201 check_expected_revs() {
202 for _rev in "$@"; do
203 if ! is_expected_rev "$_rev"
204 then
205 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
206 rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
207 return
209 done
212 bisect_skip() {
213 all=''
214 for arg in "$@"
216 case "$arg" in
217 *..*)
218 revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
220 revs=$(git rev-parse --sq-quote "$arg") ;;
221 esac
222 all="$all $revs"
223 done
224 eval bisect_state 'skip' $all
227 bisect_state() {
228 bisect_autostart
229 state=$1
230 case "$#,$state" in
231 0,*)
232 die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
233 1,bad|1,good|1,skip)
234 rev=$(git rev-parse --verify $(bisect_head)) ||
235 die "$(gettext "Bad rev input: $(bisect_head)")"
236 bisect_write "$state" "$rev"
237 check_expected_revs "$rev" ;;
238 2,bad|*,good|*,skip)
239 shift
240 hash_list=''
241 for rev in "$@"
243 sha=$(git rev-parse --verify "$rev^{commit}") ||
244 die "$(eval_gettext "Bad rev input: \$rev")"
245 hash_list="$hash_list $sha"
246 done
247 for rev in $hash_list
249 bisect_write "$state" "$rev"
250 done
251 check_expected_revs $hash_list ;;
252 *,bad)
253 die "$(gettext "'git bisect bad' can take only one argument.")" ;;
255 usage ;;
256 esac
257 bisect_auto_next
260 bisect_next_check() {
261 missing_good= missing_bad=
262 git show-ref -q --verify refs/bisect/bad || missing_bad=t
263 test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
265 case "$missing_good,$missing_bad,$1" in
266 ,,*)
267 : have both good and bad - ok
270 # do not have both but not asked to fail - just report.
271 false
273 t,,good)
274 # have bad but not good. we could bisect although
275 # this is less optimum.
276 gettextln "Warning: bisecting only with a bad commit." >&2
277 if test -t 0
278 then
279 # TRANSLATORS: Make sure to include [Y] and [n] in your
280 # translation. The program will only accept English input
281 # at this point.
282 gettext "Are you sure [Y/n]? " >&2
283 read yesno
284 case "$yesno" in [Nn]*) exit 1 ;; esac
286 : bisect without good...
290 if test -s "$GIT_DIR/BISECT_START"
291 then
292 gettextln "You need to give me at least one good and one bad revision.
293 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
294 else
295 gettextln "You need to start by \"git bisect start\".
296 You then need to give me at least one good and one bad revision.
297 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
299 exit 1 ;;
300 esac
303 bisect_auto_next() {
304 bisect_next_check && bisect_next || :
307 bisect_next() {
308 case "$#" in 0) ;; *) usage ;; esac
309 bisect_autostart
310 bisect_next_check good
312 # Perform all bisection computation, display and checkout
313 git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
314 res=$?
316 # Check if we should exit because bisection is finished
317 if test $res -eq 10
318 then
319 bad_rev=$(git show-ref --hash --verify refs/bisect/bad)
320 bad_commit=$(git show-branch $bad_rev)
321 echo "# first bad commit: $bad_commit" >>"$GIT_DIR/BISECT_LOG"
322 exit 0
323 elif test $res -eq 2
324 then
325 echo "# only skipped commits left to test" >>"$GIT_DIR/BISECT_LOG"
326 good_revs=$(git for-each-ref --format="%(objectname)" "refs/bisect/good-*")
327 for skipped in $(git rev-list refs/bisect/bad --not $good_revs)
329 skipped_commit=$(git show-branch $skipped)
330 echo "# possible first bad commit: $skipped_commit" >>"$GIT_DIR/BISECT_LOG"
331 done
332 exit $res
335 # Check for an error in the bisection process
336 test $res -ne 0 && exit $res
338 return 0
341 bisect_visualize() {
342 bisect_next_check fail
344 if test $# = 0
345 then
346 if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
347 type gitk >/dev/null 2>&1
348 then
349 set gitk
350 else
351 set git log
353 else
354 case "$1" in
355 git*|tig) ;;
356 -*) set git log "$@" ;;
357 *) set git "$@" ;;
358 esac
361 eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
364 bisect_reset() {
365 test -s "$GIT_DIR/BISECT_START" || {
366 gettextln "We are not bisecting."
367 return
369 case "$#" in
370 0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
371 1) git rev-parse --quiet --verify "$1^{commit}" >/dev/null || {
372 invalid="$1"
373 die "$(eval_gettext "'\$invalid' is not a valid commit")"
375 branch="$1" ;;
377 usage ;;
378 esac
380 if ! test -f "$GIT_DIR/BISECT_HEAD" && ! git checkout "$branch" --
381 then
382 die "$(eval_gettext "Could not check out original HEAD '\$branch'.
383 Try 'git bisect reset <commit>'.")"
385 bisect_clean_state
388 bisect_clean_state() {
389 # There may be some refs packed during bisection.
390 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
391 while read ref hash
393 git update-ref -d $ref $hash || exit
394 done
395 rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
396 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
397 rm -f "$GIT_DIR/BISECT_LOG" &&
398 rm -f "$GIT_DIR/BISECT_NAMES" &&
399 rm -f "$GIT_DIR/BISECT_RUN" &&
400 # Cleanup head-name if it got left by an old version of git-bisect
401 rm -f "$GIT_DIR/head-name" &&
402 git update-ref -d --no-deref BISECT_HEAD &&
403 # clean up BISECT_START last
404 rm -f "$GIT_DIR/BISECT_START"
407 bisect_replay () {
408 file="$1"
409 test "$#" -eq 1 || die "$(gettext "No logfile given")"
410 test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
411 bisect_reset
412 while read git bisect command rev
414 test "$git $bisect" = "git bisect" || test "$git" = "git-bisect" || continue
415 if test "$git" = "git-bisect"
416 then
417 rev="$command"
418 command="$bisect"
420 case "$command" in
421 start)
422 cmd="bisect_start $rev"
423 eval "$cmd" ;;
424 good|bad|skip)
425 bisect_write "$command" "$rev" ;;
427 die "$(gettext "?? what are you talking about?")" ;;
428 esac
429 done <"$file"
430 bisect_auto_next
433 bisect_run () {
434 bisect_next_check fail
436 while true
438 command="$@"
439 eval_gettextln "running \$command"
440 "$@"
441 res=$?
443 # Check for really bad run error.
444 if [ $res -lt 0 -o $res -ge 128 ]
445 then
446 eval_gettextln "bisect run failed:
447 exit code \$res from '\$command' is < 0 or >= 128" >&2
448 exit $res
451 # Find current state depending on run success or failure.
452 # A special exit code of 125 means cannot test.
453 if [ $res -eq 125 ]
454 then
455 state='skip'
456 elif [ $res -gt 0 ]
457 then
458 state='bad'
459 else
460 state='good'
463 # We have to use a subshell because "bisect_state" can exit.
464 ( bisect_state $state >"$GIT_DIR/BISECT_RUN" )
465 res=$?
467 cat "$GIT_DIR/BISECT_RUN"
469 if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
470 >/dev/null
471 then
472 gettextln "bisect run cannot continue any more" >&2
473 exit $res
476 if [ $res -ne 0 ]
477 then
478 eval_gettextln "bisect run failed:
479 'bisect_state \$state' exited with error code \$res" >&2
480 exit $res
483 if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" >/dev/null
484 then
485 gettextln "bisect run success"
486 exit 0;
489 done
492 bisect_log () {
493 test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
494 cat "$GIT_DIR/BISECT_LOG"
497 case "$#" in
499 usage ;;
501 cmd="$1"
502 shift
503 case "$cmd" in
504 help)
505 git bisect -h ;;
506 start)
507 bisect_start "$@" ;;
508 bad|good)
509 bisect_state "$cmd" "$@" ;;
510 skip)
511 bisect_skip "$@" ;;
512 next)
513 # Not sure we want "next" at the UI level anymore.
514 bisect_next "$@" ;;
515 visualize|view)
516 bisect_visualize "$@" ;;
517 reset)
518 bisect_reset "$@" ;;
519 replay)
520 bisect_replay "$@" ;;
521 log)
522 bisect_log ;;
523 run)
524 bisect_run "$@" ;;
526 usage ;;
527 esac
528 esac