From 0fe305a5d342225976688b1bd68dc4dc179b66b3 Mon Sep 17 00:00:00 2001 From: Aaron Lipman Date: Fri, 7 Aug 2020 17:58:35 -0400 Subject: 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 Signed-off-by: Aaron Lipman Signed-off-by: Junio C Hamano --- bisect.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'bisect.c') diff --git a/bisect.c b/bisect.c index d5e830410f..a11fdb1473 100644 --- a/bisect.c +++ b/bisect.c @@ -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) { -- cgit v1.2.3 From be5fe2000df5c1110d8086e4704e1a90a64f7387 Mon Sep 17 00:00:00 2001 From: Aaron Lipman Date: Fri, 7 Aug 2020 17:58:36 -0400 Subject: cmd_bisect__helper: defer parsing no-checkout flag cmd_bisect__helper() is intended as a temporary shim layer serving as an interface for git-bisect.sh. This function and git-bisect.sh should eventually be replaced by a C implementation, cmd_bisect(), serving as an entrypoint for all "git bisect ..." shell commands: cmd_bisect() will only parse the first token following "git bisect", and dispatch the remaining args to the appropriate function ["bisect_start()", "bisect_next()", etc.]. Thus, cmd_bisect__helper() should not be responsible for parsing flags like --no-checkout. Instead, let the --no-checkout flag remain in the argv array, so it may be evaluated alongside the other options already parsed by bisect_start(). Signed-off-by: Aaron Lipman Signed-off-by: Junio C Hamano --- bisect.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'bisect.c') diff --git a/bisect.c b/bisect.c index a11fdb1473..950ff6f533 100644 --- a/bisect.c +++ b/bisect.c @@ -989,7 +989,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; @@ -997,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 no_checkout = ref_exists("BISECT_HEAD"); int first_parent_only = 0; /* TODO: pass --first-parent flag from git bisect start */ read_bisect_terms(&term_bad, &term_good); -- cgit v1.2.3 From e8861ffc203fe5ea3da97210e60b2e886002f218 Mon Sep 17 00:00:00 2001 From: Aaron Lipman Date: Fri, 7 Aug 2020 17:58:37 -0400 Subject: bisect: introduce first-parent flag Upon seeing a merge commit when bisecting, this option may be used to follow only the first parent. 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. Signed-off-by: Aaron Lipman Signed-off-by: Junio C Hamano --- bisect.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'bisect.c') diff --git a/bisect.c b/bisect.c index 950ff6f533..bc4241b51f 100644 --- a/bisect.c +++ b/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; @@ -460,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 argv_array *array) @@ -998,7 +1000,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix) struct object_id *bisect_rev; char *steps_msg; int no_checkout = ref_exists("BISECT_HEAD"); - int first_parent_only = 0; /* TODO: pass --first-parent flag from git bisect start */ + int first_parent_only = file_exists(git_path_bisect_first_parent()); read_bisect_terms(&term_bad, &term_good); if (read_bisect_refs()) @@ -1142,6 +1144,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()); /* -- cgit v1.2.3 From ad464a4e84b502fdfd4671f1c443060c7e87113f Mon Sep 17 00:00:00 2001 From: Aaron Lipman Date: Fri, 7 Aug 2020 17:58:38 -0400 Subject: bisect: combine args passed to find_bisection() Now that find_bisection() accepts multiple boolean arguments, these may be combined into a single unsigned integer in order to declutter some of the code in bisect.c Also, rename the existing "flags" bitfield to "commit_flags", to explicitly differentiate it from the new "bisect_flags" bitfield. Based-on-patch-by: Harald Nordgren Signed-off-by: Aaron Lipman Signed-off-by: Junio C Hamano --- bisect.c | 67 +++++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 37 insertions(+), 30 deletions(-) (limited to 'bisect.c') diff --git a/bisect.c b/bisect.c index bc4241b51f..1585fcc6ad 100644 --- a/bisect.c +++ b/bisect.c @@ -89,7 +89,7 @@ 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, int first_parent_only) +static int count_interesting_parents(struct commit *commit, unsigned bisect_flags) { struct commit_list *p; int count; @@ -97,7 +97,7 @@ static int count_interesting_parents(struct commit *commit, int first_parent_onl for (count = 0, p = commit->parents; p; p = p->next) { if (!(p->item->object.flags & UNINTERESTING)) count++; - if (first_parent_only) + if (bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY) break; } return count; @@ -137,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, @@ -146,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 @@ -173,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) @@ -214,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) @@ -261,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, int first_parent_only) + unsigned bisect_flags) { int n, counted; struct commit_list *p; @@ -270,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, first_parent_only)) { + 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", @@ -316,13 +316,13 @@ static struct commit_list *do_find_bisection(struct commit_list *list, continue; if (weight(p) != -2) continue; - if (first_parent_only) + 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++; } @@ -332,14 +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 = first_parent_only ? NULL : q->next) { + q = bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY ? NULL : q->next) { if (q->item->object.flags & UNINTERESTING) continue; if (0 <= weight(q)) @@ -353,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", @@ -363,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 first_parent_only) + int *all, unsigned bisect_flags) { int nr, on_list; struct commit_list *list, *p, *best, *next, *last; @@ -393,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++; } @@ -413,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, first_parent_only); + 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; @@ -1000,23 +1000,30 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix) struct object_id *bisect_rev; char *steps_msg; int no_checkout = ref_exists("BISECT_HEAD"); - int first_parent_only = file_exists(git_path_bisect_first_parent()); + 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 = first_parent_only; + + 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, first_parent_only); + find_bisection(&revs.commits, &reaches, &all, bisect_flags); revs.commits = managed_skipped(revs.commits, &tried); if (!revs.commits) { -- cgit v1.2.3