bisect: add the terms old/new

When not looking for a regression during a bisect but for a fix or a change in another given property, it can be confusing to use 'good' and 'bad'. This patch introduce `git bisect new` and `git bisect old` as an alternative to 'bad' and good': the commits which have a certain property must be marked as `new` and the ones which do not as `old`. The output will be the first commit after the change in the property. During a new/old bisect session you cannot use bad/good commands and vice-versa. Some commands are still not available for old/new: * git rev-list --bisect does not treat the revs/bisect/new and revs/bisect/old-SHA1 files. Old discussions: - http://thread.gmane.org/gmane.comp.version-control.git/86063 introduced bisect fix unfixed to find fix. - http://thread.gmane.org/gmane.comp.version-control.git/182398 discussion around bisect yes/no or old/new. - http://thread.gmane.org/gmane.comp.version-control.git/199758 last discussion and reviews New discussions: - http://thread.gmane.org/gmane.comp.version-control.git/271320 ( v2 1/7-4/7 ) - http://comments.gmane.org/gmane.comp.version-control.git/271343 ( v2 5/7-7/7 ) Signed-off-by: Antoine Delaite <antoine.delaite@ensimag.grenoble-inp.fr> Signed-off-by: Louis Stuber <stuberl@ensimag.grenoble-inp.fr> Signed-off-by: Valentin Duperray <Valentin.Duperray@ensimag.imag.fr> Signed-off-by: Franck Jonas <Franck.Jonas@ensimag.imag.fr> Signed-off-by: Lucien Kong <Lucien.Kong@ensimag.imag.fr> Signed-off-by: Thomas Nguy <Thomas.Nguy@ensimag.imag.fr> Signed-off-by: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr> Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Antoine Delaite committed Jun 29, 2015 at 17:40 UTC 21e5cfd8b3d35a702b19be6964b8809045dd6278
4 files changed +119 -14
Documentation/git-bisect.txt
+56 -2
@@ -17,8 +17,8 @@ The command takes various subcommands, and different options depending
17 on the subcommand:
18
19 git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<paths>...]
20 - git bisect bad [<rev>]
21 - git bisect good [<rev>...]
20 + git bisect (bad|new) [<rev>]
21 + git bisect (good|old) [<rev>...]
22 git bisect skip [(<rev>|<range>)...]
23 git bisect reset [<commit>]
24 git bisect visualize
@@ -36,6 +36,13 @@ whether the selected commit is "good" or "bad". It continues narrowing
36 down the range until it finds the exact commit that introduced the
37 change.
38
39 +In fact, `git bisect` can be used to find the commit that changed
40 +*any* property of your project; e.g., the commit that fixed a bug, or
41 +the commit that caused a benchmark's performance to improve. To
42 +support this more general usage, the terms "old" and "new" can be used
43 +in place of "good" and "bad". See
44 +section "Alternate terms" below for more information.
45 +
46 Basic bisect commands: start, bad, good
47 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
48
@@ -111,6 +118,45 @@ bad revision, while `git bisect reset HEAD` will leave you on the
118 current bisection commit and avoid switching commits at all.
119
120
121 +Alternate terms
122 +~~~~~~~~~~~~~~~
123 +
124 +Sometimes you are not looking for the commit that introduced a
125 +breakage, but rather for a commit that caused a change between some
126 +other "old" state and "new" state. For example, you might be looking
127 +for the commit that introduced a particular fix. Or you might be
128 +looking for the first commit in which the source-code filenames were
129 +finally all converted to your company's naming standard. Or whatever.
130 +
131 +In such cases it can be very confusing to use the terms "good" and
132 +"bad" to refer to "the state before the change" and "the state after
133 +the change". So instead, you can use the terms "old" and "new",
134 +respectively, in place of "good" and "bad". (But note that you cannot
135 +mix "good" and "bad" with "old" and "new" in a single session.)
136 +
137 +In this more general usage, you provide `git bisect` with a "new"
138 +commit has some property and an "old" commit that doesn't have that
139 +property. Each time `git bisect` checks out a commit, you test if that
140 +commit has the property. If it does, mark the commit as "new";
141 +otherwise, mark it as "old". When the bisection is done, `git bisect`
142 +will report which commit introduced the property.
143 +
144 +To use "old" and "new" instead of "good" and bad, you must run `git
145 +bisect start` without commits as argument and then run the following
146 +commands to add the commits:
147 +
148 +------------------------------------------------
149 +git bisect old [<rev>]
150 +------------------------------------------------
151 +
152 +to indicate that a commit was before the sought change, or
153 +
154 +------------------------------------------------
155 +git bisect new [<rev>...]
156 +------------------------------------------------
157 +
158 +to indicate that it was after.
159 +
160 Bisect visualize
161 ~~~~~~~~~~~~~~~~
162
@@ -387,6 +433,14 @@ In this case, when 'git bisect run' finishes, bisect/bad will refer to a commit
433 has at least one parent whose reachable graph is fully traversable in the sense
434 required by 'git pack objects'.
435
436 +* Look for a fix instead of a regression in the code
437 ++
438 +------------
439 +$ git bisect start
440 +$ git bisect new HEAD # current commit is marked as new
441 +$ git bisect old HEAD~10 # the tenth commit from now is marked as old
442 +------------
443 +
444 Getting help
445 ~~~~~~~~~~~~
446
bisect.c
+8 -3
@@ -746,6 +746,11 @@ static void handle_bad_merge_base(void)
746 "This means the bug has been fixed "
747 "between %s and [%s].\n",
748 bad_hex, bad_hex, good_hex);
749 + } else if (!strcmp(term_bad, "new") && !strcmp(term_good, "old")) {
750 + fprintf(stderr, "The merge base %s is new.\n"
751 + "The property has changed "
752 + "between %s and [%s].\n",
753 + bad_hex, bad_hex, good_hex);
754 } else {
755 fprintf(stderr, "The merge base %s is %s.\n"
756 "This means the first '%s' commit is "
@@ -778,11 +783,11 @@ static void handle_skipped_merge_base(const unsigned char *mb)
783 }
784
785 /*
781 - * "check_merge_bases" checks that merge bases are not "bad".
786 + * "check_merge_bases" checks that merge bases are not "bad" (or "new").
787 *
783 - * - If one is "bad", it means the user assumed something wrong
788 + * - If one is "bad" (or "new"), it means the user assumed something wrong
789 * and we must exit with a non 0 error code.
785 - * - If one is "good", that's good, we have nothing to do.
790 + * - If one is "good" (or "old"), that's good, we have nothing to do.
791 * - If one is "skipped", we can't know but we should warn.
792 * - If we don't know, we should check it out and ask the user to test.
793 */
git-bisect.sh
+17 -9
@@ -1,14 +1,16 @@
1 #!/bin/sh
2
3 -USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]'
3 +USAGE='[help|start|bad|good|new|old|skip|next|reset|visualize|replay|log|run]'
4 LONG_USAGE='git bisect help
5 print this long help message.
6 git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...]
7 reset bisect state and start bisection.
8 -git bisect bad [<rev>]
9 - mark <rev> a known-bad revision.
10 -git bisect good [<rev>...]
11 - mark <rev>... known-good revisions.
8 +git bisect (bad|new) [<rev>]
9 + mark <rev> a known-bad revision/
10 + a revision after change in a given property.
11 +git bisect (good|old) [<rev>...]
12 + mark <rev>... known-good revisions/
13 + revisions before change in a given property.
14 git bisect skip [(<rev>|<range>)...]
15 mark <rev>... untestable revisions.
16 git bisect next
@@ -294,7 +296,7 @@ bisect_next_check() {
296 false
297 ;;
298 t,,"$TERM_GOOD")
297 - # have bad but not good. we could bisect although
299 + # have bad (or new) but not good (or old). we could bisect although
300 # this is less optimum.
301 eval_gettextln "Warning: bisecting only with a \$TERM_BAD commit." >&2
302 if test -t 0
@@ -587,14 +589,20 @@ check_and_set_terms () {
589 write_terms bad good
590 fi
591 ;;
592 + new|old)
593 + if ! test -s "$GIT_DIR/BISECT_TERMS"
594 + then
595 + write_terms new old
596 + fi
597 + ;;
598 esac ;;
599 esac
600 }
601
602 bisect_voc () {
603 case "$1" in
596 - bad) echo "bad" ;;
597 - good) echo "good" ;;
604 + bad) echo "bad|new" ;;
605 + good) echo "good|old" ;;
606 esac
607 }
608
@@ -610,7 +618,7 @@ case "$#" in
618 git bisect -h ;;
619 start)
620 bisect_start "$@" ;;
613 - bad|good|"$TERM_BAD"|"$TERM_GOOD")
621 + bad|good|new|old|"$TERM_BAD"|"$TERM_GOOD")
622 bisect_state "$cmd" "$@" ;;
623 skip)
624 bisect_skip "$@" ;;
t/t6030-bisect-porcelain.sh
+38
@@ -759,4 +759,42 @@ test_expect_success '"git bisect bad HEAD" behaves as "git bisect bad"' '
759 git bisect reset
760 '
761
762 +test_expect_success 'bisect starts with only one new' '
763 + git bisect reset &&
764 + git bisect start &&
765 + git bisect new $HASH4 &&
766 + git bisect next
767 +'
768 +
769 +test_expect_success 'bisect does not start with only one old' '
770 + git bisect reset &&
771 + git bisect start &&
772 + git bisect old $HASH1 &&
773 + test_must_fail git bisect next
774 +'
775 +
776 +test_expect_success 'bisect start with one new and old' '
777 + git bisect reset &&
778 + git bisect start &&
779 + git bisect old $HASH1 &&
780 + git bisect new $HASH4 &&
781 + git bisect new &&
782 + git bisect new >bisect_result &&
783 + grep "$HASH2 is the first new commit" bisect_result &&
784 + git bisect log >log_to_replay.txt &&
785 + git bisect reset
786 +'
787 +
788 +test_expect_success 'bisect replay with old and new' '
789 + git bisect replay log_to_replay.txt >bisect_result &&
790 + grep "$HASH2 is the first new commit" bisect_result &&
791 + git bisect reset
792 +'
793 +
794 +test_expect_success 'bisect cannot mix old/new and good/bad' '
795 + git bisect start &&
796 + git bisect bad $HASH4 &&
797 + test_must_fail git bisect old $HASH1
798 +'
799 +
800 test_done