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