Bisect: implement "git bisect run <cmd>..." to automatically bisect.
authorChristian Couder <chriscool@tuxfamily.org>
Fri, 23 Mar 2007 07:49:59 +0000 (08:49 +0100)
committerJunio C Hamano <junkio@cox.net>
Fri, 23 Mar 2007 08:54:47 +0000 (01:54 -0700)
This idea was suggested by Bill Lear
(Message-ID: <17920.38942.364466.642979@lisa.zopyra.com>)
and I think it is a very good one.

This patch adds a new test file for "git bisect run", but there
is currently only one basic test.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
Documentation/git-bisect.txt
git-bisect.sh
t/t6030-bisect-run.sh [new file with mode: 0755]

index 16ec7269b29c995fe051f74a37ba9d015d972006..b7cd715380442b5af6124e642cdbf0210594c889 100644 (file)
@@ -22,6 +22,7 @@ depending on the subcommand:
  git bisect visualize
  git bisect replay <logfile>
  git bisect log
+ git bisect run <cmd>...
 
 This command uses 'git-rev-list --bisect' option to help drive
 the binary search process to find which change introduced a bug,
@@ -121,6 +122,35 @@ like this:
 $ git bisect start arch/i386 include/asm-i386
 ------------
 
+If you have a script that can tell if the current
+source code is good or bad, you can automatically bisect using:
+
+------------
+$ git bisect run my_script
+------------
+
+Note that the "run" script (`my_script` in the above example)
+should exit with code 0 in
+case the current source code is good and with a code between 1 and 127
+(included) in case the current source code is bad.
+
+Any other exit code (a program that does "exit(-1)" leaves $? = 255,
+see exit(3) manual page, the value is chopped with "& 0377") will
+abort the automatic bisect process.
+
+You may often find that during bisect you want to have near-constant
+tweaks (e.g., s/#define DEBUG 0/#define DEBUG 1/ in a header file, or
+"revision that does not have this commit needs this patch applied to
+work around other problem this bisection is not interested in")
+applied to the revision being tested.
+
+To cope with such a situation, after the inner git-bisect finds the
+next revision to test, with the "run" script, you can apply that tweak
+before compiling, run the real test, and after the test decides if the
+revision (possibly with the needed tweaks) passed the test, rewind the
+tree to the pristine state.  Finally the "run" script can exit with
+the status of the real test to let "git bisect run" command loop to
+know the outcome.
 
 Author
 ------
index dbce0dfec93e41f6c1246ce7648ca1c40aeef5b0..3043f65514d8789b7b8f7f72df5968762facb410 100755 (executable)
@@ -1,14 +1,15 @@
 #!/bin/sh
 
-USAGE='[start|bad|good|next|reset|visualize|replay|log]'
+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 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
@@ -185,6 +186,7 @@ bisect_reset() {
                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"
        fi
 }
 
@@ -220,6 +222,50 @@ bisect_replay () {
        bisect_auto_next
 }
 
+bisect_run () {
+    while true
+    do
+      echo "running $@"
+      "$@"
+      res=$?
+
+      # Check for really bad run error.
+      if [ $res -lt 0 -o $res -ge 128 ]; then
+         echo >&2 "bisect run failed:"
+         echo >&2 "exit code $res from '$@' is < 0 or >= 128"
+         exit $res
+      fi
+
+      # Use "bisect_good" or "bisect_bad"
+      # depending on run success or failure.
+      if [ $res -gt 0 ]; then
+         next_bisect='bisect_bad'
+      else
+         next_bisect='bisect_good'
+      fi
+
+      # We have to use a subshell because bisect_good or
+      # bisect_bad functions can exit.
+      ( $next_bisect > "$GIT_DIR/BISECT_RUN" )
+      res=$?
+
+      cat "$GIT_DIR/BISECT_RUN"
+
+      if [ $res -ne 0 ]; then
+         echo >&2 "bisect run failed:"
+         echo >&2 "$next_bisect exited with error code $res"
+         exit $res
+      fi
+
+      if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
+         echo "bisect run success"
+         exit 0;
+      fi
+
+    done
+}
+
+
 case "$#" in
 0)
     usage ;;
@@ -244,6 +290,8 @@ case "$#" in
        bisect_replay "$@" ;;
     log)
        cat "$GIT_DIR/BISECT_LOG" ;;
+    run)
+        bisect_run "$@" ;;
     *)
         usage ;;
     esac
diff --git a/t/t6030-bisect-run.sh b/t/t6030-bisect-run.sh
new file mode 100755 (executable)
index 0000000..39c7228
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Christian Couder
+#
+test_description='Tests git-bisect run functionality'
+
+. ./test-lib.sh
+
+add_line_into_file()
+{
+    _line=$1
+    _file=$2
+
+    if [ -f "$_file" ]; then
+        echo "$_line" >> $_file || return $?
+        MSG="Add <$_line> into <$_file>."
+    else
+        echo "$_line" > $_file || return $?
+        git add $_file || return $?
+        MSG="Create file <$_file> with <$_line> inside."
+    fi
+
+    git-commit -m "$MSG" $_file
+}
+
+HASH1=
+HASH3=
+HASH4=
+
+test_expect_success \
+    'set up basic repo with 1 file (hello) and 4 commits' \
+    'add_line_into_file "1: Hello World" hello &&
+     add_line_into_file "2: A new day for git" hello &&
+     add_line_into_file "3: Another new day for git" hello &&
+     add_line_into_file "4: Ciao for now" hello &&
+     HASH1=$(git rev-list HEAD | tail -1) &&
+     HASH3=$(git rev-list HEAD | head -2 | tail -1) &&
+     HASH4=$(git rev-list HEAD | head -1)'
+
+# 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 &&
+     echo "grep Another hello > /dev/null" >> test_script.sh &&
+     echo "test \$? -ne 0" >> test_script.sh &&
+     chmod +x test_script.sh &&
+     git bisect start &&
+     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'
+
+#
+#
+test_done
+