Skip to content

Commit 5bba1b3

Browse files
author
Junio C Hamano
committed
Merge branch 'cc/bisect'
* cc/bisect: git-bisect: allow bisecting with only one bad commit. t6030: add a bit more tests to git-bisect git-bisect: modernization Documentation: bisect: "start" accepts one bad and many good commits Bisect: teach "bisect start" to optionally use one bad and many good revs.
2 parents b7108a1 + 0a5280a commit 5bba1b3

File tree

3 files changed

+199
-66
lines changed

3 files changed

+199
-66
lines changed

Documentation/git-bisect.txt

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ DESCRIPTION
1515
The command takes various subcommands, and different options depending
1616
on the subcommand:
1717

18-
git bisect start [<paths>...]
18+
git bisect start [<bad> [<good>...]] [--] [<paths>...]
1919
git bisect bad <rev>
2020
git bisect good <rev>
2121
git bisect reset [<branch>]
@@ -134,15 +134,26 @@ $ git reset --hard HEAD~3 # try 3 revs before what
134134
Then compile and test the one you chose to try. After that, tell
135135
bisect what the result was as usual.
136136

137-
Cutting down bisection by giving path parameter to bisect start
138-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
137+
Cutting down bisection by giving more parameters to bisect start
138+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
139139

140140
You can further cut down the number of trials if you know what part of
141141
the tree is involved in the problem you are tracking down, by giving
142142
paths parameters when you say `bisect start`, like this:
143143

144144
------------
145-
$ git bisect start arch/i386 include/asm-i386
145+
$ git bisect start -- arch/i386 include/asm-i386
146+
------------
147+
148+
If you know beforehand more than one good commits, you can narrow the
149+
bisect space down without doing the whole tree checkout every time you
150+
give good commits. You give the bad revision immediately after `start`
151+
and then you give all the good revisions you have:
152+
153+
------------
154+
$ git bisect start v2.6.20-rc6 v2.6.20-rc4 v2.6.20-rc1 --
155+
# v2.6.20-rc6 is bad
156+
# v2.6.20-rc4 and v2.6.20-rc1 are good
146157
------------
147158

148159
Bisect run

git-bisect.sh

Lines changed: 135 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
#!/bin/sh
22

33
USAGE='[start|bad|good|next|reset|visualize|replay|log|run]'
4-
LONG_USAGE='git bisect start [<pathspec>] reset bisect state and start bisection.
5-
git bisect bad [<rev>] mark <rev> a known-bad revision.
6-
git bisect good [<rev>...] mark <rev>... known-good revisions.
7-
git bisect next find next bisection to test and check it out.
8-
git bisect reset [<branch>] finish bisection search and go back to branch.
9-
git bisect visualize show bisect status in gitk.
10-
git bisect replay <logfile> replay bisection log.
11-
git bisect log show bisect log.
12-
git bisect run <cmd>... use <cmd>... to automatically bisect.'
4+
LONG_USAGE='git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
5+
reset bisect state and start bisection.
6+
git bisect bad [<rev>]
7+
mark <rev> a known-bad revision.
8+
git bisect good [<rev>...]
9+
mark <rev>... known-good revisions.
10+
git bisect next
11+
find next bisection to test and check it out.
12+
git bisect reset [<branch>]
13+
finish bisection search and go back to branch.
14+
git bisect visualize
15+
show bisect status in gitk.
16+
git bisect replay <logfile>
17+
replay bisection log.
18+
git bisect log
19+
show bisect log.
20+
git bisect run <cmd>...
21+
use <cmd>... to automatically bisect.'
1322

1423
. git-sh-setup
1524
require_work_tree
@@ -70,14 +79,48 @@ bisect_start() {
7079
#
7180
# Get rid of any old bisect state
7281
#
73-
rm -f "$GIT_DIR/refs/heads/bisect"
74-
rm -rf "$GIT_DIR/refs/bisect/"
82+
bisect_clean_state
7583
mkdir "$GIT_DIR/refs/bisect"
84+
85+
#
86+
# Check for one bad and then some good revisions.
87+
#
88+
has_double_dash=0
89+
for arg; do
90+
case "$arg" in --) has_double_dash=1; break ;; esac
91+
done
92+
orig_args=$(sq "$@")
93+
bad_seen=0
94+
while [ $# -gt 0 ]; do
95+
arg="$1"
96+
case "$arg" in
97+
--)
98+
shift
99+
break
100+
;;
101+
*)
102+
rev=$(git-rev-parse --verify "$arg^{commit}" 2>/dev/null) || {
103+
test $has_double_dash -eq 1 &&
104+
die "'$arg' does not appear to be a valid revision"
105+
break
106+
}
107+
if [ $bad_seen -eq 0 ]; then
108+
bad_seen=1
109+
bisect_write_bad "$rev"
110+
else
111+
bisect_write_good "$rev"
112+
fi
113+
shift
114+
;;
115+
esac
116+
done
117+
118+
sq "$@" >"$GIT_DIR/BISECT_NAMES"
76119
{
77120
printf "git-bisect start"
78-
sq "$@"
79-
} >"$GIT_DIR/BISECT_LOG"
80-
sq "$@" >"$GIT_DIR/BISECT_NAMES"
121+
echo "$orig_args"
122+
} >>"$GIT_DIR/BISECT_LOG"
123+
bisect_auto_next
81124
}
82125

