diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index e97f2de21bdc58cc2de35731f7b339353b121bd0..abaf462273c1ad367474058d4abc630155e99a37 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -17,8 +17,8 @@ The command takes various subcommands, and different options depending on the subcommand: git bisect start [--no-checkout] [ [...]] [--] [...] - git bisect bad [] - git bisect good [...] + git bisect (bad|new) [] + git bisect (good|old) [...] git bisect skip [(|)...] git bisect reset [] git bisect visualize @@ -36,6 +36,13 @@ whether the selected commit is "good" or "bad". It continues narrowing down the range until it finds the exact commit that introduced the change. +In fact, `git bisect` can be used to find the commit that changed +*any* property of your project; e.g., the commit that fixed a bug, or +the commit that caused a benchmark's performance to improve. To +support this more general usage, the terms "old" and "new" can be used +in place of "good" and "bad". See +section "Alternate terms" below for more information. + Basic bisect commands: start, bad, good ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -111,6 +118,45 @@ bad revision, while `git bisect reset HEAD` will leave you on the current bisection commit and avoid switching commits at all. +Alternate terms +~~~~~~~~~~~~~~~ + +Sometimes you are not looking for the commit that introduced a +breakage, but rather for a commit that caused a change between some +other "old" state and "new" state. For example, you might be looking +for the commit that introduced a particular fix. Or you might be +looking for the first commit in which the source-code filenames were +finally all converted to your company's naming standard. Or whatever. + +In such cases it can be very confusing to use the terms "good" and +"bad" to refer to "the state before the change" and "the state after +the change". So instead, you can use the terms "old" and "new", +respectively, in place of "good" and "bad". (But note that you cannot +mix "good" and "bad" with "old" and "new" in a single session.) + +In this more general usage, you provide `git bisect` with a "new" +commit has some property and an "old" commit that doesn't have that +property. Each time `git bisect` checks out a commit, you test if that +commit has the property. If it does, mark the commit as "new"; +otherwise, mark it as "old". When the bisection is done, `git bisect` +will report which commit introduced the property. + +To use "old" and "new" instead of "good" and bad, you must run `git +bisect start` without commits as argument and then run the following +commands to add the commits: + +------------------------------------------------ +git bisect old [] +------------------------------------------------ + +to indicate that a commit was before the sought change, or + +------------------------------------------------ +git bisect new [...] +------------------------------------------------ + +to indicate that it was after. + Bisect visualize ~~~~~~~~~~~~~~~~ @@ -387,6 +433,14 @@ In this case, when 'git bisect run' finishes, bisect/bad will refer to a commit has at least one parent whose reachable graph is fully traversable in the sense required by 'git pack objects'. +* Look for a fix instead of a regression in the code ++ +------------ +$ git bisect start +$ git bisect new HEAD # current commit is marked as new +$ git bisect old HEAD~10 # the tenth commit from now is marked as old +------------ + Getting help ~~~~~~~~~~~~ diff --git a/bisect.c b/bisect.c index 857cf59aa3e33add42e465c68855dddde5ea7cb4..f7292cbac4def85cdc4394df02567b73ba2cc91d 100644 --- a/bisect.c +++ b/bisect.c @@ -746,6 +746,11 @@ static void handle_bad_merge_base(void) "This means the bug has been fixed " "between %s and [%s].\n", bad_hex, bad_hex, good_hex); + } else if (!strcmp(term_bad, "new") && !strcmp(term_good, "old")) { + fprintf(stderr, "The merge base %s is new.\n" + "The property has changed " + "between %s and [%s].\n", + bad_hex, bad_hex, good_hex); } else { fprintf(stderr, "The merge base %s is %s.\n" "This means the first '%s' commit is " @@ -778,11 +783,11 @@ static void handle_skipped_merge_base(const unsigned char *mb) } /* - * "check_merge_bases" checks that merge bases are not "bad". + * "check_merge_bases" checks that merge bases are not "bad" (or "new"). * - * - If one is "bad", it means the user assumed something wrong + * - If one is "bad" (or "new"), it means the user assumed something wrong * and we must exit with a non 0 error code. - * - If one is "good", that's good, we have nothing to do. + * - If one is "good" (or "old"), that's good, we have nothing to do. * - 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. */ diff --git a/git-bisect.sh b/git-bisect.sh index 761ca6cca0ff7a5c3ee71bde77da52a1f7234853..d78b043b11dabea1b3bf82002a305b427578e681 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -1,14 +1,16 @@ #!/bin/sh -USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]' +USAGE='[help|start|bad|good|new|old|skip|next|reset|visualize|replay|log|run]' LONG_USAGE='git bisect help print this long help message. git bisect start [--no-checkout] [ [...]] [--] [...] reset bisect state and start bisection. -git bisect bad [] - mark a known-bad revision. -git bisect good [...] - mark ... known-good revisions. +git bisect (bad|new) [] + mark a known-bad revision/ + a revision after change in a given property. +git bisect (good|old) [...] + mark ... known-good revisions/ + revisions before change in a given property. git bisect skip [(|)...] mark ... untestable revisions. git bisect next @@ -294,7 +296,7 @@ bisect_next_check() { false ;; t,,"$TERM_GOOD") - # have bad but not good. we could bisect although + # have bad (or new) but not good (or old). we could bisect although # this is less optimum. eval_gettextln "Warning: bisecting only with a \$TERM_BAD commit." >&2 if test -t 0 @@ -587,14 +589,20 @@ check_and_set_terms () { write_terms bad good fi ;; + new|old) + if ! test -s "$GIT_DIR/BISECT_TERMS" + then + write_terms new old + fi + ;; esac ;; esac } bisect_voc () { case "$1" in - bad) echo "bad" ;; - good) echo "good" ;; + bad) echo "bad|new" ;; + good) echo "good|old" ;; esac } @@ -610,7 +618,7 @@ case "$#" in git bisect -h ;; start) bisect_start "$@" ;; - bad|good|"$TERM_BAD"|"$TERM_GOOD") + bad|good|new|old|"$TERM_BAD"|"$TERM_GOOD") bisect_state "$cmd" "$@" ;; skip) bisect_skip "$@" ;; diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 9e2c20374732d790edefc4cd87aa28223fbb24c4..983c5033c9c6e0e2206fbfd16285df4f747ba5ed 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -759,4 +759,42 @@ test_expect_success '"git bisect bad HEAD" behaves as "git bisect bad"' ' git bisect reset ' +test_expect_success 'bisect starts with only one new' ' + git bisect reset && + git bisect start && + git bisect new $HASH4 && + git bisect next +' + +test_expect_success 'bisect does not start with only one old' ' + git bisect reset && + git bisect start && + git bisect old $HASH1 && + test_must_fail git bisect next +' + +test_expect_success 'bisect start with one new and old' ' + git bisect reset && + git bisect start && + git bisect old $HASH1 && + git bisect new $HASH4 && + git bisect new && + git bisect new >bisect_result && + grep "$HASH2 is the first new commit" bisect_result && + git bisect log >log_to_replay.txt && + git bisect reset +' + +test_expect_success 'bisect replay with old and new' ' + git bisect replay log_to_replay.txt >bisect_result && + grep "$HASH2 is the first new commit" bisect_result && + git bisect reset +' + +test_expect_success 'bisect cannot mix old/new and good/bad' ' + git bisect start && + git bisect bad $HASH4 && + test_must_fail git bisect old $HASH1 +' + test_done