diff options
author | Junio C Hamano <junkio@cox.net> | 2007-04-07 13:20:39 +0400 |
---|---|---|
committer | Junio C Hamano <junkio@cox.net> | 2007-04-07 13:20:39 +0400 |
commit | 5bba1b355ec505407dc7aef56dd0c94f60760f1a (patch) | |
tree | fa2d5645b69bce480740e88ff4932a832cbf25ed | |
parent | b7108a16a6d6a9cc9542d53e8898c0b927bfd59b (diff) | |
parent | 0a5280a9f444c33b0e4ebf2f073df5899c112cf8 (diff) |
Merge branch 'cc/bisect'
* cc/bisect:
git-bisect: allow bisecting with only one bad commit.
t6030: add a bit more tests to git-bisect
git-bisect: modernization
Documentation: bisect: "start" accepts one bad and many good commits
Bisect: teach "bisect start" to optionally use one bad and many good revs.
-rw-r--r-- | Documentation/git-bisect.txt | 19 | ||||
-rwxr-xr-x | git-bisect.sh | 193 | ||||
-rwxr-xr-x | t/t6030-bisect-run.sh | 53 |
3 files changed, 199 insertions, 66 deletions
diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index b2bc58d851..5f68ee1584 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -15,7 +15,7 @@ DESCRIPTION The command takes various subcommands, and different options depending on the subcommand: - git bisect start [<paths>...] + git bisect start [<bad> [<good>...]] [--] [<paths>...] git bisect bad <rev> git bisect good <rev> git bisect reset [<branch>] @@ -134,15 +134,26 @@ $ git reset --hard HEAD~3 # try 3 revs before what Then compile and test the one you chose to try. After that, tell bisect what the result was as usual. -Cutting down bisection by giving path parameter to bisect start -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Cutting down bisection by giving more parameters to bisect start +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can further cut down the number of trials if you know what part of the tree is involved in the problem you are tracking down, by giving paths parameters when you say `bisect start`, like this: ------------ -$ git bisect start arch/i386 include/asm-i386 +$ git bisect start -- arch/i386 include/asm-i386 +------------ + +If you know beforehand more than one good commits, you can narrow the +bisect space down without doing the whole tree checkout every time you +give good commits. You give the bad revision immediately after `start` +and then you give all the good revisions you have: + +------------ +$ git bisect start v2.6.20-rc6 v2.6.20-rc4 v2.6.20-rc1 -- + # v2.6.20-rc6 is bad + # v2.6.20-rc4 and v2.6.20-rc1 are good ------------ Bisect run diff --git a/git-bisect.sh b/git-bisect.sh index 11313a7949..85c374e21e 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -1,15 +1,24 @@ #!/bin/sh USAGE='[start|bad|good|next|reset|visualize|replay|log|run]' -LONG_USAGE='git bisect start [<pathspec>] reset bisect state and start bisection. -git bisect bad [<rev>] mark <rev> a known-bad revision. -git bisect good [<rev>...] mark <rev>... known-good revisions. -git bisect next find next bisection to test and check it out. -git bisect reset [<branch>] finish bisection search and go back to branch. -git bisect visualize show bisect status in gitk. -git bisect replay <logfile> replay bisection log. -git bisect log show bisect log. -git bisect run <cmd>... use <cmd>... to automatically bisect.' +LONG_USAGE='git bisect start [<bad> [<good>...]] [--] [<pathspec>...] + reset bisect state and start bisection. +git bisect bad [<rev>] + mark <rev> a known-bad revision. +git bisect good [<rev>...] + mark <rev>... known-good revisions. +git bisect next + find next bisection to test and check it out. +git bisect reset [<branch>] + finish bisection search and go back to branch. +git bisect visualize + show bisect status in gitk. +git bisect replay <logfile> + replay bisection log. +git bisect log + show bisect log. +git bisect run <cmd>... + use <cmd>... to automatically bisect.' . git-sh-setup require_work_tree @@ -70,14 +79,48 @@ bisect_start() { # # Get rid of any old bisect state # - rm -f "$GIT_DIR/refs/heads/bisect" - rm -rf "$GIT_DIR/refs/bisect/" + bisect_clean_state mkdir "$GIT_DIR/refs/bisect" + + # + # Check for one bad and then some good revisions. + # + has_double_dash=0 + for arg; do + case "$arg" in --) has_double_dash=1; break ;; esac + done + orig_args=$(sq "$@") + bad_seen=0 + while [ $# -gt 0 ]; do + arg="$1" + case "$arg" in + --) + shift + break + ;; + *) + rev=$(git-rev-parse --verify "$arg^{commit}" 2>/dev/null) || { + test $has_double_dash -eq 1 && + die "'$arg' does not appear to be a valid revision" + break + } + if [ $bad_seen -eq 0 ]; then + bad_seen=1 + bisect_write_bad "$rev" + else + bisect_write_good "$rev" + fi + shift + ;; + esac + done + + sq "$@" >"$GIT_DIR/BISECT_NAMES" { printf "git-bisect start" - sq "$@" - } >"$GIT_DIR/BISECT_LOG" - sq "$@" >"$GIT_DIR/BISECT_NAMES" + echo "$orig_args" + } >>"$GIT_DIR/BISECT_LOG" + bisect_auto_next } bisect_bad() { @@ -90,12 +133,17 @@ bisect_bad() { *) usage ;; esac || exit - echo "$rev" >"$GIT_DIR/refs/bisect/bad" - echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG" + bisect_write_bad "$rev" echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG" bisect_auto_next } +bisect_write_bad() { + rev="$1" + echo "$rev" >"$GIT_DIR/refs/bisect/bad" + echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG" +} + bisect_good() { bisect_autostart case "$#" in @@ -106,35 +154,54 @@ bisect_good() { for rev in $revs do rev=$(git-rev-parse --verify "$rev^{commit}") || exit - echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev" - echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG" + bisect_write_good "$rev" echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG" + done bisect_auto_next } +bisect_write_good() { + rev="$1" + echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev" + echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG" +} + 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) - THEN='' - test -d "$GIT_DIR/refs/bisect" || { - echo >&2 'You need to start by "git bisect start".' - THEN='then ' - } - echo >&2 'You '$THEN'need to give me at least one good' \ - 'and one bad revisions.' - echo >&2 '(You can use "git bisect bad" and' \ - '"git bisect good" for that.)' - exit 1 ;; + missing_good= missing_bad= + git show-ref -q --verify refs/bisect/bad || missing_bad=t + test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t + + case "$missing_good,$missing_bad,$1" in + ,,*) + : have both good and bad - ok + ;; + *,) + # do not have both but not asked to fail - just report. + false + ;; + t,,good) + # have bad but not good. we could bisect although + # this is less optimum. + echo >&2 'Warning: bisecting only with a bad commit.' + if test -t 0 + then + printf >&2 'Are you sure [Y/n]? ' + case "$(read yesno)" in [Nn]*) exit 1 ;; esac + fi + : bisect without good... + ;; *) - true ;; + THEN='' + test -d "$GIT_DIR/refs/bisect" || { + echo >&2 'You need to start by "git bisect start".' + THEN='then ' + } + echo >&2 'You '$THEN'need to give me at least one good' \ + 'and one bad revisions.' + echo >&2 '(You can use "git bisect bad" and' \ + '"git bisect good" for that.)' + exit 1 ;; esac } @@ -145,27 +212,32 @@ bisect_auto_next() { bisect_next() { case "$#" in 0) ;; *) usage ;; esac bisect_autostart - bisect_next_check fail + bisect_next_check good + 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 -- $(cat "$GIT_DIR/BISECT_NAMES")") || exit - if [ -z "$rev" ]; then - echo "$bad was both good and bad" - exit 1 + good=$(git for-each-ref --format='^%(objectname)' \ + "refs/bisect/good-*" | tr '[\012]' ' ') && + eval="git-rev-list --bisect-vars $good $bad --" && + eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" && + eval=$(eval "$eval") && + eval "$eval" || exit + + if [ -z "$bisect_rev" ]; then + echo "$bad was both good and bad" + exit 1 fi - if [ "$rev" = "$bad" ]; then - echo "$rev is first bad commit" - git-diff-tree --pretty $rev - exit 0 + if [ "$bisect_rev" = "$bad" ]; then + echo "$bisect_rev is first bad commit" + git-diff-tree --pretty $bisect_rev + exit 0 fi - nr=$(eval "git-rev-list $rev $good -- $(cat $GIT_DIR/BISECT_NAMES)" | wc -l) || exit - echo "Bisecting: $nr revisions left to test after this" - echo "$rev" > "$GIT_DIR/refs/heads/new-bisect" + + echo "Bisecting: $bisect_nr revisions left to test after this" + echo "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect" git checkout -q new-bisect || exit mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" && GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD refs/heads/bisect - git-show-branch "$rev" + git-show-branch "$bisect_rev" } bisect_visualize() { @@ -190,14 +262,19 @@ bisect_reset() { usage ;; esac if git checkout "$branch"; then - rm -fr "$GIT_DIR/refs/bisect" - rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name" - rm -f "$GIT_DIR/BISECT_LOG" - rm -f "$GIT_DIR/BISECT_NAMES" - rm -f "$GIT_DIR/BISECT_RUN" + rm -f "$GIT_DIR/head-name" + bisect_clean_state fi } +bisect_clean_state() { + rm -fr "$GIT_DIR/refs/bisect" + rm -f "$GIT_DIR/refs/heads/bisect" + rm -f "$GIT_DIR/BISECT_LOG" + rm -f "$GIT_DIR/BISECT_NAMES" + rm -f "$GIT_DIR/BISECT_RUN" +} + bisect_replay () { test -r "$1" || { echo >&2 "cannot read $1 for replaying" diff --git a/t/t6030-bisect-run.sh b/t/t6030-bisect-run.sh index 39c72283b5..de3123522a 100755 --- a/t/t6030-bisect-run.sh +++ b/t/t6030-bisect-run.sh @@ -2,7 +2,9 @@ # # Copyright (c) 2007 Christian Couder # -test_description='Tests git-bisect run functionality' +test_description='Tests git-bisect functionality' + +exec </dev/null . ./test-lib.sh @@ -37,11 +39,40 @@ test_expect_success \ HASH3=$(git rev-list HEAD | head -2 | tail -1) && HASH4=$(git rev-list HEAD | head -1)' +test_expect_success 'bisect starts with only one bad' ' + git bisect reset && + git bisect start && + git bisect bad $HASH4 && + git bisect next +' + +test_expect_success 'bisect starts with only one good' ' + git bisect reset && + git bisect start && + git bisect good $HASH1 || return 1 + + if git bisect next + then + echo Oops, should have failed. + false + else + : + fi +' + +test_expect_success 'bisect start with one bad and good' ' + git bisect reset && + git bisect start && + git bisect good $HASH1 && + git bisect bad $HASH4 && + git bisect next +' + # We want to automatically find the commit that # introduced "Another" into hello. test_expect_success \ - 'git bisect run simple case' \ - 'echo "#!/bin/sh" > test_script.sh && + '"git bisect run" simple case' \ + 'echo "#"\!"/bin/sh" > test_script.sh && echo "grep Another hello > /dev/null" >> test_script.sh && echo "test \$? -ne 0" >> test_script.sh && chmod +x test_script.sh && @@ -49,7 +80,21 @@ test_expect_success \ git bisect good $HASH1 && git bisect bad $HASH4 && git bisect run ./test_script.sh > my_bisect_log.txt && - grep "$HASH3 is first bad commit" my_bisect_log.txt' + grep "$HASH3 is first bad commit" my_bisect_log.txt && + git bisect reset' + +# We want to automatically find the commit that +# introduced "Ciao" into hello. +test_expect_success \ + '"git bisect run" with more complex "git bisect start"' \ + 'echo "#"\!"/bin/sh" > test_script.sh && + echo "grep Ciao hello > /dev/null" >> test_script.sh && + echo "test \$? -ne 0" >> test_script.sh && + chmod +x test_script.sh && + git bisect start $HASH4 $HASH1 && + git bisect run ./test_script.sh > my_bisect_log.txt && + grep "$HASH4 is first bad commit" my_bisect_log.txt && + git bisect reset' # # |