]> git.ipfire.org Git - thirdparty/git.git/blame - git-bisect.sh
Merge branch 'maint-1.5.4' into maint
[thirdparty/git.git] / git-bisect.sh
CommitLineData
8cc6a083 1#!/bin/sh
d025524d 2
6ca8b977 3USAGE='[start|bad|good|skip|next|reset|visualize|replay|log|run]'
38a47fd6
CC
4LONG_USAGE='git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
5 reset bisect state and start bisection.
6git bisect bad [<rev>]
7 mark <rev> a known-bad revision.
8git bisect good [<rev>...]
9 mark <rev>... known-good revisions.
6ca8b977
CC
10git bisect skip [<rev>...]
11 mark <rev>... untestable revisions.
38a47fd6
CC
12git bisect next
13 find next bisection to test and check it out.
14git bisect reset [<branch>]
15 finish bisection search and go back to branch.
16git bisect visualize
17 show bisect status in gitk.
18git bisect replay <logfile>
19 replay bisection log.
20git bisect log
21 show bisect log.
22git bisect run <cmd>...
23 use <cmd>... to automatically bisect.'
d025524d 24
8f321a39 25OPTIONS_SPEC=
ae2b0f15 26. git-sh-setup
4c550686 27require_work_tree
8cc6a083 28
ce32660e
JS
29_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
30_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
31
e9a45d75 32sq() {
d9bffc08 33 @@PERL@@ -e '
e9a45d75
JH
34 for (@ARGV) {
35 s/'\''/'\'\\\\\'\''/g;
36 print " '\''$_'\''";
37 }
38 print "\n";
39 ' "$@"
40}
41
8cc6a083 42bisect_autostart() {
6459c7c6 43 test -f "$GIT_DIR/BISECT_NAMES" || {
8cc6a083
LT
44 echo >&2 'You need to start by "git bisect start"'
45 if test -t 0
46 then
47 echo >&2 -n 'Do you want me to do it for you [Y/n]? '
48 read yesno
49 case "$yesno" in
50 [Nn]*)
51 exit ;;
52 esac
53 bisect_start
54 else
55 exit 1
56 fi
57 }
58}
59
60bisect_start() {
8cc6a083
LT
61 #
62 # Verify HEAD. If we were bisecting before this, reset to the
63 # top-of-line master first!
64 #
48949a18 65 head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
ce32660e
JS
66 head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
67 die "Bad HEAD - I need a HEAD"
d3e54c88 68 start_head=''
8cc6a083 69 case "$head" in
673e5838 70 refs/heads/bisect)
b577bb92
CW
71 if [ -s "$GIT_DIR/BISECT_START" ]; then
72 branch=`cat "$GIT_DIR/BISECT_START"`
810255fd
PB
73 else
74 branch=master
8fe26f44 75 fi
810255fd 76 git checkout $branch || exit
8cc6a083 77 ;;
ce32660e 78 refs/heads/*|$_x40)
b577bb92
CW
79 # This error message should only be triggered by cogito usage,
80 # and cogito users should understand it relates to cg-seek.
810255fd 81 [ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree"
d3e54c88 82 start_head="${head#refs/heads/}"
8cc6a083
LT
83 ;;
84 *)
8098a178 85 die "Bad HEAD - strange symbolic ref"
8cc6a083
LT
86 ;;
87 esac
88
89 #
90 # Get rid of any old bisect state
91 #
38a47fd6 92 bisect_clean_state
38a47fd6
CC
93
94 #
95 # Check for one bad and then some good revisions.
96 #
97 has_double_dash=0
98 for arg; do
99 case "$arg" in --) has_double_dash=1; break ;; esac
100 done
101 orig_args=$(sq "$@")
102 bad_seen=0
d3e54c88 103 eval=''
38a47fd6
CC
104 while [ $# -gt 0 ]; do
105 arg="$1"
106 case "$arg" in
107 --)
8fe26f44 108 shift
38a47fd6
CC
109 break
110 ;;
111 *)
8fe26f44 112 rev=$(git rev-parse --verify "$arg^{commit}" 2>/dev/null) || {
38a47fd6
CC
113 test $has_double_dash -eq 1 &&
114 die "'$arg' does not appear to be a valid revision"
115 break
116 }
737c74ee
CC
117 case $bad_seen in
118 0) state='bad' ; bad_seen=1 ;;
119 *) state='good' ;;
120 esac
d3e54c88 121 eval="$eval bisect_write '$state' '$rev' 'nolog'; "
8fe26f44 122 shift
38a47fd6
CC
123 ;;
124 esac
8fe26f44 125 done
38a47fd6
CC
126
127 sq "$@" >"$GIT_DIR/BISECT_NAMES"
d3e54c88
CC
128 test -n "$start_head" && echo "$start_head" >"$GIT_DIR/BISECT_START"
129 eval "$eval"
b8652b4d 130 echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG"
38a47fd6 131 bisect_auto_next
8cc6a083
LT
132}
133
55624f9a
CC
134bisect_write() {
135 state="$1"
136 rev="$2"
737c74ee 137 nolog="$3"
55624f9a
CC
138 case "$state" in
139 bad) tag="$state" ;;
140 good|skip) tag="$state"-"$rev" ;;
141 *) die "Bad bisect_write argument: $state" ;;
142 esac
3d7cd64c 143 git update-ref "refs/bisect/$tag" "$rev"
f454cdc4 144 echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG"
737c74ee 145 test -z "$nolog" && echo "git-bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
55624f9a
CC
146}
147
155fc795 148bisect_state() {
8cc6a083 149 bisect_autostart
155fc795
CC
150 state=$1
151 case "$#,$state" in
152 0,*)
153 die "Please call 'bisect_state' with at least one argument." ;;
154 1,bad|1,good|1,skip)
155 rev=$(git rev-parse --verify HEAD) ||
156 die "Bad rev input: HEAD"
157 bisect_write "$state" "$rev" ;;
e3389075 158 2,bad|*,good|*,skip)
155fc795 159 shift
d3e54c88 160 eval=''
e3389075 161 for rev in "$@"
155fc795 162 do
a179a303 163 sha=$(git rev-parse --verify "$rev^{commit}") ||
e3389075 164 die "Bad rev input: $rev"
d3e54c88
CC
165 eval="$eval bisect_write '$state' '$sha'; "
166 done
167 eval "$eval" ;;
e3389075
CC
168 *,bad)
169 die "'git bisect bad' can take only one argument." ;;
cc9f24d0
JH
170 *)
171 usage ;;
8cc6a083 172 esac
97e1c51e
CC
173 bisect_auto_next
174}
175
8cc6a083 176bisect_next_check() {
0a5280a9
JH
177 missing_good= missing_bad=
178 git show-ref -q --verify refs/bisect/bad || missing_bad=t
179 test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
6fecf191 180
0a5280a9
JH
181 case "$missing_good,$missing_bad,$1" in
182 ,,*)
183 : have both good and bad - ok
184 ;;
185 *,)
186 # do not have both but not asked to fail - just report.
187 false
188 ;;
189 t,,good)
190 # have bad but not good. we could bisect although
191 # this is less optimum.
192 echo >&2 'Warning: bisecting only with a bad commit.'
193 if test -t 0
194 then
195 printf >&2 'Are you sure [Y/n]? '
196 case "$(read yesno)" in [Nn]*) exit 1 ;; esac
197 fi
198 : bisect without good...
199 ;;
8cc6a083 200 *)
0a5280a9 201 THEN=''
6459c7c6 202 test -f "$GIT_DIR/BISECT_NAMES" || {
0a5280a9
JH
203 echo >&2 'You need to start by "git bisect start".'
204 THEN='then '
205 }
206 echo >&2 'You '$THEN'need to give me at least one good' \
207 'and one bad revisions.'
208 echo >&2 '(You can use "git bisect bad" and' \
209 '"git bisect good" for that.)'
210 exit 1 ;;
8cc6a083
LT
211 esac
212}
213
214bisect_auto_next() {
434d036f 215 bisect_next_check && bisect_next || :
8cc6a083
LT
216}
217
97e1c51e
CC
218filter_skipped() {
219 _eval="$1"
220 _skip="$2"
221
222 if [ -z "$_skip" ]; then
223 eval $_eval
224 return
225 fi
226
227 # Let's parse the output of:
228 # "git rev-list --bisect-vars --bisect-all ..."
229 eval $_eval | while read hash line
230 do
231 case "$VARS,$FOUND,$TRIED,$hash" in
232 # We display some vars.
233 1,*,*,*) echo "$hash $line" ;;
234
235 # Split line.
236 ,*,*,---*) ;;
237
238 # We had nothing to search.
239 ,,,bisect_rev*)
240 echo "bisect_rev="
241 VARS=1
242 ;;
243
244 # We did not find a good bisect rev.
245 # This should happen only if the "bad"
246 # commit is also a "skip" commit.
247 ,,*,bisect_rev*)
248 echo "bisect_rev=$TRIED"
249 VARS=1
250 ;;
251
252 # We are searching.
253 ,,*,*)
254 TRIED="${TRIED:+$TRIED|}$hash"
255 case "$_skip" in
256 *$hash*) ;;
257 *)
258 echo "bisect_rev=$hash"
259 echo "bisect_tried=\"$TRIED\""
260 FOUND=1
261 ;;
262 esac
263 ;;
264
265 # We have already found a rev to be tested.
266 ,1,*,bisect_rev*) VARS=1 ;;
267 ,1,*,*) ;;
268
269 # ???
270 *) die "filter_skipped error " \
271 "VARS: '$VARS' " \
272 "FOUND: '$FOUND' " \
273 "TRIED: '$TRIED' " \
274 "hash: '$hash' " \
275 "line: '$line'"
276 ;;
277 esac
278 done
279}
280
281exit_if_skipped_commits () {
282 _tried=$1
283 if expr "$_tried" : ".*[|].*" > /dev/null ; then
284 echo "There are only 'skip'ped commit left to test."
285 echo "The first bad commit could be any of:"
e23cb8c0 286 echo "$_tried" | tr '[|]' '[\012]'
97e1c51e
CC
287 echo "We cannot bisect more!"
288 exit 2
289 fi
290}
291
8cc6a083 292bisect_next() {
8fe26f44 293 case "$#" in 0) ;; *) usage ;; esac
8cc6a083 294 bisect_autostart
0a5280a9
JH
295 bisect_next_check good
296
97e1c51e 297 skip=$(git for-each-ref --format='%(objectname)' \
40a7ce64 298 "refs/bisect/skip-*" | tr '\012' ' ') || exit
97e1c51e
CC
299
300 BISECT_OPT=''
301 test -n "$skip" && BISECT_OPT='--bisect-all'
302
5be60078 303 bad=$(git rev-parse --verify refs/bisect/bad) &&
0a5280a9 304 good=$(git for-each-ref --format='^%(objectname)' \
40a7ce64 305 "refs/bisect/good-*" | tr '\012' ' ') &&
97e1c51e 306 eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" &&
0a5280a9 307 eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
97e1c51e 308 eval=$(filter_skipped "$eval" "$skip") &&
0a5280a9
JH
309 eval "$eval" || exit
310
311 if [ -z "$bisect_rev" ]; then
312 echo "$bad was both good and bad"
313 exit 1
670f5fe3 314 fi
0a5280a9 315 if [ "$bisect_rev" = "$bad" ]; then
97e1c51e 316 exit_if_skipped_commits "$bisect_tried"
0a5280a9 317 echo "$bisect_rev is first bad commit"
5be60078 318 git diff-tree --pretty $bisect_rev
0a5280a9 319 exit 0
8cc6a083 320 fi
0a5280a9 321
97e1c51e
CC
322 # We should exit here only if the "bad"
323 # commit is also a "skip" commit (see above).
324 exit_if_skipped_commits "$bisect_rev"
325
0a5280a9 326 echo "Bisecting: $bisect_nr revisions left to test after this"
0bee49c6 327 git branch -f new-bisect "$bisect_rev"
08f16750 328 git checkout -q new-bisect || exit
0bee49c6 329 git branch -M new-bisect bisect
5be60078 330 git show-branch "$bisect_rev"
8cc6a083
LT
331}
332
cc9f24d0
JH
333bisect_visualize() {
334 bisect_next_check fail
235997c9
JH
335
336 if test $# = 0
337 then
508e84a7 338 case "${DISPLAY+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in
235997c9 339 '') set git log ;;
508e84a7 340 set*) set gitk ;;
235997c9
JH
341 esac
342 else
343 case "$1" in
344 git*|tig) ;;
345 -*) set git log "$@" ;;
346 *) set git "$@" ;;
347 esac
348 fi
349
e3f062bf 350 not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*")
235997c9 351 eval '"$@"' refs/bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
cc9f24d0
JH
352}
353
8cc6a083 354bisect_reset() {
fce0499f
CC
355 test -f "$GIT_DIR/BISECT_NAMES" || {
356 echo "We are not bisecting."
357 return
358 }
8cc6a083 359 case "$#" in
b577bb92
CW
360 0) if [ -s "$GIT_DIR/BISECT_START" ]; then
361 branch=`cat "$GIT_DIR/BISECT_START"`
810255fd
PB
362 else
363 branch=master
364 fi ;;
737c74ee
CC
365 1) git show-ref --verify --quiet -- "refs/heads/$1" ||
366 die "$1 does not seem to be a valid branch"
8cc6a083 367 branch="$1" ;;
8fe26f44 368 *)
8cc6a083
LT
369 usage ;;
370 esac
9b709e47 371 if git checkout "$branch"; then
b577bb92 372 # Cleanup head-name if it got left by an old version of git-bisect
38a47fd6 373 rm -f "$GIT_DIR/head-name"
b577bb92 374 rm -f "$GIT_DIR/BISECT_START"
38a47fd6 375 bisect_clean_state
9b709e47 376 fi
e204de28
JH
377}
378
38a47fd6 379bisect_clean_state() {
947a604b
CC
380 # There may be some refs packed during bisection.
381 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* refs/heads/bisect |
382 while read ref hash
383 do
384 git update-ref -d $ref $hash
385 done
38a47fd6
CC
386 rm -f "$GIT_DIR/BISECT_LOG"
387 rm -f "$GIT_DIR/BISECT_NAMES"
388 rm -f "$GIT_DIR/BISECT_RUN"
389}
390
e204de28 391bisect_replay () {
737c74ee 392 test -r "$1" || die "cannot read $1 for replaying"
e204de28
JH
393 bisect_reset
394 while read bisect command rev
395 do
396 test "$bisect" = "git-bisect" || continue
397 case "$command" in
398 start)
e9a45d75 399 cmd="bisect_start $rev"
737c74ee
CC
400 eval "$cmd" ;;
401 good|bad|skip)
402 bisect_write "$command" "$rev" ;;
e204de28 403 *)
737c74ee 404 die "?? what are you talking about?" ;;
e204de28
JH
405 esac
406 done <"$1"
407 bisect_auto_next
8cc6a083
LT
408}
409
a17c4101 410bisect_run () {
83020120
CC
411 bisect_next_check fail
412
a17c4101
CC
413 while true
414 do
415 echo "running $@"
416 "$@"
417 res=$?
418
419 # Check for really bad run error.
420 if [ $res -lt 0 -o $res -ge 128 ]; then
421 echo >&2 "bisect run failed:"
422 echo >&2 "exit code $res from '$@' is < 0 or >= 128"
423 exit $res
424 fi
425
155fc795 426 # Find current state depending on run success or failure.
71b0251c
CC
427 # A special exit code of 125 means cannot test.
428 if [ $res -eq 125 ]; then
429 state='skip'
430 elif [ $res -gt 0 ]; then
155fc795 431 state='bad'
a17c4101 432 else
155fc795 433 state='good'
a17c4101
CC
434 fi
435
155fc795
CC
436 # We have to use a subshell because "bisect_state" can exit.
437 ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
a17c4101
CC
438 res=$?
439
440 cat "$GIT_DIR/BISECT_RUN"
441
71b0251c
CC
442 if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
443 > /dev/null; then
444 echo >&2 "bisect run cannot continue any more"
445 exit $res
446 fi
447
a17c4101
CC
448 if [ $res -ne 0 ]; then
449 echo >&2 "bisect run failed:"
155fc795 450 echo >&2 "'bisect_state $state' exited with error code $res"
a17c4101
CC
451 exit $res
452 fi
453
454 if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
455 echo "bisect run success"
456 exit 0;
457 fi
458
459 done
460}
461
462
8cc6a083
LT
463case "$#" in
4640)
465 usage ;;
466*)
467 cmd="$1"
468 shift
469 case "$cmd" in
470 start)
471 bisect_start "$@" ;;
155fc795
CC
472 bad|good|skip)
473 bisect_state "$cmd" "$@" ;;
8cc6a083
LT
474 next)
475 # Not sure we want "next" at the UI level anymore.
476 bisect_next "$@" ;;
235997c9 477 visualize|view)
cc9f24d0 478 bisect_visualize "$@" ;;
8cc6a083
LT
479 reset)
480 bisect_reset "$@" ;;
e204de28
JH
481 replay)
482 bisect_replay "$@" ;;
483 log)
484 cat "$GIT_DIR/BISECT_LOG" ;;
a17c4101
CC
485 run)
486 bisect_run "$@" ;;
8cc6a083
LT
487 *)
488 usage ;;
489 esac
490esac