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