Code

bisect: replace "; then" with "\n<tab>*then"
[git.git] / git-bisect.sh
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
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_head()
38 {
39         if test -f "$GIT_DIR/BISECT_HEAD"
40         then
41                 echo BISECT_HEAD
42         else
43                 echo HEAD
44         fi
45 }
47 bisect_autostart() {
48         test -s "$GIT_DIR/BISECT_START" || {
49                 (
50                         gettext "You need to start by \"git bisect start\"" &&
51                         echo
52                 ) >&2
53                 if test -t 0
54                 then
55                         # TRANSLATORS: Make sure to include [Y] and [n] in your
56                         # translation. The program will only accept English input
57                         # at this point.
58                         gettext "Do you want me to do it for you [Y/n]? " >&2
59                         read yesno
60                         case "$yesno" in
61                         [Nn]*)
62                                 exit ;;
63                         esac
64                         bisect_start
65                 else
66                         exit 1
67                 fi
68         }
69 }
71 bisect_start() {
72         #
73         # Check for one bad and then some good revisions.
74         #
75         has_double_dash=0
76         for arg; do
77                 case "$arg" in --) has_double_dash=1; break ;; esac
78         done
79         orig_args=$(git rev-parse --sq-quote "$@")
80         bad_seen=0
81         eval=''
82         mode=''
83         while [ $# -gt 0 ]; do
84                 arg="$1"
85                 case "$arg" in
86                 --)
87                         shift
88                         break
89                 ;;
90                 --no-checkout)
91                         mode=--no-checkout
92                         shift ;;
93                 --*)
94                         die "$(eval_gettext "unrecognised option: '\$arg'")" ;;
95                 *)
96                         rev=$(git rev-parse -q --verify "$arg^{commit}") || {
97                             test $has_double_dash -eq 1 &&
98                         die "$(eval_gettext "'\$arg' does not appear to be a valid revision")"
99                             break
100                         }
101                         case $bad_seen in
102                         0) state='bad' ; bad_seen=1 ;;
103                         *) state='good' ;;
104                         esac
105                         eval="$eval bisect_write '$state' '$rev' 'nolog' &&"
106                         shift
107                         ;;
108                 esac
109         done
111         #
112         # Verify HEAD.
113         #
114         head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
115         head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
116         die "$(gettext "Bad HEAD - I need a HEAD")"
118         #
119         # Check if we are bisecting.
120         #
121         start_head=''
122         if test -s "$GIT_DIR/BISECT_START"
123         then
124                 # Reset to the rev from where we started.
125                 start_head=$(cat "$GIT_DIR/BISECT_START")
126                 if test "z$mode" != "z--no-checkout"
127                 then
128                     git checkout "$start_head" --
129                 fi
130         else
131                 # Get rev from where we start.
132                 case "$head" in
133                 refs/heads/*|$_x40)
134                         # This error message should only be triggered by
135                         # cogito usage, and cogito users should understand
136                         # it relates to cg-seek.
137                         [ -s "$GIT_DIR/head-name" ] &&
138                                 die "$(gettext "won't bisect on seeked tree")"
139                         start_head="${head#refs/heads/}"
140                         ;;
141                 *)
142                         die "$(gettext "Bad HEAD - strange symbolic ref")"
143                         ;;
144                 esac
145         fi
147         #
148         # Get rid of any old bisect state.
149         #
150         bisect_clean_state || exit
152         #
153         # Change state.
154         # In case of mistaken revs or checkout error, or signals received,
155         # "bisect_auto_next" below may exit or misbehave.
156         # We have to trap this to be able to clean up using
157         # "bisect_clean_state".
158         #
159         trap 'bisect_clean_state' 0
160         trap 'exit 255' 1 2 3 15
162         #
163         # Write new start state.
164         #
165         echo "$start_head" >"$GIT_DIR/BISECT_START" && {
166                 test "z$mode" != "z--no-checkout" ||
167                 git update-ref --no-deref BISECT_HEAD "$start_head"
168         } &&
169         git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
170         eval "$eval true" &&
171         echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
172         #
173         # Check if we can proceed to the next bisect state.
174         #
175         bisect_auto_next
177         trap '-' 0
180 bisect_write() {
181         state="$1"
182         rev="$2"
183         nolog="$3"
184         case "$state" in
185                 bad)            tag="$state" ;;
186                 good|skip)      tag="$state"-"$rev" ;;
187                 *)              die "$(eval_gettext "Bad bisect_write argument: \$state")" ;;
188         esac
189         git update-ref "refs/bisect/$tag" "$rev" || exit
190         echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG"
191         test -n "$nolog" || echo "git bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
194 is_expected_rev() {
195         test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
196         test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
199 check_expected_revs() {
200         for _rev in "$@"; do
201                 if ! is_expected_rev "$_rev"
202                 then
203                         rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
204                         rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
205                         return
206                 fi
207         done
210 bisect_skip() {
211         all=''
212         for arg in "$@"
213         do
214                 case "$arg" in
215                 *..*)
216                         revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
217                 *)
218                         revs=$(git rev-parse --sq-quote "$arg") ;;
219                 esac
220                 all="$all $revs"
221         done
222         eval bisect_state 'skip' $all
225 bisect_state() {
226         bisect_autostart
227         state=$1
228         case "$#,$state" in
229         0,*)
230                 die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
231         1,bad|1,good|1,skip)
232                 rev=$(git rev-parse --verify $(bisect_head)) ||
233                         die "$(gettext "Bad rev input: $(bisect_head)")"
234                 bisect_write "$state" "$rev"
235                 check_expected_revs "$rev" ;;
236         2,bad|*,good|*,skip)
237                 shift
238                 eval=''
239                 for rev in "$@"
240                 do
241                         sha=$(git rev-parse --verify "$rev^{commit}") ||
242                                 die "$(eval_gettext "Bad rev input: \$rev")"
243                         eval="$eval bisect_write '$state' '$sha'; "
244                 done
245                 eval "$eval"
246                 check_expected_revs "$@" ;;
247         *,bad)
248                 die "$(gettext "'git bisect bad' can take only one argument.")" ;;
249         *)
250                 usage ;;
251         esac
252         bisect_auto_next
255 bisect_next_check() {
256         missing_good= missing_bad=
257         git show-ref -q --verify refs/bisect/bad || missing_bad=t
258         test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
260         case "$missing_good,$missing_bad,$1" in
261         ,,*)
262                 : have both good and bad - ok
263                 ;;
264         *,)
265                 # do not have both but not asked to fail - just report.
266                 false
267                 ;;
268         t,,good)
269                 # have bad but not good.  we could bisect although
270                 # this is less optimum.
271                 (
272                         gettext "Warning: bisecting only with a bad commit." &&
273                         echo
274                 ) >&2
275                 if test -t 0
276                 then
277                         # TRANSLATORS: Make sure to include [Y] and [n] in your
278                         # translation. The program will only accept English input
279                         # at this point.
280                         gettext "Are you sure [Y/n]? " >&2
281                         read yesno
282                         case "$yesno" in [Nn]*) exit 1 ;; esac
283                 fi
284                 : bisect without good...
285                 ;;
286         *)
288                 if test -s "$GIT_DIR/BISECT_START"
289                 then
290                         (
291                                 gettext "You need to give me at least one good and one bad revisions.
292 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" &&
293                                 echo
294                         ) >&2
295                 else
296                         (
297                                 gettext "You need to start by \"git bisect start\".
298 You then need to give me at least one good and one bad revisions.
299 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" &&
300                                 echo
301                         ) >&2
302                 fi
303                 exit 1 ;;
304         esac
307 bisect_auto_next() {
308         bisect_next_check && bisect_next || :
311 bisect_next() {
312         case "$#" in 0) ;; *) usage ;; esac
313         bisect_autostart
314         bisect_next_check good
316         # Perform all bisection computation, display and checkout
317         git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
318         res=$?
320         # Check if we should exit because bisection is finished
321         test $res -eq 10 && exit 0
323         # Check for an error in the bisection process
324         test $res -ne 0 && exit $res
326         return 0
329 bisect_visualize() {
330         bisect_next_check fail
332         if test $# = 0
333         then
334                 if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
335                    type gitk >/dev/null 2>&1
336                 then
337                         set gitk
338                 else
339                         set git log
340                 fi
341         else
342                 case "$1" in
343                 git*|tig) ;;
344                 -*)     set git log "$@" ;;
345                 *)      set git "$@" ;;
346                 esac
347         fi
349         eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
352 bisect_reset() {
353         test -s "$GIT_DIR/BISECT_START" || {
354                 gettext "We are not bisecting."; echo
355                 return
356         }
357         case "$#" in
358         0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
359         1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || {
360                         invalid="$1"
361                         die "$(eval_gettext "'\$invalid' is not a valid commit")"
362                 }
363                 branch="$1" ;;
364         *)
365                 usage ;;
366         esac
367         if ! test -f "$GIT_DIR/BISECT_HEAD"
368         then
369                 if ! git checkout "$branch" --
370                 then
371                         die "$(eval_gettext "Could not check out original HEAD '\$branch'.
372 Try 'git bisect reset <commit>'.")"
373                 fi
374         fi
375         bisect_clean_state
378 bisect_clean_state() {
379         # There may be some refs packed during bisection.
380         git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
381         while read ref hash
382         do
383                 git update-ref -d $ref $hash || exit
384         done
385         rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
386         rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
387         rm -f "$GIT_DIR/BISECT_LOG" &&
388         rm -f "$GIT_DIR/BISECT_NAMES" &&
389         rm -f "$GIT_DIR/BISECT_RUN" &&
390         # Cleanup head-name if it got left by an old version of git-bisect
391         rm -f "$GIT_DIR/head-name" &&
392         git update-ref -d --no-deref BISECT_HEAD &&
393         # clean up BISECT_START last
394         rm -f "$GIT_DIR/BISECT_START"
397 bisect_replay () {
398         file="$1"
399         test "$#" -eq 1 || die "$(gettext "No logfile given")"
400         test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
401         bisect_reset
402         while read git bisect command rev
403         do
404                 test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
405                 if test "$git" = "git-bisect"
406                 then
407                         rev="$command"
408                         command="$bisect"
409                 fi
410                 case "$command" in
411                 start)
412                         cmd="bisect_start $rev"
413                         eval "$cmd" ;;
414                 good|bad|skip)
415                         bisect_write "$command" "$rev" ;;
416                 *)
417                         die "$(gettext "?? what are you talking about?")" ;;
418                 esac
419         done <"$file"
420         bisect_auto_next
423 bisect_run () {
424         bisect_next_check fail
426         while true
427         do
428                 command="$@"
429                 eval_gettext "running \$command"; echo
430                 "$@"
431                 res=$?
433                 # Check for really bad run error.
434                 if [ $res -lt 0 -o $res -ge 128 ]
435                 then
436                         (
437                                 eval_gettext "bisect run failed:
438 exit code \$res from '\$command' is < 0 or >= 128" &&
439                                 echo
440                         ) >&2
441                         exit $res
442                 fi
444                 # Find current state depending on run success or failure.
445                 # A special exit code of 125 means cannot test.
446                 if [ $res -eq 125 ]
447                 then
448                         state='skip'
449                 elif [ $res -gt 0 ]
450                 then
451                         state='bad'
452                 else
453                         state='good'
454                 fi
456                 # We have to use a subshell because "bisect_state" can exit.
457                 ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
458                 res=$?
460                 cat "$GIT_DIR/BISECT_RUN"
462                 if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
463                         > /dev/null
464                 then
465                         (
466                                 gettext "bisect run cannot continue any more" &&
467                                 echo
468                         ) >&2
469                         exit $res
470                 fi
472                 if [ $res -ne 0 ]
473                 then
474                         (
475                                 eval_gettext "bisect run failed:
476 'bisect_state \$state' exited with error code \$res" &&
477                                 echo
478                         ) >&2
479                         exit $res
480                 fi
482                 if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null
483                 then
484                         gettext "bisect run success"; echo
485                         exit 0;
486                 fi
488         done
491 bisect_log () {
492         test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
493         cat "$GIT_DIR/BISECT_LOG"
496 case "$#" in
497 0)
498         usage ;;
499 *)
500         cmd="$1"
501         shift
502         case "$cmd" in
503         help)
504                 git bisect -h ;;
505         start)
506                 bisect_start "$@" ;;
507         bad|good)
508                 bisect_state "$cmd" "$@" ;;
509         skip)
510                 bisect_skip "$@" ;;
511         next)
512                 # Not sure we want "next" at the UI level anymore.
513                 bisect_next "$@" ;;
514         visualize|view)
515                 bisect_visualize "$@" ;;
516         reset)
517                 bisect_reset "$@" ;;
518         replay)
519                 bisect_replay "$@" ;;
520         log)
521                 bisect_log ;;
522         run)
523                 bisect_run "$@" ;;
524         *)
525                 usage ;;
526         esac
527 esac