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