{"thread":{"id":"1371","subject":"Re: Making it easier to find which change introduced a bug","startedAt":"2005-07-30T17:08:20Z","lastAt":"2005-07-31T01:04:50Z","messageCount":2,"participants":["Linus Torvalds","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"6637","messageId":"Pine.LNX.4.58.0507300919320.29650@g5.osdl.org","threadId":"1371","inReplyTo":"20050730122007.GA8364@localhost.localdomain","subject":"Re: Making it easier to find which change introduced a bug","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-07-30T17:08:20Z","receivedAt":"2005-07-30T17:08:20Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 30 Jul 2005, Alexander Nyberg wrote:\n> \n> Linus, do you think we could have something like\n> patch-2.6.13-rc4-incremental-broken-out.tar.bz2 that could like Andrew's\n> be placed into patches/ in a tree?\n\nNot really. The thing is, since the git patches really _aren't_ serial, \nand merging isn't based on patch-merging at all (unlike quilt, that \nliterally merges patches as patches), you can't really linearize a git \ntree without getting some really strange behaviour.\n\n> As it stands today it's easier for us who don't know git to just find\n> out in which mainline kernel it works and which -mm it doesn't work in,\n> get the broken-out and start push/pop. And I know I'm not the only one\n> who has noticed this.\n\nWhat we can do is try to script the git bisection thing so that it's\nreally trivial. It's actually very simple to use, and I think somebody had\nsome example scripts around.\n\nHere's a simple starting point for somebody who wants to try.. It's not \nvery well tested, but I've done _some_ testing on it to try to make sure \nit's at least reasonable. It adds four new git commands:\n\n - \"git bisect-start\"\n\treset bisect state\n\n - \"git bisect-bad\"\n\tmark some version known-bad (if no arguments, then current HEAD)\n\n - \"git bisect-good\"\n\tmark some version known-good (if no arguments, then current HEAD)\n\n - \"git bisect\"\n\tdo a bisection between the known bad and the known good heads, and \n\tcheck that version out.\n\nThen, the way you use it is:\n\n\tgit bisect-start\n\tgit bisect-bad\t\t\t# Current version is bad\n\tgit bisect-good v2.6.13-rc2\t# v2.6.13-rc2 was the last version tested that was good\n\tgit bisect\n\nwhich will say something like\n\n\tBisecting: 675 revisions left to test after this\n\nand check out the state in the middle. Now, compile that kernel, and boot \nit. Now, let's say that this booted kernel works fine, then just do\n\n\tgit bisect-good\t\t\t# this one is good\n\tgit bisect\n\nwhich will now say\n\n\tBisecting: 337 revisions left to test after this\n\nand you continue along, compiling that one, testing it, and depending on \nwhether it is good or bad, you say \"git-bisect-good\" or \"git-bisect-bad\", \nand ask for the next bisection.\n\nUntil you have no more left, and you'll have been left with the first bad\nkernel rev in \"refs/bisect/bad\".\n\nOh, and then after you want to reset to the original head, do a\n\n\tgit checkout master\n\nto get back to the master branch, instead of being in one of the bisection \nbranches (\"git bisect-start\" will do that for you too, actually: it will \nreset the bisection state, and before it does that it checks that you're \nnot using some old bisection branch).\n\nNot really any harder than doing series of \"quilt push\" and \"quilt pop\", \nnow is it?\n\n\t\tLinus\n\n---\ndiff --git a/Makefile b/Makefile\n--- a/Makefile\n+++ b/Makefile\n@@ -62,7 +62,9 @@ SCRIPTS=git git-apply-patch-script git-m\n \tgit-format-patch-script git-sh-setup-script git-push-script \\\n \tgit-branch-script git-parse-remote git-verify-tag-script \\\n \tgit-ls-remote-script git-clone-dumb-http git-rename-script \\\n-\tgit-request-pull-script\n+\tgit-request-pull-script git-bisect-bad-script git-bisect-good-script \\\n+\tgit-bisect-script git-bisect-start-script\n+\n \n PROG=   git-update-cache git-diff-files git-init-db git-write-tree \\\n \tgit-read-tree git-commit-tree git-cat-file git-fsck-cache \\\ndiff --git a/git-bisect-bad-script b/git-bisect-bad-script\nnew file mode 100755\n--- /dev/null\n+++ b/git-bisect-bad-script\n@@ -0,0 +1,4 @@\n+#!/bin/sh\n+. git-sh-setup-script || dir \"Not a git archive\"\n+rev=$(git-rev-parse --revs-only --verify --default HEAD \"$@\") || exit\n+echo \"$rev\" > \"$GIT_DIR/refs/bisect/bad\"\ndiff --git a/git-bisect-good-script b/git-bisect-good-script\nnew file mode 100755\n--- /dev/null\n+++ b/git-bisect-good-script\n@@ -0,0 +1,4 @@\n+#!/bin/sh\n+. git-sh-setup-script || dir \"Not a git archive\"\n+rev=$(git-rev-parse --revs-only --verify --default HEAD \"$@\") || exit\n+echo \"$rev\" > \"$GIT_DIR/refs/bisect/good-$rev\"\ndiff --git a/git-bisect-script b/git-bisect-script\nnew file mode 100755\n--- /dev/null\n+++ b/git-bisect-script\n@@ -0,0 +1,15 @@\n+#!/bin/sh\n+. git-sh-setup-script || dir \"Not a git archive\"\n+bad=$(git-rev-parse --revs-only --verify refs/bisect/bad) || exit\n+good=($(git-rev-parse --revs-only --not $(cd \"$GIT_DIR\" ; ls refs/bisect/good-*))) || exit\n+rev=$(git-rev-list --bisect $bad ${good[@]}) || exit\n+nr=$(git-rev-list $rev ${good[@]} | wc -l) || exit\n+if [ \"$nr\" = \"0\" ]; then\n+\techo \"$bad is first bad commit\"\n+\tgit-diff-tree --pretty $bad\n+\texit 0\n+fi\n+echo \"Bisecting: $nr revisions left to test after this\"\n+echo \"$rev\" > \"$GIT_DIR/refs/heads/new-bisect\"\n+git checkout new-bisect || exit\n+cd \"$GIT_DIR\" && mv refs/heads/new-bisect refs/heads/bisect && ln -sf refs/heads/bisect HEAD\ndiff --git a/git-bisect-start-script b/git-bisect-start-script\nnew file mode 100755\n--- /dev/null\n+++ b/git-bisect-start-script\n@@ -0,0 +1,26 @@\n+#!/bin/sh\n+. git-sh-setup-script || die \"Not a git archive\"\n+\n+#\n+# Verify HEAD. If we were bisecting before this, reset to the\n+# top-of-line master first!\n+#\n+head=$(readlink $GIT_DIR/HEAD) || die \"Bad HEAD - I need a symlink\"\n+case \"$head\" in\n+refs/heads/bisect*)\n+\tgit checkout master || exit\n+\t;;\n+refs/heads/*)\n+\t;;\n+*)\n+\tdie \"Bad HEAD - strange symlink\"\n+\t;;\n+esac\n+\n+#\n+# Get rid of any old bisect state\n+#\n+cd \"$GIT_DIR\"\n+rm -f \"refs/heads/bisect\"\n+rm -rf \"refs/bisect/\"\n+mkdir \"refs/bisect\"\n"},{"id":"6650","messageId":"7vy87nyedp.fsf@assigned-by-dhcp.cox.net","threadId":"1371","inReplyTo":"Pine.LNX.4.58.0507300919320.29650@g5.osdl.org","subject":"[PATCH] Making it easier to find which change introduced a bug","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-07-31T01:04:50Z","receivedAt":"2005-07-31T01:04:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"I have placed this, after slight reworking, in the master\nbranch.\n\n[jc: This patch is a rework based on what Linus posted to the\n     list.  The changes are:\n\n  - The original introduced four separate commands, which was\n    three too many, so I merged them into one with subcommands.\n\n  - Since the next thing you would want to do after telling it\n    \"bad\" and \"good\" is always to bisect, this version does it\n    automatically for you.\n\n  - I think the termination condition was wrong.  The original\n    version checked if the set of revisions reachable from next\n    bisection but not rechable from any of the known good ones\n    is empty, but if the current bisection was a bad one, this\n    would not terminate, so I changed it to terminate it when\n    the set becomes a singleton or empty.\n\n  - Removed the use of shell array variable.\n]\n\nSigned-off-by: Linus Torvalds <torvalds@osdl.org>\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\n Makefile          |    2 -\n git-bisect-script |  158 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 159 insertions(+), 1 deletions(-)\n create mode 100755 git-bisect-script\n\n8cc6a083198877fc32224b73c61ec6e6cf8a96f5\ndiff --git a/Makefile b/Makefile\n--- a/Makefile\n+++ b/Makefile\n@@ -62,7 +62,7 @@ SCRIPTS=git git-apply-patch-script git-m\n \tgit-format-patch-script git-sh-setup-script git-push-script \\\n \tgit-branch-script git-parse-remote git-verify-tag-script \\\n \tgit-ls-remote-script git-clone-dumb-http git-rename-script \\\n-\tgit-request-pull-script\n+\tgit-request-pull-script git-bisect-script\n \n PROG=   git-update-cache git-diff-files git-init-db git-write-tree \\\n \tgit-read-tree git-commit-tree git-cat-file git-fsck-cache \\\ndiff --git a/git-bisect-script b/git-bisect-script\nnew file mode 100755\n--- /dev/null\n+++ b/git-bisect-script\n@@ -0,0 +1,158 @@\n+#!/bin/sh\n+. git-sh-setup-script || dir \"Not a git archive\"\n+\n+usage() {\n+    echo >&2 'usage: git bisect [start | bad | good | next | reset]\n+git bisect start\t\treset bisect state and start bisection.\n+git bisect bad [<rev>]\t\tmark <rev> a known-bad revision.\n+git bisect good [<rev>...]\tmark <rev>... known-good revisions.\n+git bisect next\t\t\tfind next bisection to test and check it out.\n+git bisect reset [<branch>]\tfinish bisection search and go back to branch.'\n+    exit 1\n+}\n+\n+bisect_autostart() {\n+\ttest -d \"$GIT_DIR/refs/bisect\" || {\n+\t\techo >&2 'You need to start by \"git bisect start\"'\n+\t\tif test -t 0\n+\t\tthen\n+\t\t\techo >&2 -n 'Do you want me to do it for you [Y/n]? '\n+\t\t\tread yesno\n+\t\t\tcase \"$yesno\" in\n+\t\t\t[Nn]*)\n+\t\t\t\texit ;;\n+\t\t\tesac\n+\t\t\tbisect_start\n+\t\telse\n+\t\t\texit 1\n+\t\tfi\n+\t}\n+}\n+\n+bisect_start() {\n+        case \"$#\" in 0) ;; *) usage ;; esac\n+\t#\n+\t# Verify HEAD. If we were bisecting before this, reset to the\n+\t# top-of-line master first!\n+\t#\n+\thead=$(readlink $GIT_DIR/HEAD) || die \"Bad HEAD - I need a symlink\"\n+\tcase \"$head\" in\n+\trefs/heads/bisect*)\n+\t\tgit checkout master || exit\n+\t\t;;\n+\trefs/heads/*)\n+\t\t;;\n+\t*)\n+\t\tdie \"Bad HEAD - strange symlink\"\n+\t\t;;\n+\tesac\n+\n+\t#\n+\t# Get rid of any old bisect state\n+\t#\n+\trm -f \"$GIT_DIR/refs/heads/bisect\"\n+\trm -rf \"$GIT_DIR/refs/bisect/\"\n+\tmkdir \"$GIT_DIR/refs/bisect\"\n+}\n+\n+bisect_bad() {\n+\tbisect_autostart\n+        case \"$#\" in 0 | 1) ;; *) usage ;; esac\n+\trev=$(git-rev-parse --revs-only --verify --default HEAD \"$@\") || exit\n+\techo \"$rev\" > \"$GIT_DIR/refs/bisect/bad\"\n+\tbisect_auto_next\n+}\n+\n+bisect_good() {\n+\tbisect_autostart\n+        case \"$#\" in\n+\t0)    revs=$(git-rev-parse --verify HEAD) || exit ;;\n+\t*)    revs=$(git-rev-parse --revs-only \"$@\") || exit ;;\n+\tesac\n+\tfor rev in $revs\n+\tdo\n+\t    echo \"$rev\" >\"$GIT_DIR/refs/bisect/good-$rev\"\n+\tdone\n+\tbisect_auto_next\n+}\n+\n+bisect_next_check() {\n+\tnext_ok=no\n+        test -f \"$GIT_DIR/refs/bisect/bad\" &&\n+\tcase \"$(cd \"$GIT_DIR\" && echo refs/bisect/good-*)\" in\n+\trefs/bisect/good-\\*) ;;\n+\t*) next_ok=yes ;;\n+\tesac\n+\tcase \"$next_ok,$1\" in\n+\tno,) false ;;\n+\tno,fail)\n+\t    echo >&2 'You need to give me at least one good and one bad revisions.'\n+\t    exit 1 ;;\n+\t*)\n+\t    true ;;\n+\tesac\n+}\n+\n+bisect_auto_next() {\n+\tbisect_next_check && bisect_next\n+}\n+\n+bisect_next() {\n+        case \"$#\" in 0) ;; *) usage ;; esac\n+\tbisect_autostart\n+\tbisect_next_check fail\n+\tbad=$(git-rev-parse --verify refs/bisect/bad) &&\n+\tgood=$(git-rev-parse --sq --revs-only --not \\\n+\t\t$(cd \"$GIT_DIR\" && ls refs/bisect/good-*)) &&\n+\trev=$(eval \"git-rev-list --bisect $good $bad\") || exit\n+\tnr=$(eval \"git-rev-list $rev $good\" | wc -l) || exit\n+\tif [ \"$nr\" -le \"1\" ]; then\n+\t    echo \"$bad is first bad commit\"\n+\t    git-diff-tree --pretty $bad\n+\t    exit 0\n+\tfi\n+\techo \"Bisecting: $nr revisions left to test after this\"\n+\techo \"$rev\" > \"$GIT_DIR/refs/heads/new-bisect\"\n+\tgit checkout new-bisect || exit\n+\tmv \"$GIT_DIR/refs/heads/new-bisect\" \"$GIT_DIR/refs/heads/bisect\" &&\n+\tln -sf refs/heads/bisect \"$GIT_DIR/HEAD\"\n+}\n+\n+bisect_reset() {\n+\tcase \"$#\" in\n+\t0) branch=master ;;\n+\t1) test -f \"$GIT_DIR/refs/heads/$1\" || {\n+\t       echo >&2 \"$1 does not seem to be a valid branch\"\n+\t       exit 1\n+\t   }\n+\t   branch=\"$1\" ;;\n+        *)\n+\t    usage ;;\n+\tesac\n+\tgit checkout \"$branch\" &&\n+\trm -fr \"$GIT_DIR/refs/bisect\"\n+\trm -f \"$GIT_DIR/refs/reads/bisect\"\n+}\n+\n+case \"$#\" in\n+0)\n+    usage ;;\n+*)\n+    cmd=\"$1\"\n+    shift\n+    case \"$cmd\" in\n+    start)\n+        bisect_start \"$@\" ;;\n+    bad)\n+        bisect_bad \"$@\" ;;\n+    good)\n+        bisect_good \"$@\" ;;\n+    next)\n+        # Not sure we want \"next\" at the UI level anymore.\n+        bisect_next \"$@\" ;;\n+    reset)\n+        bisect_reset \"$@\" ;;\n+    *)\n+        usage ;;\n+    esac\n+esac\n"}]}