rev-list: allow bisect and first-parent flags

Add first_parent_only parameter to find_bisection(), removing the
barrier that prevented combining the --bisect and --first-parent flags
when using git rev-list

Based-on-patch-by: Tiago Botelho <tiagonbotelho@hotmail.com>
Signed-off-by: Aaron Lipman <alipman88@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
maint
Aaron Lipman 2020-08-07 17:58:35 -04:00 committed by Junio C Hamano
parent 15a4802a69
commit 0fe305a5d3
7 changed files with 70 additions and 21 deletions

View File

@ -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

View File

@ -88,15 +88,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, int first_parent_only)
{
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 (first_parent_only)
break;
}
return count;
}
@ -259,7 +260,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)
int find_all, int first_parent_only)
{
int n, counted;
struct commit_list *p;
@ -271,7 +272,7 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
unsigned flags = commit->object.flags;

*commit_weight_at(&commit_weight, p->item) = &weights[n++];
switch (count_interesting_parents(commit)) {
switch (count_interesting_parents(commit, first_parent_only)) {
case 0:
if (!(flags & TREESAME)) {
weight_set(p, 1);
@ -314,6 +315,8 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
continue;
if (weight(p) != -2)
continue;
if (first_parent_only)
BUG("shouldn't be calling count-distance in fp mode");
weight_set(p, count_distance(p));
clear_distance(list);

@ -332,7 +335,10 @@ static struct commit_list *do_find_bisection(struct commit_list *list,

if (0 <= weight(p))
continue;
for (q = p->item->parents; q; q = q->next) {

for (q = p->item->parents;
q;
q = first_parent_only ? NULL : q->next) {
if (q->item->object.flags & UNINTERESTING)
continue;
if (0 <= weight(q))
@ -370,7 +376,7 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
}

void find_bisection(struct commit_list **commit_list, int *reaches,
int *all, int find_all)
int *all, int find_all, int first_parent_only)
{
int nr, on_list;
struct commit_list *list, *p, *best, *next, *last;
@ -406,7 +412,7 @@ 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, find_all, first_parent_only);
if (best) {
if (!find_all) {
list->item = best->item;
@ -991,6 +997,7 @@ 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 first_parent_only = 0; /* TODO: pass --first-parent flag from git bisect start */

read_bisect_terms(&term_bad, &term_good);
if (read_bisect_refs())
@ -1001,11 +1008,12 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix, int
return res;

bisect_rev_setup(r, &revs, prefix, "%s", "^%s", 1);
revs.first_parent_only = first_parent_only;
revs.limited = 1;

bisect_common(&revs);

find_bisection(&revs.commits, &reaches, &all, !!skipped_revs.nr);
find_bisection(&revs.commits, &reaches, &all, !!skipped_revs.nr, first_parent_only);
revs.commits = managed_skipped(revs.commits, &tried);

if (!revs.commits) {

View File

@ -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);
int find_all, int first_parent_only);

struct commit_list *filter_skipped(struct commit_list *list,
struct commit_list **tried,

View File

@ -638,7 +638,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
if (bisect_list) {
int reaches, all;

find_bisection(&revs.commits, &reaches, &all, bisect_find_all);
find_bisection(&revs.commits, &reaches, &all, bisect_find_all, revs.first_parent_only);

if (bisect_show_vars)
return show_bisect_vars(&info, reaches, all);

View File

@ -2869,9 +2869,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"));

View File

@ -125,8 +125,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' '

View File

@ -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