]> git.ipfire.org Git - thirdparty/git.git/commitdiff
bisect: introduce first-parent flag
authorAaron Lipman <alipman88@gmail.com>
Fri, 7 Aug 2020 21:58:37 +0000 (17:58 -0400)
committerJunio C Hamano <gitster@pobox.com>
Fri, 7 Aug 2020 22:13:03 +0000 (15:13 -0700)
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>
Documentation/git-bisect.txt
bisect.c
builtin/bisect--helper.c
t/t6030-bisect-porcelain.sh

index 7586c5a8437edfc146fa3b802bc5e72e5070ee9a..0e993e458717743e4e2f136d27f7291101853bba 100644 (file)
@@ -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
 --------
 
index 950ff6f533aeeb579174cc2fdacac8058c485e2a..bc4241b51f37e2f85e8d737f55c64bd0dc494cee 100644 (file)
--- 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());
        /*
index 4b175d1c05cd2fc968389ee0f58013492ebc86c6..68f8721528e2a4b5b672f3614248907ec29bcc15 100644 (file)
@@ -17,6 +17,7 @@ static GIT_PATH_FUNC(git_path_bisect_head, "BISECT_HEAD")
 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"),
@@ -28,7 +29,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
 };
 
@@ -424,6 +425,7 @@ finish:
 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;
@@ -453,6 +455,8 @@ static int bisect_start(struct bisect_terms *terms, const char **argv, int argc)
                        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++;
@@ -577,6 +581,9 @@ static int bisect_start(struct bisect_terms *terms, const char **argv, int argc)
         */
        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);
index a66c4b89bc6ccfb0952b1e27dfa8eeee59604579..b886529e5963ff9855e070747e8d010151c55c67 100755 (executable)
@@ -448,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 &&