Merge branch 'maint'
[git] / git-bisect.sh
1 #!/bin/sh
2
3 USAGE='[start|bad|good|next|reset|visualize|replay|log]'
4 LONG_USAGE='git bisect start [<pathspec>]       reset bisect state and start bisection.
5 git bisect bad [<rev>]          mark <rev> a known-bad revision.
6 git bisect good [<rev>...]      mark <rev>... known-good revisions.
7 git bisect next                 find next bisection to test and check it out.
8 git bisect reset [<branch>]     finish bisection search and go back to branch.
9 git bisect visualize            show bisect status in gitk.
10 git bisect replay <logfile>     replay bisection log
11 git bisect log                  show bisect log.'
12
13 . git-sh-setup
14 require_work_tree
15
16 sq() {
17         @@PERL@@ -e '
18                 for (@ARGV) {
19                         s/'\''/'\'\\\\\'\''/g;
20                         print " '\''$_'\''";
21                 }
22                 print "\n";
23         ' "$@"
24 }
25
26 bisect_autostart() {
27         test -d "$GIT_DIR/refs/bisect" || {
28                 echo >&2 'You need to start by "git bisect start"'
29                 if test -t 0
30                 then
31                         echo >&2 -n 'Do you want me to do it for you [Y/n]? '
32                         read yesno
33                         case "$yesno" in
34                         [Nn]*)
35                                 exit ;;
36                         esac
37                         bisect_start
38                 else
39                         exit 1
40                 fi
41         }
42 }
43
44 bisect_start() {
45         #
46         # Verify HEAD. If we were bisecting before this, reset to the
47         # top-of-line master first!
48         #
49         head=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD) ||
50         die "Bad HEAD - I need a symbolic ref"
51         case "$head" in
52         refs/heads/bisect*)
53                 if [ -s "$GIT_DIR/head-name" ]; then
54                     branch=`cat "$GIT_DIR/head-name"`
55                 else
56                     branch=master
57                 fi
58                 git checkout $branch || exit
59                 ;;
60         refs/heads/*)
61                 [ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree"
62                 echo "$head" | sed 's#^refs/heads/##' >"$GIT_DIR/head-name"
63                 ;;
64         *)
65                 die "Bad HEAD - strange symbolic ref"
66                 ;;
67         esac
68
69         #
70         # Get rid of any old bisect state
71         #
72         rm -f "$GIT_DIR/refs/heads/bisect"
73         rm -rf "$GIT_DIR/refs/bisect/"
74         mkdir "$GIT_DIR/refs/bisect"
75         {
76             printf "git-bisect start"
77             sq "$@"
78         } >"$GIT_DIR/BISECT_LOG"
79         sq "$@" >"$GIT_DIR/BISECT_NAMES"
80 }
81
82 bisect_bad() {
83         bisect_autostart
84         case "$#" in
85         0)
86                 rev=$(git-rev-parse --verify HEAD) ;;
87         1)
88                 rev=$(git-rev-parse --verify "$1") ;;
89         *)
90                 usage ;;
91         esac || exit
92         echo "$rev" >"$GIT_DIR/refs/bisect/bad"
93         echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
94         echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
95         bisect_auto_next
96 }
97
98 bisect_good() {
99         bisect_autostart
100         case "$#" in
101         0)    revs=$(git-rev-parse --verify HEAD) || exit ;;
102         *)    revs=$(git-rev-parse --revs-only --no-flags "$@") &&
103                 test '' != "$revs" || die "Bad rev input: $@" ;;
104         esac
105         for rev in $revs
106         do
107                 rev=$(git-rev-parse --verify "$rev") || exit
108                 echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
109                 echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
110                 echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
111         done
112         bisect_auto_next
113 }
114
115 bisect_next_check() {
116         next_ok=no
117         test -f "$GIT_DIR/refs/bisect/bad" &&
118         case "$(cd "$GIT_DIR" && echo refs/bisect/good-*)" in
119         refs/bisect/good-\*) ;;
120         *) next_ok=yes ;;
121         esac
122         case "$next_ok,$1" in
123         no,) false ;;
124         no,fail)
125             echo >&2 'You need to give me at least one good and one bad revisions.'
126             exit 1 ;;
127         *)
128             true ;;
129         esac
130 }
131
132 bisect_auto_next() {
133         bisect_next_check && bisect_next || :
134 }
135
136 bisect_next() {
137         case "$#" in 0) ;; *) usage ;; esac
138         bisect_autostart
139         bisect_next_check fail
140         bad=$(git-rev-parse --verify refs/bisect/bad) &&
141         good=$(git-rev-parse --sq --revs-only --not \
142                 $(cd "$GIT_DIR" && ls refs/bisect/good-*)) &&
143         rev=$(eval "git-rev-list --bisect $good $bad -- $(cat $GIT_DIR/BISECT_NAMES)") || exit
144         if [ -z "$rev" ]; then
145             echo "$bad was both good and bad"
146             exit 1
147         fi
148         if [ "$rev" = "$bad" ]; then
149             echo "$rev is first bad commit"
150             git-diff-tree --pretty $rev
151             exit 0
152         fi
153         nr=$(eval "git-rev-list $rev $good -- $(cat $GIT_DIR/BISECT_NAMES)" | wc -l) || exit
154         echo "Bisecting: $nr revisions left to test after this"
155         echo "$rev" > "$GIT_DIR/refs/heads/new-bisect"
156         git checkout -q new-bisect || exit
157         mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
158         GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD refs/heads/bisect
159         git-show-branch "$rev"
160 }
161
162 bisect_visualize() {
163         bisect_next_check fail
164         not=`cd "$GIT_DIR/refs" && echo bisect/good-*`
165         eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
166 }
167
168 bisect_reset() {
169         case "$#" in
170         0) if [ -s "$GIT_DIR/head-name" ]; then
171                branch=`cat "$GIT_DIR/head-name"`
172            else
173                branch=master
174            fi ;;
175         1) test -f "$GIT_DIR/refs/heads/$1" || {
176                echo >&2 "$1 does not seem to be a valid branch"
177                exit 1
178            }
179            branch="$1" ;;
180         *)
181             usage ;;
182         esac
183         if git checkout "$branch"; then
184                 rm -fr "$GIT_DIR/refs/bisect"
185                 rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
186                 rm -f "$GIT_DIR/BISECT_LOG"
187                 rm -f "$GIT_DIR/BISECT_NAMES"
188         fi
189 }
190
191 bisect_replay () {
192         test -r "$1" || {
193                 echo >&2 "cannot read $1 for replaying"
194                 exit 1
195         }
196         bisect_reset
197         while read bisect command rev
198         do
199                 test "$bisect" = "git-bisect" || continue
200                 case "$command" in
201                 start)
202                         cmd="bisect_start $rev"
203                         eval "$cmd"
204                         ;;
205                 good)
206                         echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
207                         echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
208                         echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
209                         ;;
210                 bad)
211                         echo "$rev" >"$GIT_DIR/refs/bisect/bad"
212                         echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
213                         echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
214                         ;;
215                 *)
216                         echo >&2 "?? what are you talking about?"
217                         exit 1 ;;
218                 esac
219         done <"$1"
220         bisect_auto_next
221 }
222
223 case "$#" in
224 0)
225     usage ;;
226 *)
227     cmd="$1"
228     shift
229     case "$cmd" in
230     start)
231         bisect_start "$@" ;;
232     bad)
233         bisect_bad "$@" ;;
234     good)
235         bisect_good "$@" ;;
236     next)
237         # Not sure we want "next" at the UI level anymore.
238         bisect_next "$@" ;;
239     visualize)
240         bisect_visualize "$@" ;;
241     reset)
242         bisect_reset "$@" ;;
243     replay)
244         bisect_replay "$@" ;;
245     log)
246         cat "$GIT_DIR/BISECT_LOG" ;;
247     *)
248         usage ;;
249     esac
250 esac