mirror of
https://github.com/git/git
synced 2024-08-25 02:35:46 +00:00
Merge branch 'cc/skip' into HEAD
* cc/skip: Bisect: add "skip" to the short usage string. Bisect run: "skip" current commit if script exit code is 125. Bisect: add a "bisect replay" test case. Bisect: add "bisect skip" to the documentation. Bisect: refactor "bisect_{bad,good,skip}" into "bisect_state". Bisect: refactor some logging into "bisect_write". Bisect: refactor "bisect_write_*" functions. Bisect: implement "bisect skip" to mark untestable revisions. Bisect: fix some white spaces and empty lines breakages. rev-list documentation: add "--bisect-all". rev-list: implement --bisect-all
This commit is contained in:
commit
9725bb8b85
|
@ -16,8 +16,9 @@ The command takes various subcommands, and different options depending
|
|||
on the subcommand:
|
||||
|
||||
git bisect start [<bad> [<good>...]] [--] [<paths>...]
|
||||
git bisect bad <rev>
|
||||
git bisect good <rev>
|
||||
git bisect bad [<rev>]
|
||||
git bisect good [<rev>...]
|
||||
git bisect skip [<rev>...]
|
||||
git bisect reset [<branch>]
|
||||
git bisect visualize
|
||||
git bisect replay <logfile>
|
||||
|
@ -134,6 +135,20 @@ $ git reset --hard HEAD~3 # try 3 revs before what
|
|||
Then compile and test the one you chose to try. After that, tell
|
||||
bisect what the result was as usual.
|
||||
|
||||
Bisect skip
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Instead of choosing by yourself a nearby commit, you may just want git
|
||||
to do it for you using:
|
||||
|
||||
------------
|
||||
$ git bisect skip # Current version cannot be tested
|
||||
------------
|
||||
|
||||
But computing the commit to test may be slower afterwards and git may
|
||||
eventually not be able to tell the first bad among a bad and one or
|
||||
more "skip"ped commits.
|
||||
|
||||
Cutting down bisection by giving more parameters to bisect start
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -167,14 +182,18 @@ $ git bisect run my_script
|
|||
------------
|
||||
|
||||
Note that the "run" script (`my_script` in the above example) should
|
||||
exit with code 0 in case the current source code is good and with a
|
||||
code between 1 and 127 (included) in case the current source code is
|
||||
bad.
|
||||
exit with code 0 in case the current source code is good. Exit with a
|
||||
code between 1 and 127 (inclusive), except 125, if the current
|
||||
source code is bad.
|
||||
|
||||
Any other exit code will abort the automatic bisect process. (A
|
||||
program that does "exit(-1)" leaves $? = 255, see exit(3) manual page,
|
||||
the value is chopped with "& 0377".)
|
||||
|
||||
The special exit code 125 should be used when the current source code
|
||||
cannot be tested. If the "run" script exits with this code, the current
|
||||
revision will be skipped, see `git bisect skip` above.
|
||||
|
||||
You may often find that during bisect you want to have near-constant
|
||||
tweaks (e.g., s/#define DEBUG 0/#define DEBUG 1/ in a header file, or
|
||||
"revision that does not have this commit needs this patch applied to
|
||||
|
|
|
@ -34,6 +34,7 @@ SYNOPSIS
|
|||
[ \--pretty | \--header ]
|
||||
[ \--bisect ]
|
||||
[ \--bisect-vars ]
|
||||
[ \--bisect-all ]
|
||||
[ \--merge ]
|
||||
[ \--reverse ]
|
||||
[ \--walk-reflogs ]
|
||||
|
@ -354,6 +355,21 @@ the expected number of commits to be tested if `bisect_rev`
|
|||
turns out to be bad to `bisect_bad`, and the number of commits
|
||||
we are bisecting right now to `bisect_all`.
|
||||
|
||||
--bisect-all::
|
||||
|
||||
This outputs all the commit objects between the included and excluded
|
||||
commits, ordered by their distance to the included and excluded
|
||||
commits. The farthest from them is displayed first. (This is the only
|
||||
one displayed by `--bisect`.)
|
||||
|
||||
This is useful because it makes it easy to choose a good commit to
|
||||
test when you want to avoid to test some of them for some reason (they
|
||||
may not compile for example).
|
||||
|
||||
This option can be used along with `--bisect-vars`, in this case,
|
||||
after all the sorted commit objects, there will be the same text as if
|
||||
`--bisect-vars` had been used alone.
|
||||
|
||||
--
|
||||
|
||||
Commit Ordering
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "revision.h"
|
||||
#include "list-objects.h"
|
||||
#include "builtin.h"
|
||||
#include "log-tree.h"
|
||||
|
||||
/* bits #0-15 in revision.h */
|
||||
|
||||
|
@ -38,7 +39,8 @@ static const char rev_list_usage[] =
|
|||
" --left-right\n"
|
||||
" special purpose:\n"
|
||||
" --bisect\n"
|
||||
" --bisect-vars"
|
||||
" --bisect-vars\n"
|
||||
" --bisect-all"
|
||||
;
|
||||
|
||||
static struct rev_info revs;
|
||||
|
@ -74,6 +76,7 @@ static void show_commit(struct commit *commit)
|
|||
parents = parents->next;
|
||||
}
|
||||
}
|
||||
show_decorations(commit);
|
||||
if (revs.commit_format == CMIT_FMT_ONELINE)
|
||||
putchar(' ');
|
||||
else
|
||||
|
@ -278,6 +281,57 @@ static struct commit_list *best_bisection(struct commit_list *list, int nr)
|
|||
return best;
|
||||
}
|
||||
|
||||
struct commit_dist {
|
||||
struct commit *commit;
|
||||
int distance;
|
||||
};
|
||||
|
||||
static int compare_commit_dist(const void *a_, const void *b_)
|
||||
{
|
||||
struct commit_dist *a, *b;
|
||||
|
||||
a = (struct commit_dist *)a_;
|
||||
b = (struct commit_dist *)b_;
|
||||
if (a->distance != b->distance)
|
||||
return b->distance - a->distance; /* desc sort */
|
||||
return hashcmp(a->commit->object.sha1, b->commit->object.sha1);
|
||||
}
|
||||
|
||||
static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr)
|
||||
{
|
||||
struct commit_list *p;
|
||||
struct commit_dist *array = xcalloc(nr, sizeof(*array));
|
||||
int cnt, i;
|
||||
|
||||
for (p = list, cnt = 0; p; p = p->next) {
|
||||
int distance;
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
if (revs.prune_fn && !(flags & TREECHANGE))
|
||||
continue;
|
||||
distance = weight(p);
|
||||
if (nr - distance < distance)
|
||||
distance = nr - distance;
|
||||
array[cnt].commit = p->item;
|
||||
array[cnt].distance = distance;
|
||||
cnt++;
|
||||
}
|
||||
qsort(array, cnt, sizeof(*array), compare_commit_dist);
|
||||
for (p = list, i = 0; i < cnt; i++) {
|
||||
struct name_decoration *r = xmalloc(sizeof(*r) + 100);
|
||||
struct object *obj = &(array[i].commit->object);
|
||||
|
||||
sprintf(r->name, "dist=%d", array[i].distance);
|
||||
r->next = add_decoration(&name_decoration, obj, r);
|
||||
p->item = array[i].commit;
|
||||
p = p->next;
|
||||
}
|
||||
if (p)
|
||||
p->next = NULL;
|
||||
free(array);
|
||||
return list;
|
||||
}
|
||||
|
||||
/*
|
||||
* zero or positive weight is the number of interesting commits it can
|
||||
* reach, including itself. Especially, weight = 0 means it does not
|
||||
|
@ -292,7 +346,8 @@ static struct commit_list *best_bisection(struct commit_list *list, int nr)
|
|||
* or positive distance.
|
||||
*/
|
||||
static struct commit_list *do_find_bisection(struct commit_list *list,
|
||||
int nr, int *weights)
|
||||
int nr, int *weights,
|
||||
int find_all)
|
||||
{
|
||||
int n, counted;
|
||||
struct commit_list *p;
|
||||
|
@ -351,7 +406,7 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
|
|||
clear_distance(list);
|
||||
|
||||
/* Does it happen to be at exactly half-way? */
|
||||
if (halfway(p, nr))
|
||||
if (!find_all && halfway(p, nr))
|
||||
return p;
|
||||
counted++;
|
||||
}
|
||||
|
@ -389,19 +444,22 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
|
|||
weight_set(p, weight(q));
|
||||
|
||||
/* Does it happen to be at exactly half-way? */
|
||||
if (halfway(p, nr))
|
||||
if (!find_all && halfway(p, nr))
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
show_list("bisection 2 counted all", counted, nr, list);
|
||||
|
||||
/* Then find the best one */
|
||||
return best_bisection(list, nr);
|
||||
if (!find_all)
|
||||
return best_bisection(list, nr);
|
||||
else
|
||||
return best_bisection_sorted(list, nr);
|
||||
}
|
||||
|
||||
static struct commit_list *find_bisection(struct commit_list *list,
|
||||
int *reaches, int *all)
|
||||
int *reaches, int *all,
|
||||
int find_all)
|
||||
{
|
||||
int nr, on_list;
|
||||
struct commit_list *p, *best, *next, *last;
|
||||
|
@ -434,14 +492,13 @@ static struct commit_list *find_bisection(struct commit_list *list,
|
|||
weights = xcalloc(on_list, sizeof(*weights));
|
||||
|
||||
/* Do the real work of finding bisection commit. */
|
||||
best = do_find_bisection(list, nr, weights);
|
||||
|
||||
best = do_find_bisection(list, nr, weights, find_all);
|
||||
if (best) {
|
||||
best->next = NULL;
|
||||
if (!find_all)
|
||||
best->next = NULL;
|
||||
*reaches = weight(best);
|
||||
}
|
||||
free(weights);
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
|
@ -468,6 +525,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
|||
int i;
|
||||
int read_from_stdin = 0;
|
||||
int bisect_show_vars = 0;
|
||||
int bisect_find_all = 0;
|
||||
|
||||
git_config(git_default_config);
|
||||
init_revisions(&revs, prefix);
|
||||
|
@ -490,6 +548,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
|||
bisect_list = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--bisect-all")) {
|
||||
bisect_list = 1;
|
||||
bisect_find_all = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--bisect-vars")) {
|
||||
bisect_list = 1;
|
||||
bisect_show_vars = 1;
|
||||
|
@ -536,9 +599,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
|||
if (bisect_list) {
|
||||
int reaches = reaches, all = all;
|
||||
|
||||
revs.commits = find_bisection(revs.commits, &reaches, &all);
|
||||
revs.commits = find_bisection(revs.commits, &reaches, &all,
|
||||
bisect_find_all);
|
||||
if (bisect_show_vars) {
|
||||
int cnt;
|
||||
char hex[41];
|
||||
if (!revs.commits)
|
||||
return 1;
|
||||
/*
|
||||
|
@ -550,15 +615,22 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
|||
* A bisect set of size N has (N-1) commits further
|
||||
* to test, as we already know one bad one.
|
||||
*/
|
||||
cnt = all-reaches;
|
||||
cnt = all - reaches;
|
||||
if (cnt < reaches)
|
||||
cnt = reaches;
|
||||
strcpy(hex, sha1_to_hex(revs.commits->item->object.sha1));
|
||||
|
||||
if (bisect_find_all) {
|
||||
traverse_commit_list(&revs, show_commit, show_object);
|
||||
printf("------\n");
|
||||
}
|
||||
|
||||
printf("bisect_rev=%s\n"
|
||||
"bisect_nr=%d\n"
|
||||
"bisect_good=%d\n"
|
||||
"bisect_bad=%d\n"
|
||||
"bisect_all=%d\n",
|
||||
sha1_to_hex(revs.commits->item->object.sha1),
|
||||
hex,
|
||||
cnt - 1,
|
||||
all - reaches - 1,
|
||||
reaches - 1,
|
||||
|
|
252
git-bisect.sh
252
git-bisect.sh
|
@ -1,12 +1,14 @@
|
|||
#!/bin/sh
|
||||
|
||||
USAGE='[start|bad|good|next|reset|visualize|replay|log|run]'
|
||||
USAGE='[start|bad|good|skip|next|reset|visualize|replay|log|run]'
|
||||
LONG_USAGE='git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
|
||||
reset bisect state and start bisection.
|
||||
git bisect bad [<rev>]
|
||||
mark <rev> a known-bad revision.
|
||||
git bisect good [<rev>...]
|
||||
mark <rev>... known-good revisions.
|
||||
git bisect skip [<rev>...]
|
||||
mark <rev>... untestable revisions.
|
||||
git bisect next
|
||||
find next bisection to test and check it out.
|
||||
git bisect reset [<branch>]
|
||||
|
@ -64,7 +66,7 @@ bisect_start() {
|
|||
branch=`cat "$GIT_DIR/head-name"`
|
||||
else
|
||||
branch=master
|
||||
fi
|
||||
fi
|
||||
git checkout $branch || exit
|
||||
;;
|
||||
refs/heads/*)
|
||||
|
@ -95,75 +97,74 @@ bisect_start() {
|
|||
arg="$1"
|
||||
case "$arg" in
|
||||
--)
|
||||
shift
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
rev=$(git rev-parse --verify "$arg^{commit}" 2>/dev/null) || {
|
||||
rev=$(git rev-parse --verify "$arg^{commit}" 2>/dev/null) || {
|
||||
test $has_double_dash -eq 1 &&
|
||||
die "'$arg' does not appear to be a valid revision"
|
||||
break
|
||||
}
|
||||
if [ $bad_seen -eq 0 ]; then
|
||||
bad_seen=1
|
||||
bisect_write_bad "$rev"
|
||||
else
|
||||
bisect_write_good "$rev"
|
||||
fi
|
||||
shift
|
||||
case $bad_seen in
|
||||
0) state='bad' ; bad_seen=1 ;;
|
||||
*) state='good' ;;
|
||||
esac
|
||||
bisect_write "$state" "$rev" 'nolog'
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
done
|
||||
|
||||
sq "$@" >"$GIT_DIR/BISECT_NAMES"
|
||||
echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG"
|
||||
bisect_auto_next
|
||||
}
|
||||
|
||||
bisect_bad() {
|
||||
bisect_write() {
|
||||
state="$1"
|
||||
rev="$2"
|
||||
nolog="$3"
|
||||
case "$state" in
|
||||
bad) tag="$state" ;;
|
||||
good|skip) tag="$state"-"$rev" ;;
|
||||
*) die "Bad bisect_write argument: $state" ;;
|
||||
esac
|
||||
echo "$rev" >"$GIT_DIR/refs/bisect/$tag"
|
||||
echo "# $state: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
|
||||
test -z "$nolog" && echo "git-bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
|
||||
}
|
||||
|
||||
bisect_state() {
|
||||
bisect_autostart
|
||||
case "$#" in
|
||||
0)
|
||||
rev=$(git rev-parse --verify HEAD) ;;
|
||||
1)
|
||||
rev=$(git rev-parse --verify "$1^{commit}") ;;
|
||||
state=$1
|
||||
case "$#,$state" in
|
||||
0,*)
|
||||
die "Please call 'bisect_state' with at least one argument." ;;
|
||||
1,bad|1,good|1,skip)
|
||||
rev=$(git rev-parse --verify HEAD) ||
|
||||
die "Bad rev input: HEAD"
|
||||
bisect_write "$state" "$rev" ;;
|
||||
2,bad)
|
||||
rev=$(git rev-parse --verify "$2^{commit}") ||
|
||||
die "Bad rev input: $2"
|
||||
bisect_write "$state" "$rev" ;;
|
||||
*,good|*,skip)
|
||||
shift
|
||||
revs=$(git rev-parse --revs-only --no-flags "$@") &&
|
||||
test '' != "$revs" || die "Bad rev input: $@"
|
||||
for rev in $revs
|
||||
do
|
||||
rev=$(git rev-parse --verify "$rev^{commit}") ||
|
||||
die "Bad rev commit: $rev^{commit}"
|
||||
bisect_write "$state" "$rev"
|
||||
done ;;
|
||||
*)
|
||||
usage ;;
|
||||
esac || exit
|
||||
bisect_write_bad "$rev"
|
||||
echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
|
||||
bisect_auto_next
|
||||
}
|
||||
|
||||
bisect_write_bad() {
|
||||
rev="$1"
|
||||
echo "$rev" >"$GIT_DIR/refs/bisect/bad"
|
||||
echo "# bad: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
|
||||
}
|
||||
|
||||
bisect_good() {
|
||||
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_good "$rev"
|
||||
echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
|
||||
|
||||
done
|
||||
bisect_auto_next
|
||||
}
|
||||
|
||||
bisect_write_good() {
|
||||
rev="$1"
|
||||
echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
|
||||
echo "# good: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
|
||||
}
|
||||
|
||||
bisect_next_check() {
|
||||
missing_good= missing_bad=
|
||||
git show-ref -q --verify refs/bisect/bad || missing_bad=t
|
||||
|
@ -206,17 +207,97 @@ bisect_auto_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() {
|
||||
case "$#" in 0) ;; *) usage ;; esac
|
||||
case "$#" in 0) ;; *) usage ;; esac
|
||||
bisect_autostart
|
||||
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) &&
|
||||
good=$(git for-each-ref --format='^%(objectname)' \
|
||||
"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 "$eval") &&
|
||||
eval=$(filter_skipped "$eval" "$skip") &&
|
||||
eval "$eval" || exit
|
||||
|
||||
if [ -z "$bisect_rev" ]; then
|
||||
|
@ -224,11 +305,16 @@ bisect_next() {
|
|||
exit 1
|
||||
fi
|
||||
if [ "$bisect_rev" = "$bad" ]; then
|
||||
exit_if_skipped_commits "$bisect_tried"
|
||||
echo "$bisect_rev is first bad commit"
|
||||
git diff-tree --pretty $bisect_rev
|
||||
exit 0
|
||||
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 "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect"
|
||||
git checkout -q new-bisect || exit
|
||||
|
@ -250,12 +336,10 @@ bisect_reset() {
|
|||
else
|
||||
branch=master
|
||||
fi ;;
|
||||
1) git show-ref --verify --quiet -- "refs/heads/$1" || {
|
||||
echo >&2 "$1 does not seem to be a valid branch"
|
||||
exit 1
|
||||
}
|
||||
1) git show-ref --verify --quiet -- "refs/heads/$1" ||
|
||||
die "$1 does not seem to be a valid branch"
|
||||
branch="$1" ;;
|
||||
*)
|
||||
*)
|
||||
usage ;;
|
||||
esac
|
||||
if git checkout "$branch"; then
|
||||
|
@ -273,10 +357,7 @@ bisect_clean_state() {
|
|||
}
|
||||
|
||||
bisect_replay () {
|
||||
test -r "$1" || {
|
||||
echo >&2 "cannot read $1 for replaying"
|
||||
exit 1
|
||||
}
|
||||
test -r "$1" || die "cannot read $1 for replaying"
|
||||
bisect_reset
|
||||
while read bisect command rev
|
||||
do
|
||||
|
@ -284,21 +365,11 @@ bisect_replay () {
|
|||
case "$command" in
|
||||
start)
|
||||
cmd="bisect_start $rev"
|
||||
eval "$cmd"
|
||||
;;
|
||||
good)
|
||||
echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
|
||||
echo "# good: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
|
||||
echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
|
||||
;;
|
||||
bad)
|
||||
echo "$rev" >"$GIT_DIR/refs/bisect/bad"
|
||||
echo "# bad: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
|
||||
echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
|
||||
;;
|
||||
eval "$cmd" ;;
|
||||
good|bad|skip)
|
||||
bisect_write "$command" "$rev" ;;
|
||||
*)
|
||||
echo >&2 "?? what are you talking about?"
|
||||
exit 1 ;;
|
||||
die "?? what are you talking about?" ;;
|
||||
esac
|
||||
done <"$1"
|
||||
bisect_auto_next
|
||||
|
@ -320,24 +391,31 @@ bisect_run () {
|
|||
exit $res
|
||||
fi
|
||||
|
||||
# Use "bisect_good" or "bisect_bad"
|
||||
# depending on run success or failure.
|
||||
if [ $res -gt 0 ]; then
|
||||
next_bisect='bisect_bad'
|
||||
# Find current state depending on run success or failure.
|
||||
# A special exit code of 125 means cannot test.
|
||||
if [ $res -eq 125 ]; then
|
||||
state='skip'
|
||||
elif [ $res -gt 0 ]; then
|
||||
state='bad'
|
||||
else
|
||||
next_bisect='bisect_good'
|
||||
state='good'
|
||||
fi
|
||||
|
||||
# We have to use a subshell because bisect_good or
|
||||
# bisect_bad functions can exit.
|
||||
( $next_bisect > "$GIT_DIR/BISECT_RUN" )
|
||||
# We have to use a subshell because "bisect_state" can exit.
|
||||
( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
|
||||
res=$?
|
||||
|
||||
cat "$GIT_DIR/BISECT_RUN"
|
||||
|
||||
if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
|
||||
> /dev/null; then
|
||||
echo >&2 "bisect run cannot continue any more"
|
||||
exit $res
|
||||
fi
|
||||
|
||||
if [ $res -ne 0 ]; then
|
||||
echo >&2 "bisect run failed:"
|
||||
echo >&2 "$next_bisect exited with error code $res"
|
||||
echo >&2 "'bisect_state $state' exited with error code $res"
|
||||
exit $res
|
||||
fi
|
||||
|
||||
|
@ -359,10 +437,8 @@ case "$#" in
|
|||
case "$cmd" in
|
||||
start)
|
||||
bisect_start "$@" ;;
|
||||
bad)
|
||||
bisect_bad "$@" ;;
|
||||
good)
|
||||
bisect_good "$@" ;;
|
||||
bad|good|skip)
|
||||
bisect_state "$cmd" "$@" ;;
|
||||
next)
|
||||
# Not sure we want "next" at the UI level anymore.
|
||||
bisect_next "$@" ;;
|
||||
|
|
|
@ -15,7 +15,7 @@ static void show_parents(struct commit *commit, int abbrev)
|
|||
}
|
||||
}
|
||||
|
||||
static void show_decorations(struct commit *commit)
|
||||
void show_decorations(struct commit *commit)
|
||||
{
|
||||
const char *prefix;
|
||||
struct name_decoration *decoration;
|
||||
|
|
|
@ -12,5 +12,6 @@ int log_tree_diff_flush(struct rev_info *);
|
|||
int log_tree_commit(struct rev_info *, struct commit *);
|
||||
int log_tree_opt_parse(struct rev_info *, const char **, int);
|
||||
void show_log(struct rev_info *opt, const char *sep);
|
||||
void show_decorations(struct commit *commit);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -71,6 +71,63 @@ test_expect_success 'bisect start with one bad and good' '
|
|||
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
|
||||
# introduced "Another" into hello.
|
||||
test_expect_success \
|
||||
|
@ -99,6 +156,67 @@ test_expect_success \
|
|||
grep "$HASH4 is first bad commit" my_bisect_log.txt &&
|
||||
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 log > log_to_replay.txt
|
||||
git bisect reset
|
||||
'
|
||||
|
||||
test_expect_success 'bisect skip and bisect replay' '
|
||||
git bisect replay log_to_replay.txt > my_bisect_log.txt &&
|
||||
grep "$HASH5 is first bad commit" my_bisect_log.txt &&
|
||||
git bisect reset
|
||||
'
|
||||
|
||||
HASH6=
|
||||
test_expect_success 'bisect run & skip: cannot tell between 2' '
|
||||
add_line_into_file "6: Yet a line." hello &&
|
||||
HASH6=$(git rev-parse --verify HEAD) &&
|
||||
echo "#"\!"/bin/sh" > test_script.sh &&
|
||||
echo "tail -1 hello | grep Ciao > /dev/null && exit 125" >> test_script.sh &&
|
||||
echo "grep line hello > /dev/null" >> test_script.sh &&
|
||||
echo "test \$? -ne 0" >> test_script.sh &&
|
||||
chmod +x test_script.sh &&
|
||||
git bisect start $HASH6 $HASH1 &&
|
||||
if git bisect run ./test_script.sh > 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 $HASH3 my_bisect_log.txt &&
|
||||
! grep $HASH6 my_bisect_log.txt &&
|
||||
grep $HASH4 my_bisect_log.txt &&
|
||||
grep $HASH5 my_bisect_log.txt
|
||||
fi
|
||||
'
|
||||
|
||||
HASH7=
|
||||
test_expect_success 'bisect run & skip: find first bad' '
|
||||
git bisect reset &&
|
||||
add_line_into_file "7: Should be the last line." hello &&
|
||||
HASH7=$(git rev-parse --verify HEAD) &&
|
||||
echo "#"\!"/bin/sh" > test_script.sh &&
|
||||
echo "tail -1 hello | grep Ciao > /dev/null && exit 125" >> test_script.sh &&
|
||||
echo "tail -1 hello | grep day > /dev/null && exit 125" >> test_script.sh &&
|
||||
echo "grep Yet hello > /dev/null" >> test_script.sh &&
|
||||
echo "test \$? -ne 0" >> test_script.sh &&
|
||||
chmod +x test_script.sh &&
|
||||
git bisect start $HASH7 $HASH1 &&
|
||||
git bisect run ./test_script.sh > my_bisect_log.txt &&
|
||||
grep "$HASH6 is first bad commit" my_bisect_log.txt
|
||||
'
|
||||
|
||||
#
|
||||
#
|
||||
test_done
|
||||
|
|
Loading…
Reference in a new issue