FIXME: hotpatch for compatibility with latest hg
[git] / git-bisect.sh
1 #!/bin/sh
2
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.
26
27 Please use "git help bisect" to get the full man page.'
28
29 OPTIONS_SPEC=
30 . git-sh-setup
31 . git-sh-i18n
32
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"
35
36 bisect_head()
37 {
38         if test -f "$GIT_DIR/BISECT_HEAD"
39         then
40                 echo BISECT_HEAD
41         else
42                 echo HEAD
43         fi
44 }
45
46 bisect_autostart() {
47         test -s "$GIT_DIR/BISECT_START" || {
48                 (
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
66                 fi
67         }
68 }
69
70 bisect_start() {
71         #
72         # Check for one bad and then some good revisions.
73         #
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=''
86         fi
87         while [ $# -gt 0 ]; do
88                 arg="$1"
89                 case "$arg" in
90                 --)
91                         shift
92                         break
93                 ;;
94                 --no-checkout)
95                         mode=--no-checkout
96                         shift ;;
97                 --*)
98                         die "$(eval_gettext "unrecognised option: '\$arg'")" ;;
99                 *)
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
104                         }
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
111                         ;;
112                 esac
113         done
114
115         #
116         # Verify HEAD.
117         #
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")"
121
122         #
123         # Check if we are bisecting.
124         #
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" --
133                 fi
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/}"
144                         ;;
145                 *)
146                         die "$(gettext "Bad HEAD - strange symbolic ref")"
147                         ;;
148                 esac
149         fi
150
151         #
152         # Get rid of any old bisect state.
153         #
154         bisect_clean_state || exit
155
156         #
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".
162         #
163         trap 'bisect_clean_state' 0
164         trap 'exit 255' 1 2 3 15
165
166         #
167         # Write new start state.
168         #
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
176         #
177         # Check if we can proceed to the next bisect state.
178         #
179         bisect_auto_next
180
181         trap '-' 0
182 }
183
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"
196 }
197
198 is_expected_rev() {
199         test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
200         test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
201 }
202
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
210                 fi
211         done
212 }
213
214 bisect_skip() {
215         all=''
216         for arg in "$@"
217         do
218                 case "$arg" in
219                 *..*)
220                         revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
221                 *)
222                         revs=$(git rev-parse --sq-quote "$arg") ;;
223                 esac
224                 all="$all $revs"
225         done
226         eval bisect_state 'skip' $all
227 }
228
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 "$@"
244                 do
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.")" ;;
253         *)
254                 usage ;;
255         esac
256         bisect_auto_next
257 }
258
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
263
264         case "$missing_good,$missing_bad,$1" in
265         ,,*)
266                 : have both good and bad - ok
267                 ;;
268         *,)
269                 # do not have both but not asked to fail - just report.
270                 false
271                 ;;
272         t,,good)
273                 # have bad but not good.  we could bisect although
274                 # this is less optimum.
275                 (
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
287                 fi
288                 : bisect without good...
289                 ;;
290         *)
291
292                 if test -s "$GIT_DIR/BISECT_START"
293                 then
294                         (
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
300                         (
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
306                 fi
307                 exit 1 ;;
308         esac
309 }
310
311 bisect_auto_next() {
312         bisect_next_check && bisect_next || :
313 }
314
315 bisect_next() {
316         case "$#" in 0) ;; *) usage ;; esac
317         bisect_autostart
318         bisect_next_check good
319
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=$?
323
324         # Check if we should exit because bisection is finished
325         test $res -eq 10 && exit 0
326
327         # Check for an error in the bisection process
328         test $res -ne 0 && exit $res
329
330         return 0
331 }
332
333 bisect_visualize() {
334         bisect_next_check fail
335
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
344                 fi
345         else
346                 case "$1" in
347                 git*|tig) ;;
348                 -*)     set git log "$@" ;;
349                 *)      set git "$@" ;;
350                 esac
351         fi
352
353         eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
354 }
355
356 bisect_reset() {
357         test -s "$GIT_DIR/BISECT_START" || {
358                 gettext "We are not bisecting."; echo
359                 return
360         }
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")"
366                 }
367                 branch="$1" ;;
368         *)
369                 usage ;;
370         esac
371
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>'.")"
376         fi
377         bisect_clean_state
378 }
379
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
384         do
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"
397 }
398
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
405         do
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"
411                 fi
412                 case "$command" in
413                 start)
414                         cmd="bisect_start $rev"
415                         eval "$cmd" ;;
416                 good|bad|skip)
417                         bisect_write "$command" "$rev" ;;
418                 *)
419                         die "$(gettext "?? what are you talking about?")" ;;
420                 esac
421         done <"$file"
422         bisect_auto_next
423 }
424
425 bisect_run () {
426         bisect_next_check fail
427
428         while true
429         do
430                 command="$@"
431                 eval_gettext "running \$command"; echo
432                 "$@"
433                 res=$?
434
435                 # Check for really bad run error.
436                 if [ $res -lt 0 -o $res -ge 128 ]
437                 then
438                         (
439                                 eval_gettext "bisect run failed:
440 exit code \$res from '\$command' is < 0 or >= 128" &&
441                                 echo
442                         ) >&2
443                         exit $res
444                 fi
445
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'
456                 fi
457
458                 # We have to use a subshell because "bisect_state" can exit.
459                 ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
460                 res=$?
461
462                 cat "$GIT_DIR/BISECT_RUN"
463
464                 if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
465                         > /dev/null
466                 then
467                         (
468                                 gettext "bisect run cannot continue any more" &&
469                                 echo
470                         ) >&2
471                         exit $res
472                 fi
473
474                 if [ $res -ne 0 ]
475                 then
476                         (
477                                 eval_gettext "bisect run failed:
478 'bisect_state \$state' exited with error code \$res" &&
479                                 echo
480                         ) >&2
481                         exit $res
482                 fi
483
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;
488                 fi
489
490         done
491 }
492
493 bisect_log () {
494         test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
495         cat "$GIT_DIR/BISECT_LOG"
496 }
497
498 case "$#" in
499 0)
500         usage ;;
501 *)
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 "$@" ;;
526         *)
527                 usage ;;
528         esac
529 esac