]> git.ipfire.org Git - thirdparty/git.git/blob - git-bisect.sh
submodule: use new config API for worktree configurations
[thirdparty/git.git] / git-bisect.sh
1 #!/bin/sh
2
3 USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]'
4 LONG_USAGE='git bisect help
5 print this long help message.
6 git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...]
7 reset bisect state and start bisection.
8 git bisect bad [<rev>]
9 mark <rev> a known-bad revision.
10 git bisect good [<rev>...]
11 mark <rev>... known-good revisions.
12 git bisect skip [(<rev>|<range>)...]
13 mark <rev>... untestable revisions.
14 git bisect next
15 find next bisection to test and check it out.
16 git bisect reset [<commit>]
17 finish bisection search and go back to commit.
18 git bisect visualize
19 show bisect status in gitk.
20 git bisect replay <logfile>
21 replay bisection log.
22 git bisect log
23 show bisect log.
24 git bisect run <cmd>...
25 use <cmd>... to automatically bisect.
26
27 Please use "git help bisect" to get the full man page.'
28
29 OPTIONS_SPEC=
30 . git-sh-setup
31 . git-sh-i18n
32
33 _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
34 _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
35
36 bisect_head()
37 {
38 if test -f "$GIT_DIR/BISECT_HEAD"
39 then
40 echo BISECT_HEAD
41 else
42 echo HEAD
43 fi
44 }
45
46 bisect_autostart() {
47 test -s "$GIT_DIR/BISECT_START" || {
48 gettextln "You need to start by \"git bisect start\"" >&2
49 if test -t 0
50 then
51 # TRANSLATORS: Make sure to include [Y] and [n] in your
52 # translation. The program will only accept English input
53 # at this point.
54 gettext "Do you want me to do it for you [Y/n]? " >&2
55 read yesno
56 case "$yesno" in
57 [Nn]*)
58 exit ;;
59 esac
60 bisect_start
61 else
62 exit 1
63 fi
64 }
65 }
66
67 bisect_start() {
68 #
69 # Check for one bad and then some good revisions.
70 #
71 has_double_dash=0
72 for arg; do
73 case "$arg" in --) has_double_dash=1; break ;; esac
74 done
75 orig_args=$(git rev-parse --sq-quote "$@")
76 bad_seen=0
77 eval=''
78 if test "z$(git rev-parse --is-bare-repository)" != zfalse
79 then
80 mode=--no-checkout
81 else
82 mode=''
83 fi
84 while [ $# -gt 0 ]; do
85 arg="$1"
86 case "$arg" in
87 --)
88 shift
89 break
90 ;;
91 --no-checkout)
92 mode=--no-checkout
93 shift ;;
94 --*)
95 die "$(eval_gettext "unrecognised option: '\$arg'")" ;;
96 *)
97 rev=$(git rev-parse -q --verify "$arg^{commit}") || {
98 test $has_double_dash -eq 1 &&
99 die "$(eval_gettext "'\$arg' does not appear to be a valid revision")"
100 break
101 }
102 case $bad_seen in
103 0) state='bad' ; bad_seen=1 ;;
104 *) state='good' ;;
105 esac
106 eval="$eval bisect_write '$state' '$rev' 'nolog' &&"
107 shift
108 ;;
109 esac
110 done
111
112 #
113 # Verify HEAD.
114 #
115 head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
116 head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
117 die "$(gettext "Bad HEAD - I need a HEAD")"
118
119 #
120 # Check if we are bisecting.
121 #
122 start_head=''
123 if test -s "$GIT_DIR/BISECT_START"
124 then
125 # Reset to the rev from where we started.
126 start_head=$(cat "$GIT_DIR/BISECT_START")
127 if test "z$mode" != "z--no-checkout"
128 then
129 git checkout "$start_head" -- ||
130 die "$(eval_gettext "Checking out '\$start_head' failed. Try 'git bisect reset <valid-branch>'.")"
131 fi
132 else
133 # Get rev from where we start.
134 case "$head" in
135 refs/heads/*|$_x40)
136 # This error message should only be triggered by
137 # cogito usage, and cogito users should understand
138 # it relates to cg-seek.
139 [ -s "$GIT_DIR/head-name" ] &&
140 die "$(gettext "won't bisect on cg-seek'ed tree")"
141 start_head="${head#refs/heads/}"
142 ;;
143 *)
144 die "$(gettext "Bad HEAD - strange symbolic ref")"
145 ;;
146 esac
147 fi
148
149 #
150 # Get rid of any old bisect state.
151 #
152 bisect_clean_state || exit
153
154 #
155 # Change state.
156 # In case of mistaken revs or checkout error, or signals received,
157 # "bisect_auto_next" below may exit or misbehave.
158 # We have to trap this to be able to clean up using
159 # "bisect_clean_state".
160 #
161 trap 'bisect_clean_state' 0
162 trap 'exit 255' 1 2 3 15
163
164 #
165 # Write new start state.
166 #
167 echo "$start_head" >"$GIT_DIR/BISECT_START" && {
168 test "z$mode" != "z--no-checkout" ||
169 git update-ref --no-deref BISECT_HEAD "$start_head"
170 } &&
171 git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
172 eval "$eval true" &&
173 echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
174 #
175 # Check if we can proceed to the next bisect state.
176 #
177 bisect_auto_next
178
179 trap '-' 0
180 }
181
182 bisect_write() {
183 state="$1"
184 rev="$2"
185 nolog="$3"
186 case "$state" in
187 bad) tag="$state" ;;
188 good|skip) tag="$state"-"$rev" ;;
189 *) die "$(eval_gettext "Bad bisect_write argument: \$state")" ;;
190 esac
191 git update-ref "refs/bisect/$tag" "$rev" || exit
192 echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG"
193 test -n "$nolog" || echo "git bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
194 }
195
196 is_expected_rev() {
197 test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
198 test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
199 }
200
201 check_expected_revs() {
202 for _rev in "$@"; do
203 if ! is_expected_rev "$_rev"
204 then
205 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
206 rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
207 return
208 fi
209 done
210 }
211
212 bisect_skip() {
213 all=''
214 for arg in "$@"
215 do
216 case "$arg" in
217 *..*)
218 revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
219 *)
220 revs=$(git rev-parse --sq-quote "$arg") ;;
221 esac
222 all="$all $revs"
223 done
224 eval bisect_state 'skip' $all
225 }
226
227 bisect_state() {
228 bisect_autostart
229 state=$1
230 case "$#,$state" in
231 0,*)
232 die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
233 1,bad|1,good|1,skip)
234 rev=$(git rev-parse --verify $(bisect_head)) ||
235 die "$(gettext "Bad rev input: $(bisect_head)")"
236 bisect_write "$state" "$rev"
237 check_expected_revs "$rev" ;;
238 2,bad|*,good|*,skip)
239 shift
240 hash_list=''
241 for rev in "$@"
242 do
243 sha=$(git rev-parse --verify "$rev^{commit}") ||
244 die "$(eval_gettext "Bad rev input: \$rev")"
245 hash_list="$hash_list $sha"
246 done
247 for rev in $hash_list
248 do
249 bisect_write "$state" "$rev"
250 done
251 check_expected_revs $hash_list ;;
252 *,bad)
253 die "$(gettext "'git bisect bad' can take only one argument.")" ;;
254 *)
255 usage ;;
256 esac
257 bisect_auto_next
258 }
259
260 bisect_next_check() {
261 missing_good= missing_bad=
262 git show-ref -q --verify refs/bisect/bad || missing_bad=t
263 test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
264
265 case "$missing_good,$missing_bad,$1" in
266 ,,*)
267 : have both good and bad - ok
268 ;;
269 *,)
270 # do not have both but not asked to fail - just report.
271 false
272 ;;
273 t,,good)
274 # have bad but not good. we could bisect although
275 # this is less optimum.
276 gettextln "Warning: bisecting only with a bad commit." >&2
277 if test -t 0
278 then
279 # TRANSLATORS: Make sure to include [Y] and [n] in your
280 # translation. The program will only accept English input
281 # at this point.
282 gettext "Are you sure [Y/n]? " >&2
283 read yesno
284 case "$yesno" in [Nn]*) exit 1 ;; esac
285 fi
286 : bisect without good...
287 ;;
288 *)
289
290 if test -s "$GIT_DIR/BISECT_START"
291 then
292 gettextln "You need to give me at least one good and one bad revision.
293 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
294 else
295 gettextln "You need to start by \"git bisect start\".
296 You then need to give me at least one good and one bad revision.
297 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
298 fi
299 exit 1 ;;
300 esac
301 }
302
303 bisect_auto_next() {
304 bisect_next_check && bisect_next || :
305 }
306
307 bisect_next() {
308 case "$#" in 0) ;; *) usage ;; esac
309 bisect_autostart
310 bisect_next_check good
311
312 # Perform all bisection computation, display and checkout
313 git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
314 res=$?
315
316 # Check if we should exit because bisection is finished
317 if test $res -eq 10
318 then
319 bad_rev=$(git show-ref --hash --verify refs/bisect/bad)
320 bad_commit=$(git show-branch $bad_rev)
321 echo "# first bad commit: $bad_commit" >>"$GIT_DIR/BISECT_LOG"
322 exit 0
323 elif test $res -eq 2
324 then
325 echo "# only skipped commits left to test" >>"$GIT_DIR/BISECT_LOG"
326 good_revs=$(git for-each-ref --format="%(objectname)" "refs/bisect/good-*")
327 for skipped in $(git rev-list refs/bisect/bad --not $good_revs)
328 do
329 skipped_commit=$(git show-branch $skipped)
330 echo "# possible first bad commit: $skipped_commit" >>"$GIT_DIR/BISECT_LOG"
331 done
332 exit $res
333 fi
334
335 # Check for an error in the bisection process
336 test $res -ne 0 && exit $res
337
338 return 0
339 }
340
341 bisect_visualize() {
342 bisect_next_check fail
343
344 if test $# = 0
345 then
346 if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
347 type gitk >/dev/null 2>&1
348 then
349 set gitk
350 else
351 set git log
352 fi
353 else
354 case "$1" in
355 git*|tig) ;;
356 -*) set git log "$@" ;;
357 *) set git "$@" ;;
358 esac
359 fi
360
361 eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
362 }
363
364 bisect_reset() {
365 test -s "$GIT_DIR/BISECT_START" || {
366 gettextln "We are not bisecting."
367 return
368 }
369 case "$#" in
370 0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
371 1) git rev-parse --quiet --verify "$1^{commit}" >/dev/null || {
372 invalid="$1"
373 die "$(eval_gettext "'\$invalid' is not a valid commit")"
374 }
375 branch="$1" ;;
376 *)
377 usage ;;
378 esac
379
380 if ! test -f "$GIT_DIR/BISECT_HEAD" && ! git checkout "$branch" --
381 then
382 die "$(eval_gettext "Could not check out original HEAD '\$branch'.
383 Try 'git bisect reset <commit>'.")"
384 fi
385 bisect_clean_state
386 }
387
388 bisect_clean_state() {
389 # There may be some refs packed during bisection.
390 git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
391 while read ref hash
392 do
393 git update-ref -d $ref $hash || exit
394 done
395 rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
396 rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
397 rm -f "$GIT_DIR/BISECT_LOG" &&
398 rm -f "$GIT_DIR/BISECT_NAMES" &&
399 rm -f "$GIT_DIR/BISECT_RUN" &&
400 # Cleanup head-name if it got left by an old version of git-bisect
401 rm -f "$GIT_DIR/head-name" &&
402 git update-ref -d --no-deref BISECT_HEAD &&
403 # clean up BISECT_START last
404 rm -f "$GIT_DIR/BISECT_START"
405 }
406
407 bisect_replay () {
408 file="$1"
409 test "$#" -eq 1 || die "$(gettext "No logfile given")"
410 test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
411 bisect_reset
412 while read git bisect command rev
413 do
414 test "$git $bisect" = "git bisect" || test "$git" = "git-bisect" || continue
415 if test "$git" = "git-bisect"
416 then
417 rev="$command"
418 command="$bisect"
419 fi
420 case "$command" in
421 start)
422 cmd="bisect_start $rev"
423 eval "$cmd" ;;
424 good|bad|skip)
425 bisect_write "$command" "$rev" ;;
426 *)
427 die "$(gettext "?? what are you talking about?")" ;;
428 esac
429 done <"$file"
430 bisect_auto_next
431 }
432
433 bisect_run () {
434 bisect_next_check fail
435
436 while true
437 do
438 command="$@"
439 eval_gettextln "running \$command"
440 "$@"
441 res=$?
442
443 # Check for really bad run error.
444 if [ $res -lt 0 -o $res -ge 128 ]
445 then
446 eval_gettextln "bisect run failed:
447 exit code \$res from '\$command' is < 0 or >= 128" >&2
448 exit $res
449 fi
450
451 # Find current state depending on run success or failure.
452 # A special exit code of 125 means cannot test.
453 if [ $res -eq 125 ]
454 then
455 state='skip'
456 elif [ $res -gt 0 ]
457 then
458 state='bad'
459 else
460 state='good'
461 fi
462
463 # We have to use a subshell because "bisect_state" can exit.
464 ( bisect_state $state >"$GIT_DIR/BISECT_RUN" )
465 res=$?
466
467 cat "$GIT_DIR/BISECT_RUN"
468
469 if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
470 >/dev/null
471 then
472 gettextln "bisect run cannot continue any more" >&2
473 exit $res
474 fi
475
476 if [ $res -ne 0 ]
477 then
478 eval_gettextln "bisect run failed:
479 'bisect_state \$state' exited with error code \$res" >&2
480 exit $res
481 fi
482
483 if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" >/dev/null
484 then
485 gettextln "bisect run success"
486 exit 0;
487 fi
488
489 done
490 }
491
492 bisect_log () {
493 test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
494 cat "$GIT_DIR/BISECT_LOG"
495 }
496
497 case "$#" in
498 0)
499 usage ;;
500 *)
501 cmd="$1"
502 shift
503 case "$cmd" in
504 help)
505 git bisect -h ;;
506 start)
507 bisect_start "$@" ;;
508 bad|good)
509 bisect_state "$cmd" "$@" ;;
510 skip)
511 bisect_skip "$@" ;;
512 next)
513 # Not sure we want "next" at the UI level anymore.
514 bisect_next "$@" ;;
515 visualize|view)
516 bisect_visualize "$@" ;;
517 reset)
518 bisect_reset "$@" ;;
519 replay)
520 bisect_replay "$@" ;;
521 log)
522 bisect_log ;;
523 run)
524 bisect_run "$@" ;;
525 *)
526 usage ;;
527 esac
528 esac