83126
bisect_bad() {
@@ -90,12 +133,17 @@ bisect_bad() {
90133
*)
91134
usage ;;
92135
esac || exit
93-
echo "$rev" >"$GIT_DIR/refs/bisect/bad"
94-
echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
136+
bisect_write_bad "$rev"
95137
echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
96138
bisect_auto_next
97139
}
98140

141+
bisect_write_bad() {
142+
rev="$1"
143+
echo "$rev" >"$GIT_DIR/refs/bisect/bad"
144+
echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
145+
}
146+
99147
bisect_good() {
100148
bisect_autostart
101149
case "$#" in
@@ -106,35 +154,54 @@ bisect_good() {
106154
for rev in $revs
107155
do
108156
rev=$(git-rev-parse --verify "$rev^{commit}") || exit
109-
echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
110-
echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
157+
bisect_write_good "$rev"
111158
echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
159+
112160
done
113161
bisect_auto_next
114162
}
115163

164+
bisect_write_good() {
165+
rev="$1"
166+
echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
167+
echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
168+
}
169+
116170
bisect_next_check() {
117-
next_ok=no
118-
test -f "$GIT_DIR/refs/bisect/bad" &&
119-
case "$(cd "$GIT_DIR" && echo refs/bisect/good-*)" in
120-
refs/bisect/good-\*) ;;
121-
*) next_ok=yes ;;
122-
esac
123-
case "$next_ok,$1" in
124-
no,) false ;;
125-
no,fail)
126-
THEN=''
127-
test -d "$GIT_DIR/refs/bisect" || {
128-
echo >&2 'You need to start by "git bisect start".'
129-
THEN='then '
130-
}
131-
echo >&2 'You '$THEN'need to give me at least one good' \
132-
'and one bad revisions.'
133-
echo >&2 '(You can use "git bisect bad" and' \
134-
'"git bisect good" for that.)'
135-
exit 1 ;;
171+
missing_good= missing_bad=
172+
git show-ref -q --verify refs/bisect/bad || missing_bad=t
173+
test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
174+
175+
case "$missing_good,$missing_bad,$1" in
176+
,,*)
177+
: have both good and bad - ok
178+
;;
179+
*,)
180+
# do not have both but not asked to fail - just report.
181+
false
182+
;;
183+
t,,good)
184+
# have bad but not good. we could bisect although
185+
# this is less optimum.
186+
echo >&2 'Warning: bisecting only with a bad commit.'
187+
if test -t 0
188+
then
189+
printf >&2 'Are you sure [Y/n]? '
190+
case "$(read yesno)" in [Nn]*) exit 1 ;; esac
191+
fi
192+
: bisect without good...
193+
;;
136194
*)
137-
true ;;
195+
THEN=''
196+
test -d "$GIT_DIR/refs/bisect" || {
197+
echo >&2 'You need to start by "git bisect start".'
198+
THEN='then '
199+
}
200+
echo >&2 'You '$THEN'need to give me at least one good' \
201+
'and one bad revisions.'
202+
echo >&2 '(You can use "git bisect bad" and' \
203+
'"git bisect good" for that.)'
204+
exit 1 ;;
138205
esac
139206
}
140207

@@ -145,27 +212,32 @@ bisect_auto_next() {
145212
bisect_next() {
146213
case "$#" in 0) ;; *) usage ;; esac
147214
bisect_autostart
148-
bisect_next_check fail
215+
bisect_next_check good
216+
149217
bad=$(git-rev-parse --verify refs/bisect/bad) &&
150-
good=$(git-rev-parse --sq --revs-only --not \
151-
$(cd "$GIT_DIR" && ls refs/bisect/good-*)) &&
152-
rev=$(eval "git-rev-list --bisect $good $bad -- $(cat "$GIT_DIR/BISECT_NAMES")") || exit
153-
if [ -z "$rev" ]; then
154-
echo "$bad was both good and bad"
155-
exit 1
218+
good=$(git for-each-ref --format='^%(objectname)' \
219+
"refs/bisect/good-*" | tr '[\012]' ' ') &&
220+
eval="git-rev-list --bisect-vars $good $bad --" &&
221+
eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
222+
eval=$(eval "$eval") &&
223+
eval "$eval" || exit
224+
225+
if [ -z "$bisect_rev" ]; then
226+
echo "$bad was both good and bad"
227+
exit 1
156228
fi
157-
if [ "$rev" = "$bad" ]; then
158-
echo "$rev is first bad commit"
159-
git-diff-tree --pretty $rev
160-
exit 0
229+
if [ "$bisect_rev" = "$bad" ]; then
230+
echo "$bisect_rev is first bad commit"
231+
git-diff-tree --pretty $bisect_rev
232+
exit 0
161233
fi
162-
nr=$(eval "git-rev-list $rev $good -- $(cat $GIT_DIR/BISECT_NAMES)" | wc -l) || exit
163-
echo "Bisecting: $nr revisions left to test after this"
164-
echo "$rev" > "$GIT_DIR/refs/heads/new-bisect"
234+
235+
echo "Bisecting: $bisect_nr revisions left to test after this"
236+
echo "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect"
165237
git checkout -q new-bisect || exit
166238
mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
167239
GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD refs/heads/bisect
168-
git-show-branch "$rev"
240+
git-show-branch "$bisect_rev"
169241
}
170242

