Skip to content

Commit 22dd6eb

Browse files
committed
Merge branch 'ad/bisect-terms'
The use of 'good/bad' in "git bisect" made it confusing to use when hunting for a state change that is not a regression (e.g. bugfix). The command learned 'old/new' and then allows the end user to say e.g. "bisect start --term-old=fast --term=new=slow" to find a performance regression. Michael's idea to make 'good/bad' more intelligent does have certain attractiveness ($gname/272867), and makes some of the work on this topic a moot point. * ad/bisect-terms: bisect: allow setting any user-specified in 'git bisect start' bisect: add 'git bisect terms' to view the current terms bisect: add the terms old/new bisect: sanity check on terms
2 parents dc5400e + 06e6a74 commit 22dd6eb

File tree

4 files changed

+350
-16
lines changed

4 files changed

+350
-16
lines changed

Documentation/git-bisect.txt

+100-3
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ DESCRIPTION
1616
The command takes various subcommands, and different options depending
1717
on the subcommand:
1818

19-
git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<paths>...]
20-
git bisect bad [<rev>]
21-
git bisect good [<rev>...]
19+
git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]
20+
[--no-checkout] [<bad> [<good>...]] [--] [<paths>...]
21+
git bisect (bad|new) [<rev>]
22+
git bisect (good|old) [<rev>...]
23+
git bisect terms [--term-good | --term-bad]
2224
git bisect skip [(<rev>|<range>)...]
2325
git bisect reset [<commit>]
2426
git bisect visualize
@@ -36,6 +38,13 @@ whether the selected commit is "good" or "bad". It continues narrowing
3638
down the range until it finds the exact commit that introduced the
3739
change.
3840

41+
In fact, `git bisect` can be used to find the commit that changed
42+
*any* property of your project; e.g., the commit that fixed a bug, or
43+
the commit that caused a benchmark's performance to improve. To
44+
support this more general usage, the terms "old" and "new" can be used
45+
in place of "good" and "bad", or you can choose your own terms. See
46+
section "Alternate terms" below for more information.
47+
3948
Basic bisect commands: start, bad, good
4049
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4150

@@ -111,6 +120,79 @@ bad revision, while `git bisect reset HEAD` will leave you on the
111120
current bisection commit and avoid switching commits at all.
112121

113122

123+
Alternate terms
124+
~~~~~~~~~~~~~~~
125+
126+
Sometimes you are not looking for the commit that introduced a
127+
breakage, but rather for a commit that caused a change between some
128+
other "old" state and "new" state. For example, you might be looking
129+
for the commit that introduced a particular fix. Or you might be
130+
looking for the first commit in which the source-code filenames were
131+
finally all converted to your company's naming standard. Or whatever.
132+
133+
In such cases it can be very confusing to use the terms "good" and
134+
"bad" to refer to "the state before the change" and "the state after
135+
the change". So instead, you can use the terms "old" and "new",
136+
respectively, in place of "good" and "bad". (But note that you cannot
137+
mix "good" and "bad" with "old" and "new" in a single session.)
138+
139+
In this more general usage, you provide `git bisect` with a "new"
140+
commit has some property and an "old" commit that doesn't have that
141+
property. Each time `git bisect` checks out a commit, you test if that
142+
commit has the property. If it does, mark the commit as "new";
143+
otherwise, mark it as "old". When the bisection is done, `git bisect`
144+
will report which commit introduced the property.
145+
146+
To use "old" and "new" instead of "good" and bad, you must run `git
147+
bisect start` without commits as argument and then run the following
148+
commands to add the commits:
149+
150+
------------------------------------------------
151+
git bisect old [<rev>]
152+
------------------------------------------------
153+
154+
to indicate that a commit was before the sought change, or
155+
156+
------------------------------------------------
157+
git bisect new [<rev>...]
158+
------------------------------------------------
159+
160+
to indicate that it was after.
161+
162+
To get a reminder of the currently used terms, use
163+
164+
------------------------------------------------
165+
git bisect terms
166+
------------------------------------------------
167+
168+
You can get just the old (respectively new) term with `git bisect term
169+
--term-old` or `git bisect term --term-good`.
170+
171+
If you would like to use your own terms instead of "bad"/"good" or
172+
"new"/"old", you can choose any names you like (except existing bisect
173+
subcommands like `reset`, `start`, ...) by starting the
174+
bisection using
175+
176+
------------------------------------------------
177+
git bisect start --term-old <term-old> --term-new <term-new>
178+
------------------------------------------------
179+
180+
For example, if you are looking for a commit that introduced a
181+
performance regression, you might use
182+
183+
------------------------------------------------
184+
git bisect start --term-old fast --term-new slow
185+
------------------------------------------------
186+
187+
Or if you are looking for the commit that fixed a bug, you might use
188+
189+
------------------------------------------------
190+
git bisect start --term-new fixed --term-old broken
191+
------------------------------------------------
192+
193+
Then, use `git bisect <term-old>` and `git bisect <term-new>` instead
194+
of `git bisect good` and `git bisect bad` to mark commits.
195+
114196
Bisect visualize
115197
~~~~~~~~~~~~~~~~
116198

