Skip to content

Commit b232f7c

Browse files
committed
Merge branch 'al/bisect-first-parent' into next
"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
2 parents 27dbe05 + ad464a4 commit b232f7c

File tree

11 files changed

+195
-103
lines changed

11 files changed

+195
-103
lines changed

Documentation/git-bisect.txt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ The command takes various subcommands, and different options depending
1717
on the subcommand:
1818

1919
git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]
20-
[--no-checkout] [<bad> [<good>...]] [--] [<paths>...]
20+
[--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]
2121
git bisect (bad|new|<term-new>) [<rev>]
2222
git bisect (good|old|<term-old>) [<rev>...]
2323
git bisect terms [--term-good | --term-bad]
@@ -365,6 +365,17 @@ does not require a checked out tree.
365365
+
366366
If the repository is bare, `--no-checkout` is assumed.
367367

368+
--first-parent::
369+
+
370+
Follow only the first parent commit upon seeing a merge commit.
371+
+
372+
In detecting regressions introduced through the merging of a branch, the merge
373+
commit will be identified as introduction of the bug and its ancestors will be
374+
ignored.
375+
+
376+
This option is particularly useful in avoiding false positives when a merged
377+
branch contained broken or non-buildable commits, but the merge itself was OK.
378+
368379
EXAMPLES
369380
--------
370381

Documentation/rev-list-options.txt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,7 @@ parents) and `--max-parents=-1` (negative numbers denote no upper limit).
128128
because merges into a topic branch tend to be only about
129129
adjusting to updated upstream from time to time, and
130130
this option allows you to ignore the individual commits
131-
brought in to your history by such a merge. Cannot be
132-
combined with --bisect.
131+
brought in to your history by such a merge.
133132

134133
--not::
135134
Reverses the meaning of the '{caret}' prefix (or lack thereof)
@@ -207,7 +206,7 @@ ifndef::git-rev-list[]
207206
Pretend as if the bad bisection ref `refs/bisect/bad`
208207
was listed and as if it was followed by `--not` and the good
209208
bisection refs `refs/bisect/good-*` on the command
210-
line. Cannot be combined with --first-parent.
209+
line.
211210
endif::git-rev-list[]
212211

213212
--stdin::
@@ -743,7 +742,7 @@ outputs 'midpoint', the output of the two commands
743742
would be of roughly the same length. Finding the change which
744743
introduces a regression is thus reduced to a binary search: repeatedly
745744
generate and test new 'midpoint's until the commit chain is of length
746-
one. Cannot be combined with --first-parent.
745+
one.
747746

748747
--bisect-vars::
749748
This calculates the same as `--bisect`, except that refs in

bisect.c

Lines changed: 49 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "commit-slab.h"
1616
#include "commit-reach.h"
1717
#include "object-store.h"
18+
#include "dir.h"
1819

1920
static struct oid_array good_revs;
2021
static struct oid_array skipped_revs;
@@ -88,15 +89,16 @@ static inline void weight_set(struct commit_list *elem, int weight)
8889
**commit_weight_at(&commit_weight, elem->item) = weight;
8990
}
9091

