+145
-9
index 19bbfbd0eb..c245bbbd5b 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
+static GIT_PATH_FUNC(git_path_bisect_reset_when_found, "BISECT_RESET_WHEN_FOUND")
static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
#define BUILTIN_GIT_BISECT_START_USAGE \
N_("git bisect start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]\n" \
- " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]")
+ " [--no-checkout] [--first-parent] [--reset-when-found[=<where>]] [<bad> [<good>...]] [--] [<pathspec>...]")
#define BUILTIN_GIT_BISECT_BAD_USAGE \
N_("git bisect (bad|new|<term-new>) [<rev>]")
#define BUILTIN_GIT_BISECT_GOOD_USAGE \
#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 [--reset-when-found[=<where>]] <cmd> [<arg>...]")
#define BUILTIN_GIT_BISECT_HELP_USAGE \
"git bisect help"
NULL
};
+enum reset_when_found_mode {
+ RESET_WHEN_FOUND_NONE,
+ RESET_WHEN_FOUND_TO_ORIGINAL,
+ RESET_WHEN_FOUND_TO_FOUND,
+};
+
struct add_bisect_ref_data {
struct rev_info *revs;
unsigned int object_flags;
}
strbuf_release(&branch);
- return bisect_clean_state();
+ return 0;
+}
+
+static int parse_reset_when_found(const char *value,
+ enum reset_when_found_mode *mode)
+{
+ if (!strcmp(value, "original"))
+ *mode = RESET_WHEN_FOUND_TO_ORIGINAL;
+ else if (!strcmp(value, "found"))
+ *mode = RESET_WHEN_FOUND_TO_FOUND;
+ else
+ return error(_("invalid value for '--reset-when-found': '%s'"),
+ value);
+
+ return 0;
+}
+
+static const char *reset_when_found_mode_name(enum reset_when_found_mode mode)
+{
+ switch (mode) {
+ case RESET_WHEN_FOUND_TO_ORIGINAL:
+ return "original";
+ case RESET_WHEN_FOUND_TO_FOUND:
+ return "found";
+ case RESET_WHEN_FOUND_NONE:
+ BUG("no name for unset reset-when-found mode");
+ }
+ BUG("unknown reset-when-found mode %d", mode);
+}
+
+static int read_reset_when_found(enum reset_when_found_mode *mode)
+{
+ struct strbuf value = STRBUF_INIT;
+ int res = 0;
+
+ *mode = RESET_WHEN_FOUND_NONE;
+ if (is_empty_or_missing_file(git_path_bisect_reset_when_found()))
+ return 0;
+
+ if (strbuf_read_file(&value, git_path_bisect_reset_when_found(), 0) < 0) {
+ res = error_errno(_("could not read '%s'"),
+ git_path_bisect_reset_when_found());
+ goto out;
+ }
+ strbuf_trim(&value);
+ if (parse_reset_when_found(value.buf, mode))
+ res = -1;
+
+out:
+ strbuf_release(&value);
+ return res;
+}
+
+static int bisect_reset_when_found(enum reset_when_found_mode mode)
+{
+ struct bisect_terms terms = { 0 };
+ char *commit = NULL;
+ int res;
+
+ if (mode == RESET_WHEN_FOUND_TO_FOUND) {
+ read_bisect_terms(&terms.term_bad, &terms.term_good);
+ commit = xstrfmt("refs/bisect/%s", terms.term_bad);
+ } else if (mode == RESET_WHEN_FOUND_NONE) {
+ BUG("automatic reset requested without a reset mode");
+ }
+
+ res = bisect_reset(commit, true);
+ if (!res)
+ res = bisect_clean_state();
+
+ free(commit);
+ free_terms(&terms);
+ return res;
}
static void log_commit(FILE *fp,
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)
{
enum bisect_error res;
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)
{
if (bisect_next_check(terms, NULL)) {
bisect_print_status(terms);
struct strbuf bisect_names = STRBUF_INIT;
struct object_id head_oid;
struct object_id oid;
+ enum reset_when_found_mode reset_when_found = RESET_WHEN_FOUND_NONE;
const char *head;
if (is_bare_repository(the_repository))
no_checkout = 1;
} else if (!strcmp(arg, "--first-parent")) {
first_parent_only = 1;
+ } else if (!strcmp(arg, "--reset-when-found")) {
+ reset_when_found = RESET_WHEN_FOUND_TO_ORIGINAL;
+ } else if (skip_prefix(arg, "--reset-when-found=", &arg)) {
+ if (parse_reset_when_found(arg, &reset_when_found)) {
+ res = BISECT_FAILED;
+ goto finish;
+ }
} else if (!strcmp(arg, "--term-good") ||
!strcmp(arg, "--term-old")) {
i++;
break;
}
}
+ if (reset_when_found != RESET_WHEN_FOUND_NONE && no_checkout) {
+ res = error(_("options '%s' and '%s' cannot be used together"),
+ "--reset-when-found", "--no-checkout");
+ goto finish;
+ }
pathspec_pos = i;
/*
if (first_parent_only)
write_file(git_path_bisect_first_parent(), "\n");
+ if (reset_when_found != RESET_WHEN_FOUND_NONE)
+ write_file(git_path_bisect_reset_when_found(), "%s\n",
+ reset_when_found_mode_name(reset_when_found));
+
if (no_checkout) {
if (repo_get_oid(the_repository, start_head.buf, &oid) < 0) {
res = error(_("invalid ref: '%s'"), start_head.buf);
if (is_empty_or_missing_file(filename))
return error(_("cannot read file '%s' for replaying"), filename);
- if (bisect_reset(NULL, false))
+ if (bisect_clean_state())
return BISECT_FAILED;
fp = fopen(filename, "r");
{
int res = BISECT_OK;
struct strbuf command = STRBUF_INIT;
+ const char *reset_when_found_arg;
const char *new_state;
int temporary_stdout_fd, saved_stdout;
int is_first_run = 1;
+ enum reset_when_found_mode reset_when_found = RESET_WHEN_FOUND_NONE;
if (bisect_next_check(terms, NULL))
return BISECT_FAILED;
+ if (argc && !strcmp(argv[0], "--reset-when-found")) {
+ reset_when_found = RESET_WHEN_FOUND_TO_ORIGINAL;
+ } else if (argc && skip_prefix(argv[0], "--reset-when-found=",
+ &reset_when_found_arg)) {
+ if (parse_reset_when_found(reset_when_found_arg,
+ &reset_when_found))
+ return BISECT_FAILED;
+ }
+
+ if (reset_when_found != RESET_WHEN_FOUND_NONE &&
+ refs_ref_exists(get_main_ref_store(the_repository), "BISECT_HEAD"))
+ return error(_("options '%s' and '%s' cannot be used together"),
+ "--reset-when-found", "--no-checkout");
+
+ if (reset_when_found != RESET_WHEN_FOUND_NONE) {
+ write_file(git_path_bisect_reset_when_found(), "%s\n",
+ reset_when_found_mode_name(reset_when_found));
+ argc--;
+ argv++;
+ }
+
if (!argc) {
error(_("bisect run failed: no command provided."));
return BISECT_FAILED;
res = BISECT_OK;
} else if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {
printf(_("bisect found first '%s' commit\n"), terms->term_bad);
- res = BISECT_OK;
} else if (res) {
error(_("bisect run failed: 'git bisect %s'"
" exited with error code %d"), new_state, res);
static int cmd_bisect__reset(int argc, const char **argv, const char *prefix UNUSED,
struct repository *repo UNUSED)
{
+ int res;
+
if (argc > 1)
return error(_("'%s' requires either no argument or a commit"),
"git bisect reset");
- return bisect_reset(argc ? argv[0] : NULL, false);
+ res = bisect_reset(argc ? argv[0] : NULL, false);
+ if (res)
+ return res;
+ return bisect_clean_state();
}
static int cmd_bisect__terms(int argc, const char **argv, const char *prefix UNUSED,
!one_of(argv[0], terms.term_good, terms.term_bad, NULL))
usage_msg_optf(_("unknown command: '%s'"), git_bisect_usage,
options, argv[0]);
- res = bisect_state(&terms, argc, argv);
+ else
+ res = bisect_state(&terms, argc, argv);
free_terms(&terms);
} else {
argc--;
res = fn(argc, argv, prefix, repo);
}
+ if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {
+ enum reset_when_found_mode mode;
+
+ if (read_reset_when_found(&mode))
+ res = BISECT_FAILED;
+ else if (mode != RESET_WHEN_FOUND_NONE &&
+ bisect_reset_when_found(mode))
+ res = BISECT_FAILED;
+ }
+
return is_bisect_success(res) ? 0 : -res;
}