@@ -387,6 +469,21 @@ In this case, when 'git bisect run' finishes, bisect/bad will refer to a commit
387469
has at least one parent whose reachable graph is fully traversable in the sense
388470
required by 'git pack objects'.
389471

472+
* Look for a fix instead of a regression in the code
473+
+
474+
------------
475+
$ git bisect start
476+
$ git bisect new HEAD # current commit is marked as new
477+
$ git bisect old HEAD~10 # the tenth commit from now is marked as old
478+
------------
479+
+
480+
or:
481+
------------
482+
$ git bisect start --term-old broken --term-new fixed
483+
$ git bisect fixed
484+
$ git bisect broken HEAD~10
485+
------------
486+
390487
Getting help
391488
~~~~~~~~~~~~
392489

bisect.c

+8-3
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,11 @@ static void handle_bad_merge_base(void)
730730
"This means the bug has been fixed "
731731
"between %s and [%s].\n",
732732
bad_hex, bad_hex, good_hex);
733+
} else if (!strcmp(term_bad, "new") && !strcmp(term_good, "old")) {
734+
fprintf(stderr, "The merge base %s is new.\n"
735+
"The property has changed "
736+
"between %s and [%s].\n",
737+
bad_hex, bad_hex, good_hex);
733738
} else {
734739
fprintf(stderr, "The merge base %s is %s.\n"
735740
"This means the first '%s' commit is "
@@ -762,11 +767,11 @@ static void handle_skipped_merge_base(const unsigned char *mb)
762767
}
763768

764769
/*
765-
* "check_merge_bases" checks that merge bases are not "bad".
770+
* "check_merge_bases" checks that merge bases are not "bad" (or "new").
766771
*
767-
* - If one is "bad", it means the user assumed something wrong
772+
* - If one is "bad" (or "new"), it means the user assumed something wrong
768773
* and we must exit with a non 0 error code.
769-
* - If one is "good", that's good, we have nothing to do.
774+
* - If one is "good" (or "old"), that's good, we have nothing to do.
770775
* - If one is "skipped", we can't know but we should warn.
771776
* - If we don't know, we should check it out and ask the user to test.
772777
*/

git-bisect.sh

+107-10
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
#!/bin/sh
22

3-
USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]'
3+
USAGE='[help|start|bad|good|new|old|terms|skip|next|reset|visualize|replay|log|run]'
44
LONG_USAGE='git bisect help
55
print this long help message.
6-
git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...]
6+
git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]
7+
[--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...]
78
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.
9+
git bisect (bad|new) [<rev>]
10+
mark <rev> a known-bad revision/
11+
a revision after change in a given property.
12+
git bisect (good|old) [<rev>...]
13+
mark <rev>... known-good revisions/
14+
revisions before change in a given property.
15+
git bisect terms [--term-good | --term-bad]
16+
show the terms used for old and new commits (default: bad, good)
1217
git bisect skip [(<rev>|<range>)...]
1318
mark <rev>... untestable revisions.
1419
git bisect next
@@ -95,6 +100,24 @@ bisect_start() {
95100
--no-checkout)
96101
mode=--no-checkout
97102
shift ;;
103+
--term-good|--term-old)
104+
shift
105+
must_write_terms=1
106+
TERM_GOOD=$1
107+
shift ;;
108+
--term-good=*|--term-old=*)
109+
must_write_terms=1
110+
TERM_GOOD=${1#*=}
111+
shift ;;
112+
--term-bad|--term-new)
113+
shift
114+
must_write_terms=1
115+
TERM_BAD=$1
116+
shift ;;
117+
--term-bad=*|--term-new=*)
118+
must_write_terms=1
119+
TERM_BAD=${1#*=}
120+
shift ;;
98121
--*)
99122
die "$(eval_gettext "unrecognised option: '\$arg'")" ;;
100123
*)
@@ -294,7 +317,7 @@ bisect_next_check() {
294317
false
295318
;;
296319
t,,"$TERM_GOOD")
297-
# have bad but not good. we could bisect although
320+
# have bad (or new) but not good (or old). we could bisect although
298321
# this is less optimum.
299322
eval_gettextln "Warning: bisecting only with a \$TERM_BAD commit." >&2
300323
if test -t 0
@@ -451,6 +474,8 @@ bisect_replay () {
451474
eval "$cmd" ;;
452475
"$TERM_GOOD"|"$TERM_BAD"|skip)
453476
bisect_write "$command" "$rev" ;;
477+
terms)
478+
bisect_terms $rev ;;
454479
*)
455480
die "$(gettext "?? what are you talking about?")" ;;
456481
esac
@@ -535,9 +560,42 @@ get_terms () {
535560
write_terms () {
536561
TERM_BAD=$1
537562
TERM_GOOD=$2
563+
if test "$TERM_BAD" = "$TERM_GOOD"
564+
then
565+
die "$(gettext "please use two different terms")"
566+
fi
567+
check_term_format "$TERM_BAD" bad
568+
check_term_format "$TERM_GOOD" good
538569
printf '%s\n%s\n' "$TERM_BAD" "$TERM_GOOD" >"$GIT_DIR/BISECT_TERMS"
539570
}
540571

572+
check_term_format () {
573+
term=$1
574+
git check-ref-format refs/bisect/"$term" ||
575+
die "$(eval_gettext "'\$term' is not a valid term")"
576+
case "$term" in
577+
help|start|terms|skip|next|reset|visualize|replay|log|run)
578+
die "$(eval_gettext "can't use the builtin command '\$term' as a term")"
579+
;;
580+
bad|new)
581+
if test "$2" != bad
582+
then
583+
# In theory, nothing prevents swapping
584+
# completely good and bad, but this situation
585+
# could be confusing and hasn't been tested
586+
# enough. Forbid it for now.
587+
die "$(eval_gettext "can't change the meaning of term '\$term'")"
588+
fi
589+
;;
590+
good|old)
591+
if test "$2" != good
592+
then
593+
die "$(eval_gettext "can't change the meaning of term '\$term'")"
594+
fi
595+
;;
596+
esac
597+
}
598+
541599
check_and_set_terms () {
542600
cmd="$1"
543601
case "$cmd" in
@@ -554,14 +612,51 @@ check_and_set_terms () {
554612
write_terms bad good
555613
fi
556614
;;
615+
new|old)
616+
if ! test -s "$GIT_DIR/BISECT_TERMS"
617+
then
618+
write_terms new old
619+
fi
620+
;;
557621
esac ;;
558622
esac
559623
}
560624

