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