@matheusbsbs / matheus-bagliano / commits / e8861ffc20
Fork 0

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 <alipman88@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Aaron Lipman committed Aug 7, 2020 at 17:58 UTC e8861ffc203fe5ea3da97210e60b2e886002f218
4 files changed +42 -3
Documentation/git-bisect.txt
+12 -1
@@ -17,7 +17,7 @@ The command takes various subcommands, and different options depending
17 on the subcommand:
18
19 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>...]
21 git bisect (bad|new|<term-new>) [<rev>]
22 git bisect (good|old|<term-old>) [<rev>...]
23 git bisect terms [--term-good | --term-bad]
@@ -365,6 +365,17 @@ does not require a checked out tree.
365 +
366 If the repository is bare, `--no-checkout` is assumed.
367
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 +
379 EXAMPLES
380 --------
381
bisect.c
+4 -1
@@ -15,6 +15,7 @@
15 #include "commit-slab.h"
16 #include "commit-reach.h"
17 #include "object-store.h"
18 +#include "dir.h"
19
20 static struct oid_array good_revs;
21 static struct oid_array skipped_revs;
@@ -460,6 +461,7 @@ static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
461 static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
462 static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
463 static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
464 +static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
465 static GIT_PATH_FUNC(git_path_head_name, "head-name")
466
467 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)
1000 struct object_id *bisect_rev;
1001 char *steps_msg;
1002 int no_checkout = ref_exists("BISECT_HEAD");
1001 - int first_parent_only = 0; /* TODO: pass --first-parent flag from git bisect start */
1003 + int first_parent_only = file_exists(git_path_bisect_first_parent());
1004
1005 read_bisect_terms(&term_bad, &term_good);
1006 if (read_bisect_refs())
@@ -1142,6 +1144,7 @@ int bisect_clean_state(void)
1144 unlink_or_warn(git_path_bisect_names());
1145 unlink_or_warn(git_path_bisect_run());
1146 unlink_or_warn(git_path_bisect_terms());
1147 + unlink_or_warn(git_path_bisect_first_parent());
1148 /* Cleanup head-name if it got left by an old version of git-bisect */
1149 unlink_or_warn(git_path_head_name());
1150 /*
builtin/bisect--helper.c
+8 -1
@@ -17,6 +17,7 @@ static GIT_PATH_FUNC(git_path_bisect_head, "BISECT_HEAD")
17 static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
18 static GIT_PATH_FUNC(git_path_head_name, "head-name")
19 static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
20 +static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
21
22 static const char * const git_bisect_helper_usage[] = {
23 N_("git bisect--helper --next-all"),
@@ -28,7 +29,7 @@ static const char * const git_bisect_helper_usage[] = {
29 N_("git bisect--helper --bisect-next-check <good_term> <bad_term> [<term>]"),
30 N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"),
31 N_("git bisect--helper --bisect-start [--term-{old,good}=<term> --term-{new,bad}=<term>]"
31 - "[--no-checkout] [<bad> [<good>...]] [--] [<paths>...]"),
32 + " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"),
33 NULL
34 };
35
@@ -424,6 +425,7 @@ finish:
425 static int bisect_start(struct bisect_terms *terms, const char **argv, int argc)
426 {
427 int no_checkout = 0;
428 + int first_parent_only = 0;
429 int i, has_double_dash = 0, must_write_terms = 0, bad_seen = 0;
430 int flags, pathspec_pos, res = 0;
431 struct string_list revs = STRING_LIST_INIT_DUP;
@@ -453,6 +455,8 @@ static int bisect_start(struct bisect_terms *terms, const char **argv, int argc)
455 break;
456 } else if (!strcmp(arg, "--no-checkout")) {
457 no_checkout = 1;
458 + } else if (!strcmp(arg, "--first-parent")) {
459 + first_parent_only = 1;
460 } else if (!strcmp(arg, "--term-good") ||
461 !strcmp(arg, "--term-old")) {
462 i++;
@@ -577,6 +581,9 @@ static int bisect_start(struct bisect_terms *terms, const char **argv, int argc)
581 */
582 write_file(git_path_bisect_start(), "%s\n", start_head.buf);
583
584 + if (first_parent_only)
585 + write_file(git_path_bisect_first_parent(), "\n");
586 +
587 if (no_checkout) {
588 if (get_oid(start_head.buf, &oid) < 0) {
589 res = error(_("invalid ref: '%s'"), start_head.buf);
t/t6030-bisect-porcelain.sh
+18
@@ -448,6 +448,24 @@ test_expect_success 'many merge bases creation' '
448 grep "$SIDE_HASH5" merge_bases.txt
449 '
450
451 +# We want to automatically find the merge that
452 +# added "line" into hello.
453 +test_expect_success '"git bisect run --first-parent" simple case' '
454 + git rev-list --first-parent $B_HASH ^$HASH4 >first_parent_chain.txt &&
455 + write_script test_script.sh <<-\EOF &&
456 + grep $(git rev-parse HEAD) first_parent_chain.txt || exit -1
457 + ! grep line hello >/dev/null
458 + EOF
459 + git bisect start --first-parent &&
460 + test_path_is_file ".git/BISECT_FIRST_PARENT" &&
461 + git bisect good $HASH4 &&
462 + git bisect bad $B_HASH &&
463 + git bisect run ./test_script.sh >my_bisect_log.txt &&
464 + grep "$B_HASH is the first bad commit" my_bisect_log.txt &&
465 + git bisect reset &&
466 + test_path_is_missing .git/BISECT_FIRST_PARENT
467 +'
468 +
469 test_expect_success 'good merge bases when good and bad are siblings' '
470 git bisect start "$B_HASH" "$A_HASH" > my_bisect_log.txt &&
471 test_i18ngrep "merge base must be tested" my_bisect_log.txt &&