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