561625
bisect_voc () {
562626
case "$1" in
563-
bad) echo "bad" ;;
564-
good) echo "good" ;;
627+
bad) echo "bad|new" ;;
628+
good) echo "good|old" ;;
629+
esac
630+
}
631+
632+
bisect_terms () {
633+
get_terms
634+
if ! test -s "$GIT_DIR/BISECT_TERMS"
635+
then
636+
die "$(gettext "no terms defined")"
637+
fi
638+
case "$#" in
639+
0)
640+
gettextln "Your current terms are $TERM_GOOD for the old state
641+
and $TERM_BAD for the new state."
642+
;;
643+
1)
644+
arg=$1
645+
case "$arg" in
646+
--term-good|--term-old)
647+
printf '%s\n' "$TERM_GOOD"
648+
;;
649+
--term-bad|--term-new)
650+
printf '%s\n' "$TERM_BAD"
651+
;;
652+
*)
653+
die "$(eval_gettext "invalid argument \$arg for 'git bisect terms'.
654+
Supported options are: --term-good|--term-old and --term-bad|--term-new.")"
655+
;;
656+
esac
657+
;;
658+
*)
659+
usage ;;
565660
esac
566661
}
567662

@@ -577,7 +672,7 @@ case "$#" in
577672
git bisect -h ;;
578673
start)
579674
bisect_start "$@" ;;
580-
bad|good|"$TERM_BAD"|"$TERM_GOOD")
675+
bad|good|new|old|"$TERM_BAD"|"$TERM_GOOD")
581676
bisect_state "$cmd" "$@" ;;
582677
skip)
583678
bisect_skip "$@" ;;
@@ -594,6 +689,8 @@ case "$#" in
594689
bisect_log ;;
595690
run)
596691
bisect_run "$@" ;;
692+
terms)
693+
bisect_terms "$@" ;;
597694
*)
598695
usage ;;
599696
esac

0 commit comments

Comments
 (0)