Bisect: implement "bisect skip" to mark untestable revisions.
When there are some "skip"ped revisions, we add the '--bisect-all' option to "git rev-list --bisect-vars". Then we filter out the "skip"ped revisions from the result of the rev-list command, and we modify the "bisect_rev" var accordingly. We don't always use "--bisect-all" because it is slower than "--bisect-vars" or "--bisect". When we cannot find for sure the first bad commit because of "skip"ped commits, we print the hash of each possible first bad commit and then we exit with code 2. Signed-off-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
This commit is contained in:
parent
8fe26f4481
commit
97e1c51e15
125
git-bisect.sh
125
git-bisect.sh
@ -17,6 +17,8 @@ git bisect replay <logfile>
|
|||||||
replay bisection log.
|
replay bisection log.
|
||||||
git bisect log
|
git bisect log
|
||||||
show bisect log.
|
show bisect log.
|
||||||
|
git bisect skip [<rev>...]
|
||||||
|
mark <rev>... untestable revisions.
|
||||||
git bisect run <cmd>...
|
git bisect run <cmd>...
|
||||||
use <cmd>... to automatically bisect.'
|
use <cmd>... to automatically bisect.'
|
||||||
|
|
||||||
@ -163,6 +165,28 @@ bisect_write_good() {
|
|||||||
echo "# good: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
|
echo "# good: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bisect_skip() {
|
||||||
|
bisect_autostart
|
||||||
|
case "$#" in
|
||||||
|
0) revs=$(git rev-parse --verify HEAD) || exit ;;
|
||||||
|
*) revs=$(git rev-parse --revs-only --no-flags "$@") &&
|
||||||
|
test '' != "$revs" || die "Bad rev input: $@" ;;
|
||||||
|
esac
|
||||||
|
for rev in $revs
|
||||||
|
do
|
||||||
|
rev=$(git rev-parse --verify "$rev^{commit}") || exit
|
||||||
|
bisect_write_skip "$rev"
|
||||||
|
echo "git-bisect skip $rev" >>"$GIT_DIR/BISECT_LOG"
|
||||||
|
done
|
||||||
|
bisect_auto_next
|
||||||
|
}
|
||||||
|
|
||||||
|
bisect_write_skip() {
|
||||||
|
rev="$1"
|
||||||
|
echo "$rev" >"$GIT_DIR/refs/bisect/skip-$rev"
|
||||||
|
echo "# skip: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
|
||||||
|
}
|
||||||
|
|
||||||
bisect_next_check() {
|
bisect_next_check() {
|
||||||
missing_good= missing_bad=
|
missing_good= missing_bad=
|
||||||
git show-ref -q --verify refs/bisect/bad || missing_bad=t
|
git show-ref -q --verify refs/bisect/bad || missing_bad=t
|
||||||
@ -205,17 +229,97 @@ bisect_auto_next() {
|
|||||||
bisect_next_check && bisect_next || :
|
bisect_next_check && bisect_next || :
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filter_skipped() {
|
||||||
|
_eval="$1"
|
||||||
|
_skip="$2"
|
||||||
|
|
||||||
|
if [ -z "$_skip" ]; then
|
||||||
|
eval $_eval
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Let's parse the output of:
|
||||||
|
# "git rev-list --bisect-vars --bisect-all ..."
|
||||||
|
eval $_eval | while read hash line
|
||||||
|
do
|
||||||
|
case "$VARS,$FOUND,$TRIED,$hash" in
|
||||||
|
# We display some vars.
|
||||||
|
1,*,*,*) echo "$hash $line" ;;
|
||||||
|
|
||||||
|
# Split line.
|
||||||
|
,*,*,---*) ;;
|
||||||
|
|
||||||
|
# We had nothing to search.
|
||||||
|
,,,bisect_rev*)
|
||||||
|
echo "bisect_rev="
|
||||||
|
VARS=1
|
||||||
|
;;
|
||||||
|
|
||||||
|
# We did not find a good bisect rev.
|
||||||
|
# This should happen only if the "bad"
|
||||||
|
# commit is also a "skip" commit.
|
||||||
|
,,*,bisect_rev*)
|
||||||
|
echo "bisect_rev=$TRIED"
|
||||||
|
VARS=1
|
||||||
|
;;
|
||||||
|
|
||||||
|
# We are searching.
|
||||||
|
,,*,*)
|
||||||
|
TRIED="${TRIED:+$TRIED|}$hash"
|
||||||
|
case "$_skip" in
|
||||||
|
*$hash*) ;;
|
||||||
|
*)
|
||||||
|
echo "bisect_rev=$hash"
|
||||||
|
echo "bisect_tried=\"$TRIED\""
|
||||||
|
FOUND=1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
|
||||||
|
# We have already found a rev to be tested.
|
||||||
|
,1,*,bisect_rev*) VARS=1 ;;
|
||||||
|
,1,*,*) ;;
|
||||||
|
|
||||||
|
# ???
|
||||||
|
*) die "filter_skipped error " \
|
||||||
|
"VARS: '$VARS' " \
|
||||||
|
"FOUND: '$FOUND' " \
|
||||||
|
"TRIED: '$TRIED' " \
|
||||||
|
"hash: '$hash' " \
|
||||||
|
"line: '$line'"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
exit_if_skipped_commits () {
|
||||||
|
_tried=$1
|
||||||
|
if expr "$_tried" : ".*[|].*" > /dev/null ; then
|
||||||
|
echo "There are only 'skip'ped commit left to test."
|
||||||
|
echo "The first bad commit could be any of:"
|
||||||
|
echo "$_tried" | sed -e 's/[|]/\n/g'
|
||||||
|
echo "We cannot bisect more!"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
bisect_next() {
|
bisect_next() {
|
||||||
case "$#" in 0) ;; *) usage ;; esac
|
case "$#" in 0) ;; *) usage ;; esac
|
||||||
bisect_autostart
|
bisect_autostart
|
||||||
bisect_next_check good
|
bisect_next_check good
|
||||||
|
|
||||||
|
skip=$(git for-each-ref --format='%(objectname)' \
|
||||||
|
"refs/bisect/skip-*" | tr '[\012]' ' ') || exit
|
||||||
|
|
||||||
|
BISECT_OPT=''
|
||||||
|
test -n "$skip" && BISECT_OPT='--bisect-all'
|
||||||
|
|
||||||
bad=$(git rev-parse --verify refs/bisect/bad) &&
|
bad=$(git rev-parse --verify refs/bisect/bad) &&
|
||||||
good=$(git for-each-ref --format='^%(objectname)' \
|
good=$(git for-each-ref --format='^%(objectname)' \
|
||||||
"refs/bisect/good-*" | tr '[\012]' ' ') &&
|
"refs/bisect/good-*" | tr '[\012]' ' ') &&
|
||||||
eval="git rev-list --bisect-vars $good $bad --" &&
|
eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" &&
|
||||||
eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
|
eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
|
||||||
eval=$(eval "$eval") &&
|
eval=$(filter_skipped "$eval" "$skip") &&
|
||||||
eval "$eval" || exit
|
eval "$eval" || exit
|
||||||
|
|
||||||
if [ -z "$bisect_rev" ]; then
|
if [ -z "$bisect_rev" ]; then
|
||||||
@ -223,11 +327,16 @@ bisect_next() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ "$bisect_rev" = "$bad" ]; then
|
if [ "$bisect_rev" = "$bad" ]; then
|
||||||
|
exit_if_skipped_commits "$bisect_tried"
|
||||||
echo "$bisect_rev is first bad commit"
|
echo "$bisect_rev is first bad commit"
|
||||||
git diff-tree --pretty $bisect_rev
|
git diff-tree --pretty $bisect_rev
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# We should exit here only if the "bad"
|
||||||
|
# commit is also a "skip" commit (see above).
|
||||||
|
exit_if_skipped_commits "$bisect_rev"
|
||||||
|
|
||||||
echo "Bisecting: $bisect_nr revisions left to test after this"
|
echo "Bisecting: $bisect_nr revisions left to test after this"
|
||||||
echo "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect"
|
echo "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect"
|
||||||
git checkout -q new-bisect || exit
|
git checkout -q new-bisect || exit
|
||||||
@ -286,15 +395,17 @@ bisect_replay () {
|
|||||||
eval "$cmd"
|
eval "$cmd"
|
||||||
;;
|
;;
|
||||||
good)
|
good)
|
||||||
echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
|
bisect_write_good "$rev"
|
||||||
echo "# good: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
|
|
||||||
echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
|
echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
|
||||||
;;
|
;;
|
||||||
bad)
|
bad)
|
||||||
echo "$rev" >"$GIT_DIR/refs/bisect/bad"
|
bisect_write_bad "$rev"
|
||||||
echo "# bad: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
|
|
||||||
echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
|
echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
|
||||||
;;
|
;;
|
||||||
|
skip)
|
||||||
|
bisect_write_skip "$rev"
|
||||||
|
echo "git-bisect skip $rev" >>"$GIT_DIR/BISECT_LOG"
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
echo >&2 "?? what are you talking about?"
|
echo >&2 "?? what are you talking about?"
|
||||||
exit 1 ;;
|
exit 1 ;;
|
||||||
@ -362,6 +473,8 @@ case "$#" in
|
|||||||
bisect_bad "$@" ;;
|
bisect_bad "$@" ;;
|
||||||
good)
|
good)
|
||||||
bisect_good "$@" ;;
|
bisect_good "$@" ;;
|
||||||
|
skip)
|
||||||
|
bisect_skip "$@" ;;
|
||||||
next)
|
next)
|
||||||
# Not sure we want "next" at the UI level anymore.
|
# Not sure we want "next" at the UI level anymore.
|
||||||
bisect_next "$@" ;;
|
bisect_next "$@" ;;
|
||||||
|
@ -71,6 +71,63 @@ test_expect_success 'bisect start with one bad and good' '
|
|||||||
git bisect next
|
git bisect next
|
||||||
'
|
'
|
||||||
|
|
||||||
|
# $HASH1 is good, $HASH4 is bad, we skip $HASH3
|
||||||
|
# but $HASH2 is bad,
|
||||||
|
# so we should find $HASH2 as the first bad commit
|
||||||
|
test_expect_success 'bisect skip: successfull result' '
|
||||||
|
git bisect reset &&
|
||||||
|
git bisect start $HASH4 $HASH1 &&
|
||||||
|
git bisect skip &&
|
||||||
|
git bisect bad > my_bisect_log.txt &&
|
||||||
|
grep "$HASH2 is first bad commit" my_bisect_log.txt &&
|
||||||
|
git bisect reset
|
||||||
|
'
|
||||||
|
|
||||||
|
# $HASH1 is good, $HASH4 is bad, we skip $HASH3 and $HASH2
|
||||||
|
# so we should not be able to tell the first bad commit
|
||||||
|
# among $HASH2, $HASH3 and $HASH4
|
||||||
|
test_expect_success 'bisect skip: cannot tell between 3 commits' '
|
||||||
|
git bisect start $HASH4 $HASH1 &&
|
||||||
|
git bisect skip || return 1
|
||||||
|
|
||||||
|
if git bisect skip > my_bisect_log.txt
|
||||||
|
then
|
||||||
|
echo Oops, should have failed.
|
||||||
|
false
|
||||||
|
else
|
||||||
|
test $? -eq 2 &&
|
||||||
|
grep "first bad commit could be any of" my_bisect_log.txt &&
|
||||||
|
! grep $HASH1 my_bisect_log.txt &&
|
||||||
|
grep $HASH2 my_bisect_log.txt &&
|
||||||
|
grep $HASH3 my_bisect_log.txt &&
|
||||||
|
grep $HASH4 my_bisect_log.txt &&
|
||||||
|
git bisect reset
|
||||||
|
fi
|
||||||
|
'
|
||||||
|
|
||||||
|
# $HASH1 is good, $HASH4 is bad, we skip $HASH3
|
||||||
|
# but $HASH2 is good,
|
||||||
|
# so we should not be able to tell the first bad commit
|
||||||
|
# among $HASH3 and $HASH4
|
||||||
|
test_expect_success 'bisect skip: cannot tell between 2 commits' '
|
||||||
|
git bisect start $HASH4 $HASH1 &&
|
||||||
|
git bisect skip || return 1
|
||||||
|
|
||||||
|
if git bisect good > my_bisect_log.txt
|
||||||
|
then
|
||||||
|
echo Oops, should have failed.
|
||||||
|
false
|
||||||
|
else
|
||||||
|
test $? -eq 2 &&
|
||||||
|
grep "first bad commit could be any of" my_bisect_log.txt &&
|
||||||
|
! grep $HASH1 my_bisect_log.txt &&
|
||||||
|
! grep $HASH2 my_bisect_log.txt &&
|
||||||
|
grep $HASH3 my_bisect_log.txt &&
|
||||||
|
grep $HASH4 my_bisect_log.txt &&
|
||||||
|
git bisect reset
|
||||||
|
fi
|
||||||
|
'
|
||||||
|
|
||||||
# We want to automatically find the commit that
|
# We want to automatically find the commit that
|
||||||
# introduced "Another" into hello.
|
# introduced "Another" into hello.
|
||||||
test_expect_success \
|
test_expect_success \
|
||||||
@ -99,6 +156,20 @@ test_expect_success \
|
|||||||
grep "$HASH4 is first bad commit" my_bisect_log.txt &&
|
grep "$HASH4 is first bad commit" my_bisect_log.txt &&
|
||||||
git bisect reset'
|
git bisect reset'
|
||||||
|
|
||||||
|
# $HASH1 is good, $HASH5 is bad, we skip $HASH3
|
||||||
|
# but $HASH4 is good,
|
||||||
|
# so we should find $HASH5 as the first bad commit
|
||||||
|
HASH5=
|
||||||
|
test_expect_success 'bisect skip: add line and then a new test' '
|
||||||
|
add_line_into_file "5: Another new line." hello &&
|
||||||
|
HASH5=$(git rev-parse --verify HEAD) &&
|
||||||
|
git bisect start $HASH5 $HASH1 &&
|
||||||
|
git bisect skip &&
|
||||||
|
git bisect good > my_bisect_log.txt &&
|
||||||
|
grep "$HASH5 is first bad commit" my_bisect_log.txt &&
|
||||||
|
git bisect reset
|
||||||
|
'
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
test_done
|
test_done
|
||||||
|
Loading…
Reference in New Issue
Block a user