commit: search author pattern against mailmap
[git/raj.git] / git-bisect.sh
blob9f064b6f4f0c209a0315538e48049c2d013316df
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 <validbranch>'.")"
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 seeked 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 eval=''
241 for rev in "$@"
243 sha=$(git rev-parse --verify "$rev^{commit}") ||
244 die "$(eval_gettext "Bad rev input: \$rev")"
245 eval="$eval bisect_write '$state' '$sha'; "
246 done
247 eval "$eval"
248 check_expected_revs "$@" ;;
249 *,bad)
250 die "$(gettext "'git bisect bad' can take only one argument.")" ;;
252 usage ;;
253 esac
254 bisect_auto_next
257 bisect_next_check() {
258 missing_good= missing_bad=
259 git show-ref -q --verify refs/bisect/bad || missing_bad=t
260 test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
262 case "$missing_good,$missing_bad,$1" in
263 ,,*)
264 : have both good and bad - ok
267 # do not have both but not asked to fail - just report.
268 false
270 t,,good)
271 # have bad but not good. we could bisect although
272 # this is less optimum.
273 gettextln "Warning: bisecting only with a bad commit." >&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
289 gettextln "You need to give me at least one good and one bad revisions.
290 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
291 else
292 gettextln "You need to start by \"git bisect start\".
293 You then need to give me at least one good and one bad revisions.
294 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
296 exit 1 ;;
297 esac
300 bisect_auto_next() {
301 bisect_next_check && bisect_next || :
304 bisect_next() {
305 case "$#" in 0) ;; *) usage ;; esac
306 bisect_autostart
307 bisect_next_check good
309 # Perform all bisection computation, display and checkout
310 git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
311 res=$?
313 # Check if we should exit because bisection is finished
314 if test $res -eq 10
315 then
316 bad_rev=$(git show-ref --hash --verify refs/bisect/bad)
317 bad_commit=$(git show-branch $bad_rev)
318 echo "# first bad commit: $bad_commit" >>"$GIT_DIR/BISECT_LOG"
319 exit 0
320 elif test $res -eq 2
321 then
322 echo "# only skipped commits left to test" >>"$GIT_DIR/BISECT_LOG"
323 good_revs=$(git for-each-ref --format="%(objectname)" "refs/bisect/good-*")
324 for skipped in $(git rev-list refs/bisect/bad --not $good_revs)
326 skipped_commit=$(git show-branch $skipped)
327 echo "# possible first bad commit: $skipped_commit" >>"$GIT_DIR/BISECT_LOG"
328 done
329 exit $res
332 # Check for an error in the bisection process
333 test $res -ne 0 && exit $res
335 return 0
338 bisect_visualize() {
339 bisect_next_check fail
341 if test $# = 0
342 then
343 if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
344 type gitk >/dev/null 2>&1
345 then
346 set gitk
347 else
348 set git log
350 else
351 case "$1" in
352 git*|tig) ;;
353 -*) set git log "$@" ;;
354 *) set git "$@" ;;
355 esac
358 eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
361 bisect_reset() {
362 test -s "$GIT_DIR/BISECT_START" || {
363 gettextln "We are not bisecting."
364 return
366 case "$#" in
367 0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
368 1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || {
369 invalid="$1"
370 die "$(eval_gettext "'\$invalid' is not a valid commit")"
372 branch="$1" ;;
374 usage ;;
375 esac
377 if ! test -f "$GIT_DIR/BISECT_HEAD" && ! git checkout "$branch" --
378 then
379 die "$(eval_gettext "Could not check out original HEAD '\$branch'.
380 Try 'git bisect reset <commit>'.")"
382 bisect_clean_state
385 bisect_clean_state() {
386 # There may be some refs packed during bisection.
387 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
388 while read ref hash
390 git update-ref -d $ref $hash || exit
391 done
392 rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
393 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
394 rm -f "$GIT_DIR/BISECT_LOG" &&
395 rm -f "$GIT_DIR/BISECT_NAMES" &&
396 rm -f "$GIT_DIR/BISECT_RUN" &&
397 # Cleanup head-name if it got left by an old version of git-bisect
398 rm -f "$GIT_DIR/head-name" &&
399 git update-ref -d --no-deref BISECT_HEAD &&
400 # clean up BISECT_START last
401 rm -f "$GIT_DIR/BISECT_START"
404 bisect_replay () {
405 file="$1"
406 test "$#" -eq 1 || die "$(gettext "No logfile given")"
407 test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
408 bisect_reset
409 while read git bisect command rev
411 test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
412 if test "$git" = "git-bisect"
413 then
414 rev="$command"
415 command="$bisect"
417 case "$command" in
418 start)
419 cmd="bisect_start $rev"
420 eval "$cmd" ;;
421 good|bad|skip)
422 bisect_write "$command" "$rev" ;;
424 die "$(gettext "?? what are you talking about?")" ;;
425 esac
426 done <"$file"
427 bisect_auto_next
430 bisect_run () {
431 bisect_next_check fail
433 while true
435 command="$@"
436 eval_gettextln "running \$command"
437 "$@"
438 res=$?
440 # Check for really bad run error.
441 if [ $res -lt 0 -o $res -ge 128 ]
442 then
443 eval_gettextln "bisect run failed:
444 exit code \$res from '\$command' is < 0 or >= 128" >&2
445 exit $res
448 # Find current state depending on run success or failure.
449 # A special exit code of 125 means cannot test.
450 if [ $res -eq 125 ]
451 then
452 state='skip'
453 elif [ $res -gt 0 ]
454 then
455 state='bad'
456 else
457 state='good'
460 # We have to use a subshell because "bisect_state" can exit.
461 ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
462 res=$?
464 cat "$GIT_DIR/BISECT_RUN"
466 if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
467 > /dev/null
468 then
469 gettextln "bisect run cannot continue any more" >&2
470 exit $res
473 if [ $res -ne 0 ]
474 then
475 eval_gettextln "bisect run failed:
476 'bisect_state \$state' exited with error code \$res" >&2
477 exit $res
480 if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null
481 then
482 gettextln "bisect run success"
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