Merge branch 'mr/bisect-in-c-4'

Piecemeal of rewrite of "git bisect" in C continues.

* mr/bisect-in-c-4:
  bisect--helper: retire `--check-and-set-terms` subcommand
  bisect--helper: reimplement `bisect_skip` shell function in C
  bisect--helper: retire `--bisect-auto-next` subcommand
  bisect--helper: use `res` instead of return in BISECT_RESET case option
  bisect--helper: retire `--bisect-write` subcommand
  bisect--helper: reimplement `bisect_replay` shell function in C
  bisect--helper: reimplement `bisect_log` shell function in C
This commit is contained in:
Junio C Hamano 2021-02-17 17:21:40 -08:00
commit 0871fb9af5
2 changed files with 155 additions and 86 deletions

View File

@ -21,16 +21,15 @@ static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
static const char * const git_bisect_helper_usage[] = {
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>"),
N_("git bisect--helper --bisect-next-check <good_term> <bad_term> [<term>]"),
N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"),
N_("git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]"
" [--no-checkout] [--first-parent] [<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>...]"),
N_("git bisect--helper --bisect-replay <filename>"),
N_("git bisect--helper --bisect-skip [(<rev>|<range>)...]"),
NULL
};
@ -904,28 +903,148 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, const char **a
return bisect_auto_next(terms, NULL);
}
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;
}
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);
}
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();
} else {
strvec_push(&argv_state, argv[i]);
}
}
res = bisect_state(terms, argv_state.v, argv_state.nr);
strvec_clear(&argv_state);
return res;
}
int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
{
enum {
BISECT_RESET = 1,
BISECT_WRITE,
CHECK_AND_SET_TERMS,
BISECT_NEXT_CHECK,
BISECT_TERMS,
BISECT_START,
BISECT_AUTOSTART,
BISECT_NEXT,
BISECT_AUTO_NEXT,
BISECT_STATE
BISECT_STATE,
BISECT_LOG,
BISECT_REPLAY,
BISECT_SKIP
} cmdmode = 0;
int res = 0, nolog = 0;
struct option options[] = {
OPT_CMDMODE(0, "bisect-reset", &cmdmode,
N_("reset the bisection state"), BISECT_RESET),
OPT_CMDMODE(0, "bisect-write", &cmdmode,
N_("write out the bisection state in BISECT_LOG"), BISECT_WRITE),
OPT_CMDMODE(0, "check-and-set-terms", &cmdmode,
N_("check and set terms in a bisection state"), CHECK_AND_SET_TERMS),
OPT_CMDMODE(0, "bisect-next-check", &cmdmode,
N_("check whether bad or good terms exist"), BISECT_NEXT_CHECK),
OPT_CMDMODE(0, "bisect-terms", &cmdmode,
@ -934,10 +1053,14 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
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_CMDMODE(0, "bisect-log", &cmdmode,
N_("list the bisection steps so far"), BISECT_LOG),
OPT_CMDMODE(0, "bisect-replay", &cmdmode,
N_("replay the bisection process from the given file"), BISECT_REPLAY),
OPT_CMDMODE(0, "bisect-skip", &cmdmode,
N_("skip some commits for checkout"), BISECT_SKIP),
OPT_BOOL(0, "no-log", &nolog,
N_("no log for BISECT_WRITE")),
OPT_END()
@ -955,18 +1078,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
case BISECT_RESET:
if (argc > 1)
return error(_("--bisect-reset requires either no argument or a commit"));
return !!bisect_reset(argc ? argv[0] : NULL);
case BISECT_WRITE:
if (argc != 4 && argc != 5)
return error(_("--bisect-write requires either 4 or 5 arguments"));
set_terms(&terms, argv[3], argv[2]);
res = bisect_write(argv[0], argv[1], &terms, nolog);
break;
case CHECK_AND_SET_TERMS:
if (argc != 3)
return error(_("--check-and-set-terms requires 3 arguments"));
set_terms(&terms, argv[2], argv[1]);
res = check_and_set_terms(&terms, argv[0]);
res = bisect_reset(argc ? argv[0] : NULL);
break;
case BISECT_NEXT_CHECK:
if (argc != 2 && argc != 3)
@ -989,17 +1101,26 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
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;
case BISECT_LOG:
if (argc)
return error(_("--bisect-log requires 0 arguments"));
res = bisect_log();
break;
case BISECT_REPLAY:
if (argc != 1)
return error(_("no logfile given"));
set_terms(&terms, "bad", "good");
res = bisect_replay(&terms, argv[0]);
break;
case BISECT_SKIP:
set_terms(&terms, "bad", "good");
res = bisect_skip(&terms, argv, argc);
break;
default:
BUG("unknown subcommand %d", cmdmode);
}

View File

@ -39,21 +39,6 @@ _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
TERM_BAD=bad
TERM_GOOD=good
bisect_skip() {
all=''
for arg in "$@"
do
case "$arg" in
*..*)
revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
*)
revs=$(git rev-parse --sq-quote "$arg") ;;
esac
all="$all $revs"
done
eval git bisect--helper --bisect-state 'skip' $all
}
bisect_visualize() {
git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit
@ -77,38 +62,6 @@ bisect_visualize() {
eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
}
bisect_replay () {
file="$1"
test "$#" -eq 1 || die "$(gettext "No logfile given")"
test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
git bisect--helper --bisect-reset || exit
oIFS="$IFS" IFS="$IFS$(printf '\015')"
while read git bisect command rev tail
do
test "$git $bisect" = "git bisect" || test "$git" = "git-bisect" || continue
if test "$git" = "git-bisect"
then
rev="$command"
command="$bisect"
fi
get_terms
git bisect--helper --check-and-set-terms "$command" "$TERM_GOOD" "$TERM_BAD" || exit
get_terms
case "$command" in
start)
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)
git bisect--helper --bisect-terms $rev || exit;;
*)
die "$(gettext "?? what are you talking about?")" ;;
esac
done <"$file"
IFS="$oIFS"
git bisect--helper --bisect-auto-next || exit
}
bisect_run () {
git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit
@ -169,11 +122,6 @@ exit code \$res from '\$command' is < 0 or >= 128" >&2
done
}
bisect_log () {
test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
cat "$GIT_DIR/BISECT_LOG"
}
get_terms () {
if test -s "$GIT_DIR/BISECT_TERMS"
then
@ -199,7 +147,7 @@ case "$#" in
bad|good|new|old|"$TERM_BAD"|"$TERM_GOOD")
git bisect--helper --bisect-state "$cmd" "$@" ;;
skip)
bisect_skip "$@" ;;
git bisect--helper --bisect-skip "$@" || exit;;
next)
# Not sure we want "next" at the UI level anymore.
git bisect--helper --bisect-next "$@" || exit ;;
@ -208,9 +156,9 @@ case "$#" in
reset)
git bisect--helper --bisect-reset "$@" ;;
replay)
bisect_replay "$@" ;;
git bisect--helper --bisect-replay "$@" || exit;;
log)
bisect_log ;;
git bisect--helper --bisect-log || exit ;;
run)
bisect_run "$@" ;;
terms)