Code

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