2009-03-26 05:55:54 +01:00
|
|
|
#include "builtin.h"
|
|
|
|
#include "cache.h"
|
|
|
|
#include "parse-options.h"
|
|
|
|
#include "bisect.h"
|
2017-09-29 08:49:39 +02:00
|
|
|
#include "refs.h"
|
2019-01-02 16:38:31 +01:00
|
|
|
#include "dir.h"
|
2020-07-28 22:23:39 +02:00
|
|
|
#include "strvec.h"
|
2019-01-02 16:38:31 +01:00
|
|
|
#include "run-command.h"
|
2019-01-02 16:38:34 +01:00
|
|
|
#include "prompt.h"
|
2019-01-02 16:38:36 +01:00
|
|
|
#include "quote.h"
|
2020-09-24 14:33:40 +02:00
|
|
|
#include "revision.h"
|
2009-03-26 05:55:54 +01:00
|
|
|
|
2017-09-29 08:49:39 +02:00
|
|
|
static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
|
2017-09-29 08:49:40 +02:00
|
|
|
static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
|
|
|
|
static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
|
2019-01-02 16:38:31 +01:00
|
|
|
static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
|
2019-01-02 16:38:32 +01:00
|
|
|
static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
|
2019-01-02 16:38:36 +01:00
|
|
|
static GIT_PATH_FUNC(git_path_head_name, "head-name")
|
|
|
|
static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
|
2020-08-07 23:58:37 +02:00
|
|
|
static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
|
2021-09-13 19:39:03 +02:00
|
|
|
static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
|
2017-09-29 08:49:39 +02:00
|
|
|
|
2009-03-26 05:55:54 +01:00
|
|
|
static const char * const git_bisect_helper_usage[] = {
|
2019-01-02 16:38:31 +01:00
|
|
|
N_("git bisect--helper --bisect-reset [<commit>]"),
|
2022-01-31 23:07:48 +01:00
|
|
|
"git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]",
|
2020-08-28 17:31:19 +02:00
|
|
|
N_("git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]"
|
2020-08-07 23:58:37 +02:00
|
|
|
" [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"),
|
2022-01-31 23:07:48 +01:00
|
|
|
"git bisect--helper --bisect-next",
|
2020-10-15 15:38:35 +02:00
|
|
|
N_("git bisect--helper --bisect-state (bad|new) [<rev>]"),
|
|
|
|
N_("git bisect--helper --bisect-state (good|old) [<rev>...]"),
|
2021-02-03 22:54:33 +01:00
|
|
|
N_("git bisect--helper --bisect-replay <filename>"),
|
2021-02-03 22:54:37 +01:00
|
|
|
N_("git bisect--helper --bisect-skip [(<rev>|<range>)...]"),
|
2022-01-31 23:07:48 +01:00
|
|
|
"git bisect--helper --bisect-visualize",
|
2021-09-13 19:39:03 +02:00
|
|
|
N_("git bisect--helper --bisect-run <cmd>..."),
|
2009-03-26 05:55:54 +01:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2020-09-24 14:33:40 +02:00
|
|
|
struct add_bisect_ref_data {
|
|
|
|
struct rev_info *revs;
|
|
|
|
unsigned int object_flags;
|
|
|
|
};
|
|
|
|
|
2019-01-02 16:38:32 +01:00
|
|
|
struct bisect_terms {
|
|
|
|
char *term_good;
|
|
|
|
char *term_bad;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void free_terms(struct bisect_terms *terms)
|
|
|
|
{
|
|
|
|
FREE_AND_NULL(terms->term_good);
|
|
|
|
FREE_AND_NULL(terms->term_bad);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void set_terms(struct bisect_terms *terms, const char *bad,
|
|
|
|
const char *good)
|
|
|
|
{
|
|
|
|
free((void *)terms->term_good);
|
|
|
|
terms->term_good = xstrdup(good);
|
|
|
|
free((void *)terms->term_bad);
|
|
|
|
terms->term_bad = xstrdup(bad);
|
|
|
|
}
|
|
|
|
|
2020-02-17 09:40:28 +01:00
|
|
|
static const char vocab_bad[] = "bad|new";
|
|
|
|
static const char vocab_good[] = "good|old";
|
2019-01-02 16:38:34 +01:00
|
|
|
|
2020-09-24 14:33:40 +02:00
|
|
|
static int bisect_autostart(struct bisect_terms *terms);
|
|
|
|
|
2017-09-29 08:49:39 +02:00
|
|
|
/*
|
|
|
|
* Check whether the string `term` belongs to the set of strings
|
|
|
|
* included in the variable arguments.
|
|
|
|
*/
|
|
|
|
LAST_ARG_MUST_BE_NULL
|
|
|
|
static int one_of(const char *term, ...)
|
|
|
|
{
|
|
|
|
int res = 0;
|
|
|
|
va_list matches;
|
|
|
|
const char *match;
|
|
|
|
|
|
|
|
va_start(matches, term);
|
|
|
|
while (!res && (match = va_arg(matches, const char *)))
|
|
|
|
res = !strcmp(term, match);
|
|
|
|
va_end(matches);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2020-10-15 15:38:32 +02:00
|
|
|
/*
|
|
|
|
* return code BISECT_INTERNAL_SUCCESS_MERGE_BASE
|
|
|
|
* and BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND are codes
|
|
|
|
* that indicate special success.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int is_bisect_success(enum bisect_error res)
|
|
|
|
{
|
|
|
|
return !res ||
|
|
|
|
res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND ||
|
|
|
|
res == BISECT_INTERNAL_SUCCESS_MERGE_BASE;
|
|
|
|
}
|
|
|
|
|
2020-08-28 14:46:07 +02:00
|
|
|
static int write_in_file(const char *path, const char *mode, const char *format, va_list args)
|
|
|
|
{
|
|
|
|
FILE *fp = NULL;
|
|
|
|
int res = 0;
|
|
|
|
|
2020-09-24 14:33:40 +02:00
|
|
|
if (strcmp(mode, "w") && strcmp(mode, "a"))
|
2020-08-28 14:46:07 +02:00
|
|
|
BUG("write-in-file does not support '%s' mode", mode);
|
|
|
|
fp = fopen(path, mode);
|
|
|
|
if (!fp)
|
|
|
|
return error_errno(_("cannot open file '%s' in mode '%s'"), path, mode);
|
|
|
|
res = vfprintf(fp, format, args);
|
|
|
|
|
|
|
|
if (res < 0) {
|
|
|
|
int saved_errno = errno;
|
|
|
|
fclose(fp);
|
|
|
|
errno = saved_errno;
|
|
|
|
return error_errno(_("could not write to file '%s'"), path);
|
|
|
|
}
|
|
|
|
|
|
|
|
return fclose(fp);
|
|
|
|
}
|
|
|
|
|
2021-07-13 10:05:18 +02:00
|
|
|
__attribute__((format (printf, 2, 3)))
|
2020-08-28 14:46:07 +02:00
|
|
|
static int write_to_file(const char *path, const char *format, ...)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
va_start(args, format);
|
|
|
|
res = write_in_file(path, "w", format, args);
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2021-07-13 10:05:18 +02:00
|
|
|
__attribute__((format (printf, 2, 3)))
|
2020-09-24 14:33:40 +02:00
|
|
|
static int append_to_file(const char *path, const char *format, ...)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
va_start(args, format);
|
|
|
|
res = write_in_file(path, "a", format, args);
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2021-09-13 19:39:03 +02:00
|
|
|
static int print_file_to_stdout(const char *path)
|
|
|
|
{
|
|
|
|
int fd = open(path, O_RDONLY);
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (fd < 0)
|
|
|
|
return error_errno(_("cannot open file '%s' for reading"), path);
|
|
|
|
if (copy_fd(fd, 1) < 0)
|
|
|
|
ret = error_errno(_("failed to read '%s'"), path);
|
|
|
|
close(fd);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-09-29 08:49:39 +02:00
|
|
|
static int check_term_format(const char *term, const char *orig_term)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
char *new_term = xstrfmt("refs/bisect/%s", term);
|
|
|
|
|
|
|
|
res = check_refname_format(new_term, 0);
|
|
|
|
free(new_term);
|
|
|
|
|
|
|
|
if (res)
|
|
|
|
return error(_("'%s' is not a valid term"), term);
|
|
|
|
|
|
|
|
if (one_of(term, "help", "start", "skip", "next", "reset",
|
2017-11-12 10:30:38 +01:00
|
|
|
"visualize", "view", "replay", "log", "run", "terms", NULL))
|
2017-09-29 08:49:39 +02:00
|
|
|
return error(_("can't use the builtin command '%s' as a term"), term);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In theory, nothing prevents swapping completely good and bad,
|
|
|
|
* but this situation could be confusing and hasn't been tested
|
|
|
|
* enough. Forbid it for now.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if ((strcmp(orig_term, "bad") && one_of(term, "bad", "new", NULL)) ||
|
|
|
|
(strcmp(orig_term, "good") && one_of(term, "good", "old", NULL)))
|
|
|
|
return error(_("can't change the meaning of the term '%s'"), term);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-09-29 08:49:39 +02:00
|
|
|
static int write_terms(const char *bad, const char *good)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
|
|
|
|
if (!strcmp(bad, good))
|
|
|
|
return error(_("please use two different terms"));
|
|
|
|
|
|
|
|
if (check_term_format(bad, "bad") || check_term_format(good, "good"))
|
|
|
|
return -1;
|
|
|
|
|
2020-08-28 14:46:07 +02:00
|
|
|
res = write_to_file(git_path_bisect_terms(), "%s\n%s\n", bad, good);
|
2017-09-29 08:49:39 +02:00
|
|
|
|
2020-08-28 14:46:07 +02:00
|
|
|
return res;
|
2017-09-29 08:49:39 +02:00
|
|
|
}
|
|
|
|
|
2019-01-02 16:38:31 +01:00
|
|
|
static int bisect_reset(const char *commit)
|
|
|
|
{
|
|
|
|
struct strbuf branch = STRBUF_INIT;
|
|
|
|
|
|
|
|
if (!commit) {
|
|
|
|
if (strbuf_read_file(&branch, git_path_bisect_start(), 0) < 1) {
|
|
|
|
printf(_("We are not bisecting.\n"));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
strbuf_rtrim(&branch);
|
|
|
|
} else {
|
|
|
|
struct object_id oid;
|
|
|
|
|
|
|
|
if (get_oid_commit(commit, &oid))
|
|
|
|
return error(_("'%s' is not a valid commit"), commit);
|
|
|
|
strbuf_addstr(&branch, commit);
|
|
|
|
}
|
|
|
|
|
2020-07-10 19:19:52 +02:00
|
|
|
if (!ref_exists("BISECT_HEAD")) {
|
2020-07-28 22:24:27 +02:00
|
|
|
struct strvec argv = STRVEC_INIT;
|
2019-01-02 16:38:31 +01:00
|
|
|
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushl(&argv, "checkout", branch.buf, "--", NULL);
|
2020-07-29 02:37:20 +02:00
|
|
|
if (run_command_v_opt(argv.v, RUN_GIT_CMD)) {
|
2019-12-09 11:56:47 +01:00
|
|
|
error(_("could not check out original"
|
|
|
|
" HEAD '%s'. Try 'git bisect"
|
|
|
|
" reset <commit>'."), branch.buf);
|
2019-01-02 16:38:31 +01:00
|
|
|
strbuf_release(&branch);
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_clear(&argv);
|
2019-12-09 11:56:47 +01:00
|
|
|
return -1;
|
2019-01-02 16:38:31 +01:00
|
|
|
}
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_clear(&argv);
|
2019-01-02 16:38:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
strbuf_release(&branch);
|
|
|
|
return bisect_clean_state();
|
|
|
|
}
|
|
|
|
|
2019-01-02 16:38:32 +01:00
|
|
|
static void log_commit(FILE *fp, char *fmt, const char *state,
|
|
|
|
struct commit *commit)
|
|
|
|
{
|
|
|
|
struct pretty_print_context pp = {0};
|
|
|
|
struct strbuf commit_msg = STRBUF_INIT;
|
|
|
|
char *label = xstrfmt(fmt, state);
|
|
|
|
|
|
|
|
format_commit_message(commit, "%s", &commit_msg, &pp);
|
|
|
|
|
|
|
|
fprintf(fp, "# %s: [%s] %s\n", label, oid_to_hex(&commit->object.oid),
|
|
|
|
commit_msg.buf);
|
|
|
|
|
|
|
|
strbuf_release(&commit_msg);
|
|
|
|
free(label);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bisect_write(const char *state, const char *rev,
|
|
|
|
const struct bisect_terms *terms, int nolog)
|
|
|
|
{
|
|
|
|
struct strbuf tag = STRBUF_INIT;
|
|
|
|
struct object_id oid;
|
|
|
|
struct commit *commit;
|
|
|
|
FILE *fp = NULL;
|
2020-02-17 09:40:29 +01:00
|
|
|
int res = 0;
|
2019-01-02 16:38:32 +01:00
|
|
|
|
|
|
|
if (!strcmp(state, terms->term_bad)) {
|
|
|
|
strbuf_addf(&tag, "refs/bisect/%s", state);
|
|
|
|
} else if (one_of(state, terms->term_good, "skip", NULL)) {
|
|
|
|
strbuf_addf(&tag, "refs/bisect/%s-%s", state, rev);
|
|
|
|
} else {
|
2020-02-17 09:40:29 +01:00
|
|
|
res = error(_("Bad bisect_write argument: %s"), state);
|
2019-01-02 16:38:32 +01:00
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (get_oid(rev, &oid)) {
|
2020-02-17 09:40:29 +01:00
|
|
|
res = error(_("couldn't get the oid of the rev '%s'"), rev);
|
2019-01-02 16:38:32 +01:00
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (update_ref(NULL, tag.buf, &oid, NULL, 0,
|
|
|
|
UPDATE_REFS_MSG_ON_ERR)) {
|
2020-02-17 09:40:29 +01:00
|
|
|
res = -1;
|
2019-01-02 16:38:32 +01:00
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
fp = fopen(git_path_bisect_log(), "a");
|
|
|
|
if (!fp) {
|
2020-02-17 09:40:29 +01:00
|
|
|
res = error_errno(_("couldn't open the file '%s'"), git_path_bisect_log());
|
2019-01-02 16:38:32 +01:00
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
commit = lookup_commit_reference(the_repository, &oid);
|
|
|
|
log_commit(fp, "%s", state, commit);
|
|
|
|
|
|
|
|
if (!nolog)
|
|
|
|
fprintf(fp, "git bisect %s %s\n", state, rev);
|
|
|
|
|
|
|
|
finish:
|
|
|
|
if (fp)
|
|
|
|
fclose(fp);
|
|
|
|
strbuf_release(&tag);
|
2020-02-17 09:40:29 +01:00
|
|
|
return res;
|
2019-01-02 16:38:32 +01:00
|
|
|
}
|
|
|
|
|
2019-01-02 16:38:33 +01:00
|
|
|
static int check_and_set_terms(struct bisect_terms *terms, const char *cmd)
|
|
|
|
{
|
|
|
|
int has_term_file = !is_empty_or_missing_file(git_path_bisect_terms());
|
|
|
|
|
|
|
|
if (one_of(cmd, "skip", "start", "terms", NULL))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (has_term_file && strcmp(cmd, terms->term_bad) &&
|
|
|
|
strcmp(cmd, terms->term_good))
|
|
|
|
return error(_("Invalid command: you're currently in a "
|
|
|
|
"%s/%s bisect"), terms->term_bad,
|
|
|
|
terms->term_good);
|
|
|
|
|
|
|
|
if (!has_term_file) {
|
|
|
|
if (one_of(cmd, "bad", "good", NULL)) {
|
|
|
|
set_terms(terms, "bad", "good");
|
|
|
|
return write_terms(terms->term_bad, terms->term_good);
|
|
|
|
}
|
|
|
|
if (one_of(cmd, "new", "old", NULL)) {
|
|
|
|
set_terms(terms, "new", "old");
|
|
|
|
return write_terms(terms->term_bad, terms->term_good);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-08-25 19:09:48 +02:00
|
|
|
static int inc_nr(const char *refname UNUSED,
|
|
|
|
const struct object_id *oid UNUSED,
|
|
|
|
int flag UNUSED, void *cb_data)
|
2019-01-02 16:38:34 +01:00
|
|
|
{
|
bisect: output state before we are ready to compute bisection
Commit 73c6de06aff8 ("bisect: don't use invalid oid as rev when
starting") changes the behaviour of `git bisect` to consider invalid
oids as pathspecs again, as in the old shell implementation.
While that behaviour may be desirable, it can also cause confusion. For
example, while bisecting in a particular repo I encountered this:
$ git bisect start d93ff48803f0 v6.3
$
...which led to me sitting for a few moments, wondering why there's no
printout stating the first rev to check.
It turns out that the tag was actually "6.3", not "v6.3", and thus the
bisect was still silently started with only a bad rev, because
d93ff48803f0 was a valid oid and "v6.3" was silently considered to be a
pathspec.
While this behaviour may be desirable, it can be confusing, especially
with different repo conventions either using or not using "v" before
release names, or when a branch name or tag is simply misspelled on the
command line.
In order to avoid situations like this, make it more clear what we're
waiting for:
$ git bisect start d93ff48803f0 v6.3
status: waiting for good commit(s), bad commit known
We already have good output once the bisect process has begun in
earnest, so we don't need to do anything more there.
Signed-off-by: Chris Down <chris@chrisdown.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-05-11 20:00:09 +02:00
|
|
|
unsigned int *nr = (unsigned int *)cb_data;
|
|
|
|
(*nr)++;
|
|
|
|
return 0;
|
2019-01-02 16:38:34 +01:00
|
|
|
}
|
|
|
|
|
2019-12-14 09:11:24 +01:00
|
|
|
static const char need_bad_and_good_revision_warning[] =
|
2019-01-02 16:38:34 +01:00
|
|
|
N_("You need to give me at least one %s and %s revision.\n"
|
|
|
|
"You can use \"git bisect %s\" and \"git bisect %s\" for that.");
|
|
|
|
|
2019-12-14 09:11:24 +01:00
|
|
|
static const char need_bisect_start_warning[] =
|
2019-01-02 16:38:34 +01:00
|
|
|
N_("You need to start by \"git bisect start\".\n"
|
|
|
|
"You then need to give me at least one %s and %s revision.\n"
|
|
|
|
"You can use \"git bisect %s\" and \"git bisect %s\" for that.");
|
|
|
|
|
2020-02-17 09:40:31 +01:00
|
|
|
static int decide_next(const struct bisect_terms *terms,
|
|
|
|
const char *current_term, int missing_good,
|
|
|
|
int missing_bad)
|
2019-01-02 16:38:34 +01:00
|
|
|
{
|
|
|
|
if (!missing_good && !missing_bad)
|
2020-02-17 09:40:31 +01:00
|
|
|
return 0;
|
|
|
|
if (!current_term)
|
|
|
|
return -1;
|
2019-01-02 16:38:34 +01:00
|
|
|
|
|
|
|
if (missing_good && !missing_bad &&
|
|
|
|
!strcmp(current_term, terms->term_good)) {
|
|
|
|
char *yesno;
|
|
|
|
/*
|
|
|
|
* have bad (or new) but not good (or old). We could bisect
|
|
|
|
* although this is less optimum.
|
|
|
|
*/
|
|
|
|
warning(_("bisecting only with a %s commit"), terms->term_bad);
|
|
|
|
if (!isatty(0))
|
2020-02-17 09:40:31 +01:00
|
|
|
return 0;
|
2019-01-02 16:38:34 +01:00
|
|
|
/*
|
|
|
|
* TRANSLATORS: Make sure to include [Y] and [n] in your
|
|
|
|
* translation. The program will only accept English input
|
|
|
|
* at this point.
|
|
|
|
*/
|
|
|
|
yesno = git_prompt(_("Are you sure [Y/n]? "), PROMPT_ECHO);
|
|
|
|
if (starts_with(yesno, "N") || starts_with(yesno, "n"))
|
2020-02-17 09:40:31 +01:00
|
|
|
return -1;
|
|
|
|
return 0;
|
2019-01-02 16:38:34 +01:00
|
|
|
}
|
|
|
|
|
2020-02-17 09:40:31 +01:00
|
|
|
if (!is_empty_or_missing_file(git_path_bisect_start()))
|
|
|
|
return error(_(need_bad_and_good_revision_warning),
|
|
|
|
vocab_bad, vocab_good, vocab_bad, vocab_good);
|
|
|
|
else
|
|
|
|
return error(_(need_bisect_start_warning),
|
|
|
|
vocab_good, vocab_bad, vocab_good, vocab_bad);
|
|
|
|
}
|
|
|
|
|
bisect: output state before we are ready to compute bisection
Commit 73c6de06aff8 ("bisect: don't use invalid oid as rev when
starting") changes the behaviour of `git bisect` to consider invalid
oids as pathspecs again, as in the old shell implementation.
While that behaviour may be desirable, it can also cause confusion. For
example, while bisecting in a particular repo I encountered this:
$ git bisect start d93ff48803f0 v6.3
$
...which led to me sitting for a few moments, wondering why there's no
printout stating the first rev to check.
It turns out that the tag was actually "6.3", not "v6.3", and thus the
bisect was still silently started with only a bad rev, because
d93ff48803f0 was a valid oid and "v6.3" was silently considered to be a
pathspec.
While this behaviour may be desirable, it can be confusing, especially
with different repo conventions either using or not using "v" before
release names, or when a branch name or tag is simply misspelled on the
command line.
In order to avoid situations like this, make it more clear what we're
waiting for:
$ git bisect start d93ff48803f0 v6.3
status: waiting for good commit(s), bad commit known
We already have good output once the bisect process has begun in
earnest, so we don't need to do anything more there.
Signed-off-by: Chris Down <chris@chrisdown.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-05-11 20:00:09 +02:00
|
|
|
static void bisect_status(struct bisect_state *state,
|
|
|
|
const struct bisect_terms *terms)
|
2020-02-17 09:40:31 +01:00
|
|
|
{
|
|
|
|
char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad);
|
|
|
|
char *good_glob = xstrfmt("%s-*", terms->term_good);
|
|
|
|
|
|
|
|
if (ref_exists(bad_ref))
|
bisect: output state before we are ready to compute bisection
Commit 73c6de06aff8 ("bisect: don't use invalid oid as rev when
starting") changes the behaviour of `git bisect` to consider invalid
oids as pathspecs again, as in the old shell implementation.
While that behaviour may be desirable, it can also cause confusion. For
example, while bisecting in a particular repo I encountered this:
$ git bisect start d93ff48803f0 v6.3
$
...which led to me sitting for a few moments, wondering why there's no
printout stating the first rev to check.
It turns out that the tag was actually "6.3", not "v6.3", and thus the
bisect was still silently started with only a bad rev, because
d93ff48803f0 was a valid oid and "v6.3" was silently considered to be a
pathspec.
While this behaviour may be desirable, it can be confusing, especially
with different repo conventions either using or not using "v" before
release names, or when a branch name or tag is simply misspelled on the
command line.
In order to avoid situations like this, make it more clear what we're
waiting for:
$ git bisect start d93ff48803f0 v6.3
status: waiting for good commit(s), bad commit known
We already have good output once the bisect process has begun in
earnest, so we don't need to do anything more there.
Signed-off-by: Chris Down <chris@chrisdown.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-05-11 20:00:09 +02:00
|
|
|
state->nr_bad = 1;
|
2020-02-17 09:40:31 +01:00
|
|
|
|
bisect: output state before we are ready to compute bisection
Commit 73c6de06aff8 ("bisect: don't use invalid oid as rev when
starting") changes the behaviour of `git bisect` to consider invalid
oids as pathspecs again, as in the old shell implementation.
While that behaviour may be desirable, it can also cause confusion. For
example, while bisecting in a particular repo I encountered this:
$ git bisect start d93ff48803f0 v6.3
$
...which led to me sitting for a few moments, wondering why there's no
printout stating the first rev to check.
It turns out that the tag was actually "6.3", not "v6.3", and thus the
bisect was still silently started with only a bad rev, because
d93ff48803f0 was a valid oid and "v6.3" was silently considered to be a
pathspec.
While this behaviour may be desirable, it can be confusing, especially
with different repo conventions either using or not using "v" before
release names, or when a branch name or tag is simply misspelled on the
command line.
In order to avoid situations like this, make it more clear what we're
waiting for:
$ git bisect start d93ff48803f0 v6.3
status: waiting for good commit(s), bad commit known
We already have good output once the bisect process has begun in
earnest, so we don't need to do anything more there.
Signed-off-by: Chris Down <chris@chrisdown.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-05-11 20:00:09 +02:00
|
|
|
for_each_glob_ref_in(inc_nr, good_glob, "refs/bisect/",
|
|
|
|
(void *) &state->nr_good);
|
2020-02-17 09:40:31 +01:00
|
|
|
|
|
|
|
free(good_glob);
|
|
|
|
free(bad_ref);
|
bisect: output state before we are ready to compute bisection
Commit 73c6de06aff8 ("bisect: don't use invalid oid as rev when
starting") changes the behaviour of `git bisect` to consider invalid
oids as pathspecs again, as in the old shell implementation.
While that behaviour may be desirable, it can also cause confusion. For
example, while bisecting in a particular repo I encountered this:
$ git bisect start d93ff48803f0 v6.3
$
...which led to me sitting for a few moments, wondering why there's no
printout stating the first rev to check.
It turns out that the tag was actually "6.3", not "v6.3", and thus the
bisect was still silently started with only a bad rev, because
d93ff48803f0 was a valid oid and "v6.3" was silently considered to be a
pathspec.
While this behaviour may be desirable, it can be confusing, especially
with different repo conventions either using or not using "v" before
release names, or when a branch name or tag is simply misspelled on the
command line.
In order to avoid situations like this, make it more clear what we're
waiting for:
$ git bisect start d93ff48803f0 v6.3
status: waiting for good commit(s), bad commit known
We already have good output once the bisect process has begun in
earnest, so we don't need to do anything more there.
Signed-off-by: Chris Down <chris@chrisdown.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-05-11 20:00:09 +02:00
|
|
|
}
|
|
|
|
|
2022-05-11 20:00:14 +02:00
|
|
|
__attribute__((format (printf, 1, 2)))
|
|
|
|
static void bisect_log_printf(const char *fmt, ...)
|
|
|
|
{
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
strbuf_vaddf(&buf, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
printf("%s", buf.buf);
|
|
|
|
append_to_file(git_path_bisect_log(), "# %s", buf.buf);
|
|
|
|
|
|
|
|
strbuf_release(&buf);
|
|
|
|
}
|
|
|
|
|
bisect: output state before we are ready to compute bisection
Commit 73c6de06aff8 ("bisect: don't use invalid oid as rev when
starting") changes the behaviour of `git bisect` to consider invalid
oids as pathspecs again, as in the old shell implementation.
While that behaviour may be desirable, it can also cause confusion. For
example, while bisecting in a particular repo I encountered this:
$ git bisect start d93ff48803f0 v6.3
$
...which led to me sitting for a few moments, wondering why there's no
printout stating the first rev to check.
It turns out that the tag was actually "6.3", not "v6.3", and thus the
bisect was still silently started with only a bad rev, because
d93ff48803f0 was a valid oid and "v6.3" was silently considered to be a
pathspec.
While this behaviour may be desirable, it can be confusing, especially
with different repo conventions either using or not using "v" before
release names, or when a branch name or tag is simply misspelled on the
command line.
In order to avoid situations like this, make it more clear what we're
waiting for:
$ git bisect start d93ff48803f0 v6.3
status: waiting for good commit(s), bad commit known
We already have good output once the bisect process has begun in
earnest, so we don't need to do anything more there.
Signed-off-by: Chris Down <chris@chrisdown.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-05-11 20:00:09 +02:00
|
|
|
static void bisect_print_status(const struct bisect_terms *terms)
|
|
|
|
{
|
|
|
|
struct bisect_state state = { 0 };
|
|
|
|
|
|
|
|
bisect_status(&state, terms);
|
|
|
|
|
|
|
|
/* If we had both, we'd already be started, and shouldn't get here. */
|
|
|
|
if (state.nr_good && state.nr_bad)
|
|
|
|
return;
|
2020-02-17 09:40:31 +01:00
|
|
|
|
bisect: output state before we are ready to compute bisection
Commit 73c6de06aff8 ("bisect: don't use invalid oid as rev when
starting") changes the behaviour of `git bisect` to consider invalid
oids as pathspecs again, as in the old shell implementation.
While that behaviour may be desirable, it can also cause confusion. For
example, while bisecting in a particular repo I encountered this:
$ git bisect start d93ff48803f0 v6.3
$
...which led to me sitting for a few moments, wondering why there's no
printout stating the first rev to check.
It turns out that the tag was actually "6.3", not "v6.3", and thus the
bisect was still silently started with only a bad rev, because
d93ff48803f0 was a valid oid and "v6.3" was silently considered to be a
pathspec.
While this behaviour may be desirable, it can be confusing, especially
with different repo conventions either using or not using "v" before
release names, or when a branch name or tag is simply misspelled on the
command line.
In order to avoid situations like this, make it more clear what we're
waiting for:
$ git bisect start d93ff48803f0 v6.3
status: waiting for good commit(s), bad commit known
We already have good output once the bisect process has begun in
earnest, so we don't need to do anything more there.
Signed-off-by: Chris Down <chris@chrisdown.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-05-11 20:00:09 +02:00
|
|
|
if (!state.nr_good && !state.nr_bad)
|
2022-05-11 20:00:14 +02:00
|
|
|
bisect_log_printf(_("status: waiting for both good and bad commits\n"));
|
bisect: output state before we are ready to compute bisection
Commit 73c6de06aff8 ("bisect: don't use invalid oid as rev when
starting") changes the behaviour of `git bisect` to consider invalid
oids as pathspecs again, as in the old shell implementation.
While that behaviour may be desirable, it can also cause confusion. For
example, while bisecting in a particular repo I encountered this:
$ git bisect start d93ff48803f0 v6.3
$
...which led to me sitting for a few moments, wondering why there's no
printout stating the first rev to check.
It turns out that the tag was actually "6.3", not "v6.3", and thus the
bisect was still silently started with only a bad rev, because
d93ff48803f0 was a valid oid and "v6.3" was silently considered to be a
pathspec.
While this behaviour may be desirable, it can be confusing, especially
with different repo conventions either using or not using "v" before
release names, or when a branch name or tag is simply misspelled on the
command line.
In order to avoid situations like this, make it more clear what we're
waiting for:
$ git bisect start d93ff48803f0 v6.3
status: waiting for good commit(s), bad commit known
We already have good output once the bisect process has begun in
earnest, so we don't need to do anything more there.
Signed-off-by: Chris Down <chris@chrisdown.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-05-11 20:00:09 +02:00
|
|
|
else if (state.nr_good)
|
2022-05-11 20:00:14 +02:00
|
|
|
bisect_log_printf(Q_("status: waiting for bad commit, %d good commit known\n",
|
|
|
|
"status: waiting for bad commit, %d good commits known\n",
|
|
|
|
state.nr_good), state.nr_good);
|
bisect: output state before we are ready to compute bisection
Commit 73c6de06aff8 ("bisect: don't use invalid oid as rev when
starting") changes the behaviour of `git bisect` to consider invalid
oids as pathspecs again, as in the old shell implementation.
While that behaviour may be desirable, it can also cause confusion. For
example, while bisecting in a particular repo I encountered this:
$ git bisect start d93ff48803f0 v6.3
$
...which led to me sitting for a few moments, wondering why there's no
printout stating the first rev to check.
It turns out that the tag was actually "6.3", not "v6.3", and thus the
bisect was still silently started with only a bad rev, because
d93ff48803f0 was a valid oid and "v6.3" was silently considered to be a
pathspec.
While this behaviour may be desirable, it can be confusing, especially
with different repo conventions either using or not using "v" before
release names, or when a branch name or tag is simply misspelled on the
command line.
In order to avoid situations like this, make it more clear what we're
waiting for:
$ git bisect start d93ff48803f0 v6.3
status: waiting for good commit(s), bad commit known
We already have good output once the bisect process has begun in
earnest, so we don't need to do anything more there.
Signed-off-by: Chris Down <chris@chrisdown.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-05-11 20:00:09 +02:00
|
|
|
else
|
2022-05-11 20:00:14 +02:00
|
|
|
bisect_log_printf(_("status: waiting for good commit(s), bad commit known\n"));
|
bisect: output state before we are ready to compute bisection
Commit 73c6de06aff8 ("bisect: don't use invalid oid as rev when
starting") changes the behaviour of `git bisect` to consider invalid
oids as pathspecs again, as in the old shell implementation.
While that behaviour may be desirable, it can also cause confusion. For
example, while bisecting in a particular repo I encountered this:
$ git bisect start d93ff48803f0 v6.3
$
...which led to me sitting for a few moments, wondering why there's no
printout stating the first rev to check.
It turns out that the tag was actually "6.3", not "v6.3", and thus the
bisect was still silently started with only a bad rev, because
d93ff48803f0 was a valid oid and "v6.3" was silently considered to be a
pathspec.
While this behaviour may be desirable, it can be confusing, especially
with different repo conventions either using or not using "v" before
release names, or when a branch name or tag is simply misspelled on the
command line.
In order to avoid situations like this, make it more clear what we're
waiting for:
$ git bisect start d93ff48803f0 v6.3
status: waiting for good commit(s), bad commit known
We already have good output once the bisect process has begun in
earnest, so we don't need to do anything more there.
Signed-off-by: Chris Down <chris@chrisdown.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-05-11 20:00:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int bisect_next_check(const struct bisect_terms *terms,
|
|
|
|
const char *current_term)
|
|
|
|
{
|
|
|
|
struct bisect_state state = { 0 };
|
|
|
|
bisect_status(&state, terms);
|
|
|
|
return decide_next(terms, current_term, !state.nr_good, !state.nr_bad);
|
2019-01-02 16:38:34 +01:00
|
|
|
}
|
|
|
|
|
2019-01-02 16:38:35 +01:00
|
|
|
static int get_terms(struct bisect_terms *terms)
|
|
|
|
{
|
|
|
|
struct strbuf str = STRBUF_INIT;
|
|
|
|
FILE *fp = NULL;
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
fp = fopen(git_path_bisect_terms(), "r");
|
|
|
|
if (!fp) {
|
|
|
|
res = -1;
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
free_terms(terms);
|
|
|
|
strbuf_getline_lf(&str, fp);
|
|
|
|
terms->term_bad = strbuf_detach(&str, NULL);
|
|
|
|
strbuf_getline_lf(&str, fp);
|
|
|
|
terms->term_good = strbuf_detach(&str, NULL);
|
|
|
|
|
|
|
|
finish:
|
|
|
|
if (fp)
|
|
|
|
fclose(fp);
|
|
|
|
strbuf_release(&str);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bisect_terms(struct bisect_terms *terms, const char *option)
|
|
|
|
{
|
|
|
|
if (get_terms(terms))
|
|
|
|
return error(_("no terms defined"));
|
|
|
|
|
2022-05-02 18:50:37 +02:00
|
|
|
if (!option) {
|
2019-01-02 16:38:35 +01:00
|
|
|
printf(_("Your current terms are %s for the old state\n"
|
|
|
|
"and %s for the new state.\n"),
|
|
|
|
terms->term_good, terms->term_bad);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (one_of(option, "--term-good", "--term-old", NULL))
|
|
|
|
printf("%s\n", terms->term_good);
|
|
|
|
else if (one_of(option, "--term-bad", "--term-new", NULL))
|
|
|
|
printf("%s\n", terms->term_bad);
|
|
|
|
else
|
|
|
|
return error(_("invalid argument %s for 'git bisect terms'.\n"
|
|
|
|
"Supported options are: "
|
|
|
|
"--term-good|--term-old and "
|
|
|
|
"--term-bad|--term-new."), option);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-01-02 16:38:36 +01:00
|
|
|
static int bisect_append_log_quoted(const char **argv)
|
|
|
|
{
|
2020-02-17 09:40:29 +01:00
|
|
|
int res = 0;
|
2019-01-02 16:38:36 +01:00
|
|
|
FILE *fp = fopen(git_path_bisect_log(), "a");
|
|
|
|
struct strbuf orig_args = STRBUF_INIT;
|
|
|
|
|
|
|
|
if (!fp)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (fprintf(fp, "git bisect start") < 1) {
|
2020-02-17 09:40:29 +01:00
|
|
|
res = -1;
|
2019-01-02 16:38:36 +01:00
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
sq_quote_argv(&orig_args, argv);
|
|
|
|
if (fprintf(fp, "%s\n", orig_args.buf) < 1)
|
2020-02-17 09:40:29 +01:00
|
|
|
res = -1;
|
2019-01-02 16:38:36 +01:00
|
|
|
|
|
|
|
finish:
|
|
|
|
fclose(fp);
|
|
|
|
strbuf_release(&orig_args);
|
2020-02-17 09:40:29 +01:00
|
|
|
return res;
|
2019-01-02 16:38:36 +01:00
|
|
|
}
|
|
|
|
|
2020-09-24 14:33:40 +02:00
|
|
|
static int add_bisect_ref(const char *refname, const struct object_id *oid,
|
2022-08-25 19:09:48 +02:00
|
|
|
int flags UNUSED, void *cb)
|
2020-09-24 14:33:40 +02:00
|
|
|
{
|
|
|
|
struct add_bisect_ref_data *data = cb;
|
|
|
|
|
|
|
|
add_pending_oid(data->revs, refname, oid, data->object_flags);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int prepare_revs(struct bisect_terms *terms, struct rev_info *revs)
|
|
|
|
{
|
|
|
|
int res = 0;
|
|
|
|
struct add_bisect_ref_data cb = { revs };
|
|
|
|
char *good = xstrfmt("%s-*", terms->term_good);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We cannot use terms->term_bad directly in
|
|
|
|
* for_each_glob_ref_in() and we have to append a '*' to it,
|
|
|
|
* otherwise for_each_glob_ref_in() will append '/' and '*'.
|
|
|
|
*/
|
|
|
|
char *bad = xstrfmt("%s*", terms->term_bad);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It is important to reset the flags used by revision walks
|
|
|
|
* as the previous call to bisect_next_all() in turn
|
|
|
|
* sets up a revision walk.
|
|
|
|
*/
|
|
|
|
reset_revision_walk();
|
|
|
|
init_revisions(revs, NULL);
|
|
|
|
setup_revisions(0, NULL, revs, NULL);
|
|
|
|
for_each_glob_ref_in(add_bisect_ref, bad, "refs/bisect/", &cb);
|
|
|
|
cb.object_flags = UNINTERESTING;
|
|
|
|
for_each_glob_ref_in(add_bisect_ref, good, "refs/bisect/", &cb);
|
|
|
|
if (prepare_revision_walk(revs))
|
|
|
|
res = error(_("revision walk setup failed\n"));
|
|
|
|
|
|
|
|
free(good);
|
|
|
|
free(bad);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bisect_skipped_commits(struct bisect_terms *terms)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
FILE *fp = NULL;
|
|
|
|
struct rev_info revs;
|
|
|
|
struct commit *commit;
|
|
|
|
struct pretty_print_context pp = {0};
|
|
|
|
struct strbuf commit_name = STRBUF_INIT;
|
|
|
|
|
|
|
|
res = prepare_revs(terms, &revs);
|
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
|
|
|
|
fp = fopen(git_path_bisect_log(), "a");
|
|
|
|
if (!fp)
|
|
|
|
return error_errno(_("could not open '%s' for appending"),
|
|
|
|
git_path_bisect_log());
|
|
|
|
|
|
|
|
if (fprintf(fp, "# only skipped commits left to test\n") < 0)
|
|
|
|
return error_errno(_("failed to write to '%s'"), git_path_bisect_log());
|
|
|
|
|
|
|
|
while ((commit = get_revision(&revs)) != NULL) {
|
|
|
|
strbuf_reset(&commit_name);
|
|
|
|
format_commit_message(commit, "%s",
|
|
|
|
&commit_name, &pp);
|
|
|
|
fprintf(fp, "# possible first %s commit: [%s] %s\n",
|
|
|
|
terms->term_bad, oid_to_hex(&commit->object.oid),
|
|
|
|
commit_name.buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset the flags used by revision walks in case
|
|
|
|
* there is another revision walk after this one.
|
|
|
|
*/
|
|
|
|
reset_revision_walk();
|
|
|
|
|
|
|
|
strbuf_release(&commit_name);
|
2022-04-13 22:01:36 +02:00
|
|
|
release_revisions(&revs);
|
2020-09-24 14:33:40 +02:00
|
|
|
fclose(fp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bisect_successful(struct bisect_terms *terms)
|
|
|
|
{
|
|
|
|
struct object_id oid;
|
|
|
|
struct commit *commit;
|
|
|
|
struct pretty_print_context pp = {0};
|
|
|
|
struct strbuf commit_name = STRBUF_INIT;
|
|
|
|
char *bad_ref = xstrfmt("refs/bisect/%s",terms->term_bad);
|
|
|
|
int res;
|
|
|
|
|
|
|
|
read_ref(bad_ref, &oid);
|
|
|
|
commit = lookup_commit_reference_by_name(bad_ref);
|
|
|
|
format_commit_message(commit, "%s", &commit_name, &pp);
|
|
|
|
|
|
|
|
res = append_to_file(git_path_bisect_log(), "# first %s commit: [%s] %s\n",
|
|
|
|
terms->term_bad, oid_to_hex(&commit->object.oid),
|
|
|
|
commit_name.buf);
|
|
|
|
|
|
|
|
strbuf_release(&commit_name);
|
|
|
|
free(bad_ref);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static enum bisect_error bisect_next(struct bisect_terms *terms, const char *prefix)
|
|
|
|
{
|
|
|
|
enum bisect_error res;
|
|
|
|
|
|
|
|
if (bisect_autostart(terms))
|
|
|
|
return BISECT_FAILED;
|
|
|
|
|
|
|
|
if (bisect_next_check(terms, terms->term_good))
|
|
|
|
return BISECT_FAILED;
|
|
|
|
|
|
|
|
/* Perform all bisection computation */
|
|
|
|
res = bisect_next_all(the_repository, prefix);
|
|
|
|
|
|
|
|
if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {
|
|
|
|
res = bisect_successful(terms);
|
|
|
|
return res ? res : BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;
|
|
|
|
} else if (res == BISECT_ONLY_SKIPPED_LEFT) {
|
|
|
|
res = bisect_skipped_commits(terms);
|
|
|
|
return res ? res : BISECT_ONLY_SKIPPED_LEFT;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static enum bisect_error bisect_auto_next(struct bisect_terms *terms, const char *prefix)
|
|
|
|
{
|
bisect: output state before we are ready to compute bisection
Commit 73c6de06aff8 ("bisect: don't use invalid oid as rev when
starting") changes the behaviour of `git bisect` to consider invalid
oids as pathspecs again, as in the old shell implementation.
While that behaviour may be desirable, it can also cause confusion. For
example, while bisecting in a particular repo I encountered this:
$ git bisect start d93ff48803f0 v6.3
$
...which led to me sitting for a few moments, wondering why there's no
printout stating the first rev to check.
It turns out that the tag was actually "6.3", not "v6.3", and thus the
bisect was still silently started with only a bad rev, because
d93ff48803f0 was a valid oid and "v6.3" was silently considered to be a
pathspec.
While this behaviour may be desirable, it can be confusing, especially
with different repo conventions either using or not using "v" before
release names, or when a branch name or tag is simply misspelled on the
command line.
In order to avoid situations like this, make it more clear what we're
waiting for:
$ git bisect start d93ff48803f0 v6.3
status: waiting for good commit(s), bad commit known
We already have good output once the bisect process has begun in
earnest, so we don't need to do anything more there.
Signed-off-by: Chris Down <chris@chrisdown.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-05-11 20:00:09 +02:00
|
|
|
if (bisect_next_check(terms, NULL)) {
|
|
|
|
bisect_print_status(terms);
|
2020-09-24 14:33:40 +02:00
|
|
|
return BISECT_OK;
|
bisect: output state before we are ready to compute bisection
Commit 73c6de06aff8 ("bisect: don't use invalid oid as rev when
starting") changes the behaviour of `git bisect` to consider invalid
oids as pathspecs again, as in the old shell implementation.
While that behaviour may be desirable, it can also cause confusion. For
example, while bisecting in a particular repo I encountered this:
$ git bisect start d93ff48803f0 v6.3
$
...which led to me sitting for a few moments, wondering why there's no
printout stating the first rev to check.
It turns out that the tag was actually "6.3", not "v6.3", and thus the
bisect was still silently started with only a bad rev, because
d93ff48803f0 was a valid oid and "v6.3" was silently considered to be a
pathspec.
While this behaviour may be desirable, it can be confusing, especially
with different repo conventions either using or not using "v" before
release names, or when a branch name or tag is simply misspelled on the
command line.
In order to avoid situations like this, make it more clear what we're
waiting for:
$ git bisect start d93ff48803f0 v6.3
status: waiting for good commit(s), bad commit known
We already have good output once the bisect process has begun in
earnest, so we don't need to do anything more there.
Signed-off-by: Chris Down <chris@chrisdown.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-05-11 20:00:09 +02:00
|
|
|
}
|
2020-09-24 14:33:40 +02:00
|
|
|
|
|
|
|
return bisect_next(terms, prefix);
|
|
|
|
}
|
|
|
|
|
2020-10-15 15:38:32 +02:00
|
|
|
static enum bisect_error bisect_start(struct bisect_terms *terms, const char **argv, int argc)
|
2019-01-02 16:38:36 +01:00
|
|
|
{
|
2020-08-07 23:58:36 +02:00
|
|
|
int no_checkout = 0;
|
2020-08-07 23:58:37 +02:00
|
|
|
int first_parent_only = 0;
|
2019-01-02 16:38:36 +01:00
|
|
|
int i, has_double_dash = 0, must_write_terms = 0, bad_seen = 0;
|
2020-10-15 15:38:32 +02:00
|
|
|
int flags, pathspec_pos;
|
|
|
|
enum bisect_error res = BISECT_OK;
|
2019-01-02 16:38:36 +01:00
|
|
|
struct string_list revs = STRING_LIST_INIT_DUP;
|
|
|
|
struct string_list states = STRING_LIST_INIT_DUP;
|
|
|
|
struct strbuf start_head = STRBUF_INIT;
|
|
|
|
struct strbuf bisect_names = STRBUF_INIT;
|
|
|
|
struct object_id head_oid;
|
|
|
|
struct object_id oid;
|
|
|
|
const char *head;
|
|
|
|
|
|
|
|
if (is_bare_repository())
|
|
|
|
no_checkout = 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for one bad and then some good revisions
|
|
|
|
*/
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
|
|
if (!strcmp(argv[i], "--")) {
|
|
|
|
has_double_dash = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
|
|
const char *arg = argv[i];
|
|
|
|
if (!strcmp(argv[i], "--")) {
|
|
|
|
break;
|
|
|
|
} else if (!strcmp(arg, "--no-checkout")) {
|
|
|
|
no_checkout = 1;
|
2020-08-07 23:58:37 +02:00
|
|
|
} else if (!strcmp(arg, "--first-parent")) {
|
|
|
|
first_parent_only = 1;
|
2019-01-02 16:38:36 +01:00
|
|
|
} else if (!strcmp(arg, "--term-good") ||
|
|
|
|
!strcmp(arg, "--term-old")) {
|
2020-05-21 01:26:27 +02:00
|
|
|
i++;
|
|
|
|
if (argc <= i)
|
|
|
|
return error(_("'' is not a valid term"));
|
2019-01-02 16:38:36 +01:00
|
|
|
must_write_terms = 1;
|
|
|
|
free((void *) terms->term_good);
|
2020-05-21 01:26:27 +02:00
|
|
|
terms->term_good = xstrdup(argv[i]);
|
2019-01-02 16:38:36 +01:00
|
|
|
} else if (skip_prefix(arg, "--term-good=", &arg) ||
|
|
|
|
skip_prefix(arg, "--term-old=", &arg)) {
|
|
|
|
must_write_terms = 1;
|
|
|
|
free((void *) terms->term_good);
|
|
|
|
terms->term_good = xstrdup(arg);
|
|
|
|
} else if (!strcmp(arg, "--term-bad") ||
|
|
|
|
!strcmp(arg, "--term-new")) {
|
2020-05-21 01:26:27 +02:00
|
|
|
i++;
|
|
|
|
if (argc <= i)
|
|
|
|
return error(_("'' is not a valid term"));
|
2019-01-02 16:38:36 +01:00
|
|
|
must_write_terms = 1;
|
|
|
|
free((void *) terms->term_bad);
|
2020-05-21 01:26:27 +02:00
|
|
|
terms->term_bad = xstrdup(argv[i]);
|
2019-01-02 16:38:36 +01:00
|
|
|
} else if (skip_prefix(arg, "--term-bad=", &arg) ||
|
|
|
|
skip_prefix(arg, "--term-new=", &arg)) {
|
|
|
|
must_write_terms = 1;
|
|
|
|
free((void *) terms->term_bad);
|
|
|
|
terms->term_bad = xstrdup(arg);
|
2020-05-21 01:26:27 +02:00
|
|
|
} else if (starts_with(arg, "--")) {
|
2019-01-02 16:38:36 +01:00
|
|
|
return error(_("unrecognized option: '%s'"), arg);
|
2020-09-25 15:01:28 +02:00
|
|
|
} else if (!get_oidf(&oid, "%s^{commit}", arg)) {
|
2019-01-02 16:38:36 +01:00
|
|
|
string_list_append(&revs, oid_to_hex(&oid));
|
2020-09-25 15:01:28 +02:00
|
|
|
} else if (has_double_dash) {
|
|
|
|
die(_("'%s' does not appear to be a valid "
|
|
|
|
"revision"), arg);
|
|
|
|
} else {
|
|
|
|
break;
|
2019-01-02 16:38:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
pathspec_pos = i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The user ran "git bisect start <sha1> <sha1>", hence did not
|
|
|
|
* explicitly specify the terms, but we are already starting to
|
|
|
|
* set references named with the default terms, and won't be able
|
|
|
|
* to change afterwards.
|
|
|
|
*/
|
|
|
|
if (revs.nr)
|
|
|
|
must_write_terms = 1;
|
|
|
|
for (i = 0; i < revs.nr; i++) {
|
|
|
|
if (bad_seen) {
|
|
|
|
string_list_append(&states, terms->term_good);
|
|
|
|
} else {
|
|
|
|
bad_seen = 1;
|
|
|
|
string_list_append(&states, terms->term_bad);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Verify HEAD
|
|
|
|
*/
|
|
|
|
head = resolve_ref_unsafe("HEAD", 0, &head_oid, &flags);
|
|
|
|
if (!head)
|
|
|
|
if (get_oid("HEAD", &head_oid))
|
|
|
|
return error(_("bad HEAD - I need a HEAD"));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if we are bisecting
|
|
|
|
*/
|
|
|
|
if (!is_empty_or_missing_file(git_path_bisect_start())) {
|
|
|
|
/* Reset to the rev from where we started */
|
|
|
|
strbuf_read_file(&start_head, git_path_bisect_start(), 0);
|
|
|
|
strbuf_trim(&start_head);
|
|
|
|
if (!no_checkout) {
|
2022-10-07 17:08:42 +02:00
|
|
|
const char *argv[] = { "checkout", start_head.buf,
|
|
|
|
"--", NULL };
|
2019-01-02 16:38:36 +01:00
|
|
|
|
2022-10-07 17:08:42 +02:00
|
|
|
if (run_command_v_opt(argv, RUN_GIT_CMD)) {
|
2020-02-17 09:40:29 +01:00
|
|
|
res = error(_("checking out '%s' failed."
|
2019-01-02 16:38:36 +01:00
|
|
|
" Try 'git bisect start "
|
|
|
|
"<valid-branch>'."),
|
|
|
|
start_head.buf);
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Get the rev from where we start. */
|
|
|
|
if (!get_oid(head, &head_oid) &&
|
|
|
|
!starts_with(head, "refs/heads/")) {
|
|
|
|
strbuf_reset(&start_head);
|
|
|
|
strbuf_addstr(&start_head, oid_to_hex(&head_oid));
|
|
|
|
} else if (!get_oid(head, &head_oid) &&
|
|
|
|
skip_prefix(head, "refs/heads/", &head)) {
|
|
|
|
/*
|
|
|
|
* This error message should only be triggered by
|
|
|
|
* cogito usage, and cogito users should understand
|
|
|
|
* it relates to cg-seek.
|
|
|
|
*/
|
|
|
|
if (!is_empty_or_missing_file(git_path_head_name()))
|
|
|
|
return error(_("won't bisect on cg-seek'ed tree"));
|
|
|
|
strbuf_addstr(&start_head, head);
|
|
|
|
} else {
|
|
|
|
return error(_("bad HEAD - strange symbolic ref"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get rid of any old bisect state.
|
|
|
|
*/
|
|
|
|
if (bisect_clean_state())
|
2020-10-15 15:38:32 +02:00
|
|
|
return BISECT_FAILED;
|
2019-01-02 16:38:36 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Write new start state
|
|
|
|
*/
|
|
|
|
write_file(git_path_bisect_start(), "%s\n", start_head.buf);
|
|
|
|
|
2020-08-07 23:58:37 +02:00
|
|
|
if (first_parent_only)
|
|
|
|
write_file(git_path_bisect_first_parent(), "\n");
|
|
|
|
|
2019-01-02 16:38:36 +01:00
|
|
|
if (no_checkout) {
|
2019-05-21 19:50:21 +02:00
|
|
|
if (get_oid(start_head.buf, &oid) < 0) {
|
2020-02-17 09:40:29 +01:00
|
|
|
res = error(_("invalid ref: '%s'"), start_head.buf);
|
2019-05-21 19:50:21 +02:00
|
|
|
goto finish;
|
|
|
|
}
|
2019-01-02 16:38:36 +01:00
|
|
|
if (update_ref(NULL, "BISECT_HEAD", &oid, NULL, 0,
|
|
|
|
UPDATE_REFS_MSG_ON_ERR)) {
|
2020-10-15 15:38:32 +02:00
|
|
|
res = BISECT_FAILED;
|
2019-01-02 16:38:36 +01:00
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pathspec_pos < argc - 1)
|
|
|
|
sq_quote_argv(&bisect_names, argv + pathspec_pos);
|
|
|
|
write_file(git_path_bisect_names(), "%s\n", bisect_names.buf);
|
|
|
|
|
|
|
|
for (i = 0; i < states.nr; i++)
|
|
|
|
if (bisect_write(states.items[i].string,
|
|
|
|
revs.items[i].string, terms, 1)) {
|
2020-10-15 15:38:32 +02:00
|
|
|
res = BISECT_FAILED;
|
2019-01-02 16:38:36 +01:00
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (must_write_terms && write_terms(terms->term_bad,
|
|
|
|
terms->term_good)) {
|
2020-10-15 15:38:32 +02:00
|
|
|
res = BISECT_FAILED;
|
2019-01-02 16:38:36 +01:00
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
2020-02-17 09:40:29 +01:00
|
|
|
res = bisect_append_log_quoted(argv);
|
|
|
|
if (res)
|
2020-10-15 15:38:32 +02:00
|
|
|
res = BISECT_FAILED;
|
2019-01-02 16:38:36 +01:00
|
|
|
|
|
|
|
finish:
|
|
|
|
string_list_clear(&revs, 0);
|
|
|
|
string_list_clear(&states, 0);
|
|
|
|
strbuf_release(&start_head);
|
|
|
|
strbuf_release(&bisect_names);
|
2020-10-15 15:38:32 +02:00
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
|
|
|
|
res = bisect_auto_next(terms, NULL);
|
|
|
|
if (!is_bisect_success(res))
|
|
|
|
bisect_clean_state();
|
2020-02-17 09:40:29 +01:00
|
|
|
return res;
|
2019-01-02 16:38:36 +01:00
|
|
|
}
|
|
|
|
|
2020-09-24 14:33:38 +02:00
|
|
|
static inline int file_is_not_empty(const char *path)
|
|
|
|
{
|
|
|
|
return !is_empty_or_missing_file(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bisect_autostart(struct bisect_terms *terms)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
const char *yesno;
|
|
|
|
|
|
|
|
if (file_is_not_empty(git_path_bisect_start()))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fprintf_ln(stderr, _("You need to start by \"git bisect "
|
|
|
|
"start\"\n"));
|
|
|
|
|
|
|
|
if (!isatty(STDIN_FILENO))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TRANSLATORS: Make sure to include [Y] and [n] in your
|
|
|
|
* translation. The program will only accept English input
|
|
|
|
* at this point.
|
|
|
|
*/
|
|
|
|
yesno = git_prompt(_("Do you want me to do it for you "
|
|
|
|
"[Y/n]? "), PROMPT_ECHO);
|
|
|
|
res = tolower(*yesno) == 'n' ?
|
|
|
|
-1 : bisect_start(terms, empty_strvec, 0);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2020-10-15 15:38:35 +02:00
|
|
|
static enum bisect_error bisect_state(struct bisect_terms *terms, const char **argv,
|
|
|
|
int argc)
|
|
|
|
{
|
|
|
|
const char *state;
|
|
|
|
int i, verify_expected = 1;
|
|
|
|
struct object_id oid, expected;
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
struct oid_array revs = OID_ARRAY_INIT;
|
|
|
|
|
|
|
|
if (!argc)
|
|
|
|
return error(_("Please call `--bisect-state` with at least one argument"));
|
|
|
|
|
|
|
|
if (bisect_autostart(terms))
|
|
|
|
return BISECT_FAILED;
|
|
|
|
|
|
|
|
state = argv[0];
|
|
|
|
if (check_and_set_terms(terms, state) ||
|
|
|
|
!one_of(state, terms->term_good, terms->term_bad, "skip", NULL))
|
|
|
|
return BISECT_FAILED;
|
|
|
|
|
|
|
|
argv++;
|
|
|
|
argc--;
|
|
|
|
if (argc > 1 && !strcmp(state, terms->term_bad))
|
|
|
|
return error(_("'git bisect %s' can take only one argument."), terms->term_bad);
|
|
|
|
|
|
|
|
if (argc == 0) {
|
|
|
|
const char *head = "BISECT_HEAD";
|
|
|
|
enum get_oid_result res_head = get_oid(head, &oid);
|
|
|
|
|
|
|
|
if (res_head == MISSING_OBJECT) {
|
|
|
|
head = "HEAD";
|
|
|
|
res_head = get_oid(head, &oid);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res_head)
|
|
|
|
error(_("Bad rev input: %s"), head);
|
|
|
|
oid_array_append(&revs, &oid);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* All input revs must be checked before executing bisect_write()
|
|
|
|
* to discard junk revs.
|
|
|
|
*/
|
|
|
|
|
|
|
|
for (; argc; argc--, argv++) {
|
2021-03-16 16:15:55 +01:00
|
|
|
struct commit *commit;
|
|
|
|
|
2020-10-15 15:38:35 +02:00
|
|
|
if (get_oid(*argv, &oid)){
|
|
|
|
error(_("Bad rev input: %s"), *argv);
|
|
|
|
oid_array_clear(&revs);
|
|
|
|
return BISECT_FAILED;
|
|
|
|
}
|
2021-03-16 16:15:55 +01:00
|
|
|
|
|
|
|
commit = lookup_commit_reference(the_repository, &oid);
|
|
|
|
if (!commit)
|
|
|
|
die(_("Bad rev input (not a commit): %s"), *argv);
|
|
|
|
|
|
|
|
oid_array_append(&revs, &commit->object.oid);
|
2020-10-15 15:38:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (strbuf_read_file(&buf, git_path_bisect_expected_rev(), 0) < the_hash_algo->hexsz ||
|
|
|
|
get_oid_hex(buf.buf, &expected) < 0)
|
|
|
|
verify_expected = 0; /* Ignore invalid file contents */
|
|
|
|
strbuf_release(&buf);
|
|
|
|
|
|
|
|
for (i = 0; i < revs.nr; i++) {
|
|
|
|
if (bisect_write(state, oid_to_hex(&revs.oid[i]), terms, 0)) {
|
|
|
|
oid_array_clear(&revs);
|
|
|
|
return BISECT_FAILED;
|
|
|
|
}
|
|
|
|
if (verify_expected && !oideq(&revs.oid[i], &expected)) {
|
|
|
|
unlink_or_warn(git_path_bisect_ancestors_ok());
|
|
|
|
unlink_or_warn(git_path_bisect_expected_rev());
|
|
|
|
verify_expected = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
oid_array_clear(&revs);
|
|
|
|
return bisect_auto_next(terms, NULL);
|
|
|
|
}
|
|
|
|
|
2021-02-03 22:54:32 +01:00
|
|
|
static enum bisect_error bisect_log(void)
|
|
|
|
{
|
|
|
|
int fd, status;
|
|
|
|
const char* filename = git_path_bisect_log();
|
|
|
|
|
|
|
|
if (is_empty_or_missing_file(filename))
|
|
|
|
return error(_("We are not bisecting."));
|
|
|
|
|
|
|
|
fd = open(filename, O_RDONLY);
|
|
|
|
if (fd < 0)
|
|
|
|
return BISECT_FAILED;
|
|
|
|
|
|
|
|
status = copy_fd(fd, STDOUT_FILENO);
|
|
|
|
close(fd);
|
|
|
|
return status ? BISECT_FAILED : BISECT_OK;
|
|
|
|
}
|
|
|
|
|
2021-02-03 22:54:33 +01:00
|
|
|
static int process_replay_line(struct bisect_terms *terms, struct strbuf *line)
|
|
|
|
{
|
|
|
|
const char *p = line->buf + strspn(line->buf, " \t");
|
|
|
|
char *word_end, *rev;
|
|
|
|
|
|
|
|
if ((!skip_prefix(p, "git bisect", &p) &&
|
|
|
|
!skip_prefix(p, "git-bisect", &p)) || !isspace(*p))
|
|
|
|
return 0;
|
|
|
|
p += strspn(p, " \t");
|
|
|
|
|
|
|
|
word_end = (char *)p + strcspn(p, " \t");
|
|
|
|
rev = word_end + strspn(word_end, " \t");
|
|
|
|
*word_end = '\0'; /* NUL-terminate the word */
|
|
|
|
|
|
|
|
get_terms(terms);
|
|
|
|
if (check_and_set_terms(terms, p))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!strcmp(p, "start")) {
|
|
|
|
struct strvec argv = STRVEC_INIT;
|
|
|
|
int res;
|
|
|
|
sq_dequote_to_strvec(rev, &argv);
|
|
|
|
res = bisect_start(terms, argv.v, argv.nr);
|
|
|
|
strvec_clear(&argv);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (one_of(p, terms->term_good,
|
|
|
|
terms->term_bad, "skip", NULL))
|
|
|
|
return bisect_write(p, rev, terms, 0);
|
|
|
|
|
|
|
|
if (!strcmp(p, "terms")) {
|
|
|
|
struct strvec argv = STRVEC_INIT;
|
|
|
|
int res;
|
|
|
|
sq_dequote_to_strvec(rev, &argv);
|
|
|
|
res = bisect_terms(terms, argv.nr == 1 ? argv.v[0] : NULL);
|
|
|
|
strvec_clear(&argv);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
error(_("'%s'?? what are you talking about?"), p);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static enum bisect_error bisect_replay(struct bisect_terms *terms, const char *filename)
|
|
|
|
{
|
|
|
|
FILE *fp = NULL;
|
|
|
|
enum bisect_error res = BISECT_OK;
|
|
|
|
struct strbuf line = STRBUF_INIT;
|
|
|
|
|
|
|
|
if (is_empty_or_missing_file(filename))
|
|
|
|
return error(_("cannot read file '%s' for replaying"), filename);
|
|
|
|
|
|
|
|
if (bisect_reset(NULL))
|
|
|
|
return BISECT_FAILED;
|
|
|
|
|
|
|
|
fp = fopen(filename, "r");
|
|
|
|
if (!fp)
|
|
|
|
return BISECT_FAILED;
|
|
|
|
|
|
|
|
while ((strbuf_getline(&line, fp) != EOF) && !res)
|
|
|
|
res = process_replay_line(terms, &line);
|
|
|
|
|
|
|
|
strbuf_release(&line);
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
if (res)
|
|
|
|
return BISECT_FAILED;
|
|
|
|
|
|
|
|
return bisect_auto_next(terms, NULL);
|
|
|
|
}
|
|
|
|
|
2021-02-03 22:54:37 +01:00
|
|
|
static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **argv, int argc)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
enum bisect_error res;
|
|
|
|
struct strvec argv_state = STRVEC_INIT;
|
|
|
|
|
|
|
|
strvec_push(&argv_state, "skip");
|
|
|
|
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
|
|
const char *dotdot = strstr(argv[i], "..");
|
|
|
|
|
|
|
|
if (dotdot) {
|
|
|
|
struct rev_info revs;
|
|
|
|
struct commit *commit;
|
|
|
|
|
|
|
|
init_revisions(&revs, NULL);
|
|
|
|
setup_revisions(2, argv + i - 1, &revs, NULL);
|
|
|
|
|
|
|
|
if (prepare_revision_walk(&revs))
|
|
|
|
die(_("revision walk setup failed\n"));
|
|
|
|
while ((commit = get_revision(&revs)) != NULL)
|
|
|
|
strvec_push(&argv_state,
|
|
|
|
oid_to_hex(&commit->object.oid));
|
|
|
|
|
|
|
|
reset_revision_walk();
|
2022-04-13 22:01:36 +02:00
|
|
|
release_revisions(&revs);
|
2021-02-03 22:54:37 +01:00
|
|
|
} else {
|
|
|
|
strvec_push(&argv_state, argv[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
res = bisect_state(terms, argv_state.v, argv_state.nr);
|
|
|
|
|
|
|
|
strvec_clear(&argv_state);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2021-09-13 19:39:02 +02:00
|
|
|
static int bisect_visualize(struct bisect_terms *terms, const char **argv, int argc)
|
|
|
|
{
|
|
|
|
struct strvec args = STRVEC_INIT;
|
|
|
|
int flags = RUN_COMMAND_NO_STDIN, res = 0;
|
|
|
|
struct strbuf sb = STRBUF_INIT;
|
|
|
|
|
|
|
|
if (bisect_next_check(terms, NULL) != 0)
|
|
|
|
return BISECT_FAILED;
|
|
|
|
|
|
|
|
if (!argc) {
|
|
|
|
if ((getenv("DISPLAY") || getenv("SESSIONNAME") || getenv("MSYSTEM") ||
|
|
|
|
getenv("SECURITYSESSIONID")) && exists_in_PATH("gitk")) {
|
|
|
|
strvec_push(&args, "gitk");
|
|
|
|
} else {
|
|
|
|
strvec_push(&args, "log");
|
|
|
|
flags |= RUN_GIT_CMD;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (argv[0][0] == '-') {
|
|
|
|
strvec_push(&args, "log");
|
|
|
|
flags |= RUN_GIT_CMD;
|
|
|
|
} else if (strcmp(argv[0], "tig") && !starts_with(argv[0], "git"))
|
|
|
|
flags |= RUN_GIT_CMD;
|
|
|
|
|
|
|
|
strvec_pushv(&args, argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
strvec_pushl(&args, "--bisect", "--", NULL);
|
|
|
|
|
|
|
|
strbuf_read_file(&sb, git_path_bisect_names(), 0);
|
|
|
|
sq_dequote_to_strvec(sb.buf, &args);
|
|
|
|
strbuf_release(&sb);
|
|
|
|
|
|
|
|
res = run_command_v_opt(args.v, flags);
|
|
|
|
strvec_clear(&args);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2022-08-25 19:09:48 +02:00
|
|
|
static int get_first_good(const char *refname UNUSED,
|
2022-08-19 12:08:32 +02:00
|
|
|
const struct object_id *oid,
|
2022-08-25 19:09:48 +02:00
|
|
|
int flag UNUSED, void *cb_data)
|
bisect--helper: double-check run command on exit code 126 and 127
When a run command cannot be executed or found, shells return exit code
126 or 127, respectively. Valid run commands are allowed to return
these codes as well to indicate bad revisions, though, for historical
reasons. This means typos can cause bogus bisect runs that go over the
full distance and end up reporting invalid results.
The best solution would be to reserve exit codes 126 and 127, like
71b0251cdd (Bisect run: "skip" current commit if script exit code is
125., 2007-10-26) did for 125, and abort bisect run when we get them.
That might be inconvenient for those who relied on the documentation
stating that 126 and 127 can be used for bad revisions, though.
The workaround used by this patch is to run the command on a known-good
revision and abort if we still get the same error code. This adds one
step to runs with scripts that use exit codes 126 and 127, but keeps
them supported, with one exception: It won't work with commands that
cannot recognize the (manually marked) known-good revision as such.
Run commands that use low exit codes are unaffected. Typos are reported
after executing the missing command twice and three checkouts (the first
step, the known good revision and back to the revision of the first
step).
Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-18 13:46:32 +01:00
|
|
|
{
|
|
|
|
oidcpy(cb_data, oid);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int verify_good(const struct bisect_terms *terms,
|
|
|
|
const char **quoted_argv)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
enum bisect_error res;
|
|
|
|
struct object_id good_rev;
|
|
|
|
struct object_id current_rev;
|
|
|
|
char *good_glob = xstrfmt("%s-*", terms->term_good);
|
|
|
|
int no_checkout = ref_exists("BISECT_HEAD");
|
|
|
|
|
|
|
|
for_each_glob_ref_in(get_first_good, good_glob, "refs/bisect/",
|
|
|
|
&good_rev);
|
|
|
|
free(good_glob);
|
|
|
|
|
|
|
|
if (read_ref(no_checkout ? "BISECT_HEAD" : "HEAD", ¤t_rev))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
res = bisect_checkout(&good_rev, no_checkout);
|
|
|
|
if (res != BISECT_OK)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
printf(_("running %s\n"), quoted_argv[0]);
|
|
|
|
rc = run_command_v_opt(quoted_argv, RUN_USING_SHELL);
|
|
|
|
|
|
|
|
res = bisect_checkout(¤t_rev, no_checkout);
|
|
|
|
if (res != BISECT_OK)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2021-09-13 19:39:03 +02:00
|
|
|
static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
|
|
|
|
{
|
|
|
|
int res = BISECT_OK;
|
|
|
|
struct strbuf command = STRBUF_INIT;
|
|
|
|
struct strvec run_args = STRVEC_INIT;
|
|
|
|
const char *new_state;
|
|
|
|
int temporary_stdout_fd, saved_stdout;
|
bisect--helper: double-check run command on exit code 126 and 127
When a run command cannot be executed or found, shells return exit code
126 or 127, respectively. Valid run commands are allowed to return
these codes as well to indicate bad revisions, though, for historical
reasons. This means typos can cause bogus bisect runs that go over the
full distance and end up reporting invalid results.
The best solution would be to reserve exit codes 126 and 127, like
71b0251cdd (Bisect run: "skip" current commit if script exit code is
125., 2007-10-26) did for 125, and abort bisect run when we get them.
That might be inconvenient for those who relied on the documentation
stating that 126 and 127 can be used for bad revisions, though.
The workaround used by this patch is to run the command on a known-good
revision and abort if we still get the same error code. This adds one
step to runs with scripts that use exit codes 126 and 127, but keeps
them supported, with one exception: It won't work with commands that
cannot recognize the (manually marked) known-good revision as such.
Run commands that use low exit codes are unaffected. Typos are reported
after executing the missing command twice and three checkouts (the first
step, the known good revision and back to the revision of the first
step).
Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-18 13:46:32 +01:00
|
|
|
int is_first_run = 1;
|
2021-09-13 19:39:03 +02:00
|
|
|
|
|
|
|
if (bisect_next_check(terms, NULL))
|
|
|
|
return BISECT_FAILED;
|
|
|
|
|
|
|
|
if (argc)
|
|
|
|
sq_quote_argv(&command, argv);
|
|
|
|
else {
|
|
|
|
error(_("bisect run failed: no command provided."));
|
|
|
|
return BISECT_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
strvec_push(&run_args, command.buf);
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
printf(_("running %s\n"), command.buf);
|
|
|
|
res = run_command_v_opt(run_args.v, RUN_USING_SHELL);
|
|
|
|
|
bisect--helper: double-check run command on exit code 126 and 127
When a run command cannot be executed or found, shells return exit code
126 or 127, respectively. Valid run commands are allowed to return
these codes as well to indicate bad revisions, though, for historical
reasons. This means typos can cause bogus bisect runs that go over the
full distance and end up reporting invalid results.
The best solution would be to reserve exit codes 126 and 127, like
71b0251cdd (Bisect run: "skip" current commit if script exit code is
125., 2007-10-26) did for 125, and abort bisect run when we get them.
That might be inconvenient for those who relied on the documentation
stating that 126 and 127 can be used for bad revisions, though.
The workaround used by this patch is to run the command on a known-good
revision and abort if we still get the same error code. This adds one
step to runs with scripts that use exit codes 126 and 127, but keeps
them supported, with one exception: It won't work with commands that
cannot recognize the (manually marked) known-good revision as such.
Run commands that use low exit codes are unaffected. Typos are reported
after executing the missing command twice and three checkouts (the first
step, the known good revision and back to the revision of the first
step).
Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-18 13:46:32 +01:00
|
|
|
/*
|
|
|
|
* Exit code 126 and 127 can either come from the shell
|
|
|
|
* if it was unable to execute or even find the script,
|
|
|
|
* or from the script itself. Check with a known-good
|
|
|
|
* revision to avoid trashing the bisect run due to a
|
|
|
|
* missing or non-executable script.
|
|
|
|
*/
|
|
|
|
if (is_first_run && (res == 126 || res == 127)) {
|
|
|
|
int rc = verify_good(terms, run_args.v);
|
|
|
|
is_first_run = 0;
|
|
|
|
if (rc < 0) {
|
|
|
|
error(_("unable to verify '%s' on good"
|
|
|
|
" revision"), command.buf);
|
|
|
|
res = BISECT_FAILED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (rc == res) {
|
|
|
|
error(_("bogus exit code %d for good revision"),
|
|
|
|
rc);
|
|
|
|
res = BISECT_FAILED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-13 19:39:03 +02:00
|
|
|
if (res < 0 || 128 <= res) {
|
|
|
|
error(_("bisect run failed: exit code %d from"
|
|
|
|
" '%s' is < 0 or >= 128"), res, command.buf);
|
2022-01-18 13:46:15 +01:00
|
|
|
break;
|
2021-09-13 19:39:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (res == 125)
|
|
|
|
new_state = "skip";
|
|
|
|
else if (!res)
|
|
|
|
new_state = terms->term_good;
|
|
|
|
else
|
|
|
|
new_state = terms->term_bad;
|
|
|
|
|
|
|
|
temporary_stdout_fd = open(git_path_bisect_run(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
|
|
|
|
|
2022-01-18 13:46:15 +01:00
|
|
|
if (temporary_stdout_fd < 0) {
|
|
|
|
res = error_errno(_("cannot open file '%s' for writing"), git_path_bisect_run());
|
|
|
|
break;
|
|
|
|
}
|
2021-09-13 19:39:03 +02:00
|
|
|
|
|
|
|
fflush(stdout);
|
|
|
|
saved_stdout = dup(1);
|
|
|
|
dup2(temporary_stdout_fd, 1);
|
|
|
|
|
|
|
|
res = bisect_state(terms, &new_state, 1);
|
|
|
|
|
|
|
|
fflush(stdout);
|
|
|
|
dup2(saved_stdout, 1);
|
|
|
|
close(saved_stdout);
|
|
|
|
close(temporary_stdout_fd);
|
|
|
|
|
|
|
|
print_file_to_stdout(git_path_bisect_run());
|
|
|
|
|
|
|
|
if (res == BISECT_ONLY_SKIPPED_LEFT)
|
|
|
|
error(_("bisect run cannot continue any more"));
|
|
|
|
else if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) {
|
|
|
|
printf(_("bisect run success"));
|
|
|
|
res = BISECT_OK;
|
|
|
|
} else if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {
|
|
|
|
printf(_("bisect found first bad commit"));
|
|
|
|
res = BISECT_OK;
|
|
|
|
} else if (res) {
|
2021-10-01 13:47:45 +02:00
|
|
|
error(_("bisect run failed: 'git bisect--helper --bisect-state"
|
2022-01-18 13:45:57 +01:00
|
|
|
" %s' exited with error code %d"), new_state, res);
|
2021-09-13 19:39:03 +02:00
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
2022-01-18 13:46:15 +01:00
|
|
|
break;
|
2021-09-13 19:39:03 +02:00
|
|
|
}
|
2022-01-18 13:46:15 +01:00
|
|
|
|
|
|
|
strbuf_release(&command);
|
|
|
|
strvec_clear(&run_args);
|
|
|
|
return res;
|
2021-09-13 19:39:03 +02:00
|
|
|
}
|
|
|
|
|
2009-03-26 05:55:54 +01:00
|
|
|
int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
|
|
|
|
{
|
2017-09-29 08:49:39 +02:00
|
|
|
enum {
|
2020-10-15 15:38:37 +02:00
|
|
|
BISECT_RESET = 1,
|
2019-01-02 16:38:35 +01:00
|
|
|
BISECT_NEXT_CHECK,
|
2019-01-02 16:38:36 +01:00
|
|
|
BISECT_TERMS,
|
2020-09-24 14:33:38 +02:00
|
|
|
BISECT_START,
|
|
|
|
BISECT_AUTOSTART,
|
2020-09-24 14:33:40 +02:00
|
|
|
BISECT_NEXT,
|
2021-02-03 22:54:32 +01:00
|
|
|
BISECT_STATE,
|
2021-02-03 22:54:33 +01:00
|
|
|
BISECT_LOG,
|
2021-02-03 22:54:37 +01:00
|
|
|
BISECT_REPLAY,
|
2021-09-13 19:39:02 +02:00
|
|
|
BISECT_SKIP,
|
|
|
|
BISECT_VISUALIZE,
|
2021-09-13 19:39:03 +02:00
|
|
|
BISECT_RUN,
|
2017-09-29 08:49:39 +02:00
|
|
|
} cmdmode = 0;
|
2020-08-07 23:58:36 +02:00
|
|
|
int res = 0, nolog = 0;
|
2009-03-26 05:55:54 +01:00
|
|
|
struct option options[] = {
|
2019-01-02 16:38:31 +01:00
|
|
|
OPT_CMDMODE(0, "bisect-reset", &cmdmode,
|
|
|
|
N_("reset the bisection state"), BISECT_RESET),
|
2019-01-02 16:38:34 +01:00
|
|
|
OPT_CMDMODE(0, "bisect-next-check", &cmdmode,
|
|
|
|
N_("check whether bad or good terms exist"), BISECT_NEXT_CHECK),
|
2019-01-02 16:38:35 +01:00
|
|
|
OPT_CMDMODE(0, "bisect-terms", &cmdmode,
|
|
|
|
N_("print out the bisect terms"), BISECT_TERMS),
|
2019-01-02 16:38:36 +01:00
|
|
|
OPT_CMDMODE(0, "bisect-start", &cmdmode,
|
|
|
|
N_("start the bisect session"), BISECT_START),
|
2020-09-24 14:33:40 +02:00
|
|
|
OPT_CMDMODE(0, "bisect-next", &cmdmode,
|
|
|
|
N_("find the next bisection commit"), BISECT_NEXT),
|
2020-10-15 15:38:35 +02:00
|
|
|
OPT_CMDMODE(0, "bisect-state", &cmdmode,
|
|
|
|
N_("mark the state of ref (or refs)"), BISECT_STATE),
|
2021-02-03 22:54:32 +01:00
|
|
|
OPT_CMDMODE(0, "bisect-log", &cmdmode,
|
|
|
|
N_("list the bisection steps so far"), BISECT_LOG),
|
2021-02-03 22:54:33 +01:00
|
|
|
OPT_CMDMODE(0, "bisect-replay", &cmdmode,
|
|
|
|
N_("replay the bisection process from the given file"), BISECT_REPLAY),
|
2021-02-03 22:54:37 +01:00
|
|
|
OPT_CMDMODE(0, "bisect-skip", &cmdmode,
|
|
|
|
N_("skip some commits for checkout"), BISECT_SKIP),
|
2021-09-13 19:39:02 +02:00
|
|
|
OPT_CMDMODE(0, "bisect-visualize", &cmdmode,
|
|
|
|
N_("visualize the bisection"), BISECT_VISUALIZE),
|
2021-09-13 19:39:03 +02:00
|
|
|
OPT_CMDMODE(0, "bisect-run", &cmdmode,
|
2022-02-23 15:27:34 +01:00
|
|
|
N_("use <cmd>... to automatically bisect"), BISECT_RUN),
|
2019-01-02 16:38:32 +01:00
|
|
|
OPT_BOOL(0, "no-log", &nolog,
|
2019-02-11 07:44:53 +01:00
|
|
|
N_("no log for BISECT_WRITE")),
|
2009-03-26 05:55:54 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
2019-01-02 16:38:32 +01:00
|
|
|
struct bisect_terms terms = { .term_good = NULL, .term_bad = NULL };
|
2009-03-26 05:55:54 +01:00
|
|
|
|
2009-05-23 20:53:12 +02:00
|
|
|
argc = parse_options(argc, argv, prefix, options,
|
2019-01-02 16:38:36 +01:00
|
|
|
git_bisect_helper_usage,
|
2022-08-19 18:03:57 +02:00
|
|
|
PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_UNKNOWN_OPT);
|
2009-03-26 05:55:54 +01:00
|
|
|
|
2017-09-29 08:49:39 +02:00
|
|
|
if (!cmdmode)
|
2009-03-26 05:55:54 +01:00
|
|
|
usage_with_options(git_bisect_helper_usage, options);
|
|
|
|
|
2017-09-29 08:49:39 +02:00
|
|
|
switch (cmdmode) {
|
2019-01-02 16:38:31 +01:00
|
|
|
case BISECT_RESET:
|
|
|
|
if (argc > 1)
|
|
|
|
return error(_("--bisect-reset requires either no argument or a commit"));
|
2021-02-03 22:54:35 +01:00
|
|
|
res = bisect_reset(argc ? argv[0] : NULL);
|
|
|
|
break;
|
2019-01-02 16:38:35 +01:00
|
|
|
case BISECT_TERMS:
|
|
|
|
if (argc > 1)
|
|
|
|
return error(_("--bisect-terms requires 0 or 1 argument"));
|
|
|
|
res = bisect_terms(&terms, argc == 1 ? argv[0] : NULL);
|
|
|
|
break;
|
2019-01-02 16:38:36 +01:00
|
|
|
case BISECT_START:
|
|
|
|
set_terms(&terms, "bad", "good");
|
2020-08-07 23:58:36 +02:00
|
|
|
res = bisect_start(&terms, argv, argc);
|
2019-01-02 16:38:36 +01:00
|
|
|
break;
|
2020-09-24 14:33:40 +02:00
|
|
|
case BISECT_NEXT:
|
|
|
|
if (argc)
|
|
|
|
return error(_("--bisect-next requires 0 arguments"));
|
|
|
|
get_terms(&terms);
|
|
|
|
res = bisect_next(&terms, prefix);
|
|
|
|
break;
|
2020-10-15 15:38:35 +02:00
|
|
|
case BISECT_STATE:
|
2020-09-24 14:33:38 +02:00
|
|
|
set_terms(&terms, "bad", "good");
|
2020-10-15 15:38:35 +02:00
|
|
|
get_terms(&terms);
|
|
|
|
res = bisect_state(&terms, argv, argc);
|
2020-09-24 14:33:38 +02:00
|
|
|
break;
|
2021-02-03 22:54:32 +01:00
|
|
|
case BISECT_LOG:
|
|
|
|
if (argc)
|
|
|
|
return error(_("--bisect-log requires 0 arguments"));
|
|
|
|
res = bisect_log();
|
|
|
|
break;
|
2021-02-03 22:54:33 +01:00
|
|
|
case BISECT_REPLAY:
|
|
|
|
if (argc != 1)
|
|
|
|
return error(_("no logfile given"));
|
|
|
|
set_terms(&terms, "bad", "good");
|
|
|
|
res = bisect_replay(&terms, argv[0]);
|
|
|
|
break;
|
2021-02-03 22:54:37 +01:00
|
|
|
case BISECT_SKIP:
|
|
|
|
set_terms(&terms, "bad", "good");
|
2021-04-29 22:15:15 +02:00
|
|
|
get_terms(&terms);
|
2021-02-03 22:54:37 +01:00
|
|
|
res = bisect_skip(&terms, argv, argc);
|
|
|
|
break;
|
2021-09-13 19:39:02 +02:00
|
|
|
case BISECT_VISUALIZE:
|
|
|
|
get_terms(&terms);
|
|
|
|
res = bisect_visualize(&terms, argv, argc);
|
|
|
|
break;
|
2021-09-13 19:39:03 +02:00
|
|
|
case BISECT_RUN:
|
|
|
|
if (!argc)
|
|
|
|
return error(_("bisect run failed: no command provided."));
|
|
|
|
get_terms(&terms);
|
|
|
|
res = bisect_run(&terms, argv, argc);
|
|
|
|
break;
|
2017-09-29 08:49:39 +02:00
|
|
|
default:
|
2020-08-28 14:46:05 +02:00
|
|
|
BUG("unknown subcommand %d", cmdmode);
|
2017-09-29 08:49:39 +02:00
|
|
|
}
|
2019-01-02 16:38:32 +01:00
|
|
|
free_terms(&terms);
|
2020-02-17 09:40:37 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle early success
|
|
|
|
* From check_merge_bases > check_good_are_ancestors_of_bad > bisect_next_all
|
|
|
|
*/
|
2020-09-24 14:33:40 +02:00
|
|
|
if ((res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) || (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND))
|
2020-02-17 09:40:37 +01:00
|
|
|
res = BISECT_OK;
|
|
|
|
|
2020-08-28 14:46:06 +02:00
|
|
|
return -res;
|
2009-03-26 05:55:54 +01:00
|
|
|
}
|