Code

Remove hint to use "git help -a"
[git.git] / git-bisect.sh
1 #!/bin/sh
3 USAGE='[start|bad|good|skip|next|reset|visualize|replay|log|run]'
4 LONG_USAGE='git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
5         reset bisect state and start bisection.
6 git bisect bad [<rev>]
7         mark <rev> a known-bad revision.
8 git bisect good [<rev>...]
9         mark <rev>... known-good revisions.
10 git bisect skip [<rev>...]
11         mark <rev>... untestable revisions.
12 git bisect next
13         find next bisection to test and check it out.
14 git bisect reset [<branch>]
15         finish bisection search and go back to branch.
16 git bisect visualize
17         show bisect status in gitk.
18 git bisect replay <logfile>
19         replay bisection log.
20 git bisect log
21         show bisect log.
22 git bisect run <cmd>...
23         use <cmd>... to automatically bisect.'
25 . git-sh-setup
26 require_work_tree
28 sq() {
29         @@PERL@@ -e '
30                 for (@ARGV) {
31                         s/'\''/'\'\\\\\'\''/g;
32                         print " '\''$_'\''";
33                 }
34                 print "\n";
35         ' "$@"
36 }
38 bisect_autostart() {
39         test -d "$GIT_DIR/refs/bisect" || {
40                 echo >&2 'You need to start by "git bisect start"'
41                 if test -t 0
42                 then
43                         echo >&2 -n 'Do you want me to do it for you [Y/n]? '
44                         read yesno
45                         case "$yesno" in
46                         [Nn]*)
47                                 exit ;;
48                         esac
49                         bisect_start
50                 else
51                         exit 1
52                 fi
53         }
54 }
56 bisect_start() {
57         #
58         # Verify HEAD. If we were bisecting before this, reset to the
59         # top-of-line master first!
60         #
61         head=$(GIT_DIR="$GIT_DIR" git symbolic-ref HEAD) ||
62         die "Bad HEAD - I need a symbolic ref"
63         case "$head" in
64         refs/heads/bisect)
65                 if [ -s "$GIT_DIR/head-name" ]; then
66                     branch=`cat "$GIT_DIR/head-name"`
67                 else
68                     branch=master
69                 fi
70                 git checkout $branch || exit
71                 ;;
72         refs/heads/*)
73                 [ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree"
74                 echo "$head" | sed 's#^refs/heads/##' >"$GIT_DIR/head-name"
75                 ;;
76         *)
77                 die "Bad HEAD - strange symbolic ref"
78                 ;;
79         esac
81         #
82         # Get rid of any old bisect state
83         #
84         bisect_clean_state
85         mkdir "$GIT_DIR/refs/bisect"
87         #
88         # Check for one bad and then some good revisions.
89         #
90         has_double_dash=0
91         for arg; do
92             case "$arg" in --) has_double_dash=1; break ;; esac
93         done
94         orig_args=$(sq "$@")
95         bad_seen=0
96         while [ $# -gt 0 ]; do
97             arg="$1"
98             case "$arg" in
99             --)
100                 shift
101                 break
102                 ;;
103             *)
104                 rev=$(git rev-parse --verify "$arg^{commit}" 2>/dev/null) || {
105                     test $has_double_dash -eq 1 &&
106                         die "'$arg' does not appear to be a valid revision"
107                     break
108                 }
109                 case $bad_seen in
110                 0) state='bad' ; bad_seen=1 ;;
111                 *) state='good' ;;
112                 esac
113                 bisect_write "$state" "$rev" 'nolog'
114                 shift
115                 ;;
116             esac
117         done
119         sq "$@" >"$GIT_DIR/BISECT_NAMES"
120         echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG"
121         bisect_auto_next
124 bisect_write() {
125         state="$1"
126         rev="$2"
127         nolog="$3"
128         case "$state" in
129                 bad)            tag="$state" ;;
130                 good|skip)      tag="$state"-"$rev" ;;
131                 *)              die "Bad bisect_write argument: $state" ;;
132         esac
133         echo "$rev" >"$GIT_DIR/refs/bisect/$tag"
134         echo "# $state: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
135         test -z "$nolog" && echo "git-bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
138 bisect_state() {
139         bisect_autostart
140         state=$1
141         case "$#,$state" in
142         0,*)
143                 die "Please call 'bisect_state' with at least one argument." ;;
144         1,bad|1,good|1,skip)
145                 rev=$(git rev-parse --verify HEAD) ||
146                         die "Bad rev input: HEAD"
147                 bisect_write "$state" "$rev" ;;
148         2,bad)
149                 rev=$(git rev-parse --verify "$2^{commit}") ||
150                         die "Bad rev input: $2"
151                 bisect_write "$state" "$rev" ;;
152         *,good|*,skip)
153                 shift
154                 revs=$(git rev-parse --revs-only --no-flags "$@") &&
155                         test '' != "$revs" || die "Bad rev input: $@"
156                 for rev in $revs
157                 do
158                         rev=$(git rev-parse --verify "$rev^{commit}") ||
159                                 die "Bad rev commit: $rev^{commit}"
160                         bisect_write "$state" "$rev"
161                 done ;;
162         *)
163                 usage ;;
164         esac
165         bisect_auto_next
168 bisect_next_check() {
169         missing_good= missing_bad=
170         git show-ref -q --verify refs/bisect/bad || missing_bad=t
171         test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
173         case "$missing_good,$missing_bad,$1" in
174         ,,*)
175                 : have both good and bad - ok
176                 ;;
177         *,)
178                 # do not have both but not asked to fail - just report.
179                 false
180                 ;;
181         t,,good)
182                 # have bad but not good.  we could bisect although
183                 # this is less optimum.
184                 echo >&2 'Warning: bisecting only with a bad commit.'
185                 if test -t 0
186                 then
187                         printf >&2 'Are you sure [Y/n]? '
188                         case "$(read yesno)" in [Nn]*) exit 1 ;; esac
189                 fi
190                 : bisect without good...
191                 ;;
192         *)
193                 THEN=''
194                 test -d "$GIT_DIR/refs/bisect" || {
195                         echo >&2 'You need to start by "git bisect start".'
196                         THEN='then '
197                 }
198                 echo >&2 'You '$THEN'need to give me at least one good' \
199                         'and one bad revisions.'
200                 echo >&2 '(You can use "git bisect bad" and' \
201                         '"git bisect good" for that.)'
202                 exit 1 ;;
203         esac
206 bisect_auto_next() {
207         bisect_next_check && bisect_next || :
210 filter_skipped() {
211         _eval="$1"
212         _skip="$2"
214         if [ -z "$_skip" ]; then
215                 eval $_eval
216                 return
217         fi
219         # Let's parse the output of:
220         # "git rev-list --bisect-vars --bisect-all ..."
221         eval $_eval | while read hash line
222         do
223                 case "$VARS,$FOUND,$TRIED,$hash" in
224                         # We display some vars.
225                         1,*,*,*) echo "$hash $line" ;;
227                         # Split line.
228                         ,*,*,---*) ;;
230                         # We had nothing to search.
231                         ,,,bisect_rev*)
232                                 echo "bisect_rev="
233                                 VARS=1
234                                 ;;
236                         # We did not find a good bisect rev.
237                         # This should happen only if the "bad"
238                         # commit is also a "skip" commit.
239                         ,,*,bisect_rev*)
240                                 echo "bisect_rev=$TRIED"
241                                 VARS=1
242                                 ;;
244                         # We are searching.
245                         ,,*,*)
246                                 TRIED="${TRIED:+$TRIED|}$hash"
247                                 case "$_skip" in
248                                 *$hash*) ;;
249                                 *)
250                                         echo "bisect_rev=$hash"
251                                         echo "bisect_tried=\"$TRIED\""
252                                         FOUND=1
253                                         ;;
254                                 esac
255                                 ;;
257                         # We have already found a rev to be tested.
258                         ,1,*,bisect_rev*) VARS=1 ;;
259                         ,1,*,*) ;;
261                         # ???
262                         *) die "filter_skipped error " \
263                             "VARS: '$VARS' " \
264                             "FOUND: '$FOUND' " \
265                             "TRIED: '$TRIED' " \
266                             "hash: '$hash' " \
267                             "line: '$line'"
268                         ;;
269                 esac
270         done
273 exit_if_skipped_commits () {
274         _tried=$1
275         if expr "$_tried" : ".*[|].*" > /dev/null ; then
276                 echo "There are only 'skip'ped commit left to test."
277                 echo "The first bad commit could be any of:"
278                 echo "$_tried" | sed -e 's/[|]/\
279 /g'
280                 echo "We cannot bisect more!"
281                 exit 2
282         fi
285 bisect_next() {
286         case "$#" in 0) ;; *) usage ;; esac
287         bisect_autostart
288         bisect_next_check good
290         skip=$(git for-each-ref --format='%(objectname)' \
291                 "refs/bisect/skip-*" | tr '[\012]' ' ') || exit
293         BISECT_OPT=''
294         test -n "$skip" && BISECT_OPT='--bisect-all'
296         bad=$(git rev-parse --verify refs/bisect/bad) &&
297         good=$(git for-each-ref --format='^%(objectname)' \
298                 "refs/bisect/good-*" | tr '[\012]' ' ') &&
299         eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" &&
300         eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
301         eval=$(filter_skipped "$eval" "$skip") &&
302         eval "$eval" || exit
304         if [ -z "$bisect_rev" ]; then
305                 echo "$bad was both good and bad"
306                 exit 1
307         fi
308         if [ "$bisect_rev" = "$bad" ]; then
309                 exit_if_skipped_commits "$bisect_tried"
310                 echo "$bisect_rev is first bad commit"
311                 git diff-tree --pretty $bisect_rev
312                 exit 0
313         fi
315         # We should exit here only if the "bad"
316         # commit is also a "skip" commit (see above).
317         exit_if_skipped_commits "$bisect_rev"
319         echo "Bisecting: $bisect_nr revisions left to test after this"
320         echo "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect"
321         git checkout -q new-bisect || exit
322         mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
323         GIT_DIR="$GIT_DIR" git symbolic-ref HEAD refs/heads/bisect
324         git show-branch "$bisect_rev"
327 bisect_visualize() {
328         bisect_next_check fail
329         not=`cd "$GIT_DIR/refs" && echo bisect/good-*`
330         eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
333 bisect_reset() {
334         case "$#" in
335         0) if [ -s "$GIT_DIR/head-name" ]; then
336                branch=`cat "$GIT_DIR/head-name"`
337            else
338                branch=master
339            fi ;;
340         1) git show-ref --verify --quiet -- "refs/heads/$1" ||
341                die "$1 does not seem to be a valid branch"
342            branch="$1" ;;
343         *)
344             usage ;;
345         esac
346         if git checkout "$branch"; then
347                 rm -f "$GIT_DIR/head-name"
348                 bisect_clean_state
349         fi
352 bisect_clean_state() {
353         rm -fr "$GIT_DIR/refs/bisect"
354         rm -f "$GIT_DIR/refs/heads/bisect"
355         rm -f "$GIT_DIR/BISECT_LOG"
356         rm -f "$GIT_DIR/BISECT_NAMES"
357         rm -f "$GIT_DIR/BISECT_RUN"
360 bisect_replay () {
361         test -r "$1" || die "cannot read $1 for replaying"
362         bisect_reset
363         while read bisect command rev
364         do
365                 test "$bisect" = "git-bisect" || continue
366                 case "$command" in
367                 start)
368                         cmd="bisect_start $rev"
369                         eval "$cmd" ;;
370                 good|bad|skip)
371                         bisect_write "$command" "$rev" ;;
372                 *)
373                         die "?? what are you talking about?" ;;
374                 esac
375         done <"$1"
376         bisect_auto_next
379 bisect_run () {
380     bisect_next_check fail
382     while true
383     do
384       echo "running $@"
385       "$@"
386       res=$?
388       # Check for really bad run error.
389       if [ $res -lt 0 -o $res -ge 128 ]; then
390           echo >&2 "bisect run failed:"
391           echo >&2 "exit code $res from '$@' is < 0 or >= 128"
392           exit $res
393       fi
395       # Find current state depending on run success or failure.
396       # A special exit code of 125 means cannot test.
397       if [ $res -eq 125 ]; then
398           state='skip'
399       elif [ $res -gt 0 ]; then
400           state='bad'
401       else
402           state='good'
403       fi
405       # We have to use a subshell because "bisect_state" can exit.
406       ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
407       res=$?
409       cat "$GIT_DIR/BISECT_RUN"
411       if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
412                 > /dev/null; then
413           echo >&2 "bisect run cannot continue any more"
414           exit $res
415       fi
417       if [ $res -ne 0 ]; then
418           echo >&2 "bisect run failed:"
419           echo >&2 "'bisect_state $state' exited with error code $res"
420           exit $res
421       fi
423       if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
424           echo "bisect run success"
425           exit 0;
426       fi
428     done
432 case "$#" in
433 0)
434     usage ;;
435 *)
436     cmd="$1"
437     shift
438     case "$cmd" in
439     start)
440         bisect_start "$@" ;;
441     bad|good|skip)
442         bisect_state "$cmd" "$@" ;;
443     next)
444         # Not sure we want "next" at the UI level anymore.
445         bisect_next "$@" ;;
446     visualize)
447         bisect_visualize "$@" ;;
448     reset)
449         bisect_reset "$@" ;;
450     replay)
451         bisect_replay "$@" ;;
452     log)
453         cat "$GIT_DIR/BISECT_LOG" ;;
454     run)
455         bisect_run "$@" ;;
456     *)
457         usage ;;
458     esac
459 esac