91-
static int count_interesting_parents(struct commit *commit)
92+
static int count_interesting_parents(struct commit *commit, unsigned bisect_flags)
9293
{
9394
struct commit_list *p;
9495
int count;
9596

9697
for (count = 0, p = commit->parents; p; p = p->next) {
97-
if (p->item->object.flags & UNINTERESTING)
98-
continue;
99-
count++;
98+
if (!(p->item->object.flags & UNINTERESTING))
99+
count++;
100+
if (bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY)
101+
break;
100102
}
101103
return count;
102104
}
@@ -135,7 +137,7 @@ static void show_list(const char *debug, int counted, int nr,
135137
for (p = list; p; p = p->next) {
136138
struct commit_list *pp;
137139
struct commit *commit = p->item;
138-
unsigned flags = commit->object.flags;
140+
unsigned commit_flags = commit->object.flags;
139141
enum object_type type;
140142
unsigned long size;
141143
char *buf = read_object_file(&commit->object.oid, &type,
@@ -144,9 +146,9 @@ static void show_list(const char *debug, int counted, int nr,
144146
int subject_len;
145147

146148
fprintf(stderr, "%c%c%c ",
147-
(flags & TREESAME) ? ' ' : 'T',
148-
(flags & UNINTERESTING) ? 'U' : ' ',
149-
(flags & COUNTED) ? 'C' : ' ');
149+
(commit_flags & TREESAME) ? ' ' : 'T',
150+
(commit_flags & UNINTERESTING) ? 'U' : ' ',
151+
(commit_flags & COUNTED) ? 'C' : ' ');
150152
if (*commit_weight_at(&commit_weight, p->item))
151153
fprintf(stderr, "%3d", weight(p));
152154
else
@@ -171,9 +173,9 @@ static struct commit_list *best_bisection(struct commit_list *list, int nr)
171173
best = list;
172174
for (p = list; p; p = p->next) {
173175
int distance;
174-
unsigned flags = p->item->object.flags;
176+
unsigned commit_flags = p->item->object.flags;
175177

176-
if (flags & TREESAME)
178+
if (commit_flags & TREESAME)
177179
continue;
178180
distance = weight(p);
179181
if (nr - distance < distance)
@@ -212,9 +214,9 @@ static struct commit_list *best_bisection_sorted(struct commit_list *list, int n
212214

213215
for (p = list, cnt = 0; p; p = p->next) {
214216
int distance;
215-
unsigned flags = p->item->object.flags;
217+
unsigned commit_flags = p->item->object.flags;
216218

217-
if (flags & TREESAME)
219+
if (commit_flags & TREESAME)
218220
continue;
219221
distance = weight(p);
220222
if (nr - distance < distance)
@@ -259,7 +261,7 @@ static struct commit_list *best_bisection_sorted(struct commit_list *list, int n
259261
*/
260262
static struct commit_list *do_find_bisection(struct commit_list *list,
261263
int nr, int *weights,
262-
int find_all)
264+
unsigned bisect_flags)
263265
{
264266
int n, counted;
265267
struct commit_list *p;
@@ -268,12 +270,12 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
268270

269271
for (n = 0, p = list; p; p = p->next) {
270272
struct commit *commit = p->item;
271-
unsigned flags = commit->object.flags;
273+
unsigned commit_flags = commit->object.flags;
272274

273275
*commit_weight_at(&commit_weight, p->item) = &weights[n++];
274-
switch (count_interesting_parents(commit)) {
276+
switch (count_interesting_parents(commit, bisect_flags)) {
275277
case 0:
276-
if (!(flags & TREESAME)) {
278+
if (!(commit_flags & TREESAME)) {
277279
weight_set(p, 1);
278280
counted++;
279281
show_list("bisection 2 count one",
@@ -314,11 +316,13 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
314316
continue;
315317
if (weight(p) != -2)
316318
continue;
319+
if (bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY)
320+
BUG("shouldn't be calling count-distance in fp mode");
317321
weight_set(p, count_distance(p));
318322
clear_distance(list);
319323

320324
/* Does it happen to be at exactly half-way? */
321-
if (!find_all && halfway(p, nr))
325+
if (!(bisect_flags & FIND_BISECTION_ALL) && halfway(p, nr))
322326
return p;
323327
counted++;
324328
}
@@ -328,11 +332,14 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
328332
while (counted < nr) {
329333
for (p = list; p; p = p->next) {
330334
struct commit_list *q;
331-
unsigned flags = p->item->object.flags;
335+
unsigned commit_flags = p->item->object.flags;
332336

333337
if (0 <= weight(p))
334338
continue;
335-
for (q = p->item->parents; q; q = q->next) {
339+
340+
for (q = p->item->parents;
341+
q;
342+
q = bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY ? NULL : q->next) {
336343
if (q->item->object.flags & UNINTERESTING)
337344
continue;
338345
if (0 <= weight(q))
@@ -346,7 +353,7 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
346353
* add one for p itself if p is to be counted,
347354
* otherwise inherit it from q directly.
348355
*/
349-
if (!(flags & TREESAME)) {
356+
if (!(commit_flags & TREESAME)) {
350357
weight_set(p, weight(q)+1);
351358
counted++;
352359
show_list("bisection 2 count one",
@@ -356,21 +363,21 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
356363
weight_set(p, weight(q));
357364

358365
/* Does it happen to be at exactly half-way? */
359-
if (!find_all && halfway(p, nr))
366+
if (!(bisect_flags & FIND_BISECTION_ALL) && halfway(p, nr))
360367
return p;
361368
}
362369
}
363370

364371
show_list("bisection 2 counted all", counted, nr, list);
365372

366-
if (!find_all)
373+
if (!(bisect_flags & FIND_BISECTION_ALL))
367374
return best_bisection(list, nr);
368375
else
369376
return best_bisection_sorted(list, nr);
370377
}
371378

372379
void find_bisection(struct commit_list **commit_list, int *reaches,
373-
int *all, int find_all)
380+
int *all, unsigned bisect_flags)
374381
{
375382
int nr, on_list;
376383
struct commit_list *list, *p, *best, *next, *last;
@@ -386,16 +393,16 @@ void find_bisection(struct commit_list **commit_list, int *reaches,
386393
for (nr = on_list = 0, last = NULL, p = *commit_list;
387394
p;
388395
p = next) {
389-
unsigned flags = p->item->object.flags;
396+
unsigned commit_flags = p->item->object.flags;
390397

391398
next = p->next;
392-
if (flags & UNINTERESTING) {
399+
if (commit_flags & UNINTERESTING) {
393400
free(p);
394401
continue;
395402
}
396403
p->next = last;
397404
last = p;
398-
if (!(flags & TREESAME))
405+
if (!(commit_flags & TREESAME))
399406
nr++;
400407
on_list++;
401408
}
@@ -406,9 +413,9 @@ void find_bisection(struct commit_list **commit_list, int *reaches,
406413
weights = xcalloc(on_list, sizeof(*weights));
407414

408415
/* Do the real work of finding bisection commit. */
409-
best = do_find_bisection(list, nr, weights, find_all);
416+
best = do_find_bisection(list, nr, weights, bisect_flags);
410417
if (best) {
411-
if (!find_all) {
418+
if (!(bisect_flags & FIND_BISECTION_ALL)) {
412419
list->item = best->item;
413420
free_commit_list(list->next);
414421
best = list;
@@ -454,6 +461,7 @@ static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
454461
static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
455462
static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
456463
static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
464+
static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
457465
static GIT_PATH_FUNC(git_path_head_name, "head-name")
458466

459467
static void read_bisect_paths(struct strvec *array)
@@ -983,29 +991,39 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
983991
* If no_checkout is non-zero, the bisection process does not
984992
* checkout the trial commit but instead simply updates BISECT_HEAD.
985993
*/
986-
enum bisect_error bisect_next_all(struct repository *r, const char *prefix, int no_checkout)
994+
enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
987995
{
988996
struct rev_info revs;
989997
struct commit_list *tried;
990998
int reaches = 0, all = 0, nr, steps;
991999
enum bisect_error res = BISECT_OK;
9921000
struct object_id *bisect_rev;
9931001
char *steps_msg;
1002+
int no_checkout = ref_exists("BISECT_HEAD");
1003+
unsigned bisect_flags = 0;
9941004

9951005
read_bisect_terms(&term_bad, &term_good);
9961006
if (read_bisect_refs())
9971007
die(_("reading bisect refs failed"));
9981008

1009+
if (file_exists(git_path_bisect_first_parent()))
1010+
bisect_flags |= FIND_BISECTION_FIRST_PARENT_ONLY;
1011+
1012+
if (skipped_revs.nr)
1013+
bisect_flags |= FIND_BISECTION_ALL;
1014+
9991015
res = check_good_are_ancestors_of_bad(r, prefix, no_checkout);
10001016
if (res)
10011017
return res;
10021018

10031019
bisect_rev_setup(r, &revs, prefix, "%s", "^%s", 1);
1020+
1021+
revs.first_parent_only = !!(bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY);
10041022
revs.limited = 1;
10051023

10061024
bisect_common(&revs);
10071025

1008-
find_bisection(&revs.commits, &reaches, &all, !!skipped_revs.nr);
1026+
find_bisection(&revs.commits, &reaches, &all, bisect_flags);
10091027
revs.commits = managed_skipped(revs.commits, &tried);
10101028

10111029
if (!revs.commits) {
@@ -1133,6 +1151,7 @@ int bisect_clean_state(void)
11331151
unlink_or_warn(git_path_bisect_names());
11341152
unlink_or_warn(git_path_bisect_run());
11351153
unlink_or_warn(git_path_bisect_terms());
1154+
unlink_or_warn(git_path_bisect_first_parent());
11361155
/* Cleanup head-name if it got left by an old version of git-bisect */
11371156
unlink_or_warn(git_path_head_name());
11381157
/*

bisect.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ struct repository;
1212
* best commit, as chosen by `find_all`.
1313
*/
1414
void find_bisection(struct commit_list **list, int *reaches, int *all,
15-
int find_all);
15+
unsigned bisect_flags);
1616

1717
struct commit_list *filter_skipped(struct commit_list *list,
1818
struct commit_list **tried,
@@ -23,6 +23,9 @@ struct commit_list *filter_skipped(struct commit_list *list,
2323
#define BISECT_SHOW_ALL (1<<0)
2424
#define REV_LIST_QUIET (1<<1)
2525

26+
#define FIND_BISECTION_ALL (1u<<0)
27+
#define FIND_BISECTION_FIRST_PARENT_ONLY (1u<<1)
28+
2629
struct rev_list_info {
2730
struct rev_info *revs;
2831
int flags;
@@ -58,9 +61,7 @@ enum bisect_error {
5861
BISECT_INTERNAL_SUCCESS_MERGE_BASE = -11
5962
};
6063

61-
enum bisect_error bisect_next_all(struct repository *r,
62-
const char *prefix,
63-
int no_checkout);
64+
enum bisect_error bisect_next_all(struct repository *r, const char *prefix);
6465

6566
int estimate_bisect_steps(int all);
6667

0 commit comments

Comments
 (0)