1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
#include "builtin.h"
#include "cache.h"
#include "parse-options.h"
#include "bisect.h"
#include "refs.h"
static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
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"),
NULL
};
/*
* 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;
}
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",
"visualize", "view", "replay", "log", "run", "terms", NULL))
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;
}
static int write_terms(const char *bad, const char *good)
{
FILE *fp = NULL;
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;
fp = fopen(git_path_bisect_terms(), "w");
if (!fp)
return error_errno(_("could not open the file BISECT_TERMS"));
res = fprintf(fp, "%s\n%s\n", bad, good);
res |= fclose(fp);
return (res < 0) ? -1 : 0;
}
static int is_expected_rev(const char *expected_hex)
{
struct strbuf actual_hex = STRBUF_INIT;
int res = 0;
if (strbuf_read_file(&actual_hex, git_path_bisect_expected_rev(), 0) >= 40) {
strbuf_trim(&actual_hex);
res = !strcmp(actual_hex.buf, expected_hex);
}
strbuf_release(&actual_hex);
return res;
}
static void check_expected_revs(const char **revs, int rev_nr)
{
int i;
for (i = 0; i < rev_nr; i++) {
if (!is_expected_rev(revs[i])) {
unlink_or_warn(git_path_bisect_ancestors_ok());
unlink_or_warn(git_path_bisect_expected_rev());
}
}
}
int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
{
enum {
NEXT_ALL = 1,
WRITE_TERMS,
BISECT_CLEAN_STATE,
CHECK_EXPECTED_REVS
} cmdmode = 0;
int no_checkout = 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_BOOL(0, "no-checkout", &no_checkout,
N_("update BISECT_HEAD instead of checking out the current commit")),
OPT_END()
};
argc = parse_options(argc, argv, prefix, options,
git_bisect_helper_usage, 0);
if (!cmdmode)
usage_with_options(git_bisect_helper_usage, options);
switch (cmdmode) {
case NEXT_ALL:
return bisect_next_all(prefix, no_checkout);
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;
default:
return error("BUG: unknown subcommand '%d'", cmdmode);
}
return 0;
}
|