Merge branch 'aw/cvs'
[git] / git-bisect.sh
1 #!/bin/sh
2
3 USAGE='[start|bad|good|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 next
11         find next bisection to test and check it out.
12 git bisect reset [<branch>]
13         finish bisection search and go back to branch.
14 git bisect visualize
15         show bisect status in gitk.
16 git bisect replay <logfile>
17         replay bisection log.
18 git bisect log
19         show bisect log.
20 git bisect run <cmd>...
21         use <cmd>... to automatically bisect.'
22
23 . git-sh-setup
24 require_work_tree
25
26 sq() {
27         @@PERL@@ -e '
28                 for (@ARGV) {
29                         s/'\''/'\'\\\\\'\''/g;
30                         print " '\''$_'\''";
31                 }
32                 print "\n";
33         ' "$@"
34 }
35
36 bisect_autostart() {
37         test -d "$GIT_DIR/refs/bisect" || {
38                 echo >&2 'You need to start by "git bisect start"'
39                 if test -t 0
40                 then
41                         echo >&2 -n 'Do you want me to do it for you [Y/n]? '
42                         read yesno
43                         case "$yesno" in
44                         [Nn]*)
45                                 exit ;;
46                         esac
47                         bisect_start
48                 else
49                         exit 1
50                 fi
51         }
52 }
53
54 bisect_start() {
55         #
56         # Verify HEAD. If we were bisecting before this, reset to the
57         # top-of-line master first!
58         #
59         head=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD) ||
60         die "Bad HEAD - I need a symbolic ref"
61         case "$head" in
62         refs/heads/bisect)
63                 if [ -s "$GIT_DIR/head-name" ]; then
64                     branch=`cat "$GIT_DIR/head-name"`
65                 else
66                     branch=master
67                 fi
68                 git checkout $branch || exit
69                 ;;
70         refs/heads/*)
71                 [ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree"
72                 echo "$head" | sed 's#^refs/heads/##' >"$GIT_DIR/head-name"
73                 ;;
74         *)
75                 die "Bad HEAD - strange symbolic ref"
76                 ;;
77         esac
78
79         #
80         # Get rid of any old bisect state
81         #
82         bisect_clean_state
83         mkdir "$GIT_DIR/refs/bisect"
84
85         #
86         # Check for one bad and then some good revisions.
87         #
88         has_double_dash=0
89         for arg; do
90             case "$arg" in --) has_double_dash=1; break ;; esac
91         done
92         orig_args=$(sq "$@")
93         bad_seen=0
94         while [ $# -gt 0 ]; do
95             arg="$1"
96             case "$arg" in
97             --)
98                 shift
99                 break
100                 ;;
101             *)
102                 rev=$(git-rev-parse --verify "$arg^{commit}" 2>/dev/null) || {
103                     test $has_double_dash -eq 1 &&
104                         die "'$arg' does not appear to be a valid revision"
105                     break
106                 }
107                 if [ $bad_seen -eq 0 ]; then
108                     bad_seen=1
109                     bisect_write_bad "$rev"
110                 else
111                     bisect_write_good "$rev"
112                 fi
113                 shift
114                 ;;
115             esac
116         done
117
118         sq "$@" >"$GIT_DIR/BISECT_NAMES"
119         echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG"
120         bisect_auto_next
121 }
122
123 bisect_bad() {
124         bisect_autostart
125         case "$#" in
126         0)
127                 rev=$(git-rev-parse --verify HEAD) ;;
128         1)
129                 rev=$(git-rev-parse --verify "$1^{commit}") ;;
130         *)
131                 usage ;;
132         esac || exit
133         bisect_write_bad "$rev"
134         echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
135         bisect_auto_next
136 }
137
138 bisect_write_bad() {
139         rev="$1"
140         echo "$rev" >"$GIT_DIR/refs/bisect/bad"
141         echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
142 }
143
144 bisect_good() {
145         bisect_autostart
146         case "$#" in
147         0)    revs=$(git-rev-parse --verify HEAD) || exit ;;
148         *)    revs=$(git-rev-parse --revs-only --no-flags "$@") &&
149                 test '' != "$revs" || die "Bad rev input: $@" ;;
150         esac
151         for rev in $revs
152         do
153                 rev=$(git-rev-parse --verify "$rev^{commit}") || exit
154                 bisect_write_good "$rev"
155                 echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
156
157         done
158         bisect_auto_next
159 }
160
161 bisect_write_good() {
162         rev="$1"
163         echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
164         echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
165 }
166
167 bisect_next_check() {
168         missing_good= missing_bad=
169         git show-ref -q --verify refs/bisect/bad || missing_bad=t
170         test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
171
172         case "$missing_good,$missing_bad,$1" in
173         ,,*)
174                 : have both good and bad - ok
175                 ;;
176         *,)
177                 # do not have both but not asked to fail - just report.
178                 false
179                 ;;
180         t,,good)
181                 # have bad but not good.  we could bisect although
182                 # this is less optimum.
183                 echo >&2 'Warning: bisecting only with a bad commit.'
184                 if test -t 0
185                 then
186                         printf >&2 'Are you sure [Y/n]? '
187                         case "$(read yesno)" in [Nn]*) exit 1 ;; esac
188                 fi
189                 : bisect without good...
190                 ;;
191         *)
192                 THEN=''
193                 test -d "$GIT_DIR/refs/bisect" || {
194                         echo >&2 'You need to start by "git bisect start".'
195                         THEN='then '
196                 }
197                 echo >&2 'You '$THEN'need to give me at least one good' \
198                         'and one bad revisions.'
199                 echo >&2 '(You can use "git bisect bad" and' \
200                         '"git bisect good" for that.)'
201                 exit 1 ;;
202         esac
203 }
204
205 bisect_auto_next() {
206         bisect_next_check && bisect_next || :
207 }
208
209 bisect_next() {
210         case "$#" in 0) ;; *) usage ;; esac
211         bisect_autostart
212         bisect_next_check good
213
214         bad=$(git-rev-parse --verify refs/bisect/bad) &&
215         good=$(git for-each-ref --format='^%(objectname)' \
216                 "refs/bisect/good-*" | tr '[\012]' ' ') &&
217         eval="git-rev-list --bisect-vars $good $bad --" &&
218         eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
219         eval=$(eval "$eval") &&
220         eval "$eval" || exit
221
222         if [ -z "$bisect_rev" ]; then
223                 echo "$bad was both good and bad"
224                 exit 1
225         fi
226         if [ "$bisect_rev" = "$bad" ]; then
227                 echo "$bisect_rev is first bad commit"
228                 git-diff-tree --pretty $bisect_rev
229                 exit 0
230         fi
231
232         echo "Bisecting: $bisect_nr revisions left to test after this"
233         echo "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect"
234         git checkout -q new-bisect || exit
235         mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
236         GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD refs/heads/bisect
237         git-show-branch "$bisect_rev"
238 }
239
240 bisect_visualize() {
241         bisect_next_check fail
242         not=`cd "$GIT_DIR/refs" && echo bisect/good-*`
243         eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
244 }
245
246 bisect_reset() {
247         case "$#" in
248         0) if [ -s "$GIT_DIR/head-name" ]; then
249                branch=`cat "$GIT_DIR/head-name"`
250            else
251                branch=master
252            fi ;;
253         1) git-show-ref --verify --quiet -- "refs/heads/$1" || {
254                echo >&2 "$1 does not seem to be a valid branch"
255                exit 1
256            }
257            branch="$1" ;;
258         *)
259             usage ;;
260         esac
261         if git checkout "$branch"; then
262                 rm -f "$GIT_DIR/head-name"
263                 bisect_clean_state
264         fi
265 }
266
267 bisect_clean_state() {
268         rm -fr "$GIT_DIR/refs/bisect"
269         rm -f "$GIT_DIR/refs/heads/bisect"
270         rm -f "$GIT_DIR/BISECT_LOG"
271         rm -f "$GIT_DIR/BISECT_NAMES"
272         rm -f "$GIT_DIR/BISECT_RUN"
273 }
274
275 bisect_replay () {
276         test -r "$1" || {
277                 echo >&2 "cannot read $1 for replaying"
278                 exit 1
279         }
280         bisect_reset
281         while read bisect command rev
282         do
283                 test "$bisect" = "git-bisect" || continue
284                 case "$command" in
285                 start)
286                         cmd="bisect_start $rev"
287                         eval "$cmd"
288                         ;;
289                 good)
290                         echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
291                         echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
292                         echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
293                         ;;
294                 bad)
295                         echo "$rev" >"$GIT_DIR/refs/bisect/bad"
296                         echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
297                         echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
298                         ;;
299                 *)
300                         echo >&2 "?? what are you talking about?"
301                         exit 1 ;;
302                 esac
303         done <"$1"
304         bisect_auto_next
305 }
306
307 bisect_run () {
308     bisect_next_check fail
309
310     while true
311     do
312       echo "running $@"
313       "$@"
314       res=$?
315
316       # Check for really bad run error.
317       if [ $res -lt 0 -o $res -ge 128 ]; then
318           echo >&2 "bisect run failed:"
319           echo >&2 "exit code $res from '$@' is < 0 or >= 128"
320           exit $res
321       fi
322
323       # Use "bisect_good" or "bisect_bad"
324       # depending on run success or failure.
325       if [ $res -gt 0 ]; then
326           next_bisect='bisect_bad'
327       else
328           next_bisect='bisect_good'
329       fi
330
331       # We have to use a subshell because bisect_good or
332       # bisect_bad functions can exit.
333       ( $next_bisect > "$GIT_DIR/BISECT_RUN" )
334       res=$?
335
336       cat "$GIT_DIR/BISECT_RUN"
337
338       if [ $res -ne 0 ]; then
339           echo >&2 "bisect run failed:"
340           echo >&2 "$next_bisect exited with error code $res"
341           exit $res
342       fi
343
344       if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
345           echo "bisect run success"
346           exit 0;
347       fi
348
349     done
350 }
351
352
353 case "$#" in
354 0)
355     usage ;;
356 *)
357     cmd="$1"
358     shift
359     case "$cmd" in
360     start)
361         bisect_start "$@" ;;
362     bad)
363         bisect_bad "$@" ;;
364     good)
365         bisect_good "$@" ;;
366     next)
367         # Not sure we want "next" at the UI level anymore.
368         bisect_next "$@" ;;
369     visualize)
370         bisect_visualize "$@" ;;
371     reset)
372         bisect_reset "$@" ;;
373     replay)
374         bisect_replay "$@" ;;
375     log)
376         cat "$GIT_DIR/BISECT_LOG" ;;
377     run)
378         bisect_run "$@" ;;
379     *)
380         usage ;;
381     esac
382 esac