Code

bisect: cleanup whitespace errors in git-bisect.sh.
[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"; then
202                         rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
203                         rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
204                         return
205                 fi
206         done
209 bisect_skip() {
210         all=''
211         for arg in "$@"
212         do
213                 case "$arg" in
214                 *..*)
215                         revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
216                 *)
217                         revs=$(git rev-parse --sq-quote "$arg") ;;
218                 esac
219                 all="$all $revs"
220         done
221         eval bisect_state 'skip' $all
224 bisect_state() {
225         bisect_autostart
226         state=$1
227         case "$#,$state" in
228         0,*)
229                 die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
230         1,bad|1,good|1,skip)
231                 rev=$(git rev-parse --verify $(bisect_head)) ||
232                         die "$(gettext "Bad rev input: $(bisect_head)")"
233                 bisect_write "$state" "$rev"
234                 check_expected_revs "$rev" ;;
235         2,bad|*,good|*,skip)
236                 shift
237                 eval=''
238                 for rev in "$@"
239                 do
240                         sha=$(git rev-parse --verify "$rev^{commit}") ||
241                                 die "$(eval_gettext "Bad rev input: \$rev")"
242                         eval="$eval bisect_write '$state' '$sha'; "
243                 done
244                 eval "$eval"
245                 check_expected_revs "$@" ;;
246         *,bad)
247                 die "$(gettext "'git bisect bad' can take only one argument.")" ;;
248         *)
249                 usage ;;
250         esac
251         bisect_auto_next
254 bisect_next_check() {
255         missing_good= missing_bad=
256         git show-ref -q --verify refs/bisect/bad || missing_bad=t
257         test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
259         case "$missing_good,$missing_bad,$1" in
260         ,,*)
261                 : have both good and bad - ok
262                 ;;
263         *,)
264                 # do not have both but not asked to fail - just report.
265                 false
266                 ;;
267         t,,good)
268                 # have bad but not good.  we could bisect although
269                 # this is less optimum.
270                 (
271                         gettext "Warning: bisecting only with a bad commit." &&
272                         echo
273                 ) >&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
282                 fi
283                 : bisect without good...
284                 ;;
285         *)
287                 if test -s "$GIT_DIR/BISECT_START"
288                 then
289                         (
290                                 gettext "You need to give me at least one good and one bad revisions.
291 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" &&
292                                 echo
293                         ) >&2
294                 else
295                         (
296                                 gettext "You need to start by \"git bisect start\".
297 You then need to give me at least one good and one bad revisions.
298 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" &&
299                                 echo
300                         ) >&2
301                 fi
302                 exit 1 ;;
303         esac
306 bisect_auto_next() {
307         bisect_next_check && bisect_next || :
310 bisect_next() {
311         case "$#" in 0) ;; *) usage ;; esac
312         bisect_autostart
313         bisect_next_check good
315         # Perform all bisection computation, display and checkout
316         git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
317         res=$?
319         # Check if we should exit because bisection is finished
320         test $res -eq 10 && exit 0
322         # Check for an error in the bisection process
323         test $res -ne 0 && exit $res
325         return 0
328 bisect_visualize() {
329         bisect_next_check fail
331         if test $# = 0
332         then
333                 if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
334                    type gitk >/dev/null 2>&1; then
335                         set gitk
336                 else
337                         set git log
338                 fi
339         else
340                 case "$1" in
341                 git*|tig) ;;
342                 -*)     set git log "$@" ;;
343                 *)      set git "$@" ;;
344                 esac
345         fi
347         eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
350 bisect_reset() {
351         test -s "$GIT_DIR/BISECT_START" || {
352                 gettext "We are not bisecting."; echo
353                 return
354         }
355         case "$#" in
356         0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
357         1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || {
358                         invalid="$1"
359                         die "$(eval_gettext "'\$invalid' is not a valid commit")"
360                 }
361                 branch="$1" ;;
362         *)
363                 usage ;;
364         esac
365         if ! test -f "$GIT_DIR/BISECT_HEAD"
366         then
367                 if ! git checkout "$branch" --
368                 then
369                         die "$(eval_gettext "Could not check out original HEAD '\$branch'.
370 Try 'git bisect reset <commit>'.")"
371                 fi
372         fi
373         bisect_clean_state
376 bisect_clean_state() {
377         # There may be some refs packed during bisection.
378         git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
379         while read ref hash
380         do
381                 git update-ref -d $ref $hash || exit
382         done
383         rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
384         rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
385         rm -f "$GIT_DIR/BISECT_LOG" &&
386         rm -f "$GIT_DIR/BISECT_NAMES" &&
387         rm -f "$GIT_DIR/BISECT_RUN" &&
388         # Cleanup head-name if it got left by an old version of git-bisect
389         rm -f "$GIT_DIR/head-name" &&
390         git update-ref -d --no-deref BISECT_HEAD &&
391         # clean up BISECT_START last
392         rm -f "$GIT_DIR/BISECT_START"
395 bisect_replay () {
396         file="$1"
397         test "$#" -eq 1 || die "$(gettext "No logfile given")"
398         test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
399         bisect_reset
400         while read git bisect command rev
401         do
402                 test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
403                 if test "$git" = "git-bisect"; then
404                         rev="$command"
405                         command="$bisect"
406                 fi
407                 case "$command" in
408                 start)
409                         cmd="bisect_start $rev"
410                         eval "$cmd" ;;
411                 good|bad|skip)
412                         bisect_write "$command" "$rev" ;;
413                 *)
414                         die "$(gettext "?? what are you talking about?")" ;;
415                 esac
416         done <"$file"
417         bisect_auto_next
420 bisect_run () {
421         bisect_next_check fail
423         while true
424         do
425                 command="$@"
426                 eval_gettext "running \$command"; echo
427                 "$@"
428                 res=$?
430                 # Check for really bad run error.
431                 if [ $res -lt 0 -o $res -ge 128 ]; then
432                         (
433                                 eval_gettext "bisect run failed:
434 exit code \$res from '\$command' is < 0 or >= 128" &&
435                                 echo
436                         ) >&2
437                         exit $res
438                 fi
440                 # Find current state depending on run success or failure.
441                 # A special exit code of 125 means cannot test.
442                 if [ $res -eq 125 ]; then
443                         state='skip'
444                 elif [ $res -gt 0 ]; then
445                         state='bad'
446                 else
447                         state='good'
448                 fi
450                 # We have to use a subshell because "bisect_state" can exit.
451                 ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
452                 res=$?
454                 cat "$GIT_DIR/BISECT_RUN"
456                 if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
457                         > /dev/null; then
458                         (
459                                 gettext "bisect run cannot continue any more" &&
460                                 echo
461                         ) >&2
462                         exit $res
463                 fi
465                 if [ $res -ne 0 ]; then
466                         (
467                                 eval_gettext "bisect run failed:
468 'bisect_state \$state' exited with error code \$res" &&
469                                 echo
470                         ) >&2
471                         exit $res
472                 fi
474                 if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
475                         gettext "bisect run success"; echo
476                         exit 0;
477                 fi
479         done
482 bisect_log () {
483         test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
484         cat "$GIT_DIR/BISECT_LOG"
487 case "$#" in
488 0)
489         usage ;;
490 *)
491         cmd="$1"
492         shift
493         case "$cmd" in
494         help)
495                 git bisect -h ;;
496         start)
497                 bisect_start "$@" ;;
498         bad|good)
499                 bisect_state "$cmd" "$@" ;;
500         skip)
501                 bisect_skip "$@" ;;
502         next)
503                 # Not sure we want "next" at the UI level anymore.
504                 bisect_next "$@" ;;
505         visualize|view)
506                 bisect_visualize "$@" ;;
507         reset)
508                 bisect_reset "$@" ;;
509         replay)
510                 bisect_replay "$@" ;;
511         log)
512                 bisect_log ;;
513         run)
514                 bisect_run "$@" ;;
515         *)
516                 usage ;;
517         esac
518 esac