Sync with maint
[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
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 }
45
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 }
66
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
111
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")"
118
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                         die "$(eval_gettext "Checking out '\$start_head' failed. Try 'git bisect reset <validbranch>'.")"
131                 fi
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" ] &&
140                                 die "$(gettext "won't bisect on seeked tree")"
141                         start_head="${head#refs/heads/}"
142                         ;;
143                 *)
144                         die "$(gettext "Bad HEAD - strange symbolic ref")"
145                         ;;
146                 esac
147         fi
148
149         #
150         # Get rid of any old bisect state.
151         #
152         bisect_clean_state || exit
153
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         #
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         } &&
171         git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
172         eval "$eval true" &&
173         echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
174         #
175         # Check if we can proceed to the next bisect state.
176         #
177         bisect_auto_next
178
179         trap '-' 0
180 }
181
182 bisect_write() {
183         state="$1"
184         rev="$2"
185         nolog="$3"
186         case "$state" in
187                 bad)            tag="$state" ;;
188                 good|skip)      tag="$state"-"$rev" ;;
189                 *)              die "$(eval_gettext "Bad bisect_write argument: \$state")" ;;
190         esac
191         git update-ref "refs/bisect/$tag" "$rev" || exit
192         echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG"
193         test -n "$nolog" || echo "git bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
194 }
195
196 is_expected_rev() {
197         test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
198         test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
199 }
200
201 check_expected_revs() {
202         for _rev in "$@"; do
203                 if ! is_expected_rev "$_rev"
204                 then
205                         rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
206                         rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
207                         return
208                 fi
209         done
210 }
211
212 bisect_skip() {
213         all=''
214         for arg in "$@"
215         do
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
225 }
226
227 bisect_state() {
228         bisect_autostart
229         state=$1
230         case "$#,$state" in
231         0,*)
232                 die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
233         1,bad|1,good|1,skip)
234                 rev=$(git rev-parse --verify $(bisect_head)) ||
235                         die "$(gettext "Bad rev input: $(bisect_head)")"
236                 bisect_write "$state" "$rev"
237                 check_expected_revs "$rev" ;;
238         2,bad|*,good|*,skip)
239                 shift
240                 eval=''
241                 for rev in "$@"
242                 do
243                         sha=$(git rev-parse --verify "$rev^{commit}") ||
244                                 die "$(eval_gettext "Bad rev input: \$rev")"
245                         eval="$eval bisect_write '$state' '$sha'; "
246                 done
247                 eval "$eval"
248                 check_expected_revs "$@" ;;
249         *,bad)
250                 die "$(gettext "'git bisect bad' can take only one argument.")" ;;
251         *)
252                 usage ;;
253         esac
254         bisect_auto_next
255 }
256
257 bisect_next_check() {
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
261
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.
273                 gettextln "Warning: bisecting only with a bad commit." >&2
274                 if test -t 0
275                 then
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
280                         read yesno
281                         case "$yesno" in [Nn]*) exit 1 ;; esac
282                 fi
283                 : bisect without good...
284                 ;;
285         *)
286
287                 if test -s "$GIT_DIR/BISECT_START"
288                 then
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
291                 else
292                         gettextln "You need to start by \"git bisect start\".
293 You then need to give me at least one good and one bad revisions.
294 (You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
295                 fi
296                 exit 1 ;;
297         esac
298 }
299
300 bisect_auto_next() {
301         bisect_next_check && bisect_next || :
302 }
303
304 bisect_next() {
305         case "$#" in 0) ;; *) usage ;; esac
306         bisect_autostart
307         bisect_next_check good
308
309         # Perform all bisection computation, display and checkout
310         git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
311         res=$?
312
313         # Check if we should exit because bisection is finished
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
320         fi
321
322         # Check for an error in the bisection process
323         test $res -ne 0 && exit $res
324
325         return 0
326 }
327
328 bisect_visualize() {
329         bisect_next_check fail
330
331         if test $# = 0
332         then
333                 if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
334                         type gitk >/dev/null 2>&1
335                 then
336                         set gitk
337                 else
338                         set git log
339                 fi
340         else
341                 case "$1" in
342                 git*|tig) ;;
343                 -*)     set git log "$@" ;;
344                 *)      set git "$@" ;;
345                 esac
346         fi
347
348         eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
349 }
350
351 bisect_reset() {
352         test -s "$GIT_DIR/BISECT_START" || {
353                 gettextln "We are not bisecting."
354                 return
355         }
356         case "$#" in
357         0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
358         1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || {
359                         invalid="$1"
360                         die "$(eval_gettext "'\$invalid' is not a valid commit")"
361                 }
362                 branch="$1" ;;
363         *)
364                 usage ;;
365         esac
366
367         if ! test -f "$GIT_DIR/BISECT_HEAD" && ! git checkout "$branch" --
368         then
369                 die "$(eval_gettext "Could not check out original HEAD '\$branch'.
370 Try 'git bisect reset <commit>'.")"
371         fi
372         bisect_clean_state
373 }
374
375 bisect_clean_state() {
376         # There may be some refs packed during bisection.
377         git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
378         while read ref hash
379         do
380                 git update-ref -d $ref $hash || exit
381         done
382         rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
383         rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
384         rm -f "$GIT_DIR/BISECT_LOG" &&
385         rm -f "$GIT_DIR/BISECT_NAMES" &&
386         rm -f "$GIT_DIR/BISECT_RUN" &&
387         # Cleanup head-name if it got left by an old version of git-bisect
388         rm -f "$GIT_DIR/head-name" &&
389         git update-ref -d --no-deref BISECT_HEAD &&
390         # clean up BISECT_START last
391         rm -f "$GIT_DIR/BISECT_START"
392 }
393
394 bisect_replay () {
395         file="$1"
396         test "$#" -eq 1 || die "$(gettext "No logfile given")"
397         test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
398         bisect_reset
399         while read git bisect command rev
400         do
401                 test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
402                 if test "$git" = "git-bisect"
403                 then
404                         rev="$command"
405                         command="$bisect"
406                 fi
407                 case "$command" in
408                 start)
409                         cmd="bisect_start $rev"
410                         eval "$cmd" ;;
411                 good|bad|skip)
412                         bisect_write "$command" "$rev" ;;
413                 *)
414                         die "$(gettext "?? what are you talking about?")" ;;
415                 esac
416         done <"$file"
417         bisect_auto_next
418 }
419
420 bisect_run () {
421         bisect_next_check fail
422
423         while true
424         do
425                 command="$@"
426                 eval_gettextln "running \$command"
427                 "$@"
428                 res=$?
429
430                 # Check for really bad run error.
431                 if [ $res -lt 0 -o $res -ge 128 ]
432                 then
433                         eval_gettextln "bisect run failed:
434 exit code \$res from '\$command' is < 0 or >= 128" >&2
435                         exit $res
436                 fi
437
438                 # Find current state depending on run success or failure.
439                 # A special exit code of 125 means cannot test.
440                 if [ $res -eq 125 ]
441                 then
442                         state='skip'
443                 elif [ $res -gt 0 ]
444                 then
445                         state='bad'
446                 else
447                         state='good'
448                 fi
449
450                 # We have to use a subshell because "bisect_state" can exit.
451                 ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
452                 res=$?
453
454                 cat "$GIT_DIR/BISECT_RUN"
455
456                 if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
457                         > /dev/null
458                 then
459                         gettextln "bisect run cannot continue any more" >&2
460                         exit $res
461                 fi
462
463                 if [ $res -ne 0 ]
464                 then
465                         eval_gettextln "bisect run failed:
466 'bisect_state \$state' exited with error code \$res" >&2
467                         exit $res
468                 fi
469
470                 if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null
471                 then
472                         gettextln "bisect run success"
473                         exit 0;
474                 fi
475
476         done
477 }
478
479 bisect_log () {
480         test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
481         cat "$GIT_DIR/BISECT_LOG"
482 }
483
484 case "$#" in
485 0)
486         usage ;;
487 *)
488         cmd="$1"
489         shift
490         case "$cmd" in
491         help)
492                 git bisect -h ;;
493         start)
494                 bisect_start "$@" ;;
495         bad|good)
496                 bisect_state "$cmd" "$@" ;;
497         skip)
498                 bisect_skip "$@" ;;
499         next)
500                 # Not sure we want "next" at the UI level anymore.
501                 bisect_next "$@" ;;
502         visualize|view)
503                 bisect_visualize "$@" ;;
504         reset)
505                 bisect_reset "$@" ;;
506         replay)
507                 bisect_replay "$@" ;;
508         log)
509                 bisect_log ;;
510         run)
511                 bisect_run "$@" ;;
512         *)
513                 usage ;;
514         esac
515 esac