bisect--helper: `bisect_next_check` shell function in C

Reimplement `bisect_next_check` shell function in C and add `bisect-next-check` subcommand to `git bisect--helper` to call it from git-bisect.sh . `bisect_voc` shell function is no longer useful now and is replaced by using a char *[] of "new|bad" and "good|old" values. Using `--bisect-next-check` is a temporary measure to port shell function to C so as to use the existing test suite. As more functions are ported, this subcommand will be retired but its implementation will be called by some other methods. Helped-by: Stephan Beyer <s-beyer@gmx.net> Mentored-by: Lars Schneider <larsxschneider@gmail.com> Mentored-by: Christian Couder <chriscool@tuxfamily.org> Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de> Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com> Signed-off-by: Tanushree Tumane <tanushreetumane@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Pranit Bauva committed Jan 2, 2019 at 07:38 UTC 129a6cf344c4b352433f5a24de02a62783f6c6df
2 files changed +91 -57
builtin/bisect--helper.c
+87 -1
@@ -6,6 +6,7 @@
6 #include "dir.h"
7 #include "argv-array.h"
8 #include "run-command.h"
9 +#include "prompt.h"
10
11 static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
12 static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
@@ -21,6 +22,7 @@ static const char * const git_bisect_helper_usage[] = {
22 N_("git bisect--helper --bisect-reset [<commit>]"),
23 N_("git bisect--helper --bisect-write [--no-log] <state> <revision> <good_term> <bad_term>"),
24 N_("git bisect--helper --bisect-check-and-set-terms <command> <good_term> <bad_term>"),
25 + N_("git bisect--helper --bisect-next-check <good_term> <bad_term> [<term>]"),
26 NULL
27 };
28
@@ -44,6 +46,9 @@ static void set_terms(struct bisect_terms *terms, const char *bad,
46 terms->term_bad = xstrdup(bad);
47 }
48
49 +static const char *vocab_bad = "bad|new";
50 +static const char *vocab_good = "good|old";
51 +
52 /*
53 * Check whether the string `term` belongs to the set of strings
54 * included in the variable arguments.
@@ -262,6 +267,78 @@ static int check_and_set_terms(struct bisect_terms *terms, const char *cmd)
267 return 0;
268 }
269
270 +static int mark_good(const char *refname, const struct object_id *oid,
271 + int flag, void *cb_data)
272 +{
273 + int *m_good = (int *)cb_data;
274 + *m_good = 0;
275 + return 1;
276 +}
277 +
278 +static const char *need_bad_and_good_revision_warning =
279 + N_("You need to give me at least one %s and %s revision.\n"
280 + "You can use \"git bisect %s\" and \"git bisect %s\" for that.");
281 +
282 +static const char *need_bisect_start_warning =
283 + N_("You need to start by \"git bisect start\".\n"
284 + "You then need to give me at least one %s and %s revision.\n"
285 + "You can use \"git bisect %s\" and \"git bisect %s\" for that.");
286 +
287 +static int bisect_next_check(const struct bisect_terms *terms,
288 + const char *current_term)
289 +{
290 + int missing_good = 1, missing_bad = 1, retval = 0;
291 + const char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad);
292 + const char *good_glob = xstrfmt("%s-*", terms->term_good);
293 +
294 + if (ref_exists(bad_ref))
295 + missing_bad = 0;
296 +
297 + for_each_glob_ref_in(mark_good, good_glob, "refs/bisect/",
298 + (void *) &missing_good);
299 +
300 + if (!missing_good && !missing_bad)
301 + goto finish;
302 +
303 + if (!current_term) {
304 + retval = -1;
305 + goto finish;
306 + }
307 +
308 + if (missing_good && !missing_bad &&
309 + !strcmp(current_term, terms->term_good)) {
310 + char *yesno;
311 + /*
312 + * have bad (or new) but not good (or old). We could bisect
313 + * although this is less optimum.
314 + */
315 + warning(_("bisecting only with a %s commit"), terms->term_bad);
316 + if (!isatty(0))
317 + goto finish;
318 + /*
319 + * TRANSLATORS: Make sure to include [Y] and [n] in your
320 + * translation. The program will only accept English input
321 + * at this point.
322 + */
323 + yesno = git_prompt(_("Are you sure [Y/n]? "), PROMPT_ECHO);
324 + if (starts_with(yesno, "N") || starts_with(yesno, "n"))
325 + retval = -1;
326 + goto finish;
327 + }
328 + if (!is_empty_or_missing_file(git_path_bisect_start())) {
329 + retval = error(_(need_bad_and_good_revision_warning),
330 + vocab_bad, vocab_good, vocab_bad, vocab_good);
331 + } else {
332 + retval = error(_(need_bisect_start_warning),
333 + vocab_good, vocab_bad, vocab_good, vocab_bad);
334 + }
335 +
336 +finish:
337 + free((void *) good_glob);
338 + free((void *) bad_ref);
339 + return retval;
340 +}
341 +
342 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
343 {
344 enum {
@@ -271,7 +348,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
348 CHECK_EXPECTED_REVS,
349 BISECT_RESET,
350 BISECT_WRITE,
274 - CHECK_AND_SET_TERMS
351 + CHECK_AND_SET_TERMS,
352 + BISECT_NEXT_CHECK
353 } cmdmode = 0;
354 int no_checkout = 0, res = 0, nolog = 0;
355 struct option options[] = {
@@ -289,6 +367,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
367 N_("write out the bisection state in BISECT_LOG"), BISECT_WRITE),
368 OPT_CMDMODE(0, "check-and-set-terms", &cmdmode,
369 N_("check and set terms in a bisection state"), CHECK_AND_SET_TERMS),
370 + OPT_CMDMODE(0, "bisect-next-check", &cmdmode,
371 + N_("check whether bad or good terms exist"), BISECT_NEXT_CHECK),
372 OPT_BOOL(0, "no-checkout", &no_checkout,
373 N_("update BISECT_HEAD instead of checking out the current commit")),
374 OPT_BOOL(0, "no-log", &nolog,
@@ -333,6 +413,12 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
413 set_terms(&terms, argv[2], argv[1]);
414 res = check_and_set_terms(&terms, argv[0]);
415 break;
416 + case BISECT_NEXT_CHECK:
417 + if (argc != 2 && argc != 3)
418 + return error(_("--bisect-next-check requires 2 or 3 arguments"));
419 + set_terms(&terms, argv[1], argv[0]);
420 + res = bisect_next_check(&terms, argc == 3 ? argv[2] : NULL);
421 + break;
422 default:
423 return error("BUG: unknown subcommand '%d'", cmdmode);
424 }
git-bisect.sh
+4 -56
@@ -271,59 +271,14 @@ bisect_state() {
271 bisect_auto_next
272 }
273
274 -bisect_next_check() {
275 - missing_good= missing_bad=
276 - git show-ref -q --verify refs/bisect/$TERM_BAD || missing_bad=t
277 - test -n "$(git for-each-ref "refs/bisect/$TERM_GOOD-*")" || missing_good=t
278 -
279 - case "$missing_good,$missing_bad,$1" in
280 - ,,*)
281 - : have both $TERM_GOOD and $TERM_BAD - ok
282 - ;;
283 - *,)
284 - # do not have both but not asked to fail - just report.
285 - false
286 - ;;
287 - t,,"$TERM_GOOD")
288 - # have bad (or new) but not good (or old). we could bisect although
289 - # this is less optimum.
290 - eval_gettextln "Warning: bisecting only with a \$TERM_BAD commit." >&2
291 - if test -t 0
292 - then
293 - # TRANSLATORS: Make sure to include [Y] and [n] in your
294 - # translation. The program will only accept English input
295 - # at this point.
296 - gettext "Are you sure [Y/n]? " >&2
297 - read yesno
298 - case "$yesno" in [Nn]*) exit 1 ;; esac
299 - fi
300 - : bisect without $TERM_GOOD...
301 - ;;
302 - *)
303 - bad_syn=$(bisect_voc bad)
304 - good_syn=$(bisect_voc good)
305 - if test -s "$GIT_DIR/BISECT_START"
306 - then
307 -
308 - eval_gettextln "You need to give me at least one \$bad_syn and one \$good_syn revision.
309 -(You can use \"git bisect \$bad_syn\" and \"git bisect \$good_syn\" for that.)" >&2
310 - else
311 - eval_gettextln "You need to start by \"git bisect start\".
312 -You then need to give me at least one \$good_syn and one \$bad_syn revision.
313 -(You can use \"git bisect \$bad_syn\" and \"git bisect \$good_syn\" for that.)" >&2
314 - fi
315 - exit 1 ;;
316 - esac
317 -}
318 -
274 bisect_auto_next() {
320 - bisect_next_check && bisect_next || :
275 + git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD && bisect_next || :
276 }
277
278 bisect_next() {
279 case "$#" in 0) ;; *) usage ;; esac
280 bisect_autostart
326 - bisect_next_check $TERM_GOOD
281 + git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD $TERM_GOOD|| exit
282
283 # Perform all bisection computation, display and checkout
284 git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
@@ -355,7 +310,7 @@ bisect_next() {
310 }
311
312 bisect_visualize() {
358 - bisect_next_check fail
313 + git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit
314
315 if test $# = 0
316 then
@@ -409,7 +364,7 @@ bisect_replay () {
364 }
365
366 bisect_run () {
412 - bisect_next_check fail
367 + git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit
368
369 test -n "$*" || die "$(gettext "bisect run failed: no command provided.")"
370
@@ -484,13 +439,6 @@ get_terms () {
439 fi
440 }
441
487 -bisect_voc () {
488 - case "$1" in
489 - bad) echo "bad|new" ;;
490 - good) echo "good|old" ;;
491 - esac
492 -}
493 -
442 bisect_terms () {
443 get_terms
444 if ! test -s "$GIT_DIR/BISECT_TERMS"