Code

Merge branch 'jc/apply-blank-at-eof-fix' into maint
[git.git] / git-bisect.sh
1 #!/bin/sh
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.
27 Please use "git help bisect" to get the full man page.'
29 OPTIONS_SPEC=
30 . git-sh-setup
31 . git-sh-i18n
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"
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 }
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 }
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
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")"
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                 fi
131         else
132                 # Get rev from where we start.
133                 case "$head" in
134                 refs/heads/*|$_x40)
135                         # This error message should only be triggered by
136                         # cogito usage, and cogito users should understand
137                         # it relates to cg-seek.
138                         [ -s "$GIT_DIR/head-name" ] &&
139                                 die "$(gettext "won't bisect on seeked tree")"
140                         start_head="${head#refs/heads/}"
141                         ;;
142                 *)
143                         die "$(gettext "Bad HEAD - strange symbolic ref")"
144                         ;;
145                 esac
146         fi
148         #
149         # Get rid of any old bisect state.
150         #
151         bisect_clean_state || exit
153         #
154         # Change state.
155         # In case of mistaken revs or checkout error, or signals received,
156         # "bisect_auto_next" below may exit or misbehave.
157         # We have to trap this to be able to clean up using
158         # "bisect_clean_state".
159         #
160         trap 'bisect_clean_state' 0
161         trap 'exit 255' 1 2 3 15
163         #
164         # Write new start state.
165         #
166         echo "$start_head" >"$GIT_DIR/BISECT_START" && {
167                 test "z$mode" != "z--no-checkout" ||
168                 git update-ref --no-deref BISECT_HEAD "$start_head"
169         } &&
170         git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
171         eval "$eval true" &&
172         echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
173         #
174         # Check if we can proceed to the next bisect state.
175         #
176         bisect_auto_next
178         trap '-' 0
181 bisect_write() {
182         state="$1"
183         rev="$2"
184         nolog="$3"
185         case "$state" in
186                 bad)            tag="$state" ;;
187                 good|skip)      tag="$state"-"$rev" ;;
188                 *)              die "$(eval_gettext "Bad bisect_write argument: \$state")" ;;
189         esac
190         git update-ref "refs/bisect/$tag" "$rev" || exit
191         echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG"
192         test -n "$nolog" || echo "git bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
195 is_expected_rev() {
196         test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
197         test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
200 check_expected_revs() {
201         for _rev in "$@"; do
202                 if ! is_expected_rev "$_rev"
203                 then
204                         rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
205                         rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
206                         return
207                 fi
208         done
211 bisect_skip() {
212         all=''
213         for arg in "$@"
214         do
215                 case "$arg" in
216                 *..*)
217                         revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
218                 *)
219                         revs=$(git rev-parse --sq-quote "$arg") ;;
220                 esac
221                 all="$all $revs"
222         done
223         eval bisect_state 'skip' $all
226 bisect_state() {
227         bisect_autostart
228         state=$1
229         case "$#,$state" in
230         0,*)
231                 die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
232         1,bad|1,good|1,skip)
233                 rev=$(git rev-parse --verify $(bisect_head)) ||
234                         die "$(gettext "Bad rev input: $(bisect_head)")"
235                 bisect_write "$state" "$rev"
236                 check_expected_revs "$rev" ;;
237         2,bad|*,good|*,skip)
238                 shift
239                 eval=''
240                 for rev in "$@"
241                 do
242                         sha=$(git rev-parse --verify "$rev^{commit}") ||
243                                 die "$(eval_gettext "Bad rev input: \$rev")"
244                         eval="$eval bisect_write '$state' '$sha'; "
245                 done
246                 eval "$eval"
247                 check_expected_revs "$@" ;;
248         *,bad)
249                 die "$(gettext "'git bisect bad' can take only one argument.")" ;;
250         *)
251                 usage ;;
252         esac
253         bisect_auto_next
256 bisect_next_check() {
257         missing_good= missing_bad=
258         git show-ref -q --verify refs/bisect/bad || missing_bad=t
259         test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
261         case "$missing_good,$missing_bad,$1" in
262         ,,*)
263                 : have both good and bad - ok
264                 ;;
265         *,)
266                 # do not have both but not asked to fail - just report.
267                 false
268                 ;;
269         t,,good)
270                 # have bad but not good.  we could bisect although
271                 # this is less optimum.
272                 gettextln "Warning: bisecting only with a bad commit." >&2
273                 if test -t 0
274                 then
275                         # TRANSLATORS: Make sure to include [Y] and [n] in your
276                         # translation. The program will only accept English input
277                         # at this point.
278                         gettext "Are you sure [Y/n]? " >&2
279                         read yesno
280                         case "$yesno" in [Nn]*) exit 1 ;; esac
281                 fi
282                 : bisect without good...
283                 ;;
284         *)
286                 if test -s "$GIT_DIR/BISECT_START"
287                 then
288                         gettextln "You need to give me at least one good and one bad revisions.
289 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
290                 else
291                         gettextln "You need to start by \"git bisect start\".
292 You then need to give me at least one good and one bad revisions.
293 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
294                 fi
295                 exit 1 ;;
296         esac
299 bisect_auto_next() {
300         bisect_next_check && bisect_next || :
303 bisect_next() {
304         case "$#" in 0) ;; *) usage ;; esac
305         bisect_autostart
306         bisect_next_check good
308         # Perform all bisection computation, display and checkout
309         git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
310         res=$?
312         # Check if we should exit because bisection is finished
313         test $res -eq 10 && exit 0
315         # Check for an error in the bisection process
316         test $res -ne 0 && exit $res
318         return 0
321 bisect_visualize() {
322         bisect_next_check fail
324         if test $# = 0
325         then
326                 if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
327                         type gitk >/dev/null 2>&1
328                 then
329                         set gitk
330                 else
331                         set git log
332                 fi
333         else
334                 case "$1" in
335                 git*|tig) ;;
336                 -*)     set git log "$@" ;;
337                 *)      set git "$@" ;;
338                 esac
339         fi
341         eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
344 bisect_reset() {
345         test -s "$GIT_DIR/BISECT_START" || {
346                 gettextln "We are not bisecting."
347                 return
348         }
349         case "$#" in
350         0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
351         1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || {
352                         invalid="$1"
353                         die "$(eval_gettext "'\$invalid' is not a valid commit")"
354                 }
355                 branch="$1" ;;
356         *)
357                 usage ;;
358         esac
360         if ! test -f "$GIT_DIR/BISECT_HEAD" && ! git checkout "$branch" --
361         then
362                 die "$(eval_gettext "Could not check out original HEAD '\$branch'.
363 Try 'git bisect reset <commit>'.")"
364         fi
365         bisect_clean_state
368 bisect_clean_state() {
369         # There may be some refs packed during bisection.
370         git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
371         while read ref hash
372         do
373                 git update-ref -d $ref $hash || exit
374         done
375         rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
376         rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
377         rm -f "$GIT_DIR/BISECT_LOG" &&
378         rm -f "$GIT_DIR/BISECT_NAMES" &&
379         rm -f "$GIT_DIR/BISECT_RUN" &&
380         # Cleanup head-name if it got left by an old version of git-bisect
381         rm -f "$GIT_DIR/head-name" &&
382         git update-ref -d --no-deref BISECT_HEAD &&
383         # clean up BISECT_START last
384         rm -f "$GIT_DIR/BISECT_START"
387 bisect_replay () {
388         file="$1"
389         test "$#" -eq 1 || die "$(gettext "No logfile given")"
390         test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
391         bisect_reset
392         while read git bisect command rev
393         do
394                 test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
395                 if test "$git" = "git-bisect"
396                 then
397                         rev="$command"
398                         command="$bisect"
399                 fi
400                 case "$command" in
401                 start)
402                         cmd="bisect_start $rev"
403                         eval "$cmd" ;;
404                 good|bad|skip)
405                         bisect_write "$command" "$rev" ;;
406                 *)
407                         die "$(gettext "?? what are you talking about?")" ;;
408                 esac
409         done <"$file"
410         bisect_auto_next
413 bisect_run () {
414         bisect_next_check fail
416         while true
417         do
418                 command="$@"
419                 eval_gettextln "running \$command"
420                 "$@"
421                 res=$?
423                 # Check for really bad run error.
424                 if [ $res -lt 0 -o $res -ge 128 ]
425                 then
426                         eval_gettextln "bisect run failed:
427 exit code \$res from '\$command' is < 0 or >= 128" >&2
428                         exit $res
429                 fi
431                 # Find current state depending on run success or failure.
432                 # A special exit code of 125 means cannot test.
433                 if [ $res -eq 125 ]
434                 then
435                         state='skip'
436                 elif [ $res -gt 0 ]
437                 then
438                         state='bad'
439                 else
440                         state='good'
441                 fi
443                 # We have to use a subshell because "bisect_state" can exit.
444                 ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
445                 res=$?
447                 cat "$GIT_DIR/BISECT_RUN"
449                 if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
450                         > /dev/null
451                 then
452                         gettextln "bisect run cannot continue any more" >&2
453                         exit $res
454                 fi
456                 if [ $res -ne 0 ]
457                 then
458                         eval_gettextln "bisect run failed:
459 'bisect_state \$state' exited with error code \$res" >&2
460                         exit $res
461                 fi
463                 if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null
464                 then
465                         gettextln "bisect run success"
466                         exit 0;
467                 fi
469         done
472 bisect_log () {
473         test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
474         cat "$GIT_DIR/BISECT_LOG"
477 case "$#" in
478 0)
479         usage ;;
480 *)
481         cmd="$1"
482         shift
483         case "$cmd" in
484         help)
485                 git bisect -h ;;
486         start)
487                 bisect_start "$@" ;;
488         bad|good)
489                 bisect_state "$cmd" "$@" ;;
490         skip)
491                 bisect_skip "$@" ;;
492         next)
493                 # Not sure we want "next" at the UI level anymore.
494                 bisect_next "$@" ;;
495         visualize|view)
496                 bisect_visualize "$@" ;;
497         reset)
498                 bisect_reset "$@" ;;
499         replay)
500                 bisect_replay "$@" ;;
501         log)
502                 bisect_log ;;
503         run)
504                 bisect_run "$@" ;;
505         *)
506                 usage ;;
507         esac
508 esac