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:
|
on the subcommand:
|
||||||
|
|
||||||
git bisect start [<bad> [<good>...]] [--] [<paths>...]
|
git bisect start [<bad> [<good>...]] [--] [<paths>...]
|
||||||
git bisect bad <rev>
|
git bisect bad [<rev>]
|
||||||
git bisect good <rev>
|
git bisect good [<rev>...]
|
||||||
|
git bisect skip [<rev>...]
|
||||||
git bisect reset [<branch>]
|
git bisect reset [<branch>]
|
||||||
git bisect visualize
|
git bisect visualize
|
||||||
git bisect replay <logfile>
|
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
|
Then compile and test the one you chose to try. After that, tell
|
||||||
bisect what the result was as usual.
|
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
|
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
|
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
|
exit with code 0 in case the current source code is good. Exit with a
|
||||||
code between 1 and 127 (included) in case the current source code is
|
code between 1 and 127 (inclusive), except 125, if the current
|
||||||
bad.
|
source code is bad.
|
||||||
|
|
||||||
Any other exit code will abort the automatic bisect process. (A
|
Any other exit code will abort the automatic bisect process. (A
|
||||||
program that does "exit(-1)" leaves $? = 255, see exit(3) manual page,
|
program that does "exit(-1)" leaves $? = 255, see exit(3) manual page,
|
||||||
the value is chopped with "& 0377".)
|
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
|
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
|
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
|
"revision that does not have this commit needs this patch applied to
|
||||||
|
@ -34,6 +34,7 @@ SYNOPSIS
|
|||||||
[ \--pretty | \--header ]
|
[ \--pretty | \--header ]
|
||||||
[ \--bisect ]
|
[ \--bisect ]
|
||||||
[ \--bisect-vars ]
|
[ \--bisect-vars ]
|
||||||
|
[ \--bisect-all ]
|
||||||
[ \--merge ]
|
[ \--merge ]
|
||||||
[ \--reverse ]
|
[ \--reverse ]
|
||||||
[ \--walk-reflogs ]
|
[ \--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
|
turns out to be bad to `bisect_bad`, and the number of commits
|
||||||
we are bisecting right now to `bisect_all`.
|
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
|
Commit Ordering
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "revision.h"
|
#include "revision.h"
|
||||||
#include "list-objects.h"
|
#include "list-objects.h"
|
||||||
#include "builtin.h"
|
#include "builtin.h"
|
||||||
|
#include "log-tree.h"
|
||||||
|
|
||||||
/* bits #0-15 in revision.h */
|
/* bits #0-15 in revision.h */
|
||||||
|
|
||||||
@ -38,7 +39,8 @@ static const char rev_list_usage[] =
|
|||||||
" --left-right\n"
|
" --left-right\n"
|
||||||
" special purpose:\n"
|
" special purpose:\n"
|
||||||
" --bisect\n"
|
" --bisect\n"
|
||||||
" --bisect-vars"
|
" --bisect-vars\n"
|
||||||
|
" --bisect-all"
|
||||||
;
|
;
|
||||||
|
|
||||||
static struct rev_info revs;
|
static struct rev_info revs;
|
||||||
@ -74,6 +76,7 @@ static void show_commit(struct commit *commit)
|
|||||||
parents = parents->next;
|
parents = parents->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
show_decorations(commit);
|
||||||
if (revs.commit_format == CMIT_FMT_ONELINE)
|
if (revs.commit_format == CMIT_FMT_ONELINE)
|
||||||
putchar(' ');
|
putchar(' ');
|
||||||
else
|
else
|
||||||
@ -278,6 +281,57 @@ static struct commit_list *best_bisection(struct commit_list *list, int nr)
|
|||||||
return best;
|
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
|
* zero or positive weight is the number of interesting commits it can
|
||||||
* reach, including itself. Especially, weight = 0 means it does not
|
* 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.
|
* or positive distance.
|
||||||
*/
|
*/
|
||||||
static struct commit_list *do_find_bisection(struct commit_list *list,
|
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;
|
int n, counted;
|
||||||
struct commit_list *p;
|
struct commit_list *p;
|
||||||
@ -351,7 +406,7 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
|
|||||||
clear_distance(list);
|
clear_distance(list);
|
||||||
|
|
||||||
/* Does it happen to be at exactly half-way? */
|
/* Does it happen to be at exactly half-way? */
|
||||||
if (halfway(p, nr))
|
if (!find_all && halfway(p, nr))
|
||||||
return p;
|
return p;
|
||||||
counted++;
|
counted++;
|
||||||
}
|
}
|
||||||
@ -389,19 +444,22 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
|
|||||||
weight_set(p, weight(q));
|
weight_set(p, weight(q));
|
||||||
|
|
||||||
/* Does it happen to be at exactly half-way? */
|
/* Does it happen to be at exactly half-way? */
|
||||||
if (halfway(p, nr))
|
if (!find_all && halfway(p, nr))
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
show_list("bisection 2 counted all", counted, nr, list);
|
show_list("bisection 2 counted all", counted, nr, list);
|
||||||
|
|
||||||
/* Then find the best one */
|
if (!find_all)
|
||||||
return best_bisection(list, nr);
|
return best_bisection(list, nr);
|
||||||
|
else
|
||||||
|
return best_bisection_sorted(list, nr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct commit_list *find_bisection(struct commit_list *list,
|
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;
|
int nr, on_list;
|
||||||
struct commit_list *p, *best, *next, *last;
|
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));
|
weights = xcalloc(on_list, sizeof(*weights));
|
||||||
|
|
||||||
/* Do the real work of finding bisection commit. */
|
/* 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) {
|
if (best) {
|
||||||
best->next = NULL;
|
if (!find_all)
|
||||||
|
best->next = NULL;
|
||||||
*reaches = weight(best);
|
*reaches = weight(best);
|
||||||
}
|
}
|
||||||
free(weights);
|
free(weights);
|
||||||
|
|
||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -468,6 +525,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
|||||||
int i;
|
int i;
|
||||||
int read_from_stdin = 0;
|
int read_from_stdin = 0;
|
||||||
int bisect_show_vars = 0;
|
int bisect_show_vars = 0;
|
||||||
|
int bisect_find_all = 0;
|
||||||
|
|
||||||
git_config(git_default_config);
|
git_config(git_default_config);
|
||||||
init_revisions(&revs, prefix);
|
init_revisions(&revs, prefix);
|
||||||
@ -490,6 +548,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
|||||||
bisect_list = 1;
|
bisect_list = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!strcmp(arg, "--bisect-all")) {
|
||||||
|
bisect_list = 1;
|
||||||
|
bisect_find_all = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!strcmp(arg, "--bisect-vars")) {
|
if (!strcmp(arg, "--bisect-vars")) {
|
||||||
bisect_list = 1;
|
bisect_list = 1;
|
||||||
bisect_show_vars = 1;
|
bisect_show_vars = 1;
|
||||||
@ -536,9 +599,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
|||||||
if (bisect_list) {
|
if (bisect_list) {
|
||||||
int reaches = reaches, all = all;
|
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) {
|
if (bisect_show_vars) {
|
||||||
int cnt;
|
int cnt;
|
||||||
|
char hex[41];
|
||||||
if (!revs.commits)
|
if (!revs.commits)
|
||||||
return 1;
|
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
|
* A bisect set of size N has (N-1) commits further
|
||||||
* to test, as we already know one bad one.
|
* to test, as we already know one bad one.
|
||||||
*/
|
*/
|
||||||
cnt = all-reaches;
|
cnt = all - reaches;
|
||||||
if (cnt < reaches)
|
if (cnt < reaches)
|
||||||
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"
|
printf("bisect_rev=%s\n"
|
||||||
"bisect_nr=%d\n"
|
"bisect_nr=%d\n"
|
||||||
"bisect_good=%d\n"
|
"bisect_good=%d\n"
|
||||||
"bisect_bad=%d\n"
|
"bisect_bad=%d\n"
|
||||||
"bisect_all=%d\n",
|
"bisect_all=%d\n",
|
||||||
sha1_to_hex(revs.commits->item->object.sha1),
|
hex,
|
||||||
cnt - 1,
|
cnt - 1,
|
||||||
all - reaches - 1,
|
all - reaches - 1,
|
||||||
reaches - 1,
|
reaches - 1,
|
||||||
|
252
git-bisect.sh
252
git-bisect.sh
@ -1,12 +1,14 @@
|
|||||||
#!/bin/sh
|
#!/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>...]
|
LONG_USAGE='git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
|
||||||
reset bisect state and start bisection.
|
reset bisect state and start bisection.
|
||||||
git bisect bad [<rev>]
|
git bisect bad [<rev>]
|
||||||
mark <rev> a known-bad revision.
|
mark <rev> a known-bad revision.
|
||||||
git bisect good [<rev>...]
|
git bisect good [<rev>...]
|
||||||
mark <rev>... known-good revisions.
|
mark <rev>... known-good revisions.
|
||||||
|
git bisect skip [<rev>...]
|
||||||
|
mark <rev>... untestable revisions.
|
||||||
git bisect next
|
git bisect next
|
||||||
find next bisection to test and check it out.
|
find next bisection to test and check it out.
|
||||||
git bisect reset [<branch>]
|
git bisect reset [<branch>]
|
||||||
@ -64,7 +66,7 @@ bisect_start() {
|
|||||||
branch=`cat "$GIT_DIR/head-name"`
|
branch=`cat "$GIT_DIR/head-name"`
|
||||||
else
|
else
|
||||||
branch=master
|
branch=master
|
||||||
fi
|
fi
|
||||||
git checkout $branch || exit
|
git checkout $branch || exit
|
||||||
;;
|
;;
|
||||||
refs/heads/*)
|
refs/heads/*)
|
||||||
@ -95,75 +97,74 @@ bisect_start() {
|
|||||||
arg="$1"
|
arg="$1"
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
--)
|
--)
|
||||||
shift
|
shift
|
||||||
break
|
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 &&
|
test $has_double_dash -eq 1 &&
|
||||||
die "'$arg' does not appear to be a valid revision"
|
die "'$arg' does not appear to be a valid revision"
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if [ $bad_seen -eq 0 ]; then
|
case $bad_seen in
|
||||||
bad_seen=1
|
0) state='bad' ; bad_seen=1 ;;
|
||||||
bisect_write_bad "$rev"
|
*) state='good' ;;
|
||||||
else
|
esac
|
||||||
bisect_write_good "$rev"
|
bisect_write "$state" "$rev" 'nolog'
|
||||||
fi
|
shift
|
||||||
shift
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
sq "$@" >"$GIT_DIR/BISECT_NAMES"
|
sq "$@" >"$GIT_DIR/BISECT_NAMES"
|
||||||
echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG"
|
echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG"
|
||||||
bisect_auto_next
|
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
|
bisect_autostart
|
||||||
case "$#" in
|
state=$1
|
||||||
0)
|
case "$#,$state" in
|
||||||
rev=$(git rev-parse --verify HEAD) ;;
|
0,*)
|
||||||
1)
|
die "Please call 'bisect_state' with at least one argument." ;;
|
||||||
rev=$(git rev-parse --verify "$1^{commit}") ;;
|
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 ;;
|
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
|
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_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() {
|
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
|
||||||
@ -206,17 +207,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
|
||||||
@ -224,11 +305,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
|
||||||
@ -250,12 +336,10 @@ bisect_reset() {
|
|||||||
else
|
else
|
||||||
branch=master
|
branch=master
|
||||||
fi ;;
|
fi ;;
|
||||||
1) git show-ref --verify --quiet -- "refs/heads/$1" || {
|
1) git show-ref --verify --quiet -- "refs/heads/$1" ||
|
||||||
echo >&2 "$1 does not seem to be a valid branch"
|
die "$1 does not seem to be a valid branch"
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
branch="$1" ;;
|
branch="$1" ;;
|
||||||
*)
|
*)
|
||||||
usage ;;
|
usage ;;
|
||||||
esac
|
esac
|
||||||
if git checkout "$branch"; then
|
if git checkout "$branch"; then
|
||||||
@ -273,10 +357,7 @@ bisect_clean_state() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bisect_replay () {
|
bisect_replay () {
|
||||||
test -r "$1" || {
|
test -r "$1" || die "cannot read $1 for replaying"
|
||||||
echo >&2 "cannot read $1 for replaying"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
bisect_reset
|
bisect_reset
|
||||||
while read bisect command rev
|
while read bisect command rev
|
||||||
do
|
do
|
||||||
@ -284,21 +365,11 @@ bisect_replay () {
|
|||||||
case "$command" in
|
case "$command" in
|
||||||
start)
|
start)
|
||||||
cmd="bisect_start $rev"
|
cmd="bisect_start $rev"
|
||||||
eval "$cmd"
|
eval "$cmd" ;;
|
||||||
;;
|
good|bad|skip)
|
||||||
good)
|
bisect_write "$command" "$rev" ;;
|
||||||
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"
|
|
||||||
;;
|
|
||||||
*)
|
*)
|
||||||
echo >&2 "?? what are you talking about?"
|
die "?? what are you talking about?" ;;
|
||||||
exit 1 ;;
|
|
||||||
esac
|
esac
|
||||||
done <"$1"
|
done <"$1"
|
||||||
bisect_auto_next
|
bisect_auto_next
|
||||||
@ -320,24 +391,31 @@ bisect_run () {
|
|||||||
exit $res
|
exit $res
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Use "bisect_good" or "bisect_bad"
|
# Find current state depending on run success or failure.
|
||||||
# depending on run success or failure.
|
# A special exit code of 125 means cannot test.
|
||||||
if [ $res -gt 0 ]; then
|
if [ $res -eq 125 ]; then
|
||||||
next_bisect='bisect_bad'
|
state='skip'
|
||||||
|
elif [ $res -gt 0 ]; then
|
||||||
|
state='bad'
|
||||||
else
|
else
|
||||||
next_bisect='bisect_good'
|
state='good'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# We have to use a subshell because bisect_good or
|
# We have to use a subshell because "bisect_state" can exit.
|
||||||
# bisect_bad functions can exit.
|
( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
|
||||||
( $next_bisect > "$GIT_DIR/BISECT_RUN" )
|
|
||||||
res=$?
|
res=$?
|
||||||
|
|
||||||
cat "$GIT_DIR/BISECT_RUN"
|
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
|
if [ $res -ne 0 ]; then
|
||||||
echo >&2 "bisect run failed:"
|
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
|
exit $res
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -359,10 +437,8 @@ case "$#" in
|
|||||||
case "$cmd" in
|
case "$cmd" in
|
||||||
start)
|
start)
|
||||||
bisect_start "$@" ;;
|
bisect_start "$@" ;;
|
||||||
bad)
|
bad|good|skip)
|
||||||
bisect_bad "$@" ;;
|
bisect_state "$cmd" "$@" ;;
|
||||||
good)
|
|
||||||
bisect_good "$@" ;;
|
|
||||||
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 "$@" ;;
|
||||||
|
@ -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;
|
const char *prefix;
|
||||||
struct name_decoration *decoration;
|
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_commit(struct rev_info *, struct commit *);
|
||||||
int log_tree_opt_parse(struct rev_info *, const char **, int);
|
int log_tree_opt_parse(struct rev_info *, const char **, int);
|
||||||
void show_log(struct rev_info *opt, const char *sep);
|
void show_log(struct rev_info *opt, const char *sep);
|
||||||
|
void show_decorations(struct commit *commit);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -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,67 @@ 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 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
|
test_done
|
||||||
|
Loading…
Reference in New Issue
Block a user