Merge branch 'mr/bisect-in-c-2' into pu
Rewrite of the remainder of "git bisect" script in C continues. * mr/bisect-in-c-2: bisect--helper: retire `--bisect-autostart` subcommand bisect--helper: retire `--write-terms` subcommand bisect--helper: retire `--check-expected-revs` subcommand bisect--helper: reimplement `bisect_state` & `bisect_head` shell functions in C bisect--helper: retire `--next-all` subcommand bisect--helper: retire `--bisect-clean-state` subcommand bisect--helper: finish porting `bisect_start()` to C bisect--helper: reimplement `bisect_next` and `bisect_auto_next` shell functions in C bisect--helper: reimplement `bisect_autostart` shell function in C bisect--helper: introduce new `write_in_file()` function bisect--helper: use '-res' in 'cmd_bisect__helper' return bisect--helper: fix `cmd_*()` function switch default return
This commit is contained in:
8
bisect.c
8
bisect.c
@ -980,6 +980,12 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
|
||||
* the bisection process finished successfully.
|
||||
* In this case the calling function or command should not turn a
|
||||
* BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND return code into an error or a non zero exit code.
|
||||
*
|
||||
* Checking BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND
|
||||
* in bisect_helper::bisect_next() and only transforming it to 0 at
|
||||
* the end of bisect_helper::cmd_bisect__helper() helps bypassing
|
||||
* all the code related to finding a commit to test.
|
||||
*
|
||||
* If no_checkout is non-zero, the bisection process does not
|
||||
* checkout the trial commit but instead simply updates BISECT_HEAD.
|
||||
*/
|
||||
@ -1064,6 +1070,8 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix, int
|
||||
"Bisecting: %d revisions left to test after this %s\n",
|
||||
nr), nr, steps_msg);
|
||||
free(steps_msg);
|
||||
/* Clean up objects used, as they will be reused. */
|
||||
clear_commit_marks_all(ALL_REV_FLAGS);
|
||||
|
||||
return bisect_checkout(bisect_rev, no_checkout);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "run-command.h"
|
||||
#include "prompt.h"
|
||||
#include "quote.h"
|
||||
#include "revision.h"
|
||||
|
||||
static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
|
||||
static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
|
||||
@ -19,9 +20,6 @@ static GIT_PATH_FUNC(git_path_head_name, "head-name")
|
||||
static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
|
||||
|
||||
static const char * const git_bisect_helper_usage[] = {
|
||||
N_("git bisect--helper --next-all [--no-checkout]"),
|
||||
N_("git bisect--helper --write-terms <bad_term> <good_term>"),
|
||||
N_("git bisect--helper --bisect-clean-state"),
|
||||
N_("git bisect--helper --bisect-reset [<commit>]"),
|
||||
N_("git bisect--helper --bisect-write [--no-log] <state> <revision> <good_term> <bad_term>"),
|
||||
N_("git bisect--helper --bisect-check-and-set-terms <command> <good_term> <bad_term>"),
|
||||
@ -29,6 +27,10 @@ static const char * const git_bisect_helper_usage[] = {
|
||||
N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"),
|
||||
N_("git bisect--helper --bisect-start [--term-{old,good}=<term> --term-{new,bad}=<term>]"
|
||||
"[--no-checkout] [<bad> [<good>...]] [--] [<paths>...]"),
|
||||
N_("git bisect--helper --bisect-next"),
|
||||
N_("git bisect--helper --bisect-auto-next"),
|
||||
N_("git bisect--helper --bisect-state (bad|new) [<rev>]"),
|
||||
N_("git bisect--helper --bisect-state (good|old) [<rev>...]"),
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -55,6 +57,8 @@ static void set_terms(struct bisect_terms *terms, const char *bad,
|
||||
static const char vocab_bad[] = "bad|new";
|
||||
static const char vocab_good[] = "good|old";
|
||||
|
||||
static int bisect_autostart(struct bisect_terms *terms);
|
||||
|
||||
/*
|
||||
* Check whether the string `term` belongs to the set of strings
|
||||
* included in the variable arguments.
|
||||
@ -74,6 +78,28 @@ static int one_of(const char *term, ...)
|
||||
return res;
|
||||
}
|
||||
|
||||
static int write_in_file(const char *path, const char *mode, const char *format,...)
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
va_list args;
|
||||
int res = 0;
|
||||
|
||||
if (!strcmp(mode, "a") && !strcmp(mode, "w"))
|
||||
return error(_("wrong writing mode '%s'"), mode);
|
||||
fp = fopen(path, mode);
|
||||
if (!fp)
|
||||
return error_errno(_("cannot open file '%s' in mode '%s'"), path, mode);
|
||||
va_start(args, format);
|
||||
res = vfprintf(fp, format, args);
|
||||
va_end(args);
|
||||
if (!res) {
|
||||
fclose(fp);
|
||||
return error_errno(_("could not write to file '%s'"), path);
|
||||
}
|
||||
|
||||
return fclose(fp);
|
||||
}
|
||||
|
||||
static int check_term_format(const char *term, const char *orig_term)
|
||||
{
|
||||
int res;
|
||||
@ -104,7 +130,6 @@ static int check_term_format(const char *term, const char *orig_term)
|
||||
|
||||
static int write_terms(const char *bad, const char *good)
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
int res;
|
||||
|
||||
if (!strcmp(bad, good))
|
||||
@ -113,12 +138,8 @@ static int write_terms(const char *bad, const char *good)
|
||||
if (check_term_format(bad, "bad") || check_term_format(good, "good"))
|
||||
return -1;
|
||||
|
||||
fp = fopen(git_path_bisect_terms(), "w");
|
||||
if (!fp)
|
||||
return error_errno(_("could not open the file BISECT_TERMS"));
|
||||
res = write_in_file(git_path_bisect_terms(), "w", "%s\n%s\n", bad, good);
|
||||
|
||||
res = fprintf(fp, "%s\n%s\n", bad, good);
|
||||
res |= fclose(fp);
|
||||
return (res < 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
@ -421,11 +442,156 @@ finish:
|
||||
return res;
|
||||
}
|
||||
|
||||
static int bisect_start(struct bisect_terms *terms, int no_checkout,
|
||||
static int register_good_ref(const char *refname,
|
||||
const struct object_id *oid, int flags,
|
||||
void *cb_data)
|
||||
{
|
||||
struct argv_array *rev_argv = cb_data;
|
||||
|
||||
argv_array_push(rev_argv, oid_to_hex(oid));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void prepare_rev_argv(struct bisect_terms *terms, struct argv_array *rev_argv)
|
||||
{
|
||||
char *term_good = xstrfmt("%s-*", terms->term_good);
|
||||
|
||||
argv_array_pushl(rev_argv, "skipped_commits", "refs/bisect/bad", "--not", NULL);
|
||||
for_each_glob_ref_in(register_good_ref, term_good, "refs/bisect/", rev_argv);
|
||||
|
||||
free(term_good);
|
||||
}
|
||||
|
||||
static int prepare_revs(struct bisect_terms *terms, struct rev_info *revs)
|
||||
{
|
||||
int res = 0;
|
||||
struct argv_array rev_argv = ARGV_ARRAY_INIT;
|
||||
|
||||
prepare_rev_argv(terms, &rev_argv);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
rev_argv.argc = setup_revisions(rev_argv.argc, rev_argv.argv, revs, NULL);
|
||||
if (prepare_revision_walk(revs))
|
||||
res = error(_("revision walk setup failed\n"));
|
||||
|
||||
argv_array_clear(&rev_argv);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int process_skipped_commits(FILE *fp, struct bisect_terms *terms, struct rev_info *revs)
|
||||
{
|
||||
struct commit *commit;
|
||||
struct pretty_print_context pp = {0};
|
||||
|
||||
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) {
|
||||
struct strbuf commit_name = STRBUF_INIT;
|
||||
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);
|
||||
strbuf_release(&commit_name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the flags used by revision walks in case
|
||||
* there is another revision walk after this one.
|
||||
*/
|
||||
reset_revision_walk();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bisect_skipped_commits(struct bisect_terms *terms)
|
||||
{
|
||||
int res = 0;
|
||||
FILE *fp = NULL;
|
||||
struct rev_info revs;
|
||||
|
||||
fp = fopen(git_path_bisect_log(), "a");
|
||||
if (!fp)
|
||||
return error_errno(_("could not open '%s' for appending"),
|
||||
git_path_bisect_log());
|
||||
|
||||
res = prepare_revs(terms, &revs);
|
||||
|
||||
if (!res)
|
||||
res = process_skipped_commits(fp, terms, &revs);
|
||||
|
||||
fclose(fp);
|
||||
return res;
|
||||
}
|
||||
|
||||
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);
|
||||
printf("%s\n", bad_ref);
|
||||
commit = lookup_commit_reference(the_repository, &oid);
|
||||
format_commit_message(commit, "%s", &commit_name, &pp);
|
||||
|
||||
res = write_in_file(git_path_bisect_log(), "a", "# first %s commit: [%s] %s\n",
|
||||
terms->term_bad, oid_to_hex(&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)
|
||||
{
|
||||
int no_checkout;
|
||||
enum bisect_error res;
|
||||
|
||||
bisect_autostart(terms);
|
||||
if (bisect_next_check(terms, terms->term_good))
|
||||
return BISECT_FAILED;
|
||||
|
||||
no_checkout = file_exists(git_path_bisect_head());
|
||||
|
||||
/* Perform all bisection computation, display and checkout */
|
||||
res = bisect_next_all(the_repository, prefix, no_checkout);
|
||||
|
||||
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)
|
||||
{
|
||||
if (bisect_next_check(terms, NULL))
|
||||
return BISECT_OK;
|
||||
|
||||
return bisect_next(terms, prefix);
|
||||
}
|
||||
|
||||
static enum bisect_error bisect_start(struct bisect_terms *terms, int no_checkout,
|
||||
const char **argv, int argc)
|
||||
{
|
||||
int i, has_double_dash = 0, must_write_terms = 0, bad_seen = 0;
|
||||
int flags, pathspec_pos, res = 0;
|
||||
int flags, pathspec_pos;
|
||||
enum bisect_error res = BISECT_OK;
|
||||
struct string_list revs = STRING_LIST_INIT_DUP;
|
||||
struct string_list states = STRING_LIST_INIT_DUP;
|
||||
struct strbuf start_head = STRBUF_INIT;
|
||||
@ -483,9 +649,12 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
|
||||
return error(_("unrecognized option: '%s'"), arg);
|
||||
} else {
|
||||
char *commit_id = xstrfmt("%s^{commit}", arg);
|
||||
if (get_oid(commit_id, &oid) && has_double_dash)
|
||||
die(_("'%s' does not appear to be a valid "
|
||||
"revision"), arg);
|
||||
if (get_oid(commit_id, &oid) && has_double_dash) {
|
||||
error(_("'%s' does not appear to be a valid "
|
||||
"revision"), arg);
|
||||
free(commit_id);
|
||||
return BISECT_FAILED;
|
||||
}
|
||||
|
||||
string_list_append(&revs, oid_to_hex(&oid));
|
||||
free(commit_id);
|
||||
@ -563,12 +732,12 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
|
||||
* Get rid of any old bisect state.
|
||||
*/
|
||||
if (bisect_clean_state())
|
||||
return -1;
|
||||
return BISECT_FAILED;
|
||||
|
||||
/*
|
||||
* In case of mistaken revs or checkout error, or signals received,
|
||||
* In case of mistaken revs or checkout error,
|
||||
* "bisect_auto_next" below may exit or misbehave.
|
||||
* We have to trap this to be able to clean up using
|
||||
* We have to handle this to be able to clean up using
|
||||
* "bisect_clean_state".
|
||||
*/
|
||||
|
||||
@ -584,7 +753,7 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
|
||||
}
|
||||
if (update_ref(NULL, "BISECT_HEAD", &oid, NULL, 0,
|
||||
UPDATE_REFS_MSG_ON_ERR)) {
|
||||
res = -1;
|
||||
res = BISECT_FAILED;
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
@ -596,52 +765,153 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
|
||||
for (i = 0; i < states.nr; i++)
|
||||
if (bisect_write(states.items[i].string,
|
||||
revs.items[i].string, terms, 1)) {
|
||||
res = -1;
|
||||
res = BISECT_FAILED;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (must_write_terms && write_terms(terms->term_bad,
|
||||
terms->term_good)) {
|
||||
res = -1;
|
||||
res = BISECT_FAILED;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
res = bisect_append_log_quoted(argv);
|
||||
if (res)
|
||||
res = -1;
|
||||
res = BISECT_FAILED;
|
||||
|
||||
finish:
|
||||
string_list_clear(&revs, 0);
|
||||
string_list_clear(&states, 0);
|
||||
strbuf_release(&start_head);
|
||||
strbuf_release(&bisect_names);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
res = bisect_auto_next(terms, NULL);
|
||||
/*
|
||||
* In case of mistaken revs or checkout error, or signals received,
|
||||
* "bisect_auto_next" below may exit or misbehave.
|
||||
* We have to handle this to be able to clean up using
|
||||
* "bisect_clean_state".
|
||||
* return code BISECT_INTERNAL_SUCCESS_MERGE_BASE is special code
|
||||
* that indicates special success.
|
||||
* -> bisect_start()
|
||||
* . res = bisect_auto_next()
|
||||
* -> bisect_auto_next()
|
||||
* . return bisect_next()
|
||||
* -> bisect_next()
|
||||
* . res = bisect_next_all()
|
||||
* -> bisect_next_all()
|
||||
* . res = check_good_are_ancestors_of_bad()
|
||||
* -> check_good_are_ancestors_of_bad()
|
||||
* . res = check_merge_bases()
|
||||
* -> check_merge_bases()
|
||||
* . res = BISECT_INTERNAL_SUCCESS_MERGE_BASE
|
||||
*/
|
||||
if (res && res != BISECT_INTERNAL_SUCCESS_MERGE_BASE)
|
||||
bisect_clean_state();
|
||||
return res;
|
||||
}
|
||||
|
||||
static int bisect_autostart(struct bisect_terms *terms)
|
||||
{
|
||||
const char *yesno;
|
||||
|
||||
if (!is_empty_or_missing_file(git_path_bisect_start()))
|
||||
return 0;
|
||||
|
||||
fprintf(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);
|
||||
if (starts_with(yesno, _("n")) || starts_with(yesno, _("N")))
|
||||
return 1;
|
||||
|
||||
return bisect_start(terms, 0, NULL, 0);
|
||||
}
|
||||
|
||||
static int bisect_head(struct object_id *oid)
|
||||
{
|
||||
if (!file_exists(git_path_bisect_head()))
|
||||
return get_oid("HEAD", oid);
|
||||
|
||||
return get_oid("BISECT_HEAD", oid);
|
||||
}
|
||||
|
||||
static enum bisect_error bisect_state(struct bisect_terms *terms, const char **argv,
|
||||
int argc)
|
||||
{
|
||||
const char *state;
|
||||
const char *hex;
|
||||
int i;
|
||||
struct oid_array revs = OID_ARRAY_INIT;
|
||||
struct object_id oid;
|
||||
|
||||
if (!argc)
|
||||
return error(_("Please call `--bisect-state` with at least one argument"));
|
||||
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 (!strcmp(state, terms->term_bad) && (argc > 1))
|
||||
return error(_("'git bisect %s' can take only one argument."),terms->term_bad);
|
||||
if (argc == 0) {
|
||||
if (bisect_head(&oid))
|
||||
return error(_("Bad bisect_head rev input"));
|
||||
hex = oid_to_hex(&oid);
|
||||
if (bisect_write(state, hex, terms, 0))
|
||||
return BISECT_FAILED;
|
||||
check_expected_revs(&hex, 1);
|
||||
return bisect_auto_next(terms, NULL);
|
||||
}
|
||||
|
||||
/* Here argc > 0 */
|
||||
for (; argc; argc--, argv++) {
|
||||
struct object_id oid;
|
||||
if (get_oid(*argv, &oid))
|
||||
return error(_("Bad rev input: %s"), *argv);
|
||||
oid_array_append(&revs, &oid);
|
||||
}
|
||||
|
||||
for (i = 0; i < revs.nr; i++) {
|
||||
hex = oid_to_hex(&revs.oid[i]);
|
||||
if (bisect_write(state, hex, terms, 0)) {
|
||||
oid_array_clear(&revs);
|
||||
return BISECT_FAILED;
|
||||
}
|
||||
check_expected_revs(&hex, 1);
|
||||
}
|
||||
|
||||
oid_array_clear(&revs);
|
||||
return bisect_auto_next(terms, NULL);
|
||||
}
|
||||
|
||||
int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
enum {
|
||||
NEXT_ALL = 1,
|
||||
WRITE_TERMS,
|
||||
BISECT_CLEAN_STATE,
|
||||
CHECK_EXPECTED_REVS,
|
||||
BISECT_RESET,
|
||||
BISECT_RESET = 1,
|
||||
BISECT_WRITE,
|
||||
CHECK_AND_SET_TERMS,
|
||||
BISECT_NEXT_CHECK,
|
||||
BISECT_TERMS,
|
||||
BISECT_START
|
||||
BISECT_START,
|
||||
BISECT_NEXT,
|
||||
BISECT_AUTO_NEXT,
|
||||
BISECT_STATE
|
||||
} cmdmode = 0;
|
||||
int no_checkout = 0, res = 0, nolog = 0;
|
||||
struct option options[] = {
|
||||
OPT_CMDMODE(0, "next-all", &cmdmode,
|
||||
N_("perform 'git bisect next'"), NEXT_ALL),
|
||||
OPT_CMDMODE(0, "write-terms", &cmdmode,
|
||||
N_("write the terms to .git/BISECT_TERMS"), WRITE_TERMS),
|
||||
OPT_CMDMODE(0, "bisect-clean-state", &cmdmode,
|
||||
N_("cleanup the bisection state"), BISECT_CLEAN_STATE),
|
||||
OPT_CMDMODE(0, "check-expected-revs", &cmdmode,
|
||||
N_("check for expected revs"), CHECK_EXPECTED_REVS),
|
||||
OPT_CMDMODE(0, "bisect-reset", &cmdmode,
|
||||
N_("reset the bisection state"), BISECT_RESET),
|
||||
OPT_CMDMODE(0, "bisect-write", &cmdmode,
|
||||
@ -654,6 +924,12 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
|
||||
N_("print out the bisect terms"), BISECT_TERMS),
|
||||
OPT_CMDMODE(0, "bisect-start", &cmdmode,
|
||||
N_("start the bisect session"), BISECT_START),
|
||||
OPT_CMDMODE(0, "bisect-next", &cmdmode,
|
||||
N_("find the next bisection commit"), BISECT_NEXT),
|
||||
OPT_CMDMODE(0, "bisect-auto-next", &cmdmode,
|
||||
N_("verify the next bisection state then checkout the next bisection commit"), BISECT_AUTO_NEXT),
|
||||
OPT_CMDMODE(0, "bisect-state", &cmdmode,
|
||||
N_("mark the state of ref (or refs)"), BISECT_STATE),
|
||||
OPT_BOOL(0, "no-checkout", &no_checkout,
|
||||
N_("update BISECT_HEAD instead of checking out the current commit")),
|
||||
OPT_BOOL(0, "no-log", &nolog,
|
||||
@ -670,20 +946,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
|
||||
usage_with_options(git_bisect_helper_usage, options);
|
||||
|
||||
switch (cmdmode) {
|
||||
case NEXT_ALL:
|
||||
res = bisect_next_all(the_repository, prefix, no_checkout);
|
||||
break;
|
||||
case WRITE_TERMS:
|
||||
if (argc != 2)
|
||||
return error(_("--write-terms requires two arguments"));
|
||||
return write_terms(argv[0], argv[1]);
|
||||
case BISECT_CLEAN_STATE:
|
||||
if (argc != 0)
|
||||
return error(_("--bisect-clean-state requires no arguments"));
|
||||
return bisect_clean_state();
|
||||
case CHECK_EXPECTED_REVS:
|
||||
check_expected_revs(argv, argc);
|
||||
return 0;
|
||||
case BISECT_RESET:
|
||||
if (argc > 1)
|
||||
return error(_("--bisect-reset requires either no argument or a commit"));
|
||||
@ -715,8 +977,25 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
|
||||
set_terms(&terms, "bad", "good");
|
||||
res = bisect_start(&terms, no_checkout, argv, argc);
|
||||
break;
|
||||
case BISECT_NEXT:
|
||||
if (argc)
|
||||
return error(_("--bisect-next requires 0 arguments"));
|
||||
get_terms(&terms);
|
||||
res = bisect_next(&terms, prefix);
|
||||
break;
|
||||
case BISECT_AUTO_NEXT:
|
||||
if (argc)
|
||||
return error(_("--bisect-auto-next requires 0 arguments"));
|
||||
get_terms(&terms);
|
||||
res = bisect_auto_next(&terms, prefix);
|
||||
break;
|
||||
case BISECT_STATE:
|
||||
set_terms(&terms, "bad", "good");
|
||||
get_terms(&terms);
|
||||
res = bisect_state(&terms, argv, argc);
|
||||
break;
|
||||
default:
|
||||
return error("BUG: unknown subcommand '%d'", cmdmode);
|
||||
BUG("unknown subcommand %d", (int)cmdmode);
|
||||
}
|
||||
free_terms(&terms);
|
||||
|
||||
@ -724,8 +1003,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
|
||||
* Handle early success
|
||||
* From check_merge_bases > check_good_are_ancestors_of_bad > bisect_next_all
|
||||
*/
|
||||
if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE)
|
||||
if ((res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) || (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND))
|
||||
res = BISECT_OK;
|
||||
|
||||
return abs(res);
|
||||
return -res;
|
||||
}
|
||||
|
145
git-bisect.sh
145
git-bisect.sh
@ -39,59 +39,6 @@ _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
|
||||
TERM_BAD=bad
|
||||
TERM_GOOD=good
|
||||
|
||||
bisect_head()
|
||||
{
|
||||
if test -f "$GIT_DIR/BISECT_HEAD"
|
||||
then
|
||||
echo BISECT_HEAD
|
||||
else
|
||||
echo HEAD
|
||||
fi
|
||||
}
|
||||
|
||||
bisect_autostart() {
|
||||
test -s "$GIT_DIR/BISECT_START" || {
|
||||
gettextln "You need to start by \"git bisect start\"" >&2
|
||||
if test -t 0
|
||||
then
|
||||
# TRANSLATORS: Make sure to include [Y] and [n] in your
|
||||
# translation. The program will only accept English input
|
||||
# at this point.
|
||||
gettext "Do you want me to do it for you [Y/n]? " >&2
|
||||
read yesno
|
||||
case "$yesno" in
|
||||
[Nn]*)
|
||||
exit ;;
|
||||
esac
|
||||
bisect_start
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
}
|
||||
|
||||
bisect_start() {
|
||||
git bisect--helper --bisect-start $@ || exit
|
||||
|
||||
#
|
||||
# Change state.
|
||||
# In case of mistaken revs or checkout error, or signals received,
|
||||
# "bisect_auto_next" below may exit or misbehave.
|
||||
# We have to trap this to be able to clean up using
|
||||
# "bisect_clean_state".
|
||||
#
|
||||
trap 'git bisect--helper --bisect-clean-state' 0
|
||||
trap 'exit 255' 1 2 3 15
|
||||
|
||||
#
|
||||
# Check if we can proceed to the next bisect state.
|
||||
#
|
||||
get_terms
|
||||
bisect_auto_next
|
||||
|
||||
trap '-' 0
|
||||
}
|
||||
|
||||
bisect_skip() {
|
||||
all=''
|
||||
for arg in "$@"
|
||||
@ -104,81 +51,7 @@ bisect_skip() {
|
||||
esac
|
||||
all="$all $revs"
|
||||
done
|
||||
eval bisect_state 'skip' $all
|
||||
}
|
||||
|
||||
bisect_state() {
|
||||
bisect_autostart
|
||||
state=$1
|
||||
git bisect--helper --check-and-set-terms $state $TERM_GOOD $TERM_BAD || exit
|
||||
get_terms
|
||||
case "$#,$state" in
|
||||
0,*)
|
||||
die "Please call 'bisect_state' with at least one argument." ;;
|
||||
1,"$TERM_BAD"|1,"$TERM_GOOD"|1,skip)
|
||||
bisected_head=$(bisect_head)
|
||||
rev=$(git rev-parse --verify "$bisected_head") ||
|
||||
die "$(eval_gettext "Bad rev input: \$bisected_head")"
|
||||
git bisect--helper --bisect-write "$state" "$rev" "$TERM_GOOD" "$TERM_BAD" || exit
|
||||
git bisect--helper --check-expected-revs "$rev" ;;
|
||||
2,"$TERM_BAD"|*,"$TERM_GOOD"|*,skip)
|
||||
shift
|
||||
hash_list=''
|
||||
for rev in "$@"
|
||||
do
|
||||
sha=$(git rev-parse --verify "$rev^{commit}") ||
|
||||
die "$(eval_gettext "Bad rev input: \$rev")"
|
||||
hash_list="$hash_list $sha"
|
||||
done
|
||||
for rev in $hash_list
|
||||
do
|
||||
git bisect--helper --bisect-write "$state" "$rev" "$TERM_GOOD" "$TERM_BAD" || exit
|
||||
done
|
||||
git bisect--helper --check-expected-revs $hash_list ;;
|
||||
*,"$TERM_BAD")
|
||||
die "$(eval_gettext "'git bisect \$TERM_BAD' can take only one argument.")" ;;
|
||||
*)
|
||||
usage ;;
|
||||
esac
|
||||
bisect_auto_next
|
||||
}
|
||||
|
||||
bisect_auto_next() {
|
||||
git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD && bisect_next || :
|
||||
}
|
||||
|
||||
bisect_next() {
|
||||
case "$#" in 0) ;; *) usage ;; esac
|
||||
bisect_autostart
|
||||
git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD $TERM_GOOD|| exit
|
||||
|
||||
# Perform all bisection computation, display and checkout
|
||||
git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
|
||||
res=$?
|
||||
|
||||
# Check if we should exit because bisection is finished
|
||||
if test $res -eq 10
|
||||
then
|
||||
bad_rev=$(git show-ref --hash --verify refs/bisect/$TERM_BAD)
|
||||
bad_commit=$(git show-branch $bad_rev)
|
||||
echo "# first $TERM_BAD commit: $bad_commit" >>"$GIT_DIR/BISECT_LOG"
|
||||
exit 0
|
||||
elif test $res -eq 2
|
||||
then
|
||||
echo "# only skipped commits left to test" >>"$GIT_DIR/BISECT_LOG"
|
||||
good_revs=$(git for-each-ref --format="%(objectname)" "refs/bisect/$TERM_GOOD-*")
|
||||
for skipped in $(git rev-list refs/bisect/$TERM_BAD --not $good_revs)
|
||||
do
|
||||
skipped_commit=$(git show-branch $skipped)
|
||||
echo "# possible first $TERM_BAD commit: $skipped_commit" >>"$GIT_DIR/BISECT_LOG"
|
||||
done
|
||||
exit $res
|
||||
fi
|
||||
|
||||
# Check for an error in the bisection process
|
||||
test $res -ne 0 && exit $res
|
||||
|
||||
return 0
|
||||
eval git bisect--helper --bisect-state 'skip' $all
|
||||
}
|
||||
|
||||
bisect_visualize() {
|
||||
@ -223,8 +96,7 @@ bisect_replay () {
|
||||
get_terms
|
||||
case "$command" in
|
||||
start)
|
||||
cmd="bisect_start $rev $tail"
|
||||
eval "$cmd" ;;
|
||||
eval "git bisect--helper --bisect-start $rev $tail" ;;
|
||||
"$TERM_GOOD"|"$TERM_BAD"|skip)
|
||||
git bisect--helper --bisect-write "$command" "$rev" "$TERM_GOOD" "$TERM_BAD" || exit;;
|
||||
terms)
|
||||
@ -234,7 +106,7 @@ bisect_replay () {
|
||||
esac
|
||||
done <"$file"
|
||||
IFS="$oIFS"
|
||||
bisect_auto_next
|
||||
git bisect--helper --bisect-auto-next
|
||||
}
|
||||
|
||||
bisect_run () {
|
||||
@ -269,8 +141,7 @@ exit code \$res from '\$command' is < 0 or >= 128" >&2
|
||||
state="$TERM_GOOD"
|
||||
fi
|
||||
|
||||
# We have to use a subshell because "bisect_state" can exit.
|
||||
( bisect_state $state >"$GIT_DIR/BISECT_RUN" )
|
||||
git bisect--helper --bisect-state $state >"$GIT_DIR/BISECT_RUN"
|
||||
res=$?
|
||||
|
||||
cat "$GIT_DIR/BISECT_RUN"
|
||||
@ -285,7 +156,7 @@ exit code \$res from '\$command' is < 0 or >= 128" >&2
|
||||
if [ $res -ne 0 ]
|
||||
then
|
||||
eval_gettextln "bisect run failed:
|
||||
'bisect_state \$state' exited with error code \$res" >&2
|
||||
'git bisect--helper --bisect-state \$state' exited with error code \$res" >&2
|
||||
exit $res
|
||||
fi
|
||||
|
||||
@ -324,14 +195,14 @@ case "$#" in
|
||||
help)
|
||||
git bisect -h ;;
|
||||
start)
|
||||
bisect_start "$@" ;;
|
||||
git bisect--helper --bisect-start "$@" ;;
|
||||
bad|good|new|old|"$TERM_BAD"|"$TERM_GOOD")
|
||||
bisect_state "$cmd" "$@" ;;
|
||||
git bisect--helper --bisect-state "$cmd" "$@" ;;
|
||||
skip)
|
||||
bisect_skip "$@" ;;
|
||||
next)
|
||||
# Not sure we want "next" at the UI level anymore.
|
||||
bisect_next "$@" ;;
|
||||
git bisect--helper --bisect-next "$@" || exit ;;
|
||||
visualize|view)
|
||||
bisect_visualize "$@" ;;
|
||||
reset)
|
||||
|
Reference in New Issue
Block a user