diff --git a/Makefile b/Makefile index 7b97be65f9..d2559479b0 100644 --- a/Makefile +++ b/Makefile @@ -62,7 +62,7 @@ SCRIPTS=git git-apply-patch-script git-merge-one-file-script git-prune-script \ git-format-patch-script git-sh-setup-script git-push-script \ git-branch-script git-parse-remote git-verify-tag-script \ git-ls-remote-script git-clone-dumb-http git-rename-script \ - git-request-pull-script + git-request-pull-script git-bisect-script PROG= git-update-cache git-diff-files git-init-db git-write-tree \ git-read-tree git-commit-tree git-cat-file git-fsck-cache \ diff --git a/git-bisect-script b/git-bisect-script new file mode 100755 index 0000000000..2233b4143b --- /dev/null +++ b/git-bisect-script @@ -0,0 +1,158 @@ +#!/bin/sh +. git-sh-setup-script || dir "Not a git archive" + +usage() { + echo >&2 'usage: git bisect [start | bad | good | next | reset] +git bisect start reset bisect state and start bisection. +git bisect bad [] mark a known-bad revision. +git bisect good [...] mark ... known-good revisions. +git bisect next find next bisection to test and check it out. +git bisect reset [] finish bisection search and go back to branch.' + exit 1 +} + +bisect_autostart() { + test -d "$GIT_DIR/refs/bisect" || { + echo >&2 'You need to start by "git bisect start"' + if test -t 0 + then + echo >&2 -n 'Do you want me to do it for you [Y/n]? ' + read yesno + case "$yesno" in + [Nn]*) + exit ;; + esac + bisect_start + else + exit 1 + fi + } +} + +bisect_start() { + case "$#" in 0) ;; *) usage ;; esac + # + # Verify HEAD. If we were bisecting before this, reset to the + # top-of-line master first! + # + head=$(readlink $GIT_DIR/HEAD) || die "Bad HEAD - I need a symlink" + case "$head" in + refs/heads/bisect*) + git checkout master || exit + ;; + refs/heads/*) + ;; + *) + die "Bad HEAD - strange symlink" + ;; + esac + + # + # Get rid of any old bisect state + # + rm -f "$GIT_DIR/refs/heads/bisect" + rm -rf "$GIT_DIR/refs/bisect/" + mkdir "$GIT_DIR/refs/bisect" +} + +bisect_bad() { + bisect_autostart + case "$#" in 0 | 1) ;; *) usage ;; esac + rev=$(git-rev-parse --revs-only --verify --default HEAD "$@") || exit + echo "$rev" > "$GIT_DIR/refs/bisect/bad" + bisect_auto_next +} + +bisect_good() { + bisect_autostart + case "$#" in + 0) revs=$(git-rev-parse --verify HEAD) || exit ;; + *) revs=$(git-rev-parse --revs-only "$@") || exit ;; + esac + for rev in $revs + do + echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev" + done + bisect_auto_next +} + +bisect_next_check() { + next_ok=no + test -f "$GIT_DIR/refs/bisect/bad" && + case "$(cd "$GIT_DIR" && echo refs/bisect/good-*)" in + refs/bisect/good-\*) ;; + *) next_ok=yes ;; + esac + case "$next_ok,$1" in + no,) false ;; + no,fail) + echo >&2 'You need to give me at least one good and one bad revisions.' + exit 1 ;; + *) + true ;; + esac +} + +bisect_auto_next() { + bisect_next_check && bisect_next +} + +bisect_next() { + case "$#" in 0) ;; *) usage ;; esac + bisect_autostart + bisect_next_check fail + bad=$(git-rev-parse --verify refs/bisect/bad) && + good=$(git-rev-parse --sq --revs-only --not \ + $(cd "$GIT_DIR" && ls refs/bisect/good-*)) && + rev=$(eval "git-rev-list --bisect $good $bad") || exit + nr=$(eval "git-rev-list $rev $good" | wc -l) || exit + if [ "$nr" -le "1" ]; then + echo "$bad is first bad commit" + git-diff-tree --pretty $bad + exit 0 + fi + echo "Bisecting: $nr revisions left to test after this" + echo "$rev" > "$GIT_DIR/refs/heads/new-bisect" + git checkout new-bisect || exit + mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" && + ln -sf refs/heads/bisect "$GIT_DIR/HEAD" +} + +bisect_reset() { + case "$#" in + 0) branch=master ;; + 1) test -f "$GIT_DIR/refs/heads/$1" || { + echo >&2 "$1 does not seem to be a valid branch" + exit 1 + } + branch="$1" ;; + *) + usage ;; + esac + git checkout "$branch" && + rm -fr "$GIT_DIR/refs/bisect" + rm -f "$GIT_DIR/refs/reads/bisect" +} + +case "$#" in +0) + usage ;; +*) + cmd="$1" + shift + case "$cmd" in + start) + bisect_start "$@" ;; + bad) + bisect_bad "$@" ;; + good) + bisect_good "$@" ;; + next) + # Not sure we want "next" at the UI level anymore. + bisect_next "$@" ;; + reset) + bisect_reset "$@" ;; + *) + usage ;; + esac +esac