Code

Merge branch 'maint'
[git.git] / git-bisect.sh
1 #!/bin/sh
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.'
23 . git-sh-setup
24 require_work_tree
26 sq() {
27         @@PERL@@ -e '
28                 for (@ARGV) {
29                         s/'\''/'\'\\\\\'\''/g;
30                         print " '\''$_'\''";
31                 }
32                 print "\n";
33         ' "$@"
34 }
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 }
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
79         #
80         # Get rid of any old bisect state
81         #
82         bisect_clean_state
83         mkdir "$GIT_DIR/refs/bisect"
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
118         sq "$@" >"$GIT_DIR/BISECT_NAMES"
119         {
120             printf "git-bisect start"
121             echo "$orig_args"
122         } >>"$GIT_DIR/BISECT_LOG"
123         bisect_auto_next
126 bisect_bad() {
127         bisect_autostart
128         case "$#" in
129         0)
130                 rev=$(git-rev-parse --verify HEAD) ;;
131         1)
132                 rev=$(git-rev-parse --verify "$1^{commit}") ;;
133         *)
134                 usage ;;
135         esac || exit
136         bisect_write_bad "$rev"
137         echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
138         bisect_auto_next
141 bisect_write_bad() {
142         rev="$1"
143         echo "$rev" >"$GIT_DIR/refs/bisect/bad"
144         echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
147 bisect_good() {
148         bisect_autostart
149         case "$#" in
150         0)    revs=$(git-rev-parse --verify HEAD) || exit ;;
151         *)    revs=$(git-rev-parse --revs-only --no-flags "$@") &&
152                 test '' != "$revs" || die "Bad rev input: $@" ;;
153         esac
154         for rev in $revs
155         do
156                 rev=$(git-rev-parse --verify "$rev^{commit}") || exit
157                 bisect_write_good "$rev"
158                 echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
160         done
161         bisect_auto_next
164 bisect_write_good() {
165         rev="$1"
166         echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
167         echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
170 bisect_next_check() {
171         missing_good= missing_bad=
172         git show-ref -q --verify refs/bisect/bad || missing_bad=t
173         test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
175         case "$missing_good,$missing_bad,$1" in
176         ,,*)
177                 : have both good and bad - ok
178                 ;;
179         *,)
180                 # do not have both but not asked to fail - just report.
181                 false
182                 ;;
183         t,,good)
184                 # have bad but not good.  we could bisect although
185                 # this is less optimum.
186                 echo >&2 'Warning: bisecting only with a bad commit.'
187                 if test -t 0
188                 then
189                         printf >&2 'Are you sure [Y/n]? '
190                         case "$(read yesno)" in [Nn]*) exit 1 ;; esac
191                 fi
192                 : bisect without good...
193                 ;;
194         *)
195                 THEN=''
196                 test -d "$GIT_DIR/refs/bisect" || {
197                         echo >&2 'You need to start by "git bisect start".'
198                         THEN='then '
199                 }
200                 echo >&2 'You '$THEN'need to give me at least one good' \
201                         'and one bad revisions.'
202                 echo >&2 '(You can use "git bisect bad" and' \
203                         '"git bisect good" for that.)'
204                 exit 1 ;;
205         esac
208 bisect_auto_next() {
209         bisect_next_check && bisect_next || :
212 bisect_next() {
213         case "$#" in 0) ;; *) usage ;; esac
214         bisect_autostart
215         bisect_next_check good
217         bad=$(git-rev-parse --verify refs/bisect/bad) &&
218         good=$(git for-each-ref --format='^%(objectname)' \
219                 "refs/bisect/good-*" | tr '[\012]' ' ') &&
220         eval="git-rev-list --bisect-vars $good $bad --" &&
221         eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
222         eval=$(eval "$eval") &&
223         eval "$eval" || exit
225         if [ -z "$bisect_rev" ]; then
226                 echo "$bad was both good and bad"
227                 exit 1
228         fi
229         if [ "$bisect_rev" = "$bad" ]; then
230                 echo "$bisect_rev is first bad commit"
231                 git-diff-tree --pretty $bisect_rev
232                 exit 0
233         fi
235         echo "Bisecting: $bisect_nr revisions left to test after this"
236         echo "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect"
237         git checkout -q new-bisect || exit
238         mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
239         GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD refs/heads/bisect
240         git-show-branch "$bisect_rev"
243 bisect_visualize() {
244         bisect_next_check fail
245         not=`cd "$GIT_DIR/refs" && echo bisect/good-*`
246         eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
249 bisect_reset() {
250         case "$#" in
251         0) if [ -s "$GIT_DIR/head-name" ]; then
252                branch=`cat "$GIT_DIR/head-name"`
253            else
254                branch=master
255            fi ;;
256         1) git-show-ref --verify --quiet -- "refs/heads/$1" || {
257                echo >&2 "$1 does not seem to be a valid branch"
258                exit 1
259            }
260            branch="$1" ;;
261         *)
262             usage ;;
263         esac
264         if git checkout "$branch"; then
265                 rm -f "$GIT_DIR/head-name"
266                 bisect_clean_state
267         fi
270 bisect_clean_state() {
271         rm -fr "$GIT_DIR/refs/bisect"
272         rm -f "$GIT_DIR/refs/heads/bisect"
273         rm -f "$GIT_DIR/BISECT_LOG"
274         rm -f "$GIT_DIR/BISECT_NAMES"
275         rm -f "$GIT_DIR/BISECT_RUN"
278 bisect_replay () {
279         test -r "$1" || {
280                 echo >&2 "cannot read $1 for replaying"
281                 exit 1
282         }
283         bisect_reset
284         while read bisect command rev
285         do
286                 test "$bisect" = "git-bisect" || continue
287                 case "$command" in
288                 start)
289                         cmd="bisect_start $rev"
290                         eval "$cmd"
291                         ;;
292                 good)
293                         echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
294                         echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
295                         echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
296                         ;;
297                 bad)
298                         echo "$rev" >"$GIT_DIR/refs/bisect/bad"
299                         echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
300                         echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
301                         ;;
302                 *)
303                         echo >&2 "?? what are you talking about?"
304                         exit 1 ;;
305                 esac
306         done <"$1"
307         bisect_auto_next
310 bisect_run () {
311     bisect_next_check fail
313     while true
314     do
315       echo "running $@"
316       "$@"
317       res=$?
319       # Check for really bad run error.
320       if [ $res -lt 0 -o $res -ge 128 ]; then
321           echo >&2 "bisect run failed:"
322           echo >&2 "exit code $res from '$@' is < 0 or >= 128"
323           exit $res
324       fi
326       # Use "bisect_good" or "bisect_bad"
327       # depending on run success or failure.
328       if [ $res -gt 0 ]; then
329           next_bisect='bisect_bad'
330       else
331           next_bisect='bisect_good'
332       fi
334       # We have to use a subshell because bisect_good or
335       # bisect_bad functions can exit.
336       ( $next_bisect > "$GIT_DIR/BISECT_RUN" )
337       res=$?
339       cat "$GIT_DIR/BISECT_RUN"
341       if [ $res -ne 0 ]; then
342           echo >&2 "bisect run failed:"
343           echo >&2 "$next_bisect exited with error code $res"
344           exit $res
345       fi
347       if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
348           echo "bisect run success"
349           exit 0;
350       fi
352     done
356 case "$#" in
357 0)
358     usage ;;
359 *)
360     cmd="$1"
361     shift
362     case "$cmd" in
363     start)
364         bisect_start "$@" ;;
365     bad)
366         bisect_bad "$@" ;;
367     good)
368         bisect_good "$@" ;;
369     next)
370         # Not sure we want "next" at the UI level anymore.
371         bisect_next "$@" ;;
372     visualize)
373         bisect_visualize "$@" ;;
374     reset)
375         bisect_reset "$@" ;;
376     replay)
377         bisect_replay "$@" ;;
378     log)
379         cat "$GIT_DIR/BISECT_LOG" ;;
380     run)
381         bisect_run "$@" ;;
382     *)
383         usage ;;
384     esac
385 esac