Merge branch 'al/bisect-first-parent'
"git bisect" learns the "--first-parent" option to find the first breakage along the first-parent chain. * al/bisect-first-parent: bisect: combine args passed to find_bisection() bisect: introduce first-parent flag cmd_bisect__helper: defer parsing no-checkout flag rev-list: allow bisect and first-parent flags t6030: modernize "git bisect run" tests
This commit is contained in:
commit
47f0f94bc7
@ -17,7 +17,7 @@ The command takes various subcommands, and different options depending
|
||||
on the subcommand:
|
||||
|
||||
git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]
|
||||
[--no-checkout] [<bad> [<good>...]] [--] [<paths>...]
|
||||
[--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]
|
||||
git bisect (bad|new|<term-new>) [<rev>]
|
||||
git bisect (good|old|<term-old>) [<rev>...]
|
||||
git bisect terms [--term-good | --term-bad]
|
||||
@ -365,6 +365,17 @@ does not require a checked out tree.
|
||||
+
|
||||
If the repository is bare, `--no-checkout` is assumed.
|
||||
|
||||
--first-parent::
|
||||
+
|
||||
Follow only the first parent commit upon seeing a merge commit.
|
||||
+
|
||||
In detecting regressions introduced through the merging of a branch, the merge
|
||||
commit will be identified as introduction of the bug and its ancestors will be
|
||||
ignored.
|
||||
+
|
||||
This option is particularly useful in avoiding false positives when a merged
|
||||
branch contained broken or non-buildable commits, but the merge itself was OK.
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
|
||||
|
@ -128,8 +128,7 @@ parents) and `--max-parents=-1` (negative numbers denote no upper limit).
|
||||
because merges into a topic branch tend to be only about
|
||||
adjusting to updated upstream from time to time, and
|
||||
this option allows you to ignore the individual commits
|
||||
brought in to your history by such a merge. Cannot be
|
||||
combined with --bisect.
|
||||
brought in to your history by such a merge.
|
||||
|
||||
--not::
|
||||
Reverses the meaning of the '{caret}' prefix (or lack thereof)
|
||||
@ -207,7 +206,7 @@ ifndef::git-rev-list[]
|
||||
Pretend as if the bad bisection ref `refs/bisect/bad`
|
||||
was listed and as if it was followed by `--not` and the good
|
||||
bisection refs `refs/bisect/good-*` on the command
|
||||
line. Cannot be combined with --first-parent.
|
||||
line.
|
||||
endif::git-rev-list[]
|
||||
|
||||
--stdin::
|
||||
@ -743,7 +742,7 @@ outputs 'midpoint', the output of the two commands
|
||||
would be of roughly the same length. Finding the change which
|
||||
introduces a regression is thus reduced to a binary search: repeatedly
|
||||
generate and test new 'midpoint's until the commit chain is of length
|
||||
one. Cannot be combined with --first-parent.
|
||||
one.
|
||||
|
||||
--bisect-vars::
|
||||
This calculates the same as `--bisect`, except that refs in
|
||||
|
79
bisect.c
79
bisect.c
@ -15,6 +15,7 @@
|
||||
#include "commit-slab.h"
|
||||
#include "commit-reach.h"
|
||||
#include "object-store.h"
|
||||
#include "dir.h"
|
||||
|
||||
static struct oid_array good_revs;
|
||||
static struct oid_array skipped_revs;
|
||||
@ -88,15 +89,16 @@ static inline void weight_set(struct commit_list *elem, int weight)
|
||||
**commit_weight_at(&commit_weight, elem->item) = weight;
|
||||
}
|
||||
|
||||
static int count_interesting_parents(struct commit *commit)
|
||||
static int count_interesting_parents(struct commit *commit, unsigned bisect_flags)
|
||||
{
|
||||
struct commit_list *p;
|
||||
int count;
|
||||
|
||||
for (count = 0, p = commit->parents; p; p = p->next) {
|
||||
if (p->item->object.flags & UNINTERESTING)
|
||||
continue;
|
||||
count++;
|
||||
if (!(p->item->object.flags & UNINTERESTING))
|
||||
count++;
|
||||
if (bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY)
|
||||
break;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
@ -135,7 +137,7 @@ static void show_list(const char *debug, int counted, int nr,
|
||||
for (p = list; p; p = p->next) {
|
||||
struct commit_list *pp;
|
||||
struct commit *commit = p->item;
|
||||
unsigned flags = commit->object.flags;
|
||||
unsigned commit_flags = commit->object.flags;
|
||||
enum object_type type;
|
||||
unsigned long size;
|
||||
char *buf = read_object_file(&commit->object.oid, &type,
|
||||
@ -144,9 +146,9 @@ static void show_list(const char *debug, int counted, int nr,
|
||||
int subject_len;
|
||||
|
||||
fprintf(stderr, "%c%c%c ",
|
||||
(flags & TREESAME) ? ' ' : 'T',
|
||||
(flags & UNINTERESTING) ? 'U' : ' ',
|
||||
(flags & COUNTED) ? 'C' : ' ');
|
||||
(commit_flags & TREESAME) ? ' ' : 'T',
|
||||
(commit_flags & UNINTERESTING) ? 'U' : ' ',
|
||||
(commit_flags & COUNTED) ? 'C' : ' ');
|
||||
if (*commit_weight_at(&commit_weight, p->item))
|
||||
fprintf(stderr, "%3d", weight(p));
|
||||
else
|
||||
@ -171,9 +173,9 @@ static struct commit_list *best_bisection(struct commit_list *list, int nr)
|
||||
best = list;
|
||||
for (p = list; p; p = p->next) {
|
||||
int distance;
|
||||
unsigned flags = p->item->object.flags;
|
||||
unsigned commit_flags = p->item->object.flags;
|
||||
|
||||
if (flags & TREESAME)
|
||||
if (commit_flags & TREESAME)
|
||||
continue;
|
||||
distance = weight(p);
|
||||
if (nr - distance < distance)
|
||||
@ -212,9 +214,9 @@ static struct commit_list *best_bisection_sorted(struct commit_list *list, int n
|
||||
|
||||
for (p = list, cnt = 0; p; p = p->next) {
|
||||
int distance;
|
||||
unsigned flags = p->item->object.flags;
|
||||
unsigned commit_flags = p->item->object.flags;
|
||||
|
||||
if (flags & TREESAME)
|
||||
if (commit_flags & TREESAME)
|
||||
continue;
|
||||
distance = weight(p);
|
||||
if (nr - distance < distance)
|
||||
@ -259,7 +261,7 @@ static struct commit_list *best_bisection_sorted(struct commit_list *list, int n
|
||||
*/
|
||||
static struct commit_list *do_find_bisection(struct commit_list *list,
|
||||
int nr, int *weights,
|
||||
int find_all)
|
||||
unsigned bisect_flags)
|
||||
{
|
||||
int n, counted;
|
||||
struct commit_list *p;
|
||||
@ -268,12 +270,12 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
|
||||
|
||||
for (n = 0, p = list; p; p = p->next) {
|
||||
struct commit *commit = p->item;
|
||||
unsigned flags = commit->object.flags;
|
||||
unsigned commit_flags = commit->object.flags;
|
||||
|
||||
*commit_weight_at(&commit_weight, p->item) = &weights[n++];
|
||||
switch (count_interesting_parents(commit)) {
|
||||
switch (count_interesting_parents(commit, bisect_flags)) {
|
||||
case 0:
|
||||
if (!(flags & TREESAME)) {
|
||||
if (!(commit_flags & TREESAME)) {
|
||||
weight_set(p, 1);
|
||||
counted++;
|
||||
show_list("bisection 2 count one",
|
||||
@ -314,11 +316,13 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
|
||||
continue;
|
||||
if (weight(p) != -2)
|
||||
continue;
|
||||
if (bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY)
|
||||
BUG("shouldn't be calling count-distance in fp mode");
|
||||
weight_set(p, count_distance(p));
|
||||
clear_distance(list);
|
||||
|
||||
/* Does it happen to be at exactly half-way? */
|
||||
if (!find_all && halfway(p, nr))
|
||||
if (!(bisect_flags & FIND_BISECTION_ALL) && halfway(p, nr))
|
||||
return p;
|
||||
counted++;
|
||||
}
|
||||
@ -328,11 +332,14 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
|
||||
while (counted < nr) {
|
||||
for (p = list; p; p = p->next) {
|
||||
struct commit_list *q;
|
||||
unsigned flags = p->item->object.flags;
|
||||
unsigned commit_flags = p->item->object.flags;
|
||||
|
||||
if (0 <= weight(p))
|
||||
continue;
|
||||
for (q = p->item->parents; q; q = q->next) {
|
||||
|
||||
for (q = p->item->parents;
|
||||
q;
|
||||
q = bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY ? NULL : q->next) {
|
||||
if (q->item->object.flags & UNINTERESTING)
|
||||
continue;
|
||||
if (0 <= weight(q))
|
||||
@ -346,7 +353,7 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
|
||||
* add one for p itself if p is to be counted,
|
||||
* otherwise inherit it from q directly.
|
||||
*/
|
||||
if (!(flags & TREESAME)) {
|
||||
if (!(commit_flags & TREESAME)) {
|
||||
weight_set(p, weight(q)+1);
|
||||
counted++;
|
||||
show_list("bisection 2 count one",
|
||||
@ -356,21 +363,21 @@ 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 (!find_all && halfway(p, nr))
|
||||
if (!(bisect_flags & FIND_BISECTION_ALL) && halfway(p, nr))
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
show_list("bisection 2 counted all", counted, nr, list);
|
||||
|
||||
if (!find_all)
|
||||
if (!(bisect_flags & FIND_BISECTION_ALL))
|
||||
return best_bisection(list, nr);
|
||||
else
|
||||
return best_bisection_sorted(list, nr);
|
||||
}
|
||||
|
||||
void find_bisection(struct commit_list **commit_list, int *reaches,
|
||||
int *all, int find_all)
|
||||
int *all, unsigned bisect_flags)
|
||||
{
|
||||
int nr, on_list;
|
||||
struct commit_list *list, *p, *best, *next, *last;
|
||||
@ -386,16 +393,16 @@ void find_bisection(struct commit_list **commit_list, int *reaches,
|
||||
for (nr = on_list = 0, last = NULL, p = *commit_list;
|
||||
p;
|
||||
p = next) {
|
||||
unsigned flags = p->item->object.flags;
|
||||
unsigned commit_flags = p->item->object.flags;
|
||||
|
||||
next = p->next;
|
||||
if (flags & UNINTERESTING) {
|
||||
if (commit_flags & UNINTERESTING) {
|
||||
free(p);
|
||||
continue;
|
||||
}
|
||||
p->next = last;
|
||||
last = p;
|
||||
if (!(flags & TREESAME))
|
||||
if (!(commit_flags & TREESAME))
|
||||
nr++;
|
||||
on_list++;
|
||||
}
|
||||
@ -406,9 +413,9 @@ void find_bisection(struct commit_list **commit_list, int *reaches,
|
||||
weights = xcalloc(on_list, sizeof(*weights));
|
||||
|
||||
/* Do the real work of finding bisection commit. */
|
||||
best = do_find_bisection(list, nr, weights, find_all);
|
||||
best = do_find_bisection(list, nr, weights, bisect_flags);
|
||||
if (best) {
|
||||
if (!find_all) {
|
||||
if (!(bisect_flags & FIND_BISECTION_ALL)) {
|
||||
list->item = best->item;
|
||||
free_commit_list(list->next);
|
||||
best = list;
|
||||
@ -454,6 +461,7 @@ static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
|
||||
static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
|
||||
static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
|
||||
static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
|
||||
static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
|
||||
static GIT_PATH_FUNC(git_path_head_name, "head-name")
|
||||
|
||||
static void read_bisect_paths(struct strvec *array)
|
||||
@ -983,7 +991,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
|
||||
* If no_checkout is non-zero, the bisection process does not
|
||||
* checkout the trial commit but instead simply updates BISECT_HEAD.
|
||||
*/
|
||||
enum bisect_error bisect_next_all(struct repository *r, const char *prefix, int no_checkout)
|
||||
enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
|
||||
{
|
||||
struct rev_info revs;
|
||||
struct commit_list *tried;
|
||||
@ -991,21 +999,31 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix, int
|
||||
enum bisect_error res = BISECT_OK;
|
||||
struct object_id *bisect_rev;
|
||||
char *steps_msg;
|
||||
int no_checkout = ref_exists("BISECT_HEAD");
|
||||
unsigned bisect_flags = 0;
|
||||
|
||||
read_bisect_terms(&term_bad, &term_good);
|
||||
if (read_bisect_refs())
|
||||
die(_("reading bisect refs failed"));
|
||||
|
||||
if (file_exists(git_path_bisect_first_parent()))
|
||||
bisect_flags |= FIND_BISECTION_FIRST_PARENT_ONLY;
|
||||
|
||||
if (skipped_revs.nr)
|
||||
bisect_flags |= FIND_BISECTION_ALL;
|
||||
|
||||
res = check_good_are_ancestors_of_bad(r, prefix, no_checkout);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
bisect_rev_setup(r, &revs, prefix, "%s", "^%s", 1);
|
||||
|
||||
revs.first_parent_only = !!(bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY);
|
||||
revs.limited = 1;
|
||||
|
||||
bisect_common(&revs);
|
||||
|
||||
find_bisection(&revs.commits, &reaches, &all, !!skipped_revs.nr);
|
||||
find_bisection(&revs.commits, &reaches, &all, bisect_flags);
|
||||
revs.commits = managed_skipped(revs.commits, &tried);
|
||||
|
||||
if (!revs.commits) {
|
||||
@ -1133,6 +1151,7 @@ int bisect_clean_state(void)
|
||||
unlink_or_warn(git_path_bisect_names());
|
||||
unlink_or_warn(git_path_bisect_run());
|
||||
unlink_or_warn(git_path_bisect_terms());
|
||||
unlink_or_warn(git_path_bisect_first_parent());
|
||||
/* Cleanup head-name if it got left by an old version of git-bisect */
|
||||
unlink_or_warn(git_path_head_name());
|
||||
/*
|
||||
|
9
bisect.h
9
bisect.h
@ -12,7 +12,7 @@ struct repository;
|
||||
* best commit, as chosen by `find_all`.
|
||||
*/
|
||||
void find_bisection(struct commit_list **list, int *reaches, int *all,
|
||||
int find_all);
|
||||
unsigned bisect_flags);
|
||||
|
||||
struct commit_list *filter_skipped(struct commit_list *list,
|
||||
struct commit_list **tried,
|
||||
@ -23,6 +23,9 @@ struct commit_list *filter_skipped(struct commit_list *list,
|
||||
#define BISECT_SHOW_ALL (1<<0)
|
||||
#define REV_LIST_QUIET (1<<1)
|
||||
|
||||
#define FIND_BISECTION_ALL (1u<<0)
|
||||
#define FIND_BISECTION_FIRST_PARENT_ONLY (1u<<1)
|
||||
|
||||
struct rev_list_info {
|
||||
struct rev_info *revs;
|
||||
int flags;
|
||||
@ -58,9 +61,7 @@ enum bisect_error {
|
||||
BISECT_INTERNAL_SUCCESS_MERGE_BASE = -11
|
||||
};
|
||||
|
||||
enum bisect_error bisect_next_all(struct repository *r,
|
||||
const char *prefix,
|
||||
int no_checkout);
|
||||
enum bisect_error bisect_next_all(struct repository *r, const char *prefix);
|
||||
|
||||
int estimate_bisect_steps(int all);
|
||||
|
||||
|
@ -16,9 +16,10 @@ static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
|
||||
static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
|
||||
static GIT_PATH_FUNC(git_path_head_name, "head-name")
|
||||
static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
|
||||
static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
|
||||
|
||||
static const char * const git_bisect_helper_usage[] = {
|
||||
N_("git bisect--helper --next-all [--no-checkout]"),
|
||||
N_("git bisect--helper --next-all"),
|
||||
N_("git bisect--helper --write-terms <bad_term> <good_term>"),
|
||||
N_("git bisect--helper --bisect-clean-state"),
|
||||
N_("git bisect--helper --bisect-reset [<commit>]"),
|
||||
@ -27,7 +28,7 @@ static const char * const git_bisect_helper_usage[] = {
|
||||
N_("git bisect--helper --bisect-next-check <good_term> <bad_term> [<term>]"),
|
||||
N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"),
|
||||
N_("git bisect--helper --bisect-start [--term-{old,good}=<term> --term-{new,bad}=<term>]"
|
||||
"[--no-checkout] [<bad> [<good>...]] [--] [<paths>...]"),
|
||||
" [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"),
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -420,9 +421,10 @@ finish:
|
||||
return res;
|
||||
}
|
||||
|
||||
static int bisect_start(struct bisect_terms *terms, int no_checkout,
|
||||
const char **argv, int argc)
|
||||
static int bisect_start(struct bisect_terms *terms, const char **argv, int argc)
|
||||
{
|
||||
int no_checkout = 0;
|
||||
int first_parent_only = 0;
|
||||
int i, has_double_dash = 0, must_write_terms = 0, bad_seen = 0;
|
||||
int flags, pathspec_pos, res = 0;
|
||||
struct string_list revs = STRING_LIST_INIT_DUP;
|
||||
@ -452,6 +454,8 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
|
||||
break;
|
||||
} else if (!strcmp(arg, "--no-checkout")) {
|
||||
no_checkout = 1;
|
||||
} else if (!strcmp(arg, "--first-parent")) {
|
||||
first_parent_only = 1;
|
||||
} else if (!strcmp(arg, "--term-good") ||
|
||||
!strcmp(arg, "--term-old")) {
|
||||
i++;
|
||||
@ -576,6 +580,9 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
|
||||
*/
|
||||
write_file(git_path_bisect_start(), "%s\n", start_head.buf);
|
||||
|
||||
if (first_parent_only)
|
||||
write_file(git_path_bisect_first_parent(), "\n");
|
||||
|
||||
if (no_checkout) {
|
||||
if (get_oid(start_head.buf, &oid) < 0) {
|
||||
res = error(_("invalid ref: '%s'"), start_head.buf);
|
||||
@ -631,7 +638,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
|
||||
BISECT_TERMS,
|
||||
BISECT_START
|
||||
} cmdmode = 0;
|
||||
int no_checkout = 0, res = 0, nolog = 0;
|
||||
int res = 0, nolog = 0;
|
||||
struct option options[] = {
|
||||
OPT_CMDMODE(0, "next-all", &cmdmode,
|
||||
N_("perform 'git bisect next'"), NEXT_ALL),
|
||||
@ -653,8 +660,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
|
||||
N_("print out the bisect terms"), BISECT_TERMS),
|
||||
OPT_CMDMODE(0, "bisect-start", &cmdmode,
|
||||
N_("start the bisect session"), BISECT_START),
|
||||
OPT_BOOL(0, "no-checkout", &no_checkout,
|
||||
N_("update BISECT_HEAD instead of checking out the current commit")),
|
||||
OPT_BOOL(0, "no-log", &nolog,
|
||||
N_("no log for BISECT_WRITE")),
|
||||
OPT_END()
|
||||
@ -670,7 +675,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
|
||||
|
||||
switch (cmdmode) {
|
||||
case NEXT_ALL:
|
||||
res = bisect_next_all(the_repository, prefix, no_checkout);
|
||||
res = bisect_next_all(the_repository, prefix);
|
||||
break;
|
||||
case WRITE_TERMS:
|
||||
if (argc != 2)
|
||||
@ -712,7 +717,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
|
||||
break;
|
||||
case BISECT_START:
|
||||
set_terms(&terms, "bad", "good");
|
||||
res = bisect_start(&terms, no_checkout, argv, argc);
|
||||
res = bisect_start(&terms, argv, argc);
|
||||
break;
|
||||
default:
|
||||
return error("BUG: unknown subcommand '%d'", cmdmode);
|
||||
|
@ -637,8 +637,15 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
|
||||
if (bisect_list) {
|
||||
int reaches, all;
|
||||
unsigned bisect_flags = 0;
|
||||
|
||||
find_bisection(&revs.commits, &reaches, &all, bisect_find_all);
|
||||
if (bisect_find_all)
|
||||
bisect_flags |= FIND_BISECTION_ALL;
|
||||
|
||||
if (revs.first_parent_only)
|
||||
bisect_flags |= FIND_BISECTION_FIRST_PARENT_ONLY;
|
||||
|
||||
find_bisection(&revs.commits, &reaches, &all, bisect_flags);
|
||||
|
||||
if (bisect_show_vars)
|
||||
return show_bisect_vars(&info, reaches, all);
|
||||
|
@ -153,7 +153,7 @@ bisect_next() {
|
||||
git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD $TERM_GOOD|| exit
|
||||
|
||||
# Perform all bisection computation, display and checkout
|
||||
git bisect--helper --next-all $(git rev-parse --verify -q BISECT_HEAD > /dev/null && echo --no-checkout)
|
||||
git bisect--helper --next-all
|
||||
res=$?
|
||||
|
||||
# Check if we should exit because bisection is finished
|
||||
|
@ -2889,9 +2889,6 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
|
||||
if (!revs->reflog_info && revs->grep_filter.use_reflog_filter)
|
||||
die("cannot use --grep-reflog without --walk-reflogs");
|
||||
|
||||
if (revs->first_parent_only && revs->bisect)
|
||||
die(_("--first-parent is incompatible with --bisect"));
|
||||
|
||||
if (revs->line_level_traverse &&
|
||||
(revs->diffopt.output_format & ~(DIFF_FORMAT_PATCH | DIFF_FORMAT_NO_OUTPUT)))
|
||||
die(_("-L does not yet support diff formats besides -p and -s"));
|
||||
|
@ -128,8 +128,8 @@ test_expect_success 'rev-list can negate index objects' '
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '--bisect and --first-parent can not be combined' '
|
||||
test_must_fail git rev-list --bisect --first-parent HEAD
|
||||
test_expect_success '--bisect and --first-parent can be combined' '
|
||||
git rev-list --bisect --first-parent HEAD
|
||||
'
|
||||
|
||||
test_expect_success '--header shows a NUL after each commit' '
|
||||
|
@ -263,4 +263,49 @@ test_expect_success 'rev-parse --bisect can default to good/bad refs' '
|
||||
test_cmp expect.sorted actual.sorted
|
||||
'
|
||||
|
||||
test_output_expect_success '--bisect --first-parent' 'git rev-list --bisect --first-parent E ^F' <<EOF
|
||||
e4
|
||||
EOF
|
||||
|
||||
test_output_expect_success '--first-parent' 'git rev-list --first-parent E ^F' <<EOF
|
||||
E
|
||||
e1
|
||||
e2
|
||||
e3
|
||||
e4
|
||||
e5
|
||||
e6
|
||||
e7
|
||||
e8
|
||||
EOF
|
||||
|
||||
test_output_expect_success '--bisect-vars --first-parent' 'git rev-list --bisect-vars --first-parent E ^F' <<EOF
|
||||
bisect_rev='e5'
|
||||
bisect_nr=4
|
||||
bisect_good=4
|
||||
bisect_bad=3
|
||||
bisect_all=9
|
||||
bisect_steps=2
|
||||
EOF
|
||||
|
||||
test_expect_success '--bisect-all --first-parent' '
|
||||
cat >expect.unsorted <<-EOF &&
|
||||
$(git rev-parse E) (tag: E, dist=0)
|
||||
$(git rev-parse e1) (tag: e1, dist=1)
|
||||
$(git rev-parse e2) (tag: e2, dist=2)
|
||||
$(git rev-parse e3) (tag: e3, dist=3)
|
||||
$(git rev-parse e4) (tag: e4, dist=4)
|
||||
$(git rev-parse e5) (tag: e5, dist=4)
|
||||
$(git rev-parse e6) (tag: e6, dist=3)
|
||||
$(git rev-parse e7) (tag: e7, dist=2)
|
||||
$(git rev-parse e8) (tag: e8, dist=1)
|
||||
EOF
|
||||
|
||||
# expect results to be ordered by distance (descending),
|
||||
# commit hash (ascending)
|
||||
sort -k4,4r -k1,1 expect.unsorted >expect &&
|
||||
git rev-list --bisect-all --first-parent E ^F >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
@ -243,32 +243,30 @@ test_expect_success 'bisect skip: with commit both bad and skipped' '
|
||||
'
|
||||
|
||||
# We want to automatically find the commit that
|
||||
# introduced "Another" into hello.
|
||||
test_expect_success \
|
||||
'"git bisect run" simple case' \
|
||||
'echo "#"\!"/bin/sh" > test_script.sh &&
|
||||
echo "grep Another hello > /dev/null" >> test_script.sh &&
|
||||
echo "test \$? -ne 0" >> test_script.sh &&
|
||||
chmod +x test_script.sh &&
|
||||
git bisect start &&
|
||||
git bisect good $HASH1 &&
|
||||
git bisect bad $HASH4 &&
|
||||
git bisect run ./test_script.sh > my_bisect_log.txt &&
|
||||
grep "$HASH3 is the first bad commit" my_bisect_log.txt &&
|
||||
git bisect reset'
|
||||
# added "Another" into hello.
|
||||
test_expect_success '"git bisect run" simple case' '
|
||||
write_script test_script.sh <<-\EOF &&
|
||||
! grep Another hello >/dev/null
|
||||
EOF
|
||||
git bisect start &&
|
||||
git bisect good $HASH1 &&
|
||||
git bisect bad $HASH4 &&
|
||||
git bisect run ./test_script.sh >my_bisect_log.txt &&
|
||||
grep "$HASH3 is the first bad commit" my_bisect_log.txt &&
|
||||
git bisect reset
|
||||
'
|
||||
|
||||
# We want to automatically find the commit that
|
||||
# introduced "Ciao" into hello.
|
||||
test_expect_success \
|
||||
'"git bisect run" with more complex "git bisect start"' \
|
||||
'echo "#"\!"/bin/sh" > test_script.sh &&
|
||||
echo "grep Ciao hello > /dev/null" >> test_script.sh &&
|
||||
echo "test \$? -ne 0" >> test_script.sh &&
|
||||
chmod +x test_script.sh &&
|
||||
git bisect start $HASH4 $HASH1 &&
|
||||
git bisect run ./test_script.sh > my_bisect_log.txt &&
|
||||
grep "$HASH4 is the first bad commit" my_bisect_log.txt &&
|
||||
git bisect reset'
|
||||
# added "Ciao" into hello.
|
||||
test_expect_success '"git bisect run" with more complex "git bisect start"' '
|
||||
write_script test_script.sh <<-\EOF &&
|
||||
! grep Ciao hello >/dev/null
|
||||
EOF
|
||||
git bisect start $HASH4 $HASH1 &&
|
||||
git bisect run ./test_script.sh >my_bisect_log.txt &&
|
||||
grep "$HASH4 is the first bad commit" my_bisect_log.txt &&
|
||||
git bisect reset
|
||||
'
|
||||
|
||||
# $HASH1 is good, $HASH5 is bad, we skip $HASH3
|
||||
# but $HASH4 is good,
|
||||
@ -295,24 +293,17 @@ 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 "sed -ne \\\$p 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 &&
|
||||
write_script test_script.sh <<-\EOF &&
|
||||
sed -ne \$p hello | grep Ciao >/dev/null && exit 125
|
||||
! grep line hello >/dev/null
|
||||
EOF
|
||||
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
|
||||
test_expect_code 2 git bisect run ./test_script.sh >my_bisect_log.txt &&
|
||||
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
|
||||
'
|
||||
|
||||
HASH7=
|
||||
@ -320,14 +311,13 @@ 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 "sed -ne \\\$p hello | grep Ciao > /dev/null && exit 125" >> test_script.sh &&
|
||||
echo "sed -ne \\\$p 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 &&
|
||||
write_script test_script.sh <<-\EOF &&
|
||||
sed -ne \$p hello | grep Ciao >/dev/null && exit 125
|
||||
sed -ne \$p hello | grep day >/dev/null && exit 125
|
||||
! grep Yet hello >/dev/null
|
||||
EOF
|
||||
git bisect start $HASH7 $HASH1 &&
|
||||
git bisect run ./test_script.sh > my_bisect_log.txt &&
|
||||
git bisect run ./test_script.sh >my_bisect_log.txt &&
|
||||
grep "$HASH6 is the first bad commit" my_bisect_log.txt
|
||||
'
|
||||
|
||||
@ -458,6 +448,24 @@ test_expect_success 'many merge bases creation' '
|
||||
grep "$SIDE_HASH5" merge_bases.txt
|
||||
'
|
||||
|
||||
# We want to automatically find the merge that
|
||||
# added "line" into hello.
|
||||
test_expect_success '"git bisect run --first-parent" simple case' '
|
||||
git rev-list --first-parent $B_HASH ^$HASH4 >first_parent_chain.txt &&
|
||||
write_script test_script.sh <<-\EOF &&
|
||||
grep $(git rev-parse HEAD) first_parent_chain.txt || exit -1
|
||||
! grep line hello >/dev/null
|
||||
EOF
|
||||
git bisect start --first-parent &&
|
||||
test_path_is_file ".git/BISECT_FIRST_PARENT" &&
|
||||
git bisect good $HASH4 &&
|
||||
git bisect bad $B_HASH &&
|
||||
git bisect run ./test_script.sh >my_bisect_log.txt &&
|
||||
grep "$B_HASH is the first bad commit" my_bisect_log.txt &&
|
||||
git bisect reset &&
|
||||
test_path_is_missing .git/BISECT_FIRST_PARENT
|
||||
'
|
||||
|
||||
test_expect_success 'good merge bases when good and bad are siblings' '
|
||||
git bisect start "$B_HASH" "$A_HASH" > my_bisect_log.txt &&
|
||||
test_i18ngrep "merge base must be tested" my_bisect_log.txt &&
|
||||
|
Loading…
Reference in New Issue
Block a user