From 4764f46492b0849ce3cf2f527978acd9c2d5f7e6 Mon Sep 17 00:00:00 2001 From: Jon Seymour Date: Thu, 4 Aug 2011 22:00:57 +1000 Subject: bisect: move argument parsing before state modification. Currently 'git bisect start' modifies some state prior to checking that its arguments are valid. This change moves argument validation before state modification with the effect that state modification does not occur unless argument validations succeeds. An existing test is changed to check that new bisect state is not created if arguments are invalid. A new test is added to check that existing bisect state is not modified if arguments are invalid. Signed-off-by: Jon Seymour Signed-off-by: Junio C Hamano diff --git a/git-bisect.sh b/git-bisect.sh index b2186a8..20f6dd5 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -60,6 +60,39 @@ bisect_autostart() { bisect_start() { # + # 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=$(git rev-parse --sq-quote "$@") + bad_seen=0 + eval='' + while [ $# -gt 0 ]; do + arg="$1" + case "$arg" in + --) + shift + break + ;; + *) + rev=$(git rev-parse -q --verify "$arg^{commit}") || { + test $has_double_dash -eq 1 && + die "$(eval_gettext "'\$arg' does not appear to be a valid revision")" + break + } + case $bad_seen in + 0) state='bad' ; bad_seen=1 ;; + *) state='good' ;; + esac + eval="$eval bisect_write '$state' '$rev' 'nolog'; " + shift + ;; + esac + done + + # # Verify HEAD. # head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) || @@ -98,39 +131,6 @@ bisect_start() { bisect_clean_state || exit # - # 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=$(git rev-parse --sq-quote "$@") - bad_seen=0 - eval='' - while [ $# -gt 0 ]; do - arg="$1" - case "$arg" in - --) - shift - break - ;; - *) - rev=$(git rev-parse -q --verify "$arg^{commit}") || { - test $has_double_dash -eq 1 && - die "$(eval_gettext "'\$arg' does not appear to be a valid revision")" - break - } - case $bad_seen in - 0) state='bad' ; bad_seen=1 ;; - *) state='good' ;; - esac - eval="$eval bisect_write '$state' '$rev' 'nolog'; " - shift - ;; - esac - done - - # # Change state. # In case of mistaken revs or checkout error, or signals received, # "bisect_auto_next" below may exit or misbehave. diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index b5063b6..b3d1b14 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -138,15 +138,23 @@ test_expect_success 'bisect start: back in good branch' ' grep "* other" branch.output > /dev/null ' -test_expect_success 'bisect start: no ".git/BISECT_START" if junk rev' ' - git bisect start $HASH4 $HASH1 -- && - git bisect good && +test_expect_success 'bisect start: no ".git/BISECT_START" created if junk rev' ' + git bisect reset && test_must_fail git bisect start $HASH4 foo -- && git branch > branch.output && grep "* other" branch.output > /dev/null && test_must_fail test -e .git/BISECT_START ' +test_expect_success 'bisect start: existing ".git/BISECT_START" not modified if junk rev' ' + git bisect start $HASH4 $HASH1 -- && + git bisect good && + cp .git/BISECT_START saved && + test_must_fail git bisect start $HASH4 foo -- && + git branch > branch.output && + grep "* (no branch)" branch.output > /dev/null && + test_cmp saved .git/BISECT_START +' test_expect_success 'bisect start: no ".git/BISECT_START" if mistaken rev' ' git bisect start $HASH4 $HASH1 -- && git bisect good && -- cgit v0.10.2-6-g49f6 From 6ba7acffdd24e9ed9b31e503ce87f2af31cd0883 Mon Sep 17 00:00:00 2001 From: Jon Seymour Date: Thu, 4 Aug 2011 22:00:58 +1000 Subject: bisect: use && to connect statements that are deferred with eval. Christian Couder pointed out that the existing eval strategy swallows an initial non-zero return. Using && to connect the statements should fix this. Signed-off-by: Jon Seymour Signed-off-by: Junio C Hamano diff --git a/git-bisect.sh b/git-bisect.sh index 20f6dd5..a44ffe1 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -86,7 +86,7 @@ bisect_start() { 0) state='bad' ; bad_seen=1 ;; *) state='good' ;; esac - eval="$eval bisect_write '$state' '$rev' 'nolog'; " + eval="$eval bisect_write '$state' '$rev' 'nolog' &&" shift ;; esac @@ -145,7 +145,7 @@ bisect_start() { # echo "$start_head" >"$GIT_DIR/BISECT_START" && git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" && - eval "$eval" && + eval "$eval true" && echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit # # Check if we can proceed to the next bisect state. -- cgit v0.10.2-6-g49f6 From d3dfeedf2ec8c72bceaabf4e109c19a14576d333 Mon Sep 17 00:00:00 2001 From: Jon Seymour Date: Thu, 4 Aug 2011 22:00:59 +1000 Subject: bisect: add tests to document expected behaviour in presence of broken trees. If the repo is broken, we expect bisect to fail. Signed-off-by: Jon Seymour Signed-off-by: Junio C Hamano diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index b3d1b14..9ae2de8 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -581,5 +581,53 @@ test_expect_success 'erroring out when using bad path parameters' ' ' # +# This creates a broken branch which cannot be checked out because +# the tree created has been deleted. # +# H1-H2-H3-H4-H5-H6-H7 <--other +# \ +# S5-S6'-S7'-S8'-S9 <--broken +# +# Commits marked with ' have a missing tree. +# +test_expect_success 'broken branch creation' ' + git bisect reset && + git checkout -b broken $HASH4 && + git tag BROKEN_HASH4 $HASH4 && + add_line_into_file "5(broken): first line on a broken branch" hello2 && + git tag BROKEN_HASH5 && + mkdir missing && + :> missing/MISSING && + git add missing/MISSING && + git commit -m "6(broken): Added file that will be deleted" + git tag BROKEN_HASH6 && + add_line_into_file "7(broken): second line on a broken branch" hello2 && + git tag BROKEN_HASH7 && + add_line_into_file "8(broken): third line on a broken branch" hello2 && + git tag BROKEN_HASH8 && + git rm missing/MISSING && + git commit -m "9(broken): Remove missing file" + git tag BROKEN_HASH9 && + rm .git/objects/39/f7e61a724187ab767d2e08442d9b6b9dab587d +' + +echo "" > expected.ok +cat > expected.missing-tree.default <error.txt && + test_cmp expected.missing-tree.default error.txt +' + +test_expect_success 'bisect fails if tree is broken on trial commit' ' + git bisect reset && + test_must_fail git bisect start BROKEN_HASH9 BROKEN_HASH4 2>error.txt && + git reset --hard broken && + git checkout broken && + test_cmp expected.missing-tree.default error.txt +' + test_done -- cgit v0.10.2-6-g49f6 From fee92fc1dd4480d9d5c72bd67243d3f6a9b03132 Mon Sep 17 00:00:00 2001 From: Jon Seymour Date: Thu, 4 Aug 2011 22:01:00 +1000 Subject: bisect: introduce support for --no-checkout option. If --no-checkout is specified, then the bisection process uses: git update-ref --no-deref HEAD at each trial instead of: git checkout Improved-by: Christian Couder Signed-off-by: Jon Seymour Signed-off-by: Junio C Hamano diff --git a/bisect.c b/bisect.c index dd7e8ed..c7b7d79 100644 --- a/bisect.c +++ b/bisect.c @@ -24,6 +24,7 @@ struct argv_array { static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL}; static const char *argv_show_branch[] = {"show-branch", NULL, NULL}; +static const char *argv_update_ref[] = {"update-ref", "--no-deref", "BISECT_HEAD", NULL, NULL}; /* bits #0-15 in revision.h */ @@ -707,16 +708,23 @@ static void mark_expected_rev(char *bisect_rev_hex) die("closing file %s: %s", filename, strerror(errno)); } -static int bisect_checkout(char *bisect_rev_hex) +static int bisect_checkout(char *bisect_rev_hex, int no_checkout) { int res; mark_expected_rev(bisect_rev_hex); argv_checkout[2] = bisect_rev_hex; - res = run_command_v_opt(argv_checkout, RUN_GIT_CMD); - if (res) - exit(res); + if (no_checkout) { + argv_update_ref[3] = bisect_rev_hex; + if (run_command_v_opt(argv_update_ref, RUN_GIT_CMD)) + die("update-ref --no-deref HEAD failed on %s", + bisect_rev_hex); + } else { + res = run_command_v_opt(argv_checkout, RUN_GIT_CMD); + if (res) + exit(res); + } argv_show_branch[1] = bisect_rev_hex; return run_command_v_opt(argv_show_branch, RUN_GIT_CMD); @@ -788,7 +796,7 @@ static void handle_skipped_merge_base(const unsigned char *mb) * - If one is "skipped", we can't know but we should warn. * - If we don't know, we should check it out and ask the user to test. */ -static void check_merge_bases(void) +static void check_merge_bases(int no_checkout) { struct commit_list *result; int rev_nr; @@ -806,7 +814,7 @@ static void check_merge_bases(void) handle_skipped_merge_base(mb); } else { printf("Bisecting: a merge base must be tested\n"); - exit(bisect_checkout(sha1_to_hex(mb))); + exit(bisect_checkout(sha1_to_hex(mb), no_checkout)); } } @@ -849,7 +857,7 @@ static int check_ancestors(const char *prefix) * If a merge base must be tested by the user, its source code will be * checked out to be tested by the user and we will exit. */ -static void check_good_are_ancestors_of_bad(const char *prefix) +static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout) { const char *filename = git_path("BISECT_ANCESTORS_OK"); struct stat st; @@ -868,7 +876,7 @@ static void check_good_are_ancestors_of_bad(const char *prefix) /* Check if all good revs are ancestor of the bad rev. */ if (check_ancestors(prefix)) - check_merge_bases(); + check_merge_bases(no_checkout); /* Create file BISECT_ANCESTORS_OK. */ fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); @@ -908,8 +916,11 @@ static void show_diff_tree(const char *prefix, struct commit *commit) * We use the convention that exiting with an exit code 10 means that * the bisection process finished successfully. * In this case the calling shell script should exit 0. + * + * If no_checkout is non-zero, the bisection process does not + * checkout the trial commit but instead simply updates BISECT_HEAD. */ -int bisect_next_all(const char *prefix) +int bisect_next_all(const char *prefix, int no_checkout) { struct rev_info revs; struct commit_list *tried; @@ -920,7 +931,7 @@ int bisect_next_all(const char *prefix) if (read_bisect_refs()) die("reading bisect refs failed"); - check_good_are_ancestors_of_bad(prefix); + check_good_are_ancestors_of_bad(prefix, no_checkout); bisect_rev_setup(&revs, prefix, "%s", "^%s", 1); revs.limited = 1; @@ -966,6 +977,6 @@ int bisect_next_all(const char *prefix) "(roughly %d step%s)\n", nr, (nr == 1 ? "" : "s"), steps, (steps == 1 ? "" : "s")); - return bisect_checkout(bisect_rev_hex); + return bisect_checkout(bisect_rev_hex, no_checkout); } diff --git a/bisect.h b/bisect.h index 0862ce5..22f2e4d 100644 --- a/bisect.h +++ b/bisect.h @@ -27,7 +27,7 @@ struct rev_list_info { const char *header_prefix; }; -extern int bisect_next_all(const char *prefix); +extern int bisect_next_all(const char *prefix, int no_checkout); extern int estimate_bisect_steps(int all); diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c index 5b22639..8d325a5 100644 --- a/builtin/bisect--helper.c +++ b/builtin/bisect--helper.c @@ -4,16 +4,19 @@ #include "bisect.h" static const char * const git_bisect_helper_usage[] = { - "git bisect--helper --next-all", + "git bisect--helper --next-all [--no-checkout]", NULL }; int cmd_bisect__helper(int argc, const char **argv, const char *prefix) { int next_all = 0; + int no_checkout = 0; struct option options[] = { OPT_BOOLEAN(0, "next-all", &next_all, "perform 'git bisect next'"), + OPT_BOOLEAN(0, "no-checkout", &no_checkout, + "update BISECT_HEAD instead of checking out the current commit"), OPT_END() }; @@ -24,5 +27,5 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) usage_with_options(git_bisect_helper_usage, options); /* next-all */ - return bisect_next_all(prefix); + return bisect_next_all(prefix, no_checkout); } -- cgit v0.10.2-6-g49f6 From 4796e823a3d24d29dbcccfda166d6c88051cbfaa Mon Sep 17 00:00:00 2001 From: Jon Seymour Date: Thu, 4 Aug 2011 22:01:01 +1000 Subject: bisect: introduce --no-checkout support into porcelain. git-bisect can now perform bisection of a history without performing a checkout at each stage of the bisection process. Instead, HEAD is updated. One use-case for this function is allow git bisect to be used with damaged repositories where git checkout would fail because the tree referenced by the commit is damaged. It can also be used in other cases where actual checkout of the tree is not required to progress the bisection. Improved-by: Christian Couder Improved-by: Junio C Hamano Improved-by: Jonathan Nieder Signed-off-by: Jon Seymour Signed-off-by: Junio C Hamano diff --git a/git-bisect.sh b/git-bisect.sh index a44ffe1..b9c18dd 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -3,7 +3,7 @@ USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]' LONG_USAGE='git bisect help print this long help message. -git bisect start [ [...]] [--] [...] +git bisect start [--no-checkout] [ [...]] [--] [...] reset bisect state and start bisection. git bisect bad [] mark a known-bad revision. @@ -34,6 +34,16 @@ require_work_tree _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" +bisect_head() +{ + if test -f "$GIT_DIR/BISECT_HEAD" + then + echo BISECT_HEAD + else + echo HEAD + fi +} + bisect_autostart() { test -s "$GIT_DIR/BISECT_START" || { ( @@ -69,6 +79,7 @@ bisect_start() { orig_args=$(git rev-parse --sq-quote "$@") bad_seen=0 eval='' + mode='' while [ $# -gt 0 ]; do arg="$1" case "$arg" in @@ -76,6 +87,11 @@ bisect_start() { shift break ;; + --no-checkout) + mode=--no-checkout + shift ;; + --*) + die "$(eval_gettext "unrecognised option: '\$arg'")" ;; *) rev=$(git rev-parse -q --verify "$arg^{commit}") || { test $has_double_dash -eq 1 && @@ -107,7 +123,10 @@ bisect_start() { then # Reset to the rev from where we started. start_head=$(cat "$GIT_DIR/BISECT_START") - git checkout "$start_head" -- || exit + if test "z$mode" != "z--no-checkout" + then + git checkout "$start_head" -- + fi else # Get rev from where we start. case "$head" in @@ -143,7 +162,10 @@ bisect_start() { # # Write new start state. # - echo "$start_head" >"$GIT_DIR/BISECT_START" && + echo "$start_head" >"$GIT_DIR/BISECT_START" && { + test "z$mode" != "z--no-checkout" || + git update-ref --no-deref BISECT_HEAD "$start_head" + } && git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" && eval "$eval true" && echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit @@ -206,8 +228,8 @@ bisect_state() { 0,*) die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;; 1,bad|1,good|1,skip) - rev=$(git rev-parse --verify HEAD) || - die "$(gettext "Bad rev input: HEAD")" + rev=$(git rev-parse --verify $(bisect_head)) || + die "$(gettext "Bad rev input: $(bisect_head)")" bisect_write "$state" "$rev" check_expected_revs "$rev" ;; 2,bad|*,good|*,skip) @@ -291,7 +313,7 @@ bisect_next() { bisect_next_check good # Perform all bisection computation, display and checkout - git bisect--helper --next-all + git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout) res=$? # Check if we should exit because bisection is finished @@ -340,12 +362,15 @@ bisect_reset() { *) usage ;; esac - if git checkout "$branch" -- ; then - bisect_clean_state - else - die "$(eval_gettext "Could not check out original HEAD '\$branch'. + if ! test -f "$GIT_DIR/BISECT_HEAD" + then + if ! git checkout "$branch" -- + then + die "$(eval_gettext "Could not check out original HEAD '\$branch'. Try 'git bisect reset '.")" + fi fi + bisect_clean_state } bisect_clean_state() { @@ -362,7 +387,8 @@ bisect_clean_state() { rm -f "$GIT_DIR/BISECT_RUN" && # Cleanup head-name if it got left by an old version of git-bisect rm -f "$GIT_DIR/head-name" && - + git update-ref -d --no-deref BISECT_HEAD && + # clean up BISECT_START last rm -f "$GIT_DIR/BISECT_START" } -- cgit v0.10.2-6-g49f6 From b704a8b3fd1d5a2eeeeadcef48c962db2c84b0af Mon Sep 17 00:00:00 2001 From: Jon Seymour Date: Thu, 4 Aug 2011 22:01:02 +1000 Subject: bisect: add tests for the --no-checkout option. These tests verify that git-bisect --no-checkout can successfully bisect commit histories that reference damaged trees. Signed-off-by: Jon Seymour Signed-off-by: Junio C Hamano diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 9ae2de8..4fb7d11 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -126,6 +126,18 @@ test_expect_success 'bisect reset removes packed refs' ' test -z "$(git for-each-ref "refs/heads/bisect")" ' +test_expect_success 'bisect reset removes bisect state after --no-checkout' ' + git bisect reset && + git bisect start --no-checkout && + git bisect good $HASH1 && + git bisect bad $HASH3 && + git bisect next && + git bisect reset && + test -z "$(git for-each-ref "refs/bisect/*")" && + test -z "$(git for-each-ref "refs/heads/bisect")" && + test -z "$(git for-each-ref "BISECT_HEAD")" +' + test_expect_success 'bisect start: back in good branch' ' git branch > branch.output && grep "* other" branch.output > /dev/null && @@ -630,4 +642,74 @@ test_expect_success 'bisect fails if tree is broken on trial commit' ' test_cmp expected.missing-tree.default error.txt ' +check_same() +{ + echo "Checking $1 is the same as $2" && + git rev-parse "$1" > expected.same && + git rev-parse "$2" > expected.actual && + test_cmp expected.same expected.actual +} + +test_expect_success 'bisect: --no-checkout - start commit bad' ' + git bisect reset && + git bisect start BROKEN_HASH7 BROKEN_HASH4 --no-checkout && + check_same BROKEN_HASH6 BISECT_HEAD && + git bisect reset +' + +test_expect_success 'bisect: --no-checkout - trial commit bad' ' + git bisect reset && + git bisect start broken BROKEN_HASH4 --no-checkout && + check_same BROKEN_HASH6 BISECT_HEAD && + git bisect reset +' + +test_expect_success 'bisect: --no-checkout - target before breakage' ' + git bisect reset && + git bisect start broken BROKEN_HASH4 --no-checkout && + check_same BROKEN_HASH6 BISECT_HEAD && + git bisect bad BISECT_HEAD && + check_same BROKEN_HASH5 BISECT_HEAD && + git bisect bad BISECT_HEAD && + check_same BROKEN_HASH5 bisect/bad && + git bisect reset +' + +test_expect_success 'bisect: --no-checkout - target in breakage' ' + git bisect reset && + git bisect start broken BROKEN_HASH4 --no-checkout && + check_same BROKEN_HASH6 BISECT_HEAD && + git bisect bad BISECT_HEAD && + check_same BROKEN_HASH5 BISECT_HEAD && + git bisect good BISECT_HEAD && + check_same BROKEN_HASH6 bisect/bad && + git bisect reset +' + +test_expect_success 'bisect: --no-checkout - target after breakage' ' + git bisect reset && + git bisect start broken BROKEN_HASH4 --no-checkout && + check_same BROKEN_HASH6 BISECT_HEAD && + git bisect good BISECT_HEAD && + check_same BROKEN_HASH8 BISECT_HEAD && + git bisect good BISECT_HEAD && + check_same BROKEN_HASH9 bisect/bad && + git bisect reset +' + +test_expect_success 'bisect: demonstrate identification of damage boundary' " + git bisect reset && + git checkout broken && + git bisect start broken master --no-checkout && + git bisect run sh -c ' + GOOD=\$(git for-each-ref \"--format=%(objectname)\" refs/bisect/good-*) && + git rev-list --objects BISECT_HEAD --not \$GOOD >tmp.\$\$ && + git pack-objects --stdout >/dev/null < tmp.\$\$ + rc=\$? + rm -f tmp.\$\$ + test \$rc = 0' && + check_same BROKEN_HASH6 bisect/bad && + git bisect reset +" + test_done -- cgit v0.10.2-6-g49f6 From 88d78911accc2b6c44732a1b6f6e9540c519d463 Mon Sep 17 00:00:00 2001 From: Jon Seymour Date: Thu, 4 Aug 2011 22:01:03 +1000 Subject: bisect: add documentation for --no-checkout option. Signed-off-by: Jon Seymour Signed-off-by: Junio C Hamano diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index ab60a18..41e6ca8 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -17,7 +17,7 @@ The command takes various subcommands, and different options depending on the subcommand: git bisect help - git bisect start [ [...]] [--] [...] + git bisect start [--no-checkout] [ [...]] [--] [...] git bisect bad [] git bisect good [...] git bisect skip [(|)...] @@ -263,6 +263,17 @@ rewind the tree to the pristine state. Finally the script should exit with the status of the real test to let the "git bisect run" command loop determine the eventual outcome of the bisect session. +OPTIONS +------- +--no-checkout:: ++ +Do not checkout the new working tree at each iteration of the bisection +process. Instead just update a special reference named 'BISECT_HEAD' to make +it point to the commit that should be tested. ++ +This option may be useful when the test you would perform in each step +does not require a checked out tree. + EXAMPLES -------- @@ -343,6 +354,25 @@ $ git bisect run sh -c "make || exit 125; ~/check_test_case.sh" This shows that you can do without a run script if you write the test on a single line. +* Locate a good region of the object graph in a damaged repository ++ +------------ +$ git bisect start HEAD [ ... ] --no-checkout +$ git bisect run sh -c ' + GOOD=$(git for-each-ref "--format=%(objectname)" refs/bisect/good-*) && + git rev-list --objects BISECT_HEAD --not $GOOD >tmp.$$ && + git pack-objects --stdout >/dev/null Date: Fri, 5 Aug 2011 21:31:30 +1000 Subject: bisect: cleanup whitespace errors in git-bisect.sh. All leading whitespace is now encoded with tabs. After this patch, the following is true: RAW=$(cat git-bisect.sh | md5sum) && ROUNDTRIP=$(cat git-bisect.sh | expand -i - | unexpand --first-only - | md5sum) && LEADING=$(sed -n "/^ */p" < git-bisect.sh | wc -l) && test $RAW = $ROUNDTRIP && test $LEADING = 0 && test -z "$(git diff -w HEAD~1 HEAD)" Signed-off-by: Jon Seymour Signed-off-by: Junio C Hamano diff --git a/git-bisect.sh b/git-bisect.sh index b9c18dd..f5d9ede 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -2,27 +2,27 @@ USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]' LONG_USAGE='git bisect help - print this long help message. + print this long help message. git bisect start [--no-checkout] [ [...]] [--] [...] - reset bisect state and start bisection. + reset bisect state and start bisection. git bisect bad [] - mark a known-bad revision. + mark a known-bad revision. git bisect good [...] - mark ... known-good revisions. + mark ... known-good revisions. git bisect skip [(|)...] - mark ... untestable revisions. + mark ... untestable revisions. git bisect next - find next bisection to test and check it out. + find next bisection to test and check it out. git bisect reset [] - finish bisection search and go back to commit. + finish bisection search and go back to commit. git bisect visualize - show bisect status in gitk. + show bisect status in gitk. git bisect replay - replay bisection log. + replay bisection log. git bisect log - show bisect log. + show bisect log. git bisect run ... - use ... to automatically bisect. + use ... to automatically bisect. Please use "git help bisect" to get the full man page.' @@ -55,7 +55,7 @@ bisect_autostart() { # TRANSLATORS: Make sure to include [Y] and [n] in your # translation. The program will only accept English input # at this point. - gettext "Do you want me to do it for you [Y/n]? " >&2 + gettext "Do you want me to do it for you [Y/n]? " >&2 read yesno case "$yesno" in [Nn]*) @@ -74,38 +74,38 @@ bisect_start() { # has_double_dash=0 for arg; do - case "$arg" in --) has_double_dash=1; break ;; esac + case "$arg" in --) has_double_dash=1; break ;; esac done orig_args=$(git rev-parse --sq-quote "$@") bad_seen=0 eval='' mode='' while [ $# -gt 0 ]; do - arg="$1" - case "$arg" in - --) - shift - break + arg="$1" + case "$arg" in + --) + shift + break ;; - --no-checkout) - mode=--no-checkout - shift ;; - --*) - die "$(eval_gettext "unrecognised option: '\$arg'")" ;; - *) - rev=$(git rev-parse -q --verify "$arg^{commit}") || { - test $has_double_dash -eq 1 && + --no-checkout) + mode=--no-checkout + shift ;; + --*) + die "$(eval_gettext "unrecognised option: '\$arg'")" ;; + *) + rev=$(git rev-parse -q --verify "$arg^{commit}") || { + test $has_double_dash -eq 1 && die "$(eval_gettext "'\$arg' does not appear to be a valid revision")" - break - } - case $bad_seen in - 0) state='bad' ; bad_seen=1 ;; - *) state='good' ;; + break + } + case $bad_seen in + 0) state='bad' ; bad_seen=1 ;; + *) state='good' ;; + esac + eval="$eval bisect_write '$state' '$rev' 'nolog' &&" + shift + ;; esac - eval="$eval bisect_write '$state' '$rev' 'nolog' &&" - shift - ;; - esac done # @@ -207,18 +207,18 @@ check_expected_revs() { } bisect_skip() { - all='' + all='' for arg in "$@" do - case "$arg" in - *..*) - revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;; - *) - revs=$(git rev-parse --sq-quote "$arg") ;; - esac - all="$all $revs" - done - eval bisect_state 'skip' $all + case "$arg" in + *..*) + revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;; + *) + revs=$(git rev-parse --sq-quote "$arg") ;; + esac + all="$all $revs" + done + eval bisect_state 'skip' $all } bisect_state() { @@ -316,7 +316,7 @@ bisect_next() { git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout) res=$? - # Check if we should exit because bisection is finished + # Check if we should exit because bisection is finished test $res -eq 10 && exit 0 # Check for an error in the bisection process @@ -355,12 +355,12 @@ bisect_reset() { case "$#" in 0) branch=$(cat "$GIT_DIR/BISECT_START") ;; 1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || { - invalid="$1" - die "$(eval_gettext "'\$invalid' is not a valid commit")" - } - branch="$1" ;; + invalid="$1" + die "$(eval_gettext "'\$invalid' is not a valid commit")" + } + branch="$1" ;; *) - usage ;; + usage ;; esac if ! test -f "$GIT_DIR/BISECT_HEAD" then @@ -418,65 +418,65 @@ bisect_replay () { } bisect_run () { - bisect_next_check fail - - while true - do - command="$@" - eval_gettext "running \$command"; echo - "$@" - res=$? - - # Check for really bad run error. - if [ $res -lt 0 -o $res -ge 128 ]; then - ( - eval_gettext "bisect run failed: + bisect_next_check fail + + while true + do + command="$@" + eval_gettext "running \$command"; echo + "$@" + res=$? + + # Check for really bad run error. + if [ $res -lt 0 -o $res -ge 128 ]; then + ( + eval_gettext "bisect run failed: exit code \$res from '\$command' is < 0 or >= 128" && - echo - ) >&2 - exit $res - fi - - # Find current state depending on run success or failure. - # A special exit code of 125 means cannot test. - if [ $res -eq 125 ]; then - state='skip' - elif [ $res -gt 0 ]; then - state='bad' - else - state='good' - fi - - # We have to use a subshell because "bisect_state" can exit. - ( bisect_state $state > "$GIT_DIR/BISECT_RUN" ) - res=$? - - cat "$GIT_DIR/BISECT_RUN" - - if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \ - > /dev/null; then - ( - gettext "bisect run cannot continue any more" && - echo - ) >&2 - exit $res - fi - - if [ $res -ne 0 ]; then - ( - eval_gettext "bisect run failed: + echo + ) >&2 + exit $res + fi + + # Find current state depending on run success or failure. + # A special exit code of 125 means cannot test. + if [ $res -eq 125 ]; then + state='skip' + elif [ $res -gt 0 ]; then + state='bad' + else + state='good' + fi + + # We have to use a subshell because "bisect_state" can exit. + ( bisect_state $state > "$GIT_DIR/BISECT_RUN" ) + res=$? + + cat "$GIT_DIR/BISECT_RUN" + + if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \ + > /dev/null; then + ( + gettext "bisect run cannot continue any more" && + echo + ) >&2 + exit $res + fi + + if [ $res -ne 0 ]; then + ( + eval_gettext "bisect run failed: 'bisect_state \$state' exited with error code \$res" && - echo - ) >&2 - exit $res - fi + echo + ) >&2 + exit $res + fi - if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then - gettext "bisect run success"; echo - exit 0; - fi + if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then + gettext "bisect run success"; echo + exit 0; + fi - done + done } bisect_log () { @@ -486,33 +486,33 @@ bisect_log () { case "$#" in 0) - usage ;; + usage ;; *) - cmd="$1" - shift - case "$cmd" in - help) - git bisect -h ;; - start) - bisect_start "$@" ;; - bad|good) - bisect_state "$cmd" "$@" ;; - skip) - bisect_skip "$@" ;; - next) - # Not sure we want "next" at the UI level anymore. - bisect_next "$@" ;; - visualize|view) - bisect_visualize "$@" ;; - reset) - bisect_reset "$@" ;; - replay) - bisect_replay "$@" ;; - log) - bisect_log ;; - run) - bisect_run "$@" ;; - *) - usage ;; - esac + cmd="$1" + shift + case "$cmd" in + help) + git bisect -h ;; + start) + bisect_start "$@" ;; + bad|good) + bisect_state "$cmd" "$@" ;; + skip) + bisect_skip "$@" ;; + next) + # Not sure we want "next" at the UI level anymore. + bisect_next "$@" ;; + visualize|view) + bisect_visualize "$@" ;; + reset) + bisect_reset "$@" ;; + replay) + bisect_replay "$@" ;; + log) + bisect_log ;; + run) + bisect_run "$@" ;; + *) + usage ;; + esac esac -- cgit v0.10.2-6-g49f6 From eef12a9a7782bfe12f1ef870aa73143622be2ac1 Mon Sep 17 00:00:00 2001 From: Jon Seymour Date: Fri, 5 Aug 2011 21:31:31 +1000 Subject: bisect: replace "; then" with "\n*then" Signed-off-by: Jon Seymour Signed-off-by: Junio C Hamano diff --git a/git-bisect.sh b/git-bisect.sh index f5d9ede..1f3c46d 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -198,7 +198,8 @@ is_expected_rev() { check_expected_revs() { for _rev in "$@"; do - if ! is_expected_rev "$_rev"; then + if ! is_expected_rev "$_rev" + then rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" rm -f "$GIT_DIR/BISECT_EXPECTED_REV" return @@ -331,7 +332,8 @@ bisect_visualize() { if test $# = 0 then if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" && - type gitk >/dev/null 2>&1; then + type gitk >/dev/null 2>&1 + then set gitk else set git log @@ -400,7 +402,8 @@ bisect_replay () { while read git bisect command rev do test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue - if test "$git" = "git-bisect"; then + if test "$git" = "git-bisect" + then rev="$command" command="$bisect" fi @@ -428,7 +431,8 @@ bisect_run () { res=$? # Check for really bad run error. - if [ $res -lt 0 -o $res -ge 128 ]; then + if [ $res -lt 0 -o $res -ge 128 ] + then ( eval_gettext "bisect run failed: exit code \$res from '\$command' is < 0 or >= 128" && @@ -439,9 +443,11 @@ exit code \$res from '\$command' is < 0 or >= 128" && # Find current state depending on run success or failure. # A special exit code of 125 means cannot test. - if [ $res -eq 125 ]; then + if [ $res -eq 125 ] + then state='skip' - elif [ $res -gt 0 ]; then + elif [ $res -gt 0 ] + then state='bad' else state='good' @@ -454,7 +460,8 @@ exit code \$res from '\$command' is < 0 or >= 128" && cat "$GIT_DIR/BISECT_RUN" if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \ - > /dev/null; then + > /dev/null + then ( gettext "bisect run cannot continue any more" && echo @@ -462,7 +469,8 @@ exit code \$res from '\$command' is < 0 or >= 128" && exit $res fi - if [ $res -ne 0 ]; then + if [ $res -ne 0 ] + then ( eval_gettext "bisect run failed: 'bisect_state \$state' exited with error code \$res" && @@ -471,7 +479,8 @@ exit code \$res from '\$command' is < 0 or >= 128" && exit $res fi - if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then + if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null + then gettext "bisect run success"; echo exit 0; fi -- cgit v0.10.2-6-g49f6 From 43b8ff4b149bb3e17271608380b4a73c2e290b77 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 5 Aug 2011 10:09:23 -0700 Subject: bisect: further style nitpicks Fix a few remaining lines that indented with spaces. Also simplify the logic of checking out the original branch and reporting error during "bisect reset". Signed-off-by: Junio C Hamano diff --git a/git-bisect.sh b/git-bisect.sh index 1f3c46d..22c4da5 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -94,9 +94,9 @@ bisect_start() { die "$(eval_gettext "unrecognised option: '\$arg'")" ;; *) rev=$(git rev-parse -q --verify "$arg^{commit}") || { - test $has_double_dash -eq 1 && - die "$(eval_gettext "'\$arg' does not appear to be a valid revision")" - break + test $has_double_dash -eq 1 && + die "$(eval_gettext "'\$arg' does not appear to be a valid revision")" + break } case $bad_seen in 0) state='bad' ; bad_seen=1 ;; @@ -125,7 +125,7 @@ bisect_start() { start_head=$(cat "$GIT_DIR/BISECT_START") if test "z$mode" != "z--no-checkout" then - git checkout "$start_head" -- + git checkout "$start_head" -- fi else # Get rev from where we start. @@ -332,7 +332,7 @@ bisect_visualize() { if test $# = 0 then if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" && - type gitk >/dev/null 2>&1 + type gitk >/dev/null 2>&1 then set gitk else @@ -364,13 +364,11 @@ bisect_reset() { *) usage ;; esac - if ! test -f "$GIT_DIR/BISECT_HEAD" + + if ! test -f "$GIT_DIR/BISECT_HEAD" && ! git checkout "$branch" -- then - if ! git checkout "$branch" -- - then - die "$(eval_gettext "Could not check out original HEAD '\$branch'. + die "$(eval_gettext "Could not check out original HEAD '\$branch'. Try 'git bisect reset '.")" - fi fi bisect_clean_state } -- cgit v0.10.2-6-g49f6 From 24c512803dadf61b2f7f8965735b7c3a03f6147e Mon Sep 17 00:00:00 2001 From: Jon Seymour Date: Tue, 9 Aug 2011 12:11:54 +1000 Subject: bisect: add support for bisecting bare repositories This enhances the support for bisecting history in bare repositories. The "git bisect" command no longer needs to be run inside a repository with a working tree; it defaults to --no-checkout when run in a bare repository. Two tests are included to demonstrate this behaviour. Suggested-by: Junio C Hamano Reviewed-by: Jonathan Nieder Signed-off-by: Jon Seymour Signed-off-by: Junio C Hamano diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index 41e6ca8..e4f46bc 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -273,6 +273,8 @@ it point to the commit that should be tested. + This option may be useful when the test you would perform in each step does not require a checked out tree. ++ +If the repository is bare, `--no-checkout` is assumed. EXAMPLES -------- diff --git a/git-bisect.sh b/git-bisect.sh index 22c4da5..e0ca3fb 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -29,7 +29,6 @@ Please use "git help bisect" to get the full man page.' OPTIONS_SPEC= . git-sh-setup . git-sh-i18n -require_work_tree _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" @@ -79,7 +78,12 @@ bisect_start() { orig_args=$(git rev-parse --sq-quote "$@") bad_seen=0 eval='' - mode='' + if test "z$(git rev-parse --is-bare-repository)" != zfalse + then + mode=--no-checkout + else + mode='' + fi while [ $# -gt 0 ]; do arg="$1" case "$arg" in diff --git a/git.c b/git.c index 8828c18..7fdcab2 100644 --- a/git.c +++ b/git.c @@ -320,7 +320,7 @@ static void handle_internal_command(int argc, const char **argv) { "annotate", cmd_annotate, RUN_SETUP }, { "apply", cmd_apply, RUN_SETUP_GENTLY }, { "archive", cmd_archive }, - { "bisect--helper", cmd_bisect__helper, RUN_SETUP | NEED_WORK_TREE }, + { "bisect--helper", cmd_bisect__helper, RUN_SETUP }, { "blame", cmd_blame, RUN_SETUP }, { "branch", cmd_branch, RUN_SETUP }, { "bundle", cmd_bundle, RUN_SETUP_GENTLY }, diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 4fb7d11..62125ec 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -592,6 +592,37 @@ test_expect_success 'erroring out when using bad path parameters' ' grep "bad path parameters" error.txt ' +test_expect_success 'test bisection on bare repo - --no-checkout specified' ' + git clone --bare . bare.nocheckout && + ( + cd bare.nocheckout && + git bisect start --no-checkout && + git bisect good $HASH1 && + git bisect bad $HASH4 && + git bisect run eval \ + "test \$(git rev-list BISECT_HEAD ^$HASH2 --max-count=1 | wc -l) = 0" \ + >../nocheckout.log && + git bisect reset + ) && + grep "$HASH3 is the first bad commit" nocheckout.log +' + + +test_expect_success 'test bisection on bare repo - --no-checkout defaulted' ' + git clone --bare . bare.defaulted && + ( + cd bare.defaulted && + git bisect start && + git bisect good $HASH1 && + git bisect bad $HASH4 && + git bisect run eval \ + "test \$(git rev-list BISECT_HEAD ^$HASH2 --max-count=1 | wc -l) = 0" \ + >../defaulted.log && + git bisect reset + ) && + grep "$HASH3 is the first bad commit" defaulted.log +' + # # This creates a broken branch which cannot be checked out because # the tree created has been deleted. -- cgit v0.10.2-6-g49f6