diff options
Diffstat (limited to 'git-bisect.sh')
-rwxr-xr-x | git-bisect.sh | 260 |
1 files changed, 14 insertions, 246 deletions
diff --git a/git-bisect.sh b/git-bisect.sh index 10ad340920..8969553658 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -33,16 +33,6 @@ require_work_tree _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" -sq() { - @@PERL@@ -e ' - for (@ARGV) { - s/'\''/'\'\\\\\'\''/g; - print " '\''$_'\''"; - } - print "\n"; - ' "$@" -} - bisect_autostart() { test -s "$GIT_DIR/BISECT_START" || { echo >&2 'You need to start by "git bisect start"' @@ -77,7 +67,7 @@ bisect_start() { then # Reset to the rev from where we started. start_head=$(cat "$GIT_DIR/BISECT_START") - git checkout "$start_head" || exit + git checkout "$start_head" -- || exit else # Get rev from where we start. case "$head" in @@ -107,7 +97,7 @@ bisect_start() { for arg; do case "$arg" in --) has_double_dash=1; break ;; esac done - orig_args=$(sq "$@") + orig_args=$(git rev-parse --sq-quote "$@") bad_seen=0 eval='' while [ $# -gt 0 ]; do @@ -147,7 +137,7 @@ bisect_start() { # Write new start state. # echo "$start_head" >"$GIT_DIR/BISECT_START" && - sq "$@" >"$GIT_DIR/BISECT_NAMES" && + git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" && eval "$eval" && echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit # @@ -177,10 +167,6 @@ is_expected_rev() { test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV") } -mark_expected_rev() { - echo "$1" > "$GIT_DIR/BISECT_EXPECTED_REV" -} - check_expected_revs() { for _rev in "$@"; do if ! is_expected_rev "$_rev"; then @@ -199,7 +185,7 @@ bisect_skip() { *..*) revs=$(git rev-list "$arg") || die "Bad rev input: $arg" ;; *) - revs=$(sq "$arg") ;; + revs=$(git rev-parse --sq-quote "$arg") ;; esac all="$all $revs" done @@ -279,240 +265,22 @@ bisect_auto_next() { bisect_next_check && bisect_next || : } -filter_skipped() { - _eval="$1" - _skip="$2" - - if [ -z "$_skip" ]; then - eval "$_eval" | { - while read line - do - echo "$line &&" - done - echo ':' - } - return - fi - - # Let's parse the output of: - # "git rev-list --bisect-vars --bisect-all ..." - eval "$_eval" | { - VARS= FOUND= TRIED= - while read hash line - do - case "$VARS,$FOUND,$TRIED,$hash" in - 1,*,*,*) - # "bisect_foo=bar" read from rev-list output. - echo "$hash &&" - ;; - ,*,*,---*) - # Separator - ;; - ,,,bisect_rev*) - # We had nothing to search. - echo "bisect_rev= &&" - VARS=1 - ;; - ,,*,bisect_rev*) - # We did not find a good bisect rev. - # This should happen only if the "bad" - # commit is also a "skip" commit. - echo "bisect_rev='$TRIED' &&" - VARS=1 - ;; - ,,*,*) - # We are searching. - TRIED="${TRIED:+$TRIED|}$hash" - case "$_skip" in - *$hash*) ;; - *) - echo "bisect_rev=$hash &&" - echo "bisect_tried='$TRIED' &&" - FOUND=1 - ;; - esac - ;; - ,1,*,bisect_rev*) - # We have already found a rev to be tested. - VARS=1 - ;; - ,1,*,*) - ;; - *) - # Unexpected input - echo "die 'filter_skipped error'" - die "filter_skipped error " \ - "VARS: '$VARS' " \ - "FOUND: '$FOUND' " \ - "TRIED: '$TRIED' " \ - "hash: '$hash' " \ - "line: '$line'" - ;; - esac - done - echo ':' - } -} - -exit_if_skipped_commits () { - _tried=$1 - if expr "$_tried" : ".*[|].*" > /dev/null ; then - echo "There are only 'skip'ped commit left to test." - echo "The first bad commit could be any of:" - echo "$_tried" | tr '[|]' '[\012]' - echo "We cannot bisect more!" - exit 2 - fi -} - -bisect_checkout() { - _rev="$1" - _msg="$2" - echo "Bisecting: $_msg" - mark_expected_rev "$_rev" - git checkout -q "$_rev" || exit - git show-branch "$_rev" -} - -is_among() { - _rev="$1" - _list="$2" - case "$_list" in *$_rev*) return 0 ;; esac - return 1 -} - -handle_bad_merge_base() { - _badmb="$1" - _good="$2" - if is_expected_rev "$_badmb"; then - cat >&2 <<EOF -The merge base $_badmb is bad. -This means the bug has been fixed between $_badmb and [$_good]. -EOF - exit 3 - else - cat >&2 <<EOF -Some good revs are not ancestor of the bad rev. -git bisect cannot work properly in this case. -Maybe you mistake good and bad revs? -EOF - exit 1 - fi -} - -handle_skipped_merge_base() { - _mb="$1" - _bad="$2" - _good="$3" - cat >&2 <<EOF -Warning: the merge base between $_bad and [$_good] must be skipped. -So we cannot be sure the first bad commit is between $_mb and $_bad. -We continue anyway. -EOF -} - -# -# "check_merge_bases" checks that merge bases are not "bad". -# -# - If one is "good", that's good, we have nothing to do. -# - If one is "bad", it means the user assumed something wrong -# and we must exit. -# - If one is "skipped", we can't know but we should warn. -# - If we don't know, we should check it out and ask the user to test. -# -# In the last case we will return 1, and otherwise 0. -# -check_merge_bases() { - _bad="$1" - _good="$2" - _skip="$3" - for _mb in $(git merge-base --all $_bad $_good) - do - if is_among "$_mb" "$_good"; then - continue - elif test "$_mb" = "$_bad"; then - handle_bad_merge_base "$_bad" "$_good" - elif is_among "$_mb" "$_skip"; then - handle_skipped_merge_base "$_mb" "$_bad" "$_good" - else - bisect_checkout "$_mb" "a merge base must be tested" - return 1 - fi - done - return 0 -} - -# -# "check_good_are_ancestors_of_bad" checks that all "good" revs are -# ancestor of the "bad" rev. -# -# If that's not the case, we need to check the merge bases. -# If a merge base must be tested by the user we return 1 and -# otherwise 0. -# -check_good_are_ancestors_of_bad() { - test -f "$GIT_DIR/BISECT_ANCESTORS_OK" && - return - - _bad="$1" - _good=$(echo $2 | sed -e 's/\^//g') - _skip="$3" - - # Bisecting with no good rev is ok - test -z "$_good" && return - - _side=$(git rev-list $_good ^$_bad) - if test -n "$_side"; then - # Return if a checkout was done - check_merge_bases "$_bad" "$_good" "$_skip" || return - fi - - : > "$GIT_DIR/BISECT_ANCESTORS_OK" - - return 0 -} - bisect_next() { case "$#" in 0) ;; *) usage ;; esac bisect_autostart bisect_next_check good - # Get bad, good and skipped revs - bad=$(git rev-parse --verify refs/bisect/bad) && - good=$(git for-each-ref --format='^%(objectname)' \ - "refs/bisect/good-*" | tr '\012' ' ') && - skip=$(git for-each-ref --format='%(objectname)' \ - "refs/bisect/skip-*" | tr '\012' ' ') || exit - - # Maybe some merge bases must be tested first - check_good_are_ancestors_of_bad "$bad" "$good" "$skip" - # Return now if a checkout has already been done - test "$?" -eq "1" && return - - # Get bisection information - BISECT_OPT='' - test -n "$skip" && BISECT_OPT='--bisect-all' - eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" && - eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" && - eval=$(filter_skipped "$eval" "$skip") && - eval "$eval" || exit - - if [ -z "$bisect_rev" ]; then - echo "$bad was both good and bad" - exit 1 - fi - if [ "$bisect_rev" = "$bad" ]; then - exit_if_skipped_commits "$bisect_tried" - echo "$bisect_rev is first bad commit" - git diff-tree --pretty $bisect_rev - exit 0 - fi + # Perform all bisection computation, display and checkout + git bisect--helper --next-all + res=$? + + # Check if we should exit because bisection is finished + test $res -eq 10 && exit 0 - # We should exit here only if the "bad" - # commit is also a "skip" commit (see above). - exit_if_skipped_commits "$bisect_rev" + # Check for an error in the bisection process + test $res -ne 0 && exit $res - bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this" + return 0 } bisect_visualize() { @@ -549,7 +317,7 @@ bisect_reset() { *) usage ;; esac - git checkout "$branch" && bisect_clean_state + git checkout "$branch" -- && bisect_clean_state } bisect_clean_state() { |