171243
bisect_visualize() {
@@ -190,14 +262,19 @@ bisect_reset() {
190262
usage ;;
191263
esac
192264
if git checkout "$branch"; then
193-
rm -fr "$GIT_DIR/refs/bisect"
194-
rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
195-
rm -f "$GIT_DIR/BISECT_LOG"
196-
rm -f "$GIT_DIR/BISECT_NAMES"
197-
rm -f "$GIT_DIR/BISECT_RUN"
265+
rm -f "$GIT_DIR/head-name"
266+
bisect_clean_state
198267
fi
199268
}
200269

270+
bisect_clean_state() {
271+
rm -fr "$GIT_DIR/refs/bisect"
272+
rm -f "$GIT_DIR/refs/heads/bisect"
273+
rm -f "$GIT_DIR/BISECT_LOG"
274+
rm -f "$GIT_DIR/BISECT_NAMES"
275+
rm -f "$GIT_DIR/BISECT_RUN"
276+
}
277+
201278
bisect_replay () {
202279
test -r "$1" || {
203280
echo >&2 "cannot read $1 for replaying"

t/t6030-bisect-run.sh

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
#
33
# Copyright (c) 2007 Christian Couder
44
#
5-
test_description='Tests git-bisect run functionality'
5+
test_description='Tests git-bisect functionality'
6+
7+
exec </dev/null
68

79
. ./test-lib.sh
810

@@ -37,19 +39,62 @@ test_expect_success \
3739
HASH3=$(git rev-list HEAD | head -2 | tail -1) &&
3840
HASH4=$(git rev-list HEAD | head -1)'
3941

42+
test_expect_success 'bisect starts with only one bad' '
43+
git bisect reset &&
44+
git bisect start &&
45+
git bisect bad $HASH4 &&
46+
git bisect next
47+
'
48+
49+
test_expect_success 'bisect starts with only one good' '
50+
git bisect reset &&
51+
git bisect start &&
52+
git bisect good $HASH1 || return 1
53+
54+
if git bisect next
55+
then
56+
echo Oops, should have failed.
57+
false
58+
else
59+
:
60+
fi
61+
'
62+
63+
test_expect_success 'bisect start with one bad and good' '
64+
git bisect reset &&
65+
git bisect start &&
66+
git bisect good $HASH1 &&
67+
git bisect bad $HASH4 &&
68+
git bisect next
69+
'
70+
4071
# We want to automatically find the commit that
4172
# introduced "Another" into hello.
4273
test_expect_success \
43-
'git bisect run simple case' \
44-
'echo "#!/bin/sh" > test_script.sh &&
74+
'"git bisect run" simple case' \
75+
'echo "#"\!"/bin/sh" > test_script.sh &&
4576
echo "grep Another hello > /dev/null" >> test_script.sh &&
4677
echo "test \$? -ne 0" >> test_script.sh &&
4778
chmod +x test_script.sh &&
4879
git bisect start &&
4980
git bisect good $HASH1 &&
5081
git bisect bad $HASH4 &&
5182
git bisect run ./test_script.sh > my_bisect_log.txt &&
52-
grep "$HASH3 is first bad commit" my_bisect_log.txt'
83+
grep "$HASH3 is first bad commit" my_bisect_log.txt &&
84+
git bisect reset'
85+
86+
# We want to automatically find the commit that
87+
# introduced "Ciao" into hello.
88+
test_expect_success \
89+
'"git bisect run" with more complex "git bisect start"' \
90+
'echo "#"\!"/bin/sh" > test_script.sh &&
91+
echo "grep Ciao hello > /dev/null" >> test_script.sh &&
92+
echo "test \$? -ne 0" >> test_script.sh &&
93+
chmod +x test_script.sh &&
94+
git bisect start $HASH4 $HASH1 &&
95+
git bisect run ./test_script.sh > my_bisect_log.txt &&
96+
grep "$HASH4 is first bad commit" my_bisect_log.txt &&
97+
git bisect reset'
5398

5499
#
55100
#

0 commit comments

Comments
 (0)