{"thread":{"id":"10413","subject":"[PATCH 4/9] Bisect: implement \"bisect skip\" to mark untestable revisions.","startedAt":"2007-10-22T05:48:36Z","lastAt":"2007-10-22T05:48:36Z","messageCount":1,"participants":["Christian Couder"],"isPatch":true,"patchVersion":1,"patchTotal":9},"messages":[{"id":"56810","messageId":"20071022074836.991adc5d.chriscool@tuxfamily.org","threadId":"10413","inReplyTo":null,"subject":"[PATCH 4/9] Bisect: implement \"bisect skip\" to mark untestable revisions.","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2007-10-22T05:48:36Z","receivedAt":"2007-10-22T05:48:36Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"When there are some \"skip\"ped revisions, we add the '--bisect-all'\noption to \"git rev-list --bisect-vars\". Then we filter out the\n\"skip\"ped revisions from the result of the rev-list command, and we\nmodify the \"bisect_rev\" var accordingly.\n\nWe don't always use \"--bisect-all\" because it is slower\nthan \"--bisect-vars\" or \"--bisect\".\n\nWhen we cannot find for sure the first bad commit because of\n\"skip\"ped commits, we print the hash of each possible first bad\ncommit and then we exit with code 2.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n git-bisect.sh               |  125 ++++++++++++++++++++++++++++++++++++++++--\n t/t6030-bisect-porcelain.sh |   71 ++++++++++++++++++++++++\n 2 files changed, 190 insertions(+), 6 deletions(-)\n\ndiff --git a/git-bisect.sh b/git-bisect.sh\nindex 436ccf6..cd46190 100755\n--- a/git-bisect.sh\n+++ b/git-bisect.sh\n@@ -17,6 +17,8 @@ git bisect replay <logfile>\n         replay bisection log.\n git bisect log\n         show bisect log.\n+git bisect skip [<rev>...]\n+        mark <rev>... untestable revisions.\n git bisect run <cmd>...\n         use <cmd>... to automatically bisect.'\n \n@@ -163,6 +165,28 @@ bisect_write_good() {\n \techo \"# good: \"$(git show-branch $rev) >>\"$GIT_DIR/BISECT_LOG\"\n }\n \n+bisect_skip() {\n+\tbisect_autostart\n+\tcase \"$#\" in\n+\t0)    revs=$(git rev-parse --verify HEAD) || exit ;;\n+\t*)    revs=$(git rev-parse --revs-only --no-flags \"$@\") &&\n+\t\ttest '' != \"$revs\" || die \"Bad rev input: $@\" ;;\n+\tesac\n+\tfor rev in $revs\n+\tdo\n+\t\trev=$(git rev-parse --verify \"$rev^{commit}\") || exit\n+\t\tbisect_write_skip \"$rev\"\n+\t\techo \"git-bisect skip $rev\" >>\"$GIT_DIR/BISECT_LOG\"\n+\tdone\n+\tbisect_auto_next\n+}\n+\n+bisect_write_skip() {\n+\trev=\"$1\"\n+\techo \"$rev\" >\"$GIT_DIR/refs/bisect/skip-$rev\"\n+\techo \"# skip: \"$(git show-branch $rev) >>\"$GIT_DIR/BISECT_LOG\"\n+}\n+\n bisect_next_check() {\n \tmissing_good= missing_bad=\n \tgit show-ref -q --verify refs/bisect/bad || missing_bad=t\n@@ -205,17 +229,97 @@ bisect_auto_next() {\n \tbisect_next_check && bisect_next || :\n }\n \n+filter_skipped() {\n+\t_eval=\"$1\"\n+\t_skip=\"$2\"\n+\n+\tif [ -z \"$_skip\" ]; then\n+\t\teval $_eval\n+\t\treturn\n+\tfi\n+\n+\t# Let's parse the output of:\n+\t# \"git rev-list --bisect-vars --bisect-all ...\"\n+\teval $_eval | while read hash line\n+\tdo\n+\t\tcase \"$VARS,$FOUND,$TRIED,$hash\" in\n+\t\t\t# We display some vars.\n+\t\t\t1,*,*,*) echo \"$hash $line\" ;;\n+\n+\t\t\t# Split line.\n+\t\t\t,*,*,---*) ;;\n+\n+\t\t\t# We had nothing to search.\n+\t\t\t,,,bisect_rev*)\n+\t\t\t\techo \"bisect_rev=\"\n+\t\t\t\tVARS=1\n+\t\t\t\t;;\n+\n+\t\t\t# We did not find a good bisect rev.\n+\t\t\t# This should happen only if the \"bad\"\n+\t\t\t# commit is also a \"skip\" commit.\n+\t\t\t,,*,bisect_rev*)\n+\t\t\t\techo \"bisect_rev=$TRIED\"\n+\t\t\t\tVARS=1\n+\t\t\t\t;;\n+\n+\t\t\t# We are searching.\n+\t\t\t,,*,*)\n+\t\t\t\tTRIED=\"${TRIED:+$TRIED|}$hash\"\n+\t\t\t\tcase \"$_skip\" in\n+\t\t\t\t*$hash*) ;;\n+\t\t\t\t*)\n+\t\t\t\t\techo \"bisect_rev=$hash\"\n+\t\t\t\t\techo \"bisect_tried=\\\"$TRIED\\\"\"\n+\t\t\t\t\tFOUND=1\n+\t\t\t\t\t;;\n+\t\t\t\tesac\n+\t\t\t\t;;\n+\n+\t\t\t# We have already found a rev to be tested.\n+\t\t\t,1,*,bisect_rev*) VARS=1 ;;\n+\t\t\t,1,*,*) ;;\n+\n+\t\t\t# ???\n+\t\t\t*) die \"filter_skipped error \" \\\n+\t\t\t    \"VARS: '$VARS' \" \\\n+\t\t\t    \"FOUND: '$FOUND' \" \\\n+\t\t\t    \"TRIED: '$TRIED' \" \\\n+\t\t\t    \"hash: '$hash' \" \\\n+\t\t\t    \"line: '$line'\"\n+\t\t\t;;\n+\t\tesac\n+\tdone\n+}\n+\n+exit_if_skipped_commits () {\n+\t_tried=$1\n+\tif expr \"$_tried\" : \".*[|].*\" > /dev/null ; then\n+\t\techo \"There are only 'skip'ped commit left to test.\"\n+\t\techo \"The first bad commit could be any of:\"\n+\t\techo \"$_tried\" | sed -e 's/[|]/\\n/g'\n+\t\techo \"We cannot bisect more!\"\n+\t\texit 2\n+\tfi\n+}\n+\n bisect_next() {\n \tcase \"$#\" in 0) ;; *) usage ;; esac\n \tbisect_autostart\n \tbisect_next_check good\n \n+\tskip=$(git for-each-ref --format='%(objectname)' \\\n+\t\t\"refs/bisect/skip-*\" | tr '[\\012]' ' ') || exit\n+\n+\tBISECT_OPT=''\n+\ttest -n \"$skip\" && BISECT_OPT='--bisect-all'\n+\n \tbad=$(git rev-parse --verify refs/bisect/bad) &&\n \tgood=$(git for-each-ref --format='^%(objectname)' \\\n \t\t\"refs/bisect/good-*\" | tr '[\\012]' ' ') &&\n-\teval=\"git rev-list --bisect-vars $good $bad --\" &&\n+\teval=\"git rev-list --bisect-vars $BISECT_OPT $good $bad --\" &&\n \teval=\"$eval $(cat \"$GIT_DIR/BISECT_NAMES\")\" &&\n-\teval=$(eval \"$eval\") &&\n+\teval=$(filter_skipped \"$eval\" \"$skip\") &&\n \teval \"$eval\" || exit\n \n \tif [ -z \"$bisect_rev\" ]; then\n@@ -223,11 +327,16 @@ bisect_next() {\n \t\texit 1\n \tfi\n \tif [ \"$bisect_rev\" = \"$bad\" ]; then\n+\t\texit_if_skipped_commits \"$bisect_tried\"\n \t\techo \"$bisect_rev is first bad commit\"\n \t\tgit diff-tree --pretty $bisect_rev\n \t\texit 0\n \tfi\n \n+\t# We should exit here only if the \"bad\"\n+\t# commit is also a \"skip\" commit (see above).\n+\texit_if_skipped_commits \"$bisect_rev\"\n+\n \techo \"Bisecting: $bisect_nr revisions left to test after this\"\n \techo \"$bisect_rev\" >\"$GIT_DIR/refs/heads/new-bisect\"\n \tgit checkout -q new-bisect || exit\n@@ -286,15 +395,17 @@ bisect_replay () {\n \t\t\teval \"$cmd\"\n \t\t\t;;\n \t\tgood)\n-\t\t\techo \"$rev\" >\"$GIT_DIR/refs/bisect/good-$rev\"\n-\t\t\techo \"# good: \"$(git show-branch $rev) >>\"$GIT_DIR/BISECT_LOG\"\n+\t\t\tbisect_write_good \"$rev\"\n \t\t\techo \"git-bisect good $rev\" >>\"$GIT_DIR/BISECT_LOG\"\n \t\t\t;;\n \t\tbad)\n-\t\t\techo \"$rev\" >\"$GIT_DIR/refs/bisect/bad\"\n-\t\t\techo \"# bad: \"$(git show-branch $rev) >>\"$GIT_DIR/BISECT_LOG\"\n+\t\t\tbisect_write_bad \"$rev\"\n \t\t\techo \"git-bisect bad $rev\" >>\"$GIT_DIR/BISECT_LOG\"\n \t\t\t;;\n+\t\tskip)\n+\t\t\tbisect_write_skip \"$rev\"\n+\t\t\techo \"git-bisect skip $rev\" >>\"$GIT_DIR/BISECT_LOG\"\n+\t\t\t;;\n \t\t*)\n \t\t\techo >&2 \"?? what are you talking about?\"\n \t\t\texit 1 ;;\n@@ -362,6 +473,8 @@ case \"$#\" in\n         bisect_bad \"$@\" ;;\n     good)\n         bisect_good \"$@\" ;;\n+    skip)\n+        bisect_skip \"$@\" ;;\n     next)\n         # Not sure we want \"next\" at the UI level anymore.\n         bisect_next \"$@\" ;;\ndiff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh\nindex 03cdba5..db82259 100755\n--- a/t/t6030-bisect-porcelain.sh\n+++ b/t/t6030-bisect-porcelain.sh\n@@ -71,6 +71,63 @@ test_expect_success 'bisect start with one bad and good' '\n \tgit bisect next\n '\n \n+# $HASH1 is good, $HASH4 is bad, we skip $HASH3\n+# but $HASH2 is bad,\n+# so we should find $HASH2 as the first bad commit\n+test_expect_success 'bisect skip: successfull result' '\n+\tgit bisect reset &&\n+\tgit bisect start $HASH4 $HASH1 &&\n+\tgit bisect skip &&\n+\tgit bisect bad > my_bisect_log.txt &&\n+\tgrep \"$HASH2 is first bad commit\" my_bisect_log.txt &&\n+\tgit bisect reset\n+'\n+\n+# $HASH1 is good, $HASH4 is bad, we skip $HASH3 and $HASH2\n+# so we should not be able to tell the first bad commit\n+# among $HASH2, $HASH3 and $HASH4\n+test_expect_success 'bisect skip: cannot tell between 3 commits' '\n+\tgit bisect start $HASH4 $HASH1 &&\n+\tgit bisect skip || return 1\n+\n+\tif git bisect skip > my_bisect_log.txt\n+\tthen\n+\t\techo Oops, should have failed.\n+\t\tfalse\n+\telse\n+\t\ttest $? -eq 2 &&\n+\t\tgrep \"first bad commit could be any of\" my_bisect_log.txt &&\n+\t\t! grep $HASH1 my_bisect_log.txt &&\n+\t\tgrep $HASH2 my_bisect_log.txt &&\n+\t\tgrep $HASH3 my_bisect_log.txt &&\n+\t\tgrep $HASH4 my_bisect_log.txt &&\n+\t\tgit bisect reset\n+\tfi\n+'\n+\n+# $HASH1 is good, $HASH4 is bad, we skip $HASH3\n+# but $HASH2 is good,\n+# so we should not be able to tell the first bad commit\n+# among $HASH3 and $HASH4\n+test_expect_success 'bisect skip: cannot tell between 2 commits' '\n+\tgit bisect start $HASH4 $HASH1 &&\n+\tgit bisect skip || return 1\n+\n+\tif git bisect good > my_bisect_log.txt\n+\tthen\n+\t\techo Oops, should have failed.\n+\t\tfalse\n+\telse\n+\t\ttest $? -eq 2 &&\n+\t\tgrep \"first bad commit could be any of\" my_bisect_log.txt &&\n+\t\t! grep $HASH1 my_bisect_log.txt &&\n+\t\t! grep $HASH2 my_bisect_log.txt &&\n+\t\tgrep $HASH3 my_bisect_log.txt &&\n+\t\tgrep $HASH4 my_bisect_log.txt &&\n+\t\tgit bisect reset\n+\tfi\n+'\n+\n # We want to automatically find the commit that\n # introduced \"Another\" into hello.\n test_expect_success \\\n@@ -99,6 +156,20 @@ test_expect_success \\\n      grep \"$HASH4 is first bad commit\" my_bisect_log.txt &&\n      git bisect reset'\n \n+# $HASH1 is good, $HASH5 is bad, we skip $HASH3\n+# but $HASH4 is good,\n+# so we should find $HASH5 as the first bad commit\n+HASH5=\n+test_expect_success 'bisect skip: add line and then a new test' '\n+\tadd_line_into_file \"5: Another new line.\" hello &&\n+\tHASH5=$(git rev-parse --verify HEAD) &&\n+\tgit bisect start $HASH5 $HASH1 &&\n+\tgit bisect skip &&\n+\tgit bisect good > my_bisect_log.txt &&\n+\tgrep \"$HASH5 is first bad commit\" my_bisect_log.txt &&\n+\tgit bisect reset\n+'\n+\n #\n #\n test_done\n-- \n1.5.3.3.136.g591d1-dirty\n"}]}