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