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