@@ -19,14 +19,14 @@ on the subcommand:
git bisect start [--term-(new|bad)=<term-new> --term-(old|good)=<term-old>]
[--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]
git bisect (bad|new|<term-new>) [<rev>]
- git bisect (good|old|<term-old>) [<rev>...]
+ git bisect (good|old|<term-old>) [-f] [<rev>...]
git bisect terms [--term-good | --term-bad]
git bisect skip [(<rev>|<range>)...]
git bisect reset [<commit>]
git bisect (visualize|view)
git bisect replay <logfile>
git bisect log
- git bisect run <cmd> [<arg>...]
+ git bisect run [-f] <cmd> [<arg>...]
git bisect help
This command uses a binary search algorithm to find which commit in
@@ -381,6 +381,15 @@ 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.
+-f::
+--force::
++
+Throw away any local changes and untracked files before moving to the next
+bisection step.
++
+This option may be useful if the repository state changes when testing a
+revision.
+
EXAMPLES
--------
@@ -713,7 +713,7 @@ static int is_expected_rev(const struct object_id *oid)
}
enum bisect_error bisect_checkout(const struct object_id *bisect_rev,
- int no_checkout)
+ int no_checkout, int force_checkout)
{
struct commit *commit;
struct pretty_print_context pp = {0};
@@ -728,8 +728,13 @@ enum bisect_error bisect_checkout(const struct object_id *bisect_rev,
struct child_process cmd = CHILD_PROCESS_INIT;
cmd.git_cmd = 1;
- strvec_pushl(&cmd.args, "checkout", "-q",
- oid_to_hex(bisect_rev), "--", NULL);
+ if (force_checkout) {
+ strvec_pushl(&cmd.args, "checkout", "-f", "-q",
+ oid_to_hex(bisect_rev), "--", NULL);
+ } else {
+ strvec_pushl(&cmd.args, "checkout", "-q",
+ oid_to_hex(bisect_rev), "--", NULL);
+ }
if (run_command(&cmd))
/*
* Errors in `run_command()` itself, signaled by res < 0,
@@ -850,7 +855,7 @@ static enum bisect_error check_merge_bases(int rev_nr, struct commit **rev, int
handle_skipped_merge_base(mb);
} else {
printf(_("Bisecting: a merge base must be tested\n"));
- res = bisect_checkout(mb, no_checkout);
+ res = bisect_checkout(mb, no_checkout, 0);
if (!res)
/* indicate early success */
res = BISECT_INTERNAL_SUCCESS_MERGE_BASE;
@@ -1002,7 +1007,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
* the end of bisect_helper::cmd_bisect__helper() helps bypassing
* all the code related to finding a commit to test.
*/
-enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
+enum bisect_error bisect_next_all(struct repository *r, const char *prefix, int force_checkout)
{
struct strvec rev_argv = STRVEC_INIT;
struct rev_info revs = REV_INFO_INIT;
@@ -1104,7 +1109,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
/* Clean up objects used, as they will be reused. */
repo_clear_commit_marks(r, ALL_REV_FLAGS);
- res = bisect_checkout(bisect_rev, no_checkout);
+ res = bisect_checkout(bisect_rev, no_checkout, force_checkout);
cleanup:
release_revisions(&revs);
strvec_clear(&rev_argv);
@@ -71,7 +71,7 @@ struct bisect_state {
unsigned int nr_bad;
};
-enum bisect_error bisect_next_all(struct repository *r, const char *prefix);
+enum bisect_error bisect_next_all(struct repository *r, const char *prefix, int force_checkout);
int estimate_bisect_steps(int all);
@@ -80,6 +80,6 @@ void read_bisect_terms(const char **bad, const char **good);
int bisect_clean_state(void);
enum bisect_error bisect_checkout(const struct object_id *bisect_rev,
- int no_checkout);
+ int no_checkout, int force_checkout);
#endif
@@ -29,7 +29,7 @@ static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
" [--no-checkout] [--first-parent] [<bad> [<good>...]] [--]" \
" [<pathspec>...]")
#define BUILTIN_GIT_BISECT_STATE_USAGE \
- N_("git bisect (good|bad) [<rev>...]")
+ N_("git bisect (good|bad) [-f] [<rev>...]")
#define BUILTIN_GIT_BISECT_TERMS_USAGE \
"git bisect terms [--term-good | --term-bad]"
#define BUILTIN_GIT_BISECT_SKIP_USAGE \
@@ -45,7 +45,7 @@ static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
#define BUILTIN_GIT_BISECT_LOG_USAGE \
"git bisect log"
#define BUILTIN_GIT_BISECT_RUN_USAGE \
- N_("git bisect run <cmd> [<arg>...]")
+ N_("git bisect run [-f] <cmd> [<arg>...]")
static const char * const git_bisect_usage[] = {
BUILTIN_GIT_BISECT_START_USAGE,
@@ -651,7 +651,7 @@ static int bisect_successful(struct bisect_terms *terms)
return res;
}
-static enum bisect_error bisect_next(struct bisect_terms *terms, const char *prefix)
+static enum bisect_error bisect_next(struct bisect_terms *terms, const char *prefix, int force_checkout)
{
enum bisect_error res;
@@ -662,7 +662,7 @@ static enum bisect_error bisect_next(struct bisect_terms *terms, const char *pre
return BISECT_FAILED;
/* Perform all bisection computation */
- res = bisect_next_all(the_repository, prefix);
+ res = bisect_next_all(the_repository, prefix, force_checkout);
if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {
res = bisect_successful(terms);
@@ -674,14 +674,14 @@ static enum bisect_error bisect_next(struct bisect_terms *terms, const char *pre
return res;
}
-static enum bisect_error bisect_auto_next(struct bisect_terms *terms, const char *prefix)
+static enum bisect_error bisect_auto_next(struct bisect_terms *terms, const char *prefix, int force_checkout)
{
if (bisect_next_check(terms, NULL)) {
bisect_print_status(terms);
return BISECT_OK;
}
- return bisect_next(terms, prefix);
+ return bisect_next(terms, prefix, force_checkout);
}
static enum bisect_error bisect_start(struct bisect_terms *terms, int argc,
@@ -875,7 +875,7 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, int argc,
if (res)
return res;
- res = bisect_auto_next(terms, NULL);
+ res = bisect_auto_next(terms, NULL, 0);
if (!is_bisect_success(res))
bisect_clean_state();
return res;
@@ -917,7 +917,7 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
const char **argv)
{
const char *state;
- int i, verify_expected = 1;
+ int i, force_checkout = 0, verify_expected = 1;
struct object_id oid, expected;
struct oid_array revs = OID_ARRAY_INIT;
@@ -934,6 +934,13 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
argv++;
argc--;
+
+ if (argc > 0 && (!strcmp(argv[0], "--force") || !strcmp(argv[0], "-f"))) {
+ force_checkout = 1;
+ argv++;
+ argc--;
+ }
+
if (argc > 1 && !strcmp(state, terms->term_bad))
return error(_("'git bisect %s' can take only one argument."), terms->term_bad);
@@ -989,7 +996,7 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
}
oid_array_clear(&revs);
- return bisect_auto_next(terms, NULL);
+ return bisect_auto_next(terms, NULL, force_checkout);
}
static enum bisect_error bisect_log(void)
@@ -1078,7 +1085,7 @@ static enum bisect_error bisect_replay(struct bisect_terms *terms, const char *f
if (res)
return BISECT_FAILED;
- return bisect_auto_next(terms, NULL);
+ return bisect_auto_next(terms, NULL, 0);
}
static enum bisect_error bisect_skip(struct bisect_terms *terms, int argc,
@@ -1173,7 +1180,7 @@ static int do_bisect_run(const char *command)
return run_command(&cmd);
}
-static int verify_good(const struct bisect_terms *terms, const char *command)
+static int verify_good(const struct bisect_terms *terms, const char *command, int force_checkout)
{
int rc;
enum bisect_error res;
@@ -1189,13 +1196,13 @@ static int verify_good(const struct bisect_terms *terms, const char *command)
if (read_ref(no_checkout ? "BISECT_HEAD" : "HEAD", ¤t_rev))
return -1;
- res = bisect_checkout(&good_rev, no_checkout);
+ res = bisect_checkout(&good_rev, no_checkout, force_checkout);
if (res != BISECT_OK)
return -1;
rc = do_bisect_run(command);
- res = bisect_checkout(¤t_rev, no_checkout);
+ res = bisect_checkout(¤t_rev, no_checkout, force_checkout);
if (res != BISECT_OK)
return -1;
@@ -1209,6 +1216,7 @@ static int bisect_run(struct bisect_terms *terms, int argc, const char **argv)
const char *new_state;
int temporary_stdout_fd, saved_stdout;
int is_first_run = 1;
+ int force_checkout = 0;
if (bisect_next_check(terms, NULL))
return BISECT_FAILED;
@@ -1218,8 +1226,14 @@ static int bisect_run(struct bisect_terms *terms, int argc, const char **argv)
return BISECT_FAILED;
}
+ if (argc > 0 && (!strcmp(argv[0], "--force") || !strcmp(argv[0], "-f"))) {
+ force_checkout = 1;
+ argv++;
+ argc--;
+ }
sq_quote_argv(&command, argv);
strbuf_ltrim(&command);
+
while (1) {
res = do_bisect_run(command.buf);
@@ -1231,7 +1245,7 @@ static int bisect_run(struct bisect_terms *terms, int argc, const char **argv)
* missing or non-executable script.
*/
if (is_first_run && (res == 126 || res == 127)) {
- int rc = verify_good(terms, command.buf);
+ int rc = verify_good(terms, command.buf, force_checkout);
is_first_run = 0;
if (rc < 0 || 128 <= rc) {
error(_("unable to verify %s on good"
@@ -1271,7 +1285,11 @@ static int bisect_run(struct bisect_terms *terms, int argc, const char **argv)
saved_stdout = dup(1);
dup2(temporary_stdout_fd, 1);
- res = bisect_state(terms, 1, &new_state);
+ if (force_checkout) {
+ res = bisect_state(terms, 2, (const char *[]){ new_state, "--force" });
+ } else {
+ res = bisect_state(terms, 1, &new_state);
+ }
fflush(stdout);
dup2(saved_stdout, 1);
@@ -1342,7 +1360,7 @@ static int cmd_bisect__next(int argc, const char **argv UNUSED, const char *pref
return error(_("'%s' requires 0 arguments"),
"git bisect next");
get_terms(&terms);
- res = bisect_next(&terms, prefix);
+ res = bisect_next(&terms, prefix, 0);
free_terms(&terms);
return res;
}
@@ -1259,4 +1259,113 @@ test_expect_success 'verify correct error message' '
grep "git bisect good.*exited with error code" error
'
+test_expect_success 'bisect dirty, explicit ref' '
+ test_when_finished "git bisect reset" &&
+ add_line_into_file "Commit 1" file &&
+ add_line_into_file "Commit 2" file &&
+ add_line_into_file "Commit 3" file &&
+ git bisect start &&
+ git bisect bad HEAD &&
+ echo "modified state" >file &&
+ test_when_finished "git checkout -- file" &&
+ test_must_fail git bisect good HEAD~2
+'
+
+test_expect_success 'bisect dirty good force, explicit ref' '
+ test_when_finished "git bisect reset" &&
+ add_line_into_file "Commit 1" file &&
+ add_line_into_file "Commit 2" file &&
+ add_line_into_file "Commit 3" file &&
+ git bisect start &&
+ git bisect bad HEAD &&
+ echo "modified state" >file &&
+ test_when_finished "git checkout -- file" &&
+ git bisect good --force HEAD~2
+'
+
+test_expect_success 'bisect dirty, implicit ref' '
+ test_when_finished "git bisect reset" &&
+ add_line_into_file "Commit 1" file &&
+ add_line_into_file "Commit 2" file &&
+ add_line_into_file "Commit 3" file &&
+ add_line_into_file "Commit 4" file &&
+ add_line_into_file "Commit 5" file &&
+ git bisect start &&
+ git bisect bad HEAD &&
+ git bisect good HEAD~4 &&
+ echo "modified state" >file &&
+ test_when_finished "git checkout -- file" &&
+ test_must_fail git bisect good &&
+ test_must_fail git bisect bad
+'
+
+test_expect_success 'bisect dirty good force, implicit ref' '
+ test_when_finished "git bisect reset" &&
+ add_line_into_file "Commit 1" file &&
+ add_line_into_file "Commit 2" file &&
+ add_line_into_file "Commit 3" file &&
+ add_line_into_file "Commit 4" file &&
+ add_line_into_file "Commit 5" file &&
+ git bisect start &&
+ git bisect bad HEAD &&
+ git bisect good HEAD~4 &&
+ echo "modified state" >file &&
+ test_when_finished "git checkout -- file" &&
+ git bisect good --force
+'
+
+test_expect_success 'bisect dirty bad force, implicit ref' '
+ test_when_finished "git bisect reset" &&
+ add_line_into_file "Commit 1" file &&
+ add_line_into_file "Commit 2" file &&
+ add_line_into_file "Commit 3" file &&
+ add_line_into_file "Commit 4" file &&
+ add_line_into_file "Commit 5" file &&
+ git bisect start &&
+ git bisect bad HEAD &&
+ git bisect good HEAD~4 &&
+ echo "modified state" >file &&
+ test_when_finished "git checkout -- file" &&
+ git bisect bad --force
+'
+
+test_expect_success 'bisect run dirty' '
+ test_when_finished "git bisect reset" &&
+ test_when_finished "git checkout -- file" &&
+ add_line_into_file "Bisect run 1" file &&
+ add_line_into_file "Bisect run 2" file &&
+ add_line_into_file "Bisect run 3" file &&
+ add_line_into_file "Bisect run 4" file &&
+ add_line_into_file "Bisect run 5" file &&
+ write_script test_script.sh <<-\EOF &&
+ ! echo "modified state" >file
+ ! grep "Bisect run 3" || exit 126 >/dev/null
+ EOF
+ git bisect start &&
+ git bisect bad HEAD &&
+ git bisect good HEAD~4 &&
+ test_must_fail git bisect run ./test_script.sh
+'
+
+test_expect_success 'bisect run dirty force' '
+ test_when_finished "git bisect reset" &&
+ test_when_finished "git checkout -- file" &&
+ add_line_into_file "Bisect run 1" file &&
+ add_line_into_file "Bisect run 2" file &&
+ add_line_into_file "Bisect run 3" file &&
+ add_line_into_file "Bisect run 4" file &&
+ add_line_into_file "Bisect run 5" file &&
+ write_script test_script.sh <<-\EOF &&
+ ! echo "modified state" >file
+ ! grep "Bisect run 3" || exit 126 >/dev/null
+ EOF
+ HASH5=$(git rev-parse --verify HEAD) &&
+ git bisect start &&
+ git bisect bad HEAD &&
+ git bisect good HEAD~4 &&
+ echo "doing run\n" &&
+ git bisect run --force ./test_script.sh >my_bisect_log.txt &&
+ grep "$HASH5 is the first bad commit" my_bisect_log.txt
+'
+
test_done