threads / patch / 44299

v15, 27 partswrapper: move is_empty_file() and rename it as is_empty_or_missing_file()

Subject: [PATCH v15 06/27] wrapper: move is_empty_file() and rename it as is_empty_or_missing_file()

## tl;dr

82 messages between Oct 14, 2016 and Jan 1, 2017. Diffs are folded; open one to read it.

replies: 81people: 5as markdown or json

Pranit Bauva· Oct 14, 2016, 14:14 UTC · lore

[PATCH v15 01/27] bisect--helper: use OPT_CMDMODE instead of OPT_BOOL

`--next-all` is meant to be used as a subcommand to support multiple "operation mode" though the current implementation does not contain any other subcommand along side with `--next-all` but further commits will include some more subcommands.

Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)
Show changes to builtin/bisect--helper.c +11 −7
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 3324229..8111c91 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -10,11 +10,11 @@ static const char * const git_bisect_helper_usage[] = {
 
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
-	int next_all = 0;
+	enum { NEXT_ALL = 1 } cmdmode = 0;
 	int no_checkout = 0;
 	struct option options[] = {
-		OPT_BOOL(0, "next-all", &next_all,
-			 N_("perform 'git bisect next'")),
+		OPT_CMDMODE(0, "next-all", &cmdmode,
+			 N_("perform 'git bisect next'"), NEXT_ALL),
 		OPT_BOOL(0, "no-checkout", &no_checkout,
 			 N_("update BISECT_HEAD instead of checking out the current commit")),
 		OPT_END()
@@ -23,9 +23,14 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, options,
 			     git_bisect_helper_usage, 0);
 
-	if (!next_all)
+	if (!cmdmode)
 		usage_with_options(git_bisect_helper_usage, options);
 
-	/* next-all */
-	return bisect_next_all(prefix, no_checkout);
+	switch (cmdmode) {
+	case NEXT_ALL:
+		return bisect_next_all(prefix, no_checkout);
+	default:
+		die("BUG: unknown subcommand '%d'", cmdmode);
+	}
+	return 0;
 }

--
https://github.com/git/git/pull/287
Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

is_empty_file() can help to refactor a lot of code. This will be very helpful in porting "git bisect" to C.

Suggested-by: Torsten Bögershausen <tboegi@web.de>
Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/am.c | 20 ++------------------
 cache.h      |  3 +++
 wrapper.c    | 13 +++++++++++++
 3 files changed, 18 insertions(+), 18 deletions(-)
Show changes to 3 files +18 −19

builtin/am.c, cache.h, wrapper.c

diff --git a/builtin/am.c b/builtin/am.c
index 739b34d..9e1e9d6 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -30,22 +30,6 @@
 #include "mailinfo.h"
 
 /**
- * Returns 1 if the file is empty or does not exist, 0 otherwise.
- */
-static int is_empty_file(const char *filename)
-{
-	struct stat st;
-
-	if (stat(filename, &st) < 0) {
-		if (errno == ENOENT)
-			return 1;
-		die_errno(_("could not stat %s"), filename);
-	}
-
-	return !st.st_size;
-}
-
-/**
  * Returns the length of the first line of msg.
  */
 static int linelen(const char *msg)
@@ -1324,7 +1308,7 @@ static int parse_mail(struct am_state *state, const char *mail)
 		goto finish;
 	}
 
-	if (is_empty_file(am_path(state, "patch"))) {
+	if (is_empty_or_missing_file(am_path(state, "patch"))) {
 		printf_ln(_("Patch is empty. Was it split wrong?"));
 		die_user_resolve(state);
 	}
@@ -1896,7 +1880,7 @@ static void am_run(struct am_state *state, int resume)
 		resume = 0;
 	}
 
-	if (!is_empty_file(am_path(state, "rewritten"))) {
+	if (!is_empty_or_missing_file(am_path(state, "rewritten"))) {
 		assert(state->rebasing);
 		copy_notes_for_rebase(state);
 		run_post_rewrite_hook(state);
diff --git a/cache.h b/cache.h
index b780a91..49f214b 100644
--- a/cache.h
+++ b/cache.h
@@ -1916,4 +1916,7 @@ void sleep_millisec(int millisec);
  */
 void safe_create_dir(const char *dir, int share);
 
+/* Return 1 if the file is empty or does not exists, 0 otherwise. */
+extern int is_empty_or_missing_file(const char *filename);
+
 #endif /* CACHE_H */
diff --git a/wrapper.c b/wrapper.c
index e7f1979..78f6431 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -679,3 +679,16 @@ void sleep_millisec(int millisec)
 {
 	poll(NULL, 0, millisec);
 }
+
+int is_empty_or_missing_file(const char *filename)
+{
+	struct stat st;
+
+	if (stat(filename, &st) < 0) {
+		if (errno == ENOENT)
+			return 1;
+		die_errno(_("could not stat %s"), filename);
+	}
+
+	return !st.st_size;
+}

--
https://github.com/git/git/pull/287
Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 05/27] t6030: explicitly test for bisection cleanup

Add test to explicitly check that 'git bisect reset' is working as expected. This is already covered implicitly by the test suite.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
I faced this problem while converting `bisect_clean_state` and the tests
where showing breakages but it wasn't clear as to where exactly are they
breaking. This will patch  will help in that. Also I tested the test
coverage of the test suite before this patch and it covers this (I did
this by purposely changing names of files in git-bisect.sh and running
the test suite).
---
 t/t6030-bisect-porcelain.sh | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
Show changes to t/t6030-bisect-porcelain.sh +17 −1
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 5e5370f..18e7998 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -894,4 +894,21 @@ test_expect_success 'bisect start takes options and revs in any order' '
 	test_cmp expected actual
 '
 
+test_expect_success 'git bisect reset cleans bisection state properly' '
+	git bisect reset &&
+	git bisect start &&
+	git bisect good $HASH1 &&
+	git bisect bad $HASH4 &&
+	git bisect reset &&
+	test -z "$(git for-each-ref "refs/bisect/*")" &&
+	test_path_is_missing "$GIT_DIR/BISECT_EXPECTED_REV" &&
+	test_path_is_missing "$GIT_DIR/BISECT_ANCESTORS_OK" &&
+	test_path_is_missing "$GIT_DIR/BISECT_LOG" &&
+	test_path_is_missing "$GIT_DIR/BISECT_RUN" &&
+	test_path_is_missing "$GIT_DIR/BISECT_TERMS" &&
+	test_path_is_missing "$GIT_DIR/head-name" &&
+	test_path_is_missing "$GIT_DIR/BISECT_HEAD" &&
+	test_path_is_missing "$GIT_DIR/BISECT_START"
+'
+
 test_done

--
https://github.com/git/git/pull/287
Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 22/27] bisect--helper: `bisect_log` shell function in C

Reimplement the `bisect_log` shell function in C and also add `--bisect-log` subcommand to `git bisect--helper` to call it from git-bisect.sh .

Using `--bisect-log` subcommand 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 and will be called by some other method.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 27 ++++++++++++++++++++++++++-
 git-bisect.sh            |  7 +------
 2 files changed, 27 insertions(+), 7 deletions(-)
Show changes to 2 files +27 −8

builtin/bisect--helper.c, git-bisect.sh

diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 493034c..c18ca07 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -858,6 +858,23 @@ static int bisect_state(struct bisect_terms *terms, const char **argv,
 	return -1;
 }
 
+static int bisect_log(void)
+{
+	int fd, status;
+	fd = open(git_path_bisect_log(), O_RDONLY);
+	if (fd < 0)
+		return -1;
+
+	status = copy_fd(fd, 1);
+	if (status) {
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return status;
+}
+
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
 	enum {
@@ -870,7 +887,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		BISECT_NEXT,
 		BISECT_AUTO_NEXT,
 		BISECT_AUTOSTART,
-		BISECT_STATE
+		BISECT_STATE,
+		BISECT_LOG
 	} cmdmode = 0;
 	int no_checkout = 0, res = 0;
 	struct option options[] = {
@@ -894,6 +912,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 			 N_("start the bisection if BISECT_START empty or missing"), BISECT_AUTOSTART),
 		OPT_CMDMODE(0, "bisect-state", &cmdmode,
 			 N_("mark the state of ref (or refs)"), BISECT_STATE),
+		OPT_CMDMODE(0, "bisect-log", &cmdmode,
+			 N_("output the contents of BISECT_LOG"), BISECT_LOG),
 		OPT_BOOL(0, "no-checkout", &no_checkout,
 			 N_("update BISECT_HEAD instead of checking out the current commit")),
 		OPT_END()
@@ -973,6 +993,11 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		get_terms(&terms);
 		res = bisect_state(&terms, argv, argc);
 		break;
+	case BISECT_LOG:
+		if (argc > 1)
+			die(_("--bisect-log requires 0 arguments"));
+		res = bisect_log();
+		break;
 	default:
 		die("BUG: unknown subcommand '%d'", cmdmode);
 	}
diff --git a/git-bisect.sh b/git-bisect.sh
index a9eebbb..a47e3b5 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -166,11 +166,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
@@ -208,7 +203,7 @@ case "$#" in
 	replay)
 		bisect_replay "$@" ;;
 	log)
-		bisect_log ;;
+		git bisect--helper --bisect-log ;;
 	run)
 		bisect_run "$@" ;;
 	terms)

--
https://github.com/git/git/pull/287
Stephan Beyer· Nov 17, 2016, 21:47 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 22/27] bisect--helper: `bisect_log` shell function in C

Hi,
On 10/14/2016 04:14 PM, Pranit Bauva wrote:
Show 16 quoted lines
> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
> index 493034c..c18ca07 100644
> --- a/builtin/bisect--helper.c
> +++ b/builtin/bisect--helper.c
> @@ -858,6 +858,23 @@ static int bisect_state(struct bisect_terms *terms, const char **argv,
>  	return -1;
>  }
>  
> +static int bisect_log(void)
> +{
> +	int fd, status;
> +	fd = open(git_path_bisect_log(), O_RDONLY);
> +	if (fd < 0)
> +		return -1;
> +
> +	status = copy_fd(fd, 1);
Perhaps
	status = copy_fd(fd, STDOUT_FILENO);
Show 8 quoted lines
> +	if (status) {
> +		close(fd);
> +		return -1;
> +	}
> +
> +	close(fd);
> +	return status;
> +}

That's weird. Either get rid of the if() and actually use status:

	status = copy_fd(fd, STDOUT_FILENO);
	close(fd);
	return status ? -1 : 0;
or get rid of status and use the if:
	if (copy_fd(fd, STDOUT_FILENO)) {
		close(fd);
		return -1;
	}
	close(fd);
	return 0;
I'd recommend the shorter variant ;)
~Stephan
Pranit Bauva· Dec 6, 2016, 22:42 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 22/27] bisect--helper: `bisect_log` shell function in C

Hey Stephan,
On Fri, Nov 18, 2016 at 3:17 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
Show 23 quoted lines
> Hi,
>
> On 10/14/2016 04:14 PM, Pranit Bauva wrote:
>> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
>> index 493034c..c18ca07 100644
>> --- a/builtin/bisect--helper.c
>> +++ b/builtin/bisect--helper.c
>> @@ -858,6 +858,23 @@ static int bisect_state(struct bisect_terms *terms, const char **argv,
>>       return -1;
>>  }
>>
>> +static int bisect_log(void)
>> +{
>> +     int fd, status;
>> +     fd = open(git_path_bisect_log(), O_RDONLY);
>> +     if (fd < 0)
>> +             return -1;
>> +
>> +     status = copy_fd(fd, 1);
>
> Perhaps
>
>         status = copy_fd(fd, STDOUT_FILENO);
Sure!
Show 16 quoted lines
>> +     if (status) {
>> +             close(fd);
>> +             return -1;
>> +     }
>> +
>> +     close(fd);
>> +     return status;
>> +}
>
> That's weird.
> Either get rid of the if() and actually use status:
>
>         status = copy_fd(fd, STDOUT_FILENO);
>
>         close(fd);
>         return status ? -1 : 0;
This one seems better!
Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 10/27] bisect--helper: `check_and_set_terms` shell function in C

Reimplement the `check_and_set_terms` shell function in C and add `check-and-set-terms` subcommand to `git bisect--helper` to call it from git-bisect.sh

Using `--check-and-set-terms` subcommand is a temporary measure to port shell function in 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.

check_and_set_terms() sets and receives two global variables namely TERM_GOOD and TERM_BAD in the shell script. Luckily the file BISECT_TERMS also contains the value of those variables so its appropriate to evoke the method get_terms() after calling the subcommand so that it retrieves the value of TERM_GOOD and TERM_BAD from the file BISECT_TERMS. The two global variables are passed as arguments to the subcommand.

Also introduce set_terms() to copy the `term_good` and `term_bad` into `struct bisect_terms` and write it out to the file BISECT_TERMS.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 45 ++++++++++++++++++++++++++++++++++++++++++++-
 git-bisect.sh            | 36 ++++--------------------------------
 2 files changed, 48 insertions(+), 33 deletions(-)
Show changes to 2 files +48 −34

builtin/bisect--helper.c, git-bisect.sh

diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 3f19b68..c6c11e3 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -20,6 +20,7 @@ static const char * const git_bisect_helper_usage[] = {
 	N_("git bisect--helper --bisect-clean-state"),
 	N_("git bisect--helper --bisect-reset [<commit>]"),
 	N_("git bisect--helper --bisect-write <state> <revision> <TERM_GOOD> <TERM_BAD> [<nolog>]"),
+	N_("git bisect--helper --bisect-check-and-set-terms <command> <TERM_GOOD> <TERM_BAD>"),
 	NULL
 };
 
@@ -212,6 +213,38 @@ static int bisect_write(const char *state, const char *rev,
 	return retval;
 }
 
+static int set_terms(struct bisect_terms *terms, const char *bad,
+		     const char *good)
+{
+	terms->term_good = xstrdup(good);
+	terms->term_bad = xstrdup(bad);
+	return write_terms(terms->term_bad, terms->term_good);
+}
+
+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))
+			return set_terms(terms, "bad", "good");
+		if (one_of(cmd, "new", "old", NULL))
+			return set_terms(terms, "new", "old");
+	}
+
+	return 0;
+}
+
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
 	enum {
@@ -220,7 +253,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		BISECT_CLEAN_STATE,
 		BISECT_RESET,
 		CHECK_EXPECTED_REVS,
-		BISECT_WRITE
+		BISECT_WRITE,
+		CHECK_AND_SET_TERMS
 	} cmdmode = 0;
 	int no_checkout = 0, res = 0;
 	struct option options[] = {
@@ -236,6 +270,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 			 N_("check for expected revs"), CHECK_EXPECTED_REVS),
 		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_BOOL(0, "no-checkout", &no_checkout,
 			 N_("update BISECT_HEAD instead of checking out the current commit")),
 		OPT_END()
@@ -278,6 +314,13 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		terms.term_bad = xstrdup(argv[3]);
 		res = bisect_write(argv[0], argv[1], &terms, nolog);
 		break;
+	case CHECK_AND_SET_TERMS:
+		if (argc != 3)
+			die(_("--check-and-set-terms requires 3 arguments"));
+		terms.term_good = xstrdup(argv[1]);
+		terms.term_bad = xstrdup(argv[2]);
+		res = check_and_set_terms(&terms, argv[0]);
+		break;
 	default:
 		die("BUG: unknown subcommand '%d'", cmdmode);
 	}
diff --git a/git-bisect.sh b/git-bisect.sh
index dfdec33..bdf2227 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -238,7 +238,8 @@ bisect_skip() {
 bisect_state() {
 	bisect_autostart
 	state=$1
-	check_and_set_terms $state
+	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." ;;
@@ -390,7 +391,8 @@ bisect_replay () {
 			command="$bisect"
 		fi
 		get_terms
-		check_and_set_terms "$command"
+		git bisect--helper --check-and-set-terms "$command" "$TERM_GOOD" "$TERM_BAD" || exit
+		get_terms
 		case "$command" in
 		start)
 			cmd="bisect_start $rev"
@@ -480,36 +482,6 @@ get_terms () {
 	fi
 }
 
-check_and_set_terms () {
-	cmd="$1"
-	case "$cmd" in
-	skip|start|terms) ;;
-	*)
-		if test -s "$GIT_DIR/BISECT_TERMS" && test "$cmd" != "$TERM_BAD" && test "$cmd" != "$TERM_GOOD"
-		then
-			die "$(eval_gettext "Invalid command: you're currently in a \$TERM_BAD/\$TERM_GOOD bisect.")"
-		fi
-		case "$cmd" in
-		bad|good)
-			if ! test -s "$GIT_DIR/BISECT_TERMS"
-			then
-				TERM_BAD=bad
-				TERM_GOOD=good
-				git bisect--helper --write-terms "$TERM_BAD" "$TERM_GOOD" || exit
-			fi
-			;;
-		new|old)
-			if ! test -s "$GIT_DIR/BISECT_TERMS"
-			then
-				TERM_BAD=new
-				TERM_GOOD=old
-				git bisect--helper --write-terms "$TERM_BAD" "$TERM_GOOD" || exit
-			fi
-			;;
-		esac ;;
-	esac
-}
-
 bisect_voc () {
 	case "$1" in
 	bad) echo "bad|new" ;;

--
https://github.com/git/git/pull/287
Stephan Beyer· Nov 17, 2016, 20:25 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 10/27] bisect--helper: `check_and_set_terms` shell function in C

Hi Pranit,
On 10/14/2016 04:14 PM, Pranit Bauva wrote:
Show 9 quoted lines
> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
> index 3f19b68..c6c11e3 100644
> --- a/builtin/bisect--helper.c
> +++ b/builtin/bisect--helper.c
> @@ -20,6 +20,7 @@ static const char * const git_bisect_helper_usage[] = {
>  	N_("git bisect--helper --bisect-clean-state"),
>  	N_("git bisect--helper --bisect-reset [<commit>]"),
>  	N_("git bisect--helper --bisect-write <state> <revision> <TERM_GOOD> <TERM_BAD> [<nolog>]"),
> +	N_("git bisect--helper --bisect-check-and-set-terms <command> <TERM_GOOD> <TERM_BAD>"),

Here's the same as in the previous patch... I'd not use TERM_GOOD/TERM_BAD in capitals.

Show 13 quoted lines
>  	NULL
>  };
>  
> @@ -212,6 +213,38 @@ static int bisect_write(const char *state, const char *rev,
>  	return retval;
>  }
>  
> +static int set_terms(struct bisect_terms *terms, const char *bad,
> +		     const char *good)
> +{
> +	terms->term_good = xstrdup(good);
> +	terms->term_bad = xstrdup(bad);
> +	return write_terms(terms->term_bad, terms->term_good);

At this stage of the patch series I am wondering why you are setting "terms" here, but I guess you'll need it later.

However, you are leaking memory here. Something like
	free(terms->term_good);
	free(terms->term_bad);
	terms->term_good = xstrdup(good);
	terms->term_bad = xstrdup(bad);

should be safe (because you've always used xstrdup() for the terms members before). Or am I overseeing something?

Show 11 quoted lines
> @@ -278,6 +314,13 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>  		terms.term_bad = xstrdup(argv[3]);
>  		res = bisect_write(argv[0], argv[1], &terms, nolog);
>  		break;
> +	case CHECK_AND_SET_TERMS:
> +		if (argc != 3)
> +			die(_("--check-and-set-terms requires 3 arguments"));
> +		terms.term_good = xstrdup(argv[1]);
> +		terms.term_bad = xstrdup(argv[2]);
> +		res = check_and_set_terms(&terms, argv[0]);
> +		break;

Ha! When I reviewed the last patch, I asked you why you changed the code from returning directly from each subcommand to setting res; break; and then return res at the bottom of the function.

Now I see why this was useful. The two members of "terms" are again leaking memory: you are allocating memory by using xstrdup() but you are not freeing it. (That also applies to the last patch.)

Cheers, Stephan

Pranit Bauva· Dec 6, 2016, 22:43 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 10/27] bisect--helper: `check_and_set_terms` shell function in C

Hey Stephan,
On Fri, Nov 18, 2016 at 1:55 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
Show 15 quoted lines
> Hi Pranit,
>
> On 10/14/2016 04:14 PM, Pranit Bauva wrote:
>> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
>> index 3f19b68..c6c11e3 100644
>> --- a/builtin/bisect--helper.c
>> +++ b/builtin/bisect--helper.c
>> @@ -20,6 +20,7 @@ static const char * const git_bisect_helper_usage[] = {
>>       N_("git bisect--helper --bisect-clean-state"),
>>       N_("git bisect--helper --bisect-reset [<commit>]"),
>>       N_("git bisect--helper --bisect-write <state> <revision> <TERM_GOOD> <TERM_BAD> [<nolog>]"),
>> +     N_("git bisect--helper --bisect-check-and-set-terms <command> <TERM_GOOD> <TERM_BAD>"),
>
> Here's the same as in the previous patch... I'd not use
> TERM_GOOD/TERM_BAD in capitals.
Sure.
Show 26 quoted lines
>>       NULL
>>  };
>>
>> @@ -212,6 +213,38 @@ static int bisect_write(const char *state, const char *rev,
>>       return retval;
>>  }
>>
>> +static int set_terms(struct bisect_terms *terms, const char *bad,
>> +                  const char *good)
>> +{
>> +     terms->term_good = xstrdup(good);
>> +     terms->term_bad = xstrdup(bad);
>> +     return write_terms(terms->term_bad, terms->term_good);
>
> At this stage of the patch series I am wondering why you are setting
> "terms" here, but I guess you'll need it later.
>
> However, you are leaking memory here. Something like
>
>         free(terms->term_good);
>         free(terms->term_bad);
>         terms->term_good = xstrdup(good);
>         terms->term_bad = xstrdup(bad);
>
> should be safe (because you've always used xstrdup() for the terms
> members before). Or am I overseeing something?
Yeah it is safe.
Show 19 quoted lines
>> @@ -278,6 +314,13 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>>               terms.term_bad = xstrdup(argv[3]);
>>               res = bisect_write(argv[0], argv[1], &terms, nolog);
>>               break;
>> +     case CHECK_AND_SET_TERMS:
>> +             if (argc != 3)
>> +                     die(_("--check-and-set-terms requires 3 arguments"));
>> +             terms.term_good = xstrdup(argv[1]);
>> +             terms.term_bad = xstrdup(argv[2]);
>> +             res = check_and_set_terms(&terms, argv[0]);
>> +             break;
>
> Ha! When I reviewed the last patch, I asked you why you changed the code
> from returning directly from each subcommand to setting res; break; and
> then return res at the bottom of the function.
>
> Now I see why this was useful. The two members of "terms" are again
> leaking memory: you are allocating memory by using xstrdup() but you are
> not freeing it.
I will take care about freeing the memory.

Regards, Pranit Bauva

Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 23/27] bisect--helper: `bisect_replay` shell function in C

Reimplement the `bisect_replay` shell function in C and also add `--bisect-replay` subcommand to `git bisect--helper` to call it from git-bisect.sh

Using `--bisect-replay` subcommand 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 and will be called by some other method.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++-
 git-bisect.sh            |  32 +-----------
 2 files changed, 124 insertions(+), 33 deletions(-)
Show changes to 2 files +124 −34

builtin/bisect--helper.c, git-bisect.sh

diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index c18ca07..b367d8d 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -32,6 +32,7 @@ static const char * const git_bisect_helper_usage[] = {
 	N_("git bisect--helper --bisect-autostart"),
 	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>"),
 	NULL
 };
 
@@ -601,7 +602,6 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
 			terms->term_good = arg;
 		} else if (!strcmp(arg, "--term-bad") ||
 			 !strcmp(arg, "--term-new")) {
-			const char *next_arg;
 			if (starts_with(argv[++i], "'"))
 				terms->term_bad = sq_dequote(xstrdup(argv[i]));
 			else
@@ -875,6 +875,117 @@ static int bisect_log(void)
 	return status;
 }
 
+static int get_next_word(const char *line, int pos, struct strbuf *word)
+{
+	int i, len = strlen(line), begin = 0;
+	strbuf_reset(word);
+	for (i = pos; i < len; i++) {
+		if (line[i] == ' ' && begin)
+			return i + 1;
+
+		if (!begin)
+			begin = 1;
+		strbuf_addch(word, line[i]);
+	}
+
+	return i;
+}
+
+static int bisect_replay(struct bisect_terms *terms, const char *filename)
+{
+	struct strbuf line = STRBUF_INIT;
+	struct strbuf word = STRBUF_INIT;
+	FILE *fp = NULL;
+	int res = 0;
+
+	if (is_empty_or_missing_file(filename)) {
+		error(_("no such file with name '%s' exists"), filename);
+		res = -1;
+		goto finish;
+	}
+
+	if (bisect_reset(NULL)) {
+		res = -1;
+		goto finish;
+	}
+
+	fp = fopen(filename, "r");
+	if (!fp) {
+		res = -1;
+		goto finish;
+	}
+
+	while (strbuf_getline(&line, fp) != EOF) {
+		int pos = 0;
+		while (pos < line.len) {
+			pos = get_next_word(line.buf, pos, &word);
+
+			if (!strcmp(word.buf, "git")) {
+				continue;
+			} else if (!strcmp(word.buf, "git-bisect")) {
+				continue;
+			} else if (!strcmp(word.buf, "bisect")) {
+				continue;
+			} else if (!strcmp(word.buf, "#")) {
+				break;
+			}
+
+			get_terms(terms);
+			if (check_and_set_terms(terms, word.buf)) {
+				res = -1;
+				goto finish;
+			}
+
+			if (!strcmp(word.buf, "start")) {
+				struct argv_array argv = ARGV_ARRAY_INIT;
+				sq_dequote_to_argv_array(line.buf+pos, &argv);
+				if (bisect_start(terms, 0, argv.argv, argv.argc)) {
+					argv_array_clear(&argv);
+					res = -1;
+					goto finish;
+				}
+				argv_array_clear(&argv);
+				break;
+			}
+
+			if (one_of(word.buf, terms->term_good,
+			    terms->term_bad, "skip", NULL)) {
+				if (bisect_write(word.buf, line.buf+pos, terms, 0)) {
+					res = -1;
+					goto finish;
+				}
+				break;
+			}
+
+			if (!strcmp(word.buf, "terms")) {
+				struct argv_array argv = ARGV_ARRAY_INIT;
+				sq_dequote_to_argv_array(line.buf+pos, &argv);
+				if (bisect_terms(terms, argv.argv, argv.argc)) {
+					argv_array_clear(&argv);
+					res = -1;
+					goto finish;
+				}
+				argv_array_clear(&argv);
+				break;
+			}
+
+			error(_("?? what are you talking about?"));
+			res = -1;
+			goto finish;
+		}
+	}
+	goto finish;
+finish:
+	if (fp)
+		fclose(fp);
+	strbuf_release(&line);
+	strbuf_release(&word);
+	if (res)
+		return -1;
+
+	return bisect_auto_next(terms, NULL);
+}
+
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
 	enum {
@@ -888,7 +999,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		BISECT_AUTO_NEXT,
 		BISECT_AUTOSTART,
 		BISECT_STATE,
-		BISECT_LOG
+		BISECT_LOG,
+		BISECT_REPLAY
 	} cmdmode = 0;
 	int no_checkout = 0, res = 0;
 	struct option options[] = {
@@ -914,6 +1026,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 			 N_("mark the state of ref (or refs)"), BISECT_STATE),
 		OPT_CMDMODE(0, "bisect-log", &cmdmode,
 			 N_("output the contents of BISECT_LOG"), BISECT_LOG),
+		OPT_CMDMODE(0, "bisect-replay", &cmdmode,
+			 N_("replay the bisection process from the given file"), BISECT_REPLAY),
 		OPT_BOOL(0, "no-checkout", &no_checkout,
 			 N_("update BISECT_HEAD instead of checking out the current commit")),
 		OPT_END()
@@ -998,6 +1112,13 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 			die(_("--bisect-log requires 0 arguments"));
 		res = bisect_log();
 		break;
+	case BISECT_REPLAY:
+		if (argc != 1)
+			die(_("--bisect-replay requires 1 argument"));
+		terms.term_good = "good";
+		terms.term_bad = "bad";
+		res = bisect_replay(&terms, argv[0]);
+		break;
 	default:
 		die("BUG: unknown subcommand '%d'", cmdmode);
 	}
diff --git a/git-bisect.sh b/git-bisect.sh
index a47e3b5..bf66ee2 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -77,36 +77,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
-	while read git bisect command rev
-	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" ;;
-		"$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"
-	git bisect--helper --bisect-auto-next
-}
-
 bisect_run () {
 	git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit
 
@@ -201,7 +171,7 @@ case "$#" in
 	reset)
 		git bisect--helper --bisect-reset "$@" ;;
 	replay)
-		bisect_replay "$@" ;;
+		git bisect--helper --bisect-replay "$@" ;;
 	log)
 		git bisect--helper --bisect-log ;;
 	run)

--
https://github.com/git/git/pull/287
Stephan Beyer· Nov 22, 2016, 00:49 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 23/27] bisect--helper: `bisect_replay` shell function in C

Okay Pranit,
this is the last patch for me to review in this series.

Now that I have a coarse overview of what you did, I have the general remark that imho the "terms" variable should simply be global instead of being passed around all the time.

I also had some other remarks but I forgot them... maybe they come to my mind again when I see patch series v16.

I also want to remark again that I am not a Git developer and only reviewed this because of my interest in git-bisect. So some of my suggestions might conflict with other beliefs here. For example, I consider it very bad style to leak memory... but Git is rather written as a scripting tool than a genuine library, so perhaps many people here do not care about it as long as it works...

On 10/14/2016 04:14 PM, Pranit Bauva wrote:
Show 9 quoted lines
> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
> index c18ca07..b367d8d 100644
> --- a/builtin/bisect--helper.c
> +++ b/builtin/bisect--helper.c
> @@ -601,7 +602,6 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
>  			terms->term_good = arg;
>  		} else if (!strcmp(arg, "--term-bad") ||
>  			 !strcmp(arg, "--term-new")) {
> -			const char *next_arg;
This should already have been removed in patch 15/27, not here.
Show 25 quoted lines
> @@ -875,6 +875,117 @@ static int bisect_log(void)
>  	return status;
>  }
>  
> +static int get_next_word(const char *line, int pos, struct strbuf *word)
> +{
> +	int i, len = strlen(line), begin = 0;
> +	strbuf_reset(word);
> +	for (i = pos; i < len; i++) {
> +		if (line[i] == ' ' && begin)
> +			return i + 1;
> +
> +		if (!begin)
> +			begin = 1;
> +		strbuf_addch(word, line[i]);
> +	}
> +
> +	return i;
> +}
> +
> +static int bisect_replay(struct bisect_terms *terms, const char *filename)
> +{
> +	struct strbuf line = STRBUF_INIT;
> +	struct strbuf word = STRBUF_INIT;
> +	FILE *fp = NULL;
(The initialization is not necessary here.)
> +	int res = 0;
> +
> +	if (is_empty_or_missing_file(filename)) {
> +		error(_("no such file with name '%s' exists"), filename);

The error message is misleading if the file exists but is empty. Maybe something like "cannot read file '%s' for replaying"?

> +		res = -1;
> +		goto finish;
		goto fail;
:D
Show 5 quoted lines
> +	}
> +
> +	if (bisect_reset(NULL)) {
> +		res = -1;
> +		goto finish;
		goto fail;
Show 6 quoted lines
> +	}
> +
> +	fp = fopen(filename, "r");
> +	if (!fp) {
> +		res = -1;
> +		goto finish;
		goto fail;
Show 15 quoted lines
> +	}
> +
> +	while (strbuf_getline(&line, fp) != EOF) {
> +		int pos = 0;
> +		while (pos < line.len) {
> +			pos = get_next_word(line.buf, pos, &word);
> +
> +			if (!strcmp(word.buf, "git")) {
> +				continue;
> +			} else if (!strcmp(word.buf, "git-bisect")) {
> +				continue;
> +			} else if (!strcmp(word.buf, "bisect")) {
> +				continue;
> +			} else if (!strcmp(word.buf, "#")) {
> +				break;
Maybe it is more robust to check whether word.buf begins with #
Show 6 quoted lines
> +			}
> +
> +			get_terms(terms);
> +			if (check_and_set_terms(terms, word.buf)) {
> +				res = -1;
> +				goto finish;
				goto fail;
Show 9 quoted lines
> +			}
> +
> +			if (!strcmp(word.buf, "start")) {
> +				struct argv_array argv = ARGV_ARRAY_INIT;
> +				sq_dequote_to_argv_array(line.buf+pos, &argv);
> +				if (bisect_start(terms, 0, argv.argv, argv.argc)) {
> +					argv_array_clear(&argv);
> +					res = -1;
> +					goto finish;
					goto fail;
Show 10 quoted lines
> +				}
> +				argv_array_clear(&argv);
> +				break;
> +			}
> +
> +			if (one_of(word.buf, terms->term_good,
> +			    terms->term_bad, "skip", NULL)) {
> +				if (bisect_write(word.buf, line.buf+pos, terms, 0)) {
> +					res = -1;
> +					goto finish;
					goto fail;
Show 11 quoted lines
> +				}
> +				break;
> +			}
> +
> +			if (!strcmp(word.buf, "terms")) {
> +				struct argv_array argv = ARGV_ARRAY_INIT;
> +				sq_dequote_to_argv_array(line.buf+pos, &argv);
> +				if (bisect_terms(terms, argv.argv, argv.argc)) {
> +					argv_array_clear(&argv);
> +					res = -1;
> +					goto finish;
					goto fail;
Show 6 quoted lines
> +				}
> +				argv_array_clear(&argv);
> +				break;
> +			}
> +
> +			error(_("?? what are you talking about?"));

I know this string is taken from the original source. However, even something like error(_("Replay file contains rubbish (\"%s\")"), word.buf) is more informative.

> +			res = -1;
> +			goto finish;
			goto fail;
> +		}
> +	}
> +	goto finish;

+fail: + res = -1;

I just wanted to make finally clear what I was referring to by the "goto fail" trick. :D

Also I think the readability could be improved by extracting the body of the outer while loop into an extra function replay_line(). Then most of my suggested "goto fail;" lines simply become "return -1;" :)

Show 7 quoted lines
> @@ -998,6 +1112,13 @@ int cmd_bisect__helper(...)
>   			die(_("--bisect-log requires 0 arguments"));
>   		res = bisect_log();
>   		break;
> +	case BISECT_REPLAY:
> +		if (argc != 1)
> +			die(_("--bisect-replay requires 1 argument"));

I'd keep the (already translated) string from the original source: "No logfile given"

Cheers,
  Stephan
Pranit Bauva· Dec 6, 2016, 23:02 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 23/27] bisect--helper: `bisect_replay` shell function in C

Hey Stephan,
On Tue, Nov 22, 2016 at 6:19 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
Show 7 quoted lines
> Okay Pranit,
>
> this is the last patch for me to review in this series.
>
> Now that I have a coarse overview of what you did, I have the general
> remark that imho the "terms" variable should simply be global instead of
> being passed around all the time.

In a personal conversation with my mentors, we thought it is the best fit to keep it in a struct and pass it around so that it is easier in libification.

Show 9 quoted lines
> I also had some other remarks but I forgot them... maybe they come to my
> mind again when I see patch series v16.
>
> I also want to remark again that I am not a Git developer and only
> reviewed this because of my interest in git-bisect. So some of my
> suggestions might conflict with other beliefs here. For example, I
> consider it very bad style to leak memory... but Git is rather written
> as a scripting tool than a genuine library, so perhaps many people here
> do not care about it as long as it works...

Thanks for taking out your time to review my series extremely carefully. I will try to post a v16 next week probably.

Show 40 quoted lines
> On 10/14/2016 04:14 PM, Pranit Bauva wrote:
>> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
>> index c18ca07..b367d8d 100644
>> --- a/builtin/bisect--helper.c
>> +++ b/builtin/bisect--helper.c
>> @@ -601,7 +602,6 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
>>                       terms->term_good = arg;
>>               } else if (!strcmp(arg, "--term-bad") ||
>>                        !strcmp(arg, "--term-new")) {
>> -                     const char *next_arg;
>
> This should already have been removed in patch 15/27, not here.
>
>> @@ -875,6 +875,117 @@ static int bisect_log(void)
>>       return status;
>>  }
>>
>> +static int get_next_word(const char *line, int pos, struct strbuf *word)
>> +{
>> +     int i, len = strlen(line), begin = 0;
>> +     strbuf_reset(word);
>> +     for (i = pos; i < len; i++) {
>> +             if (line[i] == ' ' && begin)
>> +                     return i + 1;
>> +
>> +             if (!begin)
>> +                     begin = 1;
>> +             strbuf_addch(word, line[i]);
>> +     }
>> +
>> +     return i;
>> +}
>> +
>> +static int bisect_replay(struct bisect_terms *terms, const char *filename)
>> +{
>> +     struct strbuf line = STRBUF_INIT;
>> +     struct strbuf word = STRBUF_INIT;
>> +     FILE *fp = NULL;
>
> (The initialization is not necessary here.)

Um. I think it is. Otherwise if it goes to the finish block before you try to operate on fp, it will cause a seg fault.

Show 7 quoted lines
>> +     int res = 0;
>> +
>> +     if (is_empty_or_missing_file(filename)) {
>> +             error(_("no such file with name '%s' exists"), filename);
>
> The error message is misleading if the file exists but is empty.
> Maybe something like "cannot read file '%s' for replaying"?
Okay will change.
Show 40 quoted lines
>> +             res = -1;
>> +             goto finish;
>
>                 goto fail;
> :D
>
>> +     }
>> +
>> +     if (bisect_reset(NULL)) {
>> +             res = -1;
>> +             goto finish;
>
>                 goto fail;
>
>> +     }
>> +
>> +     fp = fopen(filename, "r");
>> +     if (!fp) {
>> +             res = -1;
>> +             goto finish;
>
>                 goto fail;
>
>> +     }
>> +
>> +     while (strbuf_getline(&line, fp) != EOF) {
>> +             int pos = 0;
>> +             while (pos < line.len) {
>> +                     pos = get_next_word(line.buf, pos, &word);
>> +
>> +                     if (!strcmp(word.buf, "git")) {
>> +                             continue;
>> +                     } else if (!strcmp(word.buf, "git-bisect")) {
>> +                             continue;
>> +                     } else if (!strcmp(word.buf, "bisect")) {
>> +                             continue;
>> +                     } else if (!strcmp(word.buf, "#")) {
>> +                             break;
>
> Maybe it is more robust to check whether word.buf begins with #
Assuming that you meant "# ", yes.
Show 58 quoted lines
>> +                     }
>> +
>> +                     get_terms(terms);
>> +                     if (check_and_set_terms(terms, word.buf)) {
>> +                             res = -1;
>> +                             goto finish;
>
>                                 goto fail;
>
>> +                     }
>> +
>> +                     if (!strcmp(word.buf, "start")) {
>> +                             struct argv_array argv = ARGV_ARRAY_INIT;
>> +                             sq_dequote_to_argv_array(line.buf+pos, &argv);
>> +                             if (bisect_start(terms, 0, argv.argv, argv.argc)) {
>> +                                     argv_array_clear(&argv);
>> +                                     res = -1;
>> +                                     goto finish;
>
>                                         goto fail;
>
>> +                             }
>> +                             argv_array_clear(&argv);
>> +                             break;
>> +                     }
>> +
>> +                     if (one_of(word.buf, terms->term_good,
>> +                         terms->term_bad, "skip", NULL)) {
>> +                             if (bisect_write(word.buf, line.buf+pos, terms, 0)) {
>> +                                     res = -1;
>> +                                     goto finish;
>
>                                         goto fail;
>
>> +                             }
>> +                             break;
>> +                     }
>> +
>> +                     if (!strcmp(word.buf, "terms")) {
>> +                             struct argv_array argv = ARGV_ARRAY_INIT;
>> +                             sq_dequote_to_argv_array(line.buf+pos, &argv);
>> +                             if (bisect_terms(terms, argv.argv, argv.argc)) {
>> +                                     argv_array_clear(&argv);
>> +                                     res = -1;
>> +                                     goto finish;
>
>                                         goto fail;
>
>> +                             }
>> +                             argv_array_clear(&argv);
>> +                             break;
>> +                     }
>> +
>> +                     error(_("?? what are you talking about?"));
>
> I know this string is taken from the original source. However, even
> something like error(_("Replay file contains rubbish (\"%s\")"),
> word.buf) is more informative.
Okay will change.
Show 14 quoted lines
>> +                     res = -1;
>> +                     goto finish;
>
>                         goto fail;
>
>> +             }
>> +     }
>> +     goto finish;
>
> +fail:
> +       res = -1;
>
> I just wanted to make finally clear what I was referring to by the
> "goto fail" trick. :D
I got it. Will update accordingly.
Show 14 quoted lines
> Also I think the readability could be improved by extracting the body of
> the outer while loop into an extra function replay_line(). Then most of
> my suggested "goto fail;" lines simply become "return -1;" :)
>
>> @@ -998,6 +1112,13 @@ int cmd_bisect__helper(...)
>>                       die(_("--bisect-log requires 0 arguments"));
>>               res = bisect_log();
>>               break;
>> +     case BISECT_REPLAY:
>> +             if (argc != 1)
>> +                     die(_("--bisect-replay requires 1 argument"));
>
> I'd keep the (already translated) string from the original source:
> "No logfile given"
Sure

Regards. Pranit Bauva

Stephan Beyer· Dec 6, 2016, 23:20 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 23/27] bisect--helper: `bisect_replay` shell function in C

Hey Pranit,
On 12/07/2016 12:02 AM, Pranit Bauva wrote:
Show 10 quoted lines
>>> +static int bisect_replay(struct bisect_terms *terms, const char *filename)
>>> +{
>>> +     struct strbuf line = STRBUF_INIT;
>>> +     struct strbuf word = STRBUF_INIT;
>>> +     FILE *fp = NULL;
>>
>> (The initialization is not necessary here.)
> 
> Um. I think it is. Otherwise if it goes to the finish block before you
> try to operate on fp, it will cause a seg fault.
You are right, thanks!
Show 17 quoted lines
>>> +     while (strbuf_getline(&line, fp) != EOF) {
>>> +             int pos = 0;
>>> +             while (pos < line.len) {
>>> +                     pos = get_next_word(line.buf, pos, &word);
>>> +
>>> +                     if (!strcmp(word.buf, "git")) {
>>> +                             continue;
>>> +                     } else if (!strcmp(word.buf, "git-bisect")) {
>>> +                             continue;
>>> +                     } else if (!strcmp(word.buf, "bisect")) {
>>> +                             continue;
>>> +                     } else if (!strcmp(word.buf, "#")) {
>>> +                             break;
>>
>> Maybe it is more robust to check whether word.buf begins with #
> 
> Assuming that you meant "# ", yes.

No, if I get it right "# " can never occur because the word.buf never contains a space. What I meant was that you are currently ignoring everything after a "# ", so comments like

# foo

are ignored. However, imagine a user changes the file by hand (he probably should not do it but, hey, it's git: unixy, hacky ... and he thinks he knows what he does) and then we have in the file something like

#foo

which makes perfectly sense when you are used to programming languages with # as comment-till-eol marker. The problem is that your current code does expect "#" as a single word and would hence not recognize #foo as a comment.

I hope I made it clear why I suggested to test if the word *begins* with "#" (not "# ").

~Stephan
Stephan Beyer· Dec 6, 2016, 23:40 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 23/27] bisect--helper: `bisect_replay` shell function in C

Hi Pranit and Christian and Lars ;)
On 12/07/2016 12:02 AM, Pranit Bauva wrote:
Show 12 quoted lines
> On Tue, Nov 22, 2016 at 6:19 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
>> Okay Pranit,
>>
>> this is the last patch for me to review in this series.
>>
>> Now that I have a coarse overview of what you did, I have the general
>> remark that imho the "terms" variable should simply be global instead of
>> being passed around all the time.
> 
> In a personal conversation with my mentors, we thought it is the best
> fit to keep it in a struct and pass it around so that it is easier in
> libification.

I guess you had that conversation at the beginning of the project and I think that at that stage I would have recommended it that way, too.

However, looking at the v15 bisect--helper.c, it looks like it is rather a pain to do it that way. I mean, you use the terms like they were global variables: you initialize them in cmd_bisect__helper() and then you always pass them around; you update them "globally", never using restricted scope, never ever use a second instantiation besides the one of cmd_bisect__helper(). These are all signs that *in the current code* using (static) global variables is the better way (making the code simpler).

The libification argument may be worth considering, though. I am not sure if there is really a use-case where you need two different instantiations of the terms, *except* if the lib allows bisecting with different terms at the same time (in different repos, of course). Is the lib "aware" of such use-cases like doing stuff in different repos at the same time? If that's the case, not using global variables is the right thing to do. In any other case I can imagine, I'd suggest to use global variables.

~Stephan
Christian Couder· Dec 7, 2016, 13:15 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 23/27] bisect--helper: `bisect_replay` shell function in C

Hi Stephan,
On Wed, Dec 7, 2016 at 12:40 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
Show 32 quoted lines
> Hi Pranit and Christian and Lars ;)
>
> On 12/07/2016 12:02 AM, Pranit Bauva wrote:
>> On Tue, Nov 22, 2016 at 6:19 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
>>>
>>> Now that I have a coarse overview of what you did, I have the general
>>> remark that imho the "terms" variable should simply be global instead of
>>> being passed around all the time.
>>
>> In a personal conversation with my mentors, we thought it is the best
>> fit to keep it in a struct and pass it around so that it is easier in
>> libification.
>
> I guess you had that conversation at the beginning of the project and I
> think that at that stage I would have recommended it that way, too.
>
> However, looking at the v15 bisect--helper.c, it looks like it is rather
> a pain to do it that way. I mean, you use the terms like they were
> global variables: you initialize them in cmd_bisect__helper() and then
> you always pass them around; you update them "globally", never using
> restricted scope, never ever use a second instantiation besides the one
> of cmd_bisect__helper(). These are all signs that *in the current code*
> using (static) global variables is the better way (making the code simpler).
>
> The libification argument may be worth considering, though. I am not
> sure if there is really a use-case where you need two different
> instantiations of the terms, *except* if the lib allows bisecting with
> different terms at the same time (in different repos, of course). Is the
> lib "aware" of such use-cases like doing stuff in different repos at the
> same time? If that's the case, not using global variables is the right
> thing to do. In any other case I can imagine, I'd suggest to use global
> variables.

Yeah, I wanted to avoid global variables when there is no very good reason for them to avoid possible libification work later. And I don't think passing one parameter around to many functions is a big burden.

Thanks for your reviews, Christian.

Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 04/27] bisect--helper: `bisect_clean_state` shell function in C

Reimplement `bisect_clean_state` shell function in C and add a `bisect-clean-state` subcommand to `git bisect--helper` to call it from git-bisect.sh .

Using `--bisect-clean-state` subcommand is a 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 bisect_reset() and bisect_start().

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 bisect.c                 | 43 +++++++++++++++++++++++++++++++++++++++++++
 bisect.h                 |  2 ++
 builtin/bisect--helper.c | 14 +++++++++++++-
 git-bisect.sh            | 26 +++-----------------------
 4 files changed, 61 insertions(+), 24 deletions(-)
Show changes to 4 files +61 −25

bisect.c, bisect.h, builtin/bisect--helper.c, git-bisect.sh

diff --git a/bisect.c b/bisect.c
index 6f512c2..45d598d 100644
--- a/bisect.c
+++ b/bisect.c
@@ -430,6 +430,12 @@ static int read_bisect_refs(void)
 
 static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
 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 GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
+static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
+static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
+static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
+static GIT_PATH_FUNC(git_path_head_name, "head-name")
 
 static void read_bisect_paths(struct argv_array *array)
 {
@@ -1040,3 +1046,40 @@ int estimate_bisect_steps(int all)
 
 	return (e < 3 * x) ? n : n - 1;
 }
+
+static int mark_for_removal(const char *refname, const struct object_id *oid,
+			    int flag, void *cb_data)
+{
+	struct string_list *refs = cb_data;
+	char *ref = xstrfmt("refs/bisect%s", refname);
+	string_list_append(refs, ref);
+	return 0;
+}
+
+int bisect_clean_state(void)
+{
+	int result = 0;
+
+	/* There may be some refs packed during bisection */
+	struct string_list refs_for_removal = STRING_LIST_INIT_NODUP;
+	for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal);
+	string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD"));
+	result = delete_refs(&refs_for_removal, REF_NODEREF);
+	refs_for_removal.strdup_strings = 1;
+	string_list_clear(&refs_for_removal, 0);
+	unlink_or_warn(git_path_bisect_expected_rev());
+	unlink_or_warn(git_path_bisect_ancestors_ok());
+	unlink_or_warn(git_path_bisect_log());
+	unlink_or_warn(git_path_bisect_names());
+	unlink_or_warn(git_path_bisect_run());
+	unlink_or_warn(git_path_bisect_terms());
+	/* Cleanup head-name if it got left by an old version of git-bisect */
+	unlink_or_warn(git_path_head_name());
+	/*
+	 * Cleanup BISECT_START last to support the --no-checkout option
+	 * introduced in the commit 4796e823a.
+	 */
+	unlink_or_warn(git_path_bisect_start());
+
+	return result;
+}
diff --git a/bisect.h b/bisect.h
index acd12ef..0ae63d4 100644
--- a/bisect.h
+++ b/bisect.h
@@ -28,4 +28,6 @@ extern int estimate_bisect_steps(int all);
 
 extern void read_bisect_terms(const char **bad, const char **good);
 
+extern int bisect_clean_state(void);
+
 #endif
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 65cf519..4254d61 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -5,10 +5,15 @@
 #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 GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
+static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
 
 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
 };
 
@@ -83,7 +88,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
 	enum {
 		NEXT_ALL = 1,
-		WRITE_TERMS
+		WRITE_TERMS,
+		BISECT_CLEAN_STATE
 	} cmdmode = 0;
 	int no_checkout = 0;
 	struct option options[] = {
@@ -91,6 +97,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 			 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_BOOL(0, "no-checkout", &no_checkout,
 			 N_("update BISECT_HEAD instead of checking out the current commit")),
 		OPT_END()
@@ -109,6 +117,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		if (argc != 2)
 			die(_("--write-terms requires two arguments"));
 		return write_terms(argv[0], argv[1]);
+	case BISECT_CLEAN_STATE:
+		if (argc != 0)
+			die(_("--bisect-clean-state requires no arguments"));
+		return bisect_clean_state();
 	default:
 		die("BUG: unknown subcommand '%d'", cmdmode);
 	}
diff --git a/git-bisect.sh b/git-bisect.sh
index 9ef6cb8..f1202df 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -186,7 +186,7 @@ bisect_start() {
 	#
 	# Get rid of any old bisect state.
 	#
-	bisect_clean_state || exit
+	git bisect--helper --bisect-clean-state || exit
 
 	#
 	# Change state.
@@ -195,7 +195,7 @@ bisect_start() {
 	# We have to trap this to be able to clean up using
 	# "bisect_clean_state".
 	#
-	trap 'bisect_clean_state' 0
+	trap 'git bisect--helper --bisect-clean-state' 0
 	trap 'exit 255' 1 2 3 15
 
 	#
@@ -430,27 +430,7 @@ bisect_reset() {
 		die "$(eval_gettext "Could not check out original HEAD '\$branch'.
 Try 'git bisect reset <commit>'.")"
 	fi
-	bisect_clean_state
-}
-
-bisect_clean_state() {
-	# There may be some refs packed during bisection.
-	git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
-	while read ref hash
-	do
-		git update-ref -d $ref $hash || exit
-	done
-	rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
-	rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
-	rm -f "$GIT_DIR/BISECT_LOG" &&
-	rm -f "$GIT_DIR/BISECT_NAMES" &&
-	rm -f "$GIT_DIR/BISECT_RUN" &&
-	rm -f "$GIT_DIR/BISECT_TERMS" &&
-	# Cleanup head-name if it got left by an old version of git-bisect
-	rm -f "$GIT_DIR/head-name" &&
-	git update-ref -d --no-deref BISECT_HEAD &&
-	# clean up BISECT_START last
-	rm -f "$GIT_DIR/BISECT_START"
+	git bisect--helper --bisect-clean-state || exit
 }
 
 bisect_replay () {

--
https://github.com/git/git/pull/287
Stephan Beyer· Nov 15, 2016, 21:09 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 04/27] bisect--helper: `bisect_clean_state` shell function in C

Hi,
On 10/14/2016 04:14 PM, Pranit Bauva wrote:
Show 29 quoted lines
> diff --git a/bisect.c b/bisect.c
> index 6f512c2..45d598d 100644
> --- a/bisect.c
> +++ b/bisect.c
> @@ -1040,3 +1046,40 @@ int estimate_bisect_steps(int all)
>  
>  	return (e < 3 * x) ? n : n - 1;
>  }
> +
> +static int mark_for_removal(const char *refname, const struct object_id *oid,
> +			    int flag, void *cb_data)
> +{
> +	struct string_list *refs = cb_data;
> +	char *ref = xstrfmt("refs/bisect%s", refname);
> +	string_list_append(refs, ref);
> +	return 0;
> +}
> +
> +int bisect_clean_state(void)
> +{
> +	int result = 0;
> +
> +	/* There may be some refs packed during bisection */
> +	struct string_list refs_for_removal = STRING_LIST_INIT_NODUP;
> +	for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal);
> +	string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD"));
> +	result = delete_refs(&refs_for_removal, REF_NODEREF);
> +	refs_for_removal.strdup_strings = 1;
> +	string_list_clear(&refs_for_removal, 0);

Does it have advantages to populate a list (with duplicated strings), hand it to delete_refs(), and clear the list (and strings), instead of just doing a single delete_ref() (or whatever name the singular function has) in the callback? I am only seeing the disadvantage: a list with duped strings.

> +	unlink_or_warn(git_path_bisect_expected_rev());
[...]
> +	unlink_or_warn(git_path_bisect_start());

Comparing it with the original shell code (which uses "rm -f"), I was wondering a little after reading the function name unlink_or_warn() here... Looking it up helped: despite its name, unlink_or_warn() is really the function you have to use here ;D

Show 12 quoted lines
> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
> index 65cf519..4254d61 100644
> --- a/builtin/bisect--helper.c
> +++ b/builtin/bisect--helper.c
> @@ -5,10 +5,15 @@
>  #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 GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
> +static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")

This is perhaps a non-issue, but you do not need any of these new path functions in *this* patch. I think nobody really cares, though, because you will need them later.

Cheers Stephan

Junio C Hamano· Nov 15, 2016, 21:40 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 04/27] bisect--helper: `bisect_clean_state` shell function in C

Stephan Beyer <s-beyer@gmx.net> writes:
Show 16 quoted lines
>> +int bisect_clean_state(void)
>> +{
>> +	int result = 0;
>> +
>> +	/* There may be some refs packed during bisection */
>> +	struct string_list refs_for_removal = STRING_LIST_INIT_NODUP;
>> +	for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal);
>> +	string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD"));
>> +	result = delete_refs(&refs_for_removal, REF_NODEREF);
>> +	refs_for_removal.strdup_strings = 1;
>> +	string_list_clear(&refs_for_removal, 0);
>
> Does it have advantages to populate a list (with duplicated strings),
> hand it to delete_refs(), and clear the list (and strings), instead of
> just doing a single delete_ref() (or whatever name the singular function
> has) in the callback?

Depending on ref backends, removing multiple refs may be a lot more efficient than calling a single ref removal for the same set of refs, and the comment upfront I think hints that the code was written in the way exactly with that in mind. Removing N refs from a packed refs file will involve a loop that runs N times, each iteration loading the file, locating an entry among possibly 100s of refs to remove, and then rewriting the file.

Besides, it is bad taste to delete each individual item being iterated over in an interator in general, isn't it?

Stephan Beyer· Nov 15, 2016, 21:53 UTC · re: Junio C Hamano · lore

Re: [PATCH v15 04/27] bisect--helper: `bisect_clean_state` shell function in C

On 11/15/2016 10:40 PM, Junio C Hamano wrote:
Show 26 quoted lines
> Stephan Beyer <s-beyer@gmx.net> writes:
> 
>>> +int bisect_clean_state(void)
>>> +{
>>> +	int result = 0;
>>> +
>>> +	/* There may be some refs packed during bisection */
>>> +	struct string_list refs_for_removal = STRING_LIST_INIT_NODUP;
>>> +	for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal);
>>> +	string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD"));
>>> +	result = delete_refs(&refs_for_removal, REF_NODEREF);
>>> +	refs_for_removal.strdup_strings = 1;
>>> +	string_list_clear(&refs_for_removal, 0);
>>
>> Does it have advantages to populate a list (with duplicated strings),
>> hand it to delete_refs(), and clear the list (and strings), instead of
>> just doing a single delete_ref() (or whatever name the singular function
>> has) in the callback?
> 
> Depending on ref backends, removing multiple refs may be a lot more
> efficient than calling a single ref removal for the same set of
> refs, and the comment upfront I think hints that the code was
> written in the way exactly with that in mind.  Removing N refs from
> a packed refs file will involve a loop that runs N times, each
> iteration loading the file, locating an entry among possibly 100s of
> refs to remove, and then rewriting the file.

Great, that's the reply I wanted to hear (and that I've considered but wasn't sure of) ;) [I did not want to dig into the sources and check if delete_refs() does something smarter than invoking delete_ref() on each item of the list.]

~Stephan
Pranit Bauva· Nov 16, 2016, 16:49 UTC · re: Junio C Hamano · lore

Re: [PATCH v15 04/27] bisect--helper: `bisect_clean_state` shell function in C

Hey Junio,
On Wed, Nov 16, 2016 at 3:10 AM, Junio C Hamano <gitster@pobox.com> wrote:
Show 31 quoted lines
>
> Stephan Beyer <s-beyer@gmx.net> writes:
>
> >> +int bisect_clean_state(void)
> >> +{
> >> +    int result = 0;
> >> +
> >> +    /* There may be some refs packed during bisection */
> >> +    struct string_list refs_for_removal = STRING_LIST_INIT_NODUP;
> >> +    for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal);
> >> +    string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD"));
> >> +    result = delete_refs(&refs_for_removal, REF_NODEREF);
> >> +    refs_for_removal.strdup_strings = 1;
> >> +    string_list_clear(&refs_for_removal, 0);
> >
> > Does it have advantages to populate a list (with duplicated strings),
> > hand it to delete_refs(), and clear the list (and strings), instead of
> > just doing a single delete_ref() (or whatever name the singular function
> > has) in the callback?
>
> Depending on ref backends, removing multiple refs may be a lot more
> efficient than calling a single ref removal for the same set of
> refs, and the comment upfront I think hints that the code was
> written in the way exactly with that in mind.  Removing N refs from
> a packed refs file will involve a loop that runs N times, each
> iteration loading the file, locating an entry among possibly 100s of
> refs to remove, and then rewriting the file.
>
> Besides, it is bad taste to delete each individual item being
> iterated over in an interator in general, isn't it?
>

Not just that, deleting a ref inside for_each*() is illegal because it builds some kind of index and that is spoiled if anything is deleted in between. Thus it gives a seg fault. See this[1]. I did the same mistake when making this patch and I was confused about that was happening but then Michael Haggerty pointed this out[2].

[1]: https://github.com/git/git/blob/v2.11.0-rc1/refs.h#L183-L191 [2]: http://public-inbox.org/git/574D122F.7080608@alum.mit.edu/

Regards, Pranit Bauva

Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 09/27] bisect--helper: `bisect_write` shell function in C

Reimplement the `bisect_write` shell function in C and add a `bisect-write` subcommand to `git bisect--helper` to call it from git-bisect.sh

Using `--bisect-write` subcommand is a temporary measure to port shell function in 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.

Note: bisect_write() uses two variables namely TERM_GOOD and TERM_BAD
from the global shell script thus we need to pass it to the subcommand
using the arguments. We then store them in a struct bisect_terms and
pass the memory address around functions.
Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 94 ++++++++++++++++++++++++++++++++++++++++++++----
 git-bisect.sh            | 25 +++----------
 2 files changed, 91 insertions(+), 28 deletions(-)
Show changes to 2 files +91 −29

builtin/bisect--helper.c, git-bisect.sh

diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index c542e8b..3f19b68 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -19,9 +19,15 @@ static const char * const git_bisect_helper_usage[] = {
 	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 <state> <revision> <TERM_GOOD> <TERM_BAD> [<nolog>]"),
 	NULL
 };
 
+struct bisect_terms {
+	const char *term_good;
+	const char *term_bad;
+};
+
 /*
  * Check whether the string `term` belongs to the set of strings
  * included in the variable arguments.
@@ -149,6 +155,63 @@ static int check_expected_revs(const char **revs, int rev_nr)
 	return 0;
 }
 
+static int bisect_write(const char *state, const char *rev,
+			const struct bisect_terms *terms, int nolog)
+{
+	struct strbuf tag = STRBUF_INIT;
+	struct strbuf commit_name = STRBUF_INIT;
+	struct object_id oid;
+	struct commit *commit;
+	struct pretty_print_context pp = {0};
+	FILE *fp = NULL;
+	int retval = 0;
+
+	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 {
+		error(_("Bad bisect_write argument: %s"), state);
+		retval = -1;
+		goto finish;
+	}
+
+	if (get_oid(rev, &oid)) {
+		error(_("couldn't get the oid of the rev '%s'"), rev);
+		retval = -1;
+		goto finish;
+	}
+
+	if (update_ref(NULL, tag.buf, oid.hash, NULL, 0,
+		       UPDATE_REFS_MSG_ON_ERR)) {
+		retval = -1;
+		goto finish;
+	}
+
+	fp = fopen(git_path_bisect_log(), "a");
+	if (!fp) {
+		error_errno(_("couldn't open the file '%s'"), git_path_bisect_log());
+		retval = -1;
+		goto finish;
+	}
+
+	commit = lookup_commit_reference(oid.hash);
+	format_commit_message(commit, "%s", &commit_name, &pp);
+	fprintf(fp, "# %s: [%s] %s\n", state, sha1_to_hex(oid.hash),
+		commit_name.buf);
+
+	if (!nolog)
+		fprintf(fp, "git bisect %s %s\n", state, rev);
+
+	goto finish;
+finish:
+	if (fp)
+		fclose(fp);
+	strbuf_release(&tag);
+	strbuf_release(&commit_name);
+	return retval;
+}
+
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
 	enum {
@@ -156,9 +219,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		WRITE_TERMS,
 		BISECT_CLEAN_STATE,
 		BISECT_RESET,
-		CHECK_EXPECTED_REVS
+		CHECK_EXPECTED_REVS,
+		BISECT_WRITE
 	} cmdmode = 0;
-	int no_checkout = 0;
+	int no_checkout = 0, res = 0;
 	struct option options[] = {
 		OPT_CMDMODE(0, "next-all", &cmdmode,
 			 N_("perform 'git bisect next'"), NEXT_ALL),
@@ -170,10 +234,13 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 			 N_("reset the bisection state"), BISECT_RESET),
 		OPT_CMDMODE(0, "check-expected-revs", &cmdmode,
 			 N_("check for expected revs"), CHECK_EXPECTED_REVS),
+		OPT_CMDMODE(0, "bisect-write", &cmdmode,
+			 N_("write out the bisection state in BISECT_LOG"), BISECT_WRITE),
 		OPT_BOOL(0, "no-checkout", &no_checkout,
 			 N_("update BISECT_HEAD instead of checking out the current commit")),
 		OPT_END()
 	};
+	struct bisect_terms terms;
 
 	argc = parse_options(argc, argv, prefix, options,
 			     git_bisect_helper_usage, 0);
@@ -182,24 +249,37 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		usage_with_options(git_bisect_helper_usage, options);
 
 	switch (cmdmode) {
+	int nolog;
 	case NEXT_ALL:
 		return bisect_next_all(prefix, no_checkout);
 	case WRITE_TERMS:
 		if (argc != 2)
 			die(_("--write-terms requires two arguments"));
-		return write_terms(argv[0], argv[1]);
+		res = write_terms(argv[0], argv[1]);
+		break;
 	case BISECT_CLEAN_STATE:
 		if (argc != 0)
 			die(_("--bisect-clean-state requires no arguments"));
-		return bisect_clean_state();
+		res = bisect_clean_state();
+		break;
 	case BISECT_RESET:
 		if (argc > 1)
 			die(_("--bisect-reset requires either zero or one arguments"));
-		return bisect_reset(argc ? argv[0] : NULL);
+		res = bisect_reset(argc ? argv[0] : NULL);
+		break;
 	case CHECK_EXPECTED_REVS:
-		return check_expected_revs(argv, argc);
+		res = check_expected_revs(argv, argc);
+		break;
+	case BISECT_WRITE:
+		if (argc != 4 && argc != 5)
+			die(_("--bisect-write requires either 4 or 5 arguments"));
+		nolog = (argc == 5) && !strcmp(argv[4], "nolog");
+		terms.term_good = xstrdup(argv[2]);
+		terms.term_bad = xstrdup(argv[3]);
+		res = bisect_write(argv[0], argv[1], &terms, nolog);
+		break;
 	default:
 		die("BUG: unknown subcommand '%d'", cmdmode);
 	}
-	return 0;
+	return res;
 }
diff --git a/git-bisect.sh b/git-bisect.sh
index c3e43248..dfdec33 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -144,7 +144,7 @@ bisect_start() {
 		0) state=$TERM_BAD ; bad_seen=1 ;;
 		*) state=$TERM_GOOD ;;
 		esac
-		eval="$eval bisect_write '$state' '$rev' 'nolog' &&"
+		eval="$eval git bisect--helper --bisect-write '$state' '$rev' '$TERM_GOOD' '$TERM_BAD' 'nolog' &&"
 	done
 	#
 	# Verify HEAD.
@@ -220,23 +220,6 @@ bisect_start() {
 	trap '-' 0
 }
 
-bisect_write() {
-	state="$1"
-	rev="$2"
-	nolog="$3"
-	case "$state" in
-		"$TERM_BAD")
-			tag="$state" ;;
-		"$TERM_GOOD"|skip)
-			tag="$state"-"$rev" ;;
-		*)
-			die "$(eval_gettext "Bad bisect_write argument: \$state")" ;;
-	esac
-	git update-ref "refs/bisect/$tag" "$rev" || exit
-	echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG"
-	test -n "$nolog" || echo "git bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
-}
-
 bisect_skip() {
 	all=''
 	for arg in "$@"
@@ -263,7 +246,7 @@ bisect_state() {
 		bisected_head=$(bisect_head)
 		rev=$(git rev-parse --verify "$bisected_head") ||
 			die "$(eval_gettext "Bad rev input: \$bisected_head")"
-		bisect_write "$state" "$rev"
+		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
@@ -276,7 +259,7 @@ bisect_state() {
 		done
 		for rev in $hash_list
 		do
-			bisect_write "$state" "$rev"
+			git bisect--helper --bisect-write "$state" "$rev" "$TERM_GOOD" "$TERM_BAD" || exit
 		done
 		git bisect--helper --check-expected-revs $hash_list ;;
 	*,"$TERM_BAD")
@@ -413,7 +396,7 @@ bisect_replay () {
 			cmd="bisect_start $rev"
 			eval "$cmd" ;;
 		"$TERM_GOOD"|"$TERM_BAD"|skip)
-			bisect_write "$command" "$rev" ;;
+			git bisect--helper --bisect-write "$command" "$rev" "$TERM_GOOD" "$TERM_BAD" || exit;;
 		terms)
 			bisect_terms $rev ;;
 		*)

--
https://github.com/git/git/pull/287
Stephan Beyer· Nov 17, 2016, 09:40 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 09/27] bisect--helper: `bisect_write` shell function in C

Hi,
I've only got some minors to mention here ;)
On 10/14/2016 04:14 PM, Pranit Bauva wrote:
Show 11 quoted lines
> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
> index c542e8b..3f19b68 100644
> --- a/builtin/bisect--helper.c
> +++ b/builtin/bisect--helper.c
> @@ -19,9 +19,15 @@ static const char * const git_bisect_helper_usage[] = {
>  	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 <state> <revision> <TERM_GOOD> <TERM_BAD> [<nolog>]"),
>  	NULL
>  };

I wouldn't write "<TERM_GOOD <TERM_BAD>" in capital letters. I'd use something like "<good_term> <bad_term>" as you have used for --write-terms. Note that "git bisect --help" uses "<term-old> <term-new>" in that context.

Show 36 quoted lines
> @@ -149,6 +155,63 @@ static int check_expected_revs(const char **revs, int rev_nr)
>  	return 0;
>  }
>  
> +static int bisect_write(const char *state, const char *rev,
> +			const struct bisect_terms *terms, int nolog)
> +{
> +	struct strbuf tag = STRBUF_INIT;
> +	struct strbuf commit_name = STRBUF_INIT;
> +	struct object_id oid;
> +	struct commit *commit;
> +	struct pretty_print_context pp = {0};
> +	FILE *fp = NULL;
> +	int retval = 0;
> +
> +	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 {
> +		error(_("Bad bisect_write argument: %s"), state);
> +		retval = -1;
> +		goto finish;
> +	}
> +
> +	if (get_oid(rev, &oid)) {
> +		error(_("couldn't get the oid of the rev '%s'"), rev);
> +		retval = -1;
> +		goto finish;
> +	}
> +
> +	if (update_ref(NULL, tag.buf, oid.hash, NULL, 0,
> +		       UPDATE_REFS_MSG_ON_ERR)) {
> +		retval = -1;
> +		goto finish;
> +	}

I'd like to mention that the "goto fail;" trick could apply in this function, too.

Show 10 quoted lines
> @@ -156,9 +219,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>  		WRITE_TERMS,
>  		BISECT_CLEAN_STATE,
>  		BISECT_RESET,
> -		CHECK_EXPECTED_REVS
> +		CHECK_EXPECTED_REVS,
> +		BISECT_WRITE
>  	} cmdmode = 0;
> -	int no_checkout = 0;
> +	int no_checkout = 0, res = 0;

Why do you do this "direct return" -> "set res and return res" transition? You don't need it in this patch, you do not need it in the subsequent patches (you always set "res" exactly once after the initialization), and you don't need cleanup code in this function.

Show 9 quoted lines
>  	struct option options[] = {
>  		OPT_CMDMODE(0, "next-all", &cmdmode,
>  			 N_("perform 'git bisect next'"), NEXT_ALL),
> @@ -170,10 +234,13 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>  			 N_("reset the bisection state"), BISECT_RESET),
>  		OPT_CMDMODE(0, "check-expected-revs", &cmdmode,
>  			 N_("check for expected revs"), CHECK_EXPECTED_REVS),
> +		OPT_CMDMODE(0, "bisect-write", &cmdmode,
> +			 N_("write out the bisection state in BISECT_LOG"), BISECT_WRITE),

That info text is confusing, especially considering that there is a "nolog" option. I think the action of bisect-write is two-fold: (1) update the refs, (2) log.

Show 13 quoted lines
> @@ -182,24 +249,37 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>  		usage_with_options(git_bisect_helper_usage, options);
>  
>  	switch (cmdmode) {
> +	int nolog;
>  	case NEXT_ALL:
>  		return bisect_next_all(prefix, no_checkout);
>  	case WRITE_TERMS:
>  		if (argc != 2)
>  			die(_("--write-terms requires two arguments"));
> -		return write_terms(argv[0], argv[1]);
> +		res = write_terms(argv[0], argv[1]);
> +		break;
As indicated above, I think the direct "return ...;" is cleaner.
~Stephan
Pranit Bauva· Dec 6, 2016, 21:32 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 09/27] bisect--helper: `bisect_write` shell function in C

Hey Stephan,
On Thu, Nov 17, 2016 at 3:10 PM, Stephan Beyer <s-beyer@gmx.net> wrote:
Show 21 quoted lines
> Hi,
>
> I've only got some minors to mention here ;)
>
> On 10/14/2016 04:14 PM, Pranit Bauva wrote:
>> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
>> index c542e8b..3f19b68 100644
>> --- a/builtin/bisect--helper.c
>> +++ b/builtin/bisect--helper.c
>> @@ -19,9 +19,15 @@ static const char * const git_bisect_helper_usage[] = {
>>       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 <state> <revision> <TERM_GOOD> <TERM_BAD> [<nolog>]"),
>>       NULL
>>  };
>
> I wouldn't write "<TERM_GOOD <TERM_BAD>" in capital letters. I'd use
> something like "<good_term> <bad_term>" as you have used for
> --write-terms. Note that "git bisect --help" uses "<term-old>
> <term-new>" in that context.
Keeping it in small does give less strain to the eye ;)
Show 39 quoted lines
>> @@ -149,6 +155,63 @@ static int check_expected_revs(const char **revs, int rev_nr)
>>       return 0;
>>  }
>>
>> +static int bisect_write(const char *state, const char *rev,
>> +                     const struct bisect_terms *terms, int nolog)
>> +{
>> +     struct strbuf tag = STRBUF_INIT;
>> +     struct strbuf commit_name = STRBUF_INIT;
>> +     struct object_id oid;
>> +     struct commit *commit;
>> +     struct pretty_print_context pp = {0};
>> +     FILE *fp = NULL;
>> +     int retval = 0;
>> +
>> +     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 {
>> +             error(_("Bad bisect_write argument: %s"), state);
>> +             retval = -1;
>> +             goto finish;
>> +     }
>> +
>> +     if (get_oid(rev, &oid)) {
>> +             error(_("couldn't get the oid of the rev '%s'"), rev);
>> +             retval = -1;
>> +             goto finish;
>> +     }
>> +
>> +     if (update_ref(NULL, tag.buf, oid.hash, NULL, 0,
>> +                    UPDATE_REFS_MSG_ON_ERR)) {
>> +             retval = -1;
>> +             goto finish;
>> +     }
>
> I'd like to mention that the "goto fail;" trick could apply in this
> function, too.
Sure!
Show 15 quoted lines
>> @@ -156,9 +219,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>>               WRITE_TERMS,
>>               BISECT_CLEAN_STATE,
>>               BISECT_RESET,
>> -             CHECK_EXPECTED_REVS
>> +             CHECK_EXPECTED_REVS,
>> +             BISECT_WRITE
>>       } cmdmode = 0;
>> -     int no_checkout = 0;
>> +     int no_checkout = 0, res = 0;
>
> Why do you do this "direct return" -> "set res and return res" transition?
> You don't need it in this patch, you do not need it in the subsequent
> patches (you always set "res" exactly once after the initialization),
> and you don't need cleanup code in this function.

Initially I was using strbuf but then I switched to const char * according to Junio's suggestion. It seems that in this version I have forgot to free the terms.

Show 13 quoted lines
>>       struct option options[] = {
>>               OPT_CMDMODE(0, "next-all", &cmdmode,
>>                        N_("perform 'git bisect next'"), NEXT_ALL),
>> @@ -170,10 +234,13 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>>                        N_("reset the bisection state"), BISECT_RESET),
>>               OPT_CMDMODE(0, "check-expected-revs", &cmdmode,
>>                        N_("check for expected revs"), CHECK_EXPECTED_REVS),
>> +             OPT_CMDMODE(0, "bisect-write", &cmdmode,
>> +                      N_("write out the bisection state in BISECT_LOG"), BISECT_WRITE),
>
> That info text is confusing, especially considering that there is a
> "nolog" option. I think the action of bisect-write is two-fold: (1)
> update the refs, (2) log.

I agree that it is confusing. I couldn't find a better way to describe it and since this would be gone after the whole conversion, I didn't bother putting more efforts there.

Regards, Pranit Bauva

Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 20/27] bisect--helper: retire `--check-expected-revs` subcommand

The `--check-expected-revs` subcommand is no longer used in the shell script and the function `check_expected_revs()` is called from the C implementation of `bisect_next()`.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 6 ------
 1 file changed, 6 deletions(-)
Show changes to builtin/bisect--helper.c +0 −7
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 1481c6d..d5fe35b 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -864,7 +864,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 	enum {
 		WRITE_TERMS = 1,
 		BISECT_RESET,
-		CHECK_EXPECTED_REVS,
 		BISECT_WRITE,
 		CHECK_AND_SET_TERMS,
 		BISECT_NEXT_CHECK,
@@ -881,8 +880,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 			 N_("write the terms to .git/BISECT_TERMS"), WRITE_TERMS),
 		OPT_CMDMODE(0, "bisect-reset", &cmdmode,
 			 N_("reset the bisection state"), BISECT_RESET),
-		OPT_CMDMODE(0, "check-expected-revs", &cmdmode,
-			 N_("check for expected revs"), CHECK_EXPECTED_REVS),
 		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,
@@ -926,9 +923,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 			die(_("--bisect-reset requires either zero or one arguments"));
 		res = bisect_reset(argc ? argv[0] : NULL);
 		break;
-	case CHECK_EXPECTED_REVS:
-		res = check_expected_revs(argv, argc);
-		break;
 	case BISECT_WRITE:
 		if (argc != 4 && argc != 5)
 			die(_("--bisect-write requires either 4 or 5 arguments"));

--
https://github.com/git/git/pull/287
Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 12/27] bisect--helper: `get_terms` & `bisect_terms` shell function in C

Reimplement the `get_terms` and `bisect_terms` shell function in C and add `bisect-terms` subcommand to `git bisect--helper` to call it from git-bisect.sh .

Using `--bisect-terms` subcommand is a temporary measure to port shell function in 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.

Also use error() to report "no terms defined" and accordingly change the test in t6030.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c    | 72 +++++++++++++++++++++++++++++++++++++++++++--
 git-bisect.sh               | 35 ++--------------------
 t/t6030-bisect-porcelain.sh |  2 +-
 3 files changed, 73 insertions(+), 36 deletions(-)
Show changes to 3 files +73 −37

builtin/bisect--helper.c, git-bisect.sh, t/t6030-bisect-porcelain.sh

diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 317d671..6a5878c 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -23,6 +23,7 @@ static const char * const git_bisect_helper_usage[] = {
 	N_("git bisect--helper --bisect-write <state> <revision> <TERM_GOOD> <TERM_BAD> [<nolog>]"),
 	N_("git bisect--helper --bisect-check-and-set-terms <command> <TERM_GOOD> <TERM_BAD>"),
 	N_("git bisect--helper --bisect-next-check [<term>] <TERM_GOOD> <TERM_BAD"),
+	N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"),
 	NULL
 };
 
@@ -343,6 +344,65 @@ static int bisect_next_check(const struct bisect_terms *terms,
 	return retval;
 }
 
+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;
+	}
+	strbuf_getline_lf(&str, fp);
+	terms->term_bad = strbuf_detach(&str, NULL);
+	strbuf_getline_lf(&str, fp);
+	terms->term_good = strbuf_detach(&str, NULL);
+	goto finish;
+finish:
+	if (fp)
+		fclose(fp);
+	strbuf_release(&str);
+	return res;
+}
+
+static int bisect_terms(struct bisect_terms *terms, const char **argv, int argc)
+{
+	int i;
+	const char bisect_term_usage[] =
+"git bisect--helper --bisect-terms [--term-good | --term-bad | ]"
+"--term-old | --term-new";
+
+	if (get_terms(terms))
+		return error(_("no terms defined"));
+
+	if (argc > 1) {
+		usage(bisect_term_usage);
+		return -1;
+	}
+
+	if (argc == 0) {
+		printf(_("Your current terms are %s for the old state\nand "
+		       "%s for the new state.\n"), terms->term_good,
+		       terms->term_bad);
+		return 0;
+	}
+
+	for (i = 0; i < argc; i++) {
+		if (!strcmp(argv[i], "--term-good"))
+			printf("%s\n", terms->term_good);
+		else if (!strcmp(argv[i], "--term-bad"))
+			printf("%s\n", terms->term_bad);
+		else
+			die(_("invalid argument %s for 'git bisect "
+				  "terms'.\nSupported options are: "
+				  "--term-good|--term-old and "
+				  "--term-bad|--term-new."), argv[i]);
+	}
+
+	return 0;
+}
+
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
 	enum {
@@ -353,7 +413,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		CHECK_EXPECTED_REVS,
 		BISECT_WRITE,
 		CHECK_AND_SET_TERMS,
-		BISECT_NEXT_CHECK
+		BISECT_NEXT_CHECK,
+		BISECT_TERMS
 	} cmdmode = 0;
 	int no_checkout = 0, res = 0;
 	struct option options[] = {
@@ -373,6 +434,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 			 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,
+			 N_("print out the bisect terms"), BISECT_TERMS),
 		OPT_BOOL(0, "no-checkout", &no_checkout,
 			 N_("update BISECT_HEAD instead of checking out the current commit")),
 		OPT_END()
@@ -380,7 +443,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 	struct bisect_terms terms;
 
 	argc = parse_options(argc, argv, prefix, options,
-			     git_bisect_helper_usage, 0);
+			     git_bisect_helper_usage, PARSE_OPT_KEEP_UNKNOWN);
 
 	if (!cmdmode)
 		usage_with_options(git_bisect_helper_usage, options);
@@ -429,6 +492,11 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		terms.term_bad = xstrdup(argv[1]);
 		res = bisect_next_check(&terms, argc == 3 ? argv[2] : NULL);
 		break;
+	case BISECT_TERMS:
+		if (argc > 1)
+			die(_("--bisect-terms requires 0 or 1 argument"));
+		res = bisect_terms(&terms, argv, argc);
+		break;
 	default:
 		die("BUG: unknown subcommand '%d'", cmdmode);
 	}
diff --git a/git-bisect.sh b/git-bisect.sh
index fe6c9d0..d6c8b5a 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -355,7 +355,7 @@ bisect_replay () {
 		"$TERM_GOOD"|"$TERM_BAD"|skip)
 			git bisect--helper --bisect-write "$command" "$rev" "$TERM_GOOD" "$TERM_BAD" || exit;;
 		terms)
-			bisect_terms $rev ;;
+			git bisect--helper --bisect-terms $rev  || exit;;
 		*)
 			die "$(gettext "?? what are you talking about?")" ;;
 		esac
@@ -437,37 +437,6 @@ get_terms () {
 	fi
 }
 
-bisect_terms () {
-	get_terms
-	if ! test -s "$GIT_DIR/BISECT_TERMS"
-	then
-		die "$(gettext "no terms defined")"
-	fi
-	case "$#" in
-	0)
-		gettextln "Your current terms are $TERM_GOOD for the old state
-and $TERM_BAD for the new state."
-		;;
-	1)
-		arg=$1
-		case "$arg" in
-			--term-good|--term-old)
-				printf '%s\n' "$TERM_GOOD"
-				;;
-			--term-bad|--term-new)
-				printf '%s\n' "$TERM_BAD"
-				;;
-			*)
-				die "$(eval_gettext "invalid argument \$arg for 'git bisect terms'.
-Supported options are: --term-good|--term-old and --term-bad|--term-new.")"
-				;;
-		esac
-		;;
-	*)
-		usage ;;
-	esac
-}
-
 case "$#" in
 0)
 	usage ;;
@@ -498,7 +467,7 @@ case "$#" in
 	run)
 		bisect_run "$@" ;;
 	terms)
-		bisect_terms "$@" ;;
+		git bisect--helper --bisect-terms "$@" || exit;;
 	*)
 		usage ;;
 	esac
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 18e7998..e62e2a8 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -802,7 +802,7 @@ test_expect_success 'bisect terms needs 0 or 1 argument' '
 	test_must_fail git bisect terms only-one &&
 	test_must_fail git bisect terms 1 2 &&
 	test_must_fail git bisect terms 2>actual &&
-	echo "no terms defined" >expected &&
+	echo "error: no terms defined" >expected &&
 	test_i18ncmp expected actual
 '
 

--
https://github.com/git/git/pull/287
Stephan Beyer· Nov 17, 2016, 21:32 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 12/27] bisect--helper: `get_terms` & `bisect_terms` shell function in C

Hi,
On 10/14/2016 04:14 PM, Pranit Bauva wrote:
> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
> index 317d671..6a5878c 100644
> --- a/builtin/bisect--helper.c
> +++ b/builtin/bisect--helper.c
[...]
Show 6 quoted lines
> +static int bisect_terms(struct bisect_terms *terms, const char **argv, int argc)
> +{
> +	int i;
> +	const char bisect_term_usage[] =
> +"git bisect--helper --bisect-terms [--term-good | --term-bad | ]"
> +"--term-old | --term-new";
Three things:
(1) Is that indentation intentional?

(2) You have a "]" at the end of the first part of the string instead of the end of the second part.

(3) After the correction, bisect_term_usage and git_bisect_helper_usage[7] are the same strings. I don't recommend to use git_bisect_helper_usage[7] instead because keeping the index up-to-date is a maintenance hell. (At the end of your patch series it is a 3 instead of a 7.) However, if - for whatever reason - the usage of bisect--helper --bisect-terms changes, you always have to sync the two strings which is also nasty....

Show 8 quoted lines
> +
> +	if (get_terms(terms))
> +		return error(_("no terms defined"));
> +
> +	if (argc > 1) {
> +		usage(bisect_term_usage);
> +		return -1;
> +	}
...and since you only use it once, why not simply do something like
return error(_("--bisect-term requires exactly one argument"));
and drop the definition of bisect_term_usage.
Show 5 quoted lines
> +
> +	if (argc == 0) {
> +		printf(_("Your current terms are %s for the old state\nand "
> +		       "%s for the new state.\n"), terms->term_good,
> +		       terms->term_bad);

Very minor: It improves the readability if you'd split the string after the \n and put the "and "in the next line.

Show 13 quoted lines
> +		return 0;
> +	}
> +
> +	for (i = 0; i < argc; i++) {
> +		if (!strcmp(argv[i], "--term-good"))
> +			printf("%s\n", terms->term_good);
> +		else if (!strcmp(argv[i], "--term-bad"))
> +			printf("%s\n", terms->term_bad);
> +		else
> +			die(_("invalid argument %s for 'git bisect "
> +				  "terms'.\nSupported options are: "
> +				  "--term-good|--term-old and "
> +				  "--term-bad|--term-new."), argv[i]);

Hm, "return error(...)" and "die(...)" seems to be quasi-equivalent in this case. Because I am always looking from a library perspective, I'd prefer "return error(...)".

Show 9 quoted lines
> @@ -429,6 +492,11 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>  		terms.term_bad = xstrdup(argv[1]);
>  		res = bisect_next_check(&terms, argc == 3 ? argv[2] : NULL);
>  		break;
> +	case BISECT_TERMS:
> +		if (argc > 1)
> +			die(_("--bisect-terms requires 0 or 1 argument"));
> +		res = bisect_terms(&terms, argv, argc);
> +		break;
Also here: "terms" is leaking...
~Stephan
Pranit Bauva· Dec 6, 2016, 21:14 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 12/27] bisect--helper: `get_terms` & `bisect_terms` shell function in C

Hey Stephan,
On Fri, Nov 18, 2016 at 3:02 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
Show 18 quoted lines
> Hi,
>
> On 10/14/2016 04:14 PM, Pranit Bauva wrote:
>> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
>> index 317d671..6a5878c 100644
>> --- a/builtin/bisect--helper.c
>> +++ b/builtin/bisect--helper.c
> [...]
>> +static int bisect_terms(struct bisect_terms *terms, const char **argv, int argc)
>> +{
>> +     int i;
>> +     const char bisect_term_usage[] =
>> +"git bisect--helper --bisect-terms [--term-good | --term-bad | ]"
>> +"--term-old | --term-new";
>
> Three things:
>
> (1) Is that indentation intentional?

Yes it was intentional but now I cannot recollect why. I think it was because I found something similar. Nevertheless, I will fix this indentation/

> (2) You have a "]" at the end of the first part of the string instead of
> the end of the second part.
This should be corrected.
Show 22 quoted lines
> (3) After the correction, bisect_term_usage and
> git_bisect_helper_usage[7] are the same strings. I don't recommend to
> use git_bisect_helper_usage[7] instead because keeping the index
> up-to-date is a maintenance hell. (At the end of your patch series it is
> a 3 instead of a 7.) However, if - for whatever reason - the usage of
> bisect--helper --bisect-terms changes, you always have to sync the two
> strings which is also nasty....
>
>> +
>> +     if (get_terms(terms))
>> +             return error(_("no terms defined"));
>> +
>> +     if (argc > 1) {
>> +             usage(bisect_term_usage);
>> +             return -1;
>> +     }
>
> ...and since you only use it once, why not simply do something like
>
> return error(_("--bisect-term requires exactly one argument"));
>
> and drop the definition of bisect_term_usage.
Sure that would be better.
Show 8 quoted lines
>> +
>> +     if (argc == 0) {
>> +             printf(_("Your current terms are %s for the old state\nand "
>> +                    "%s for the new state.\n"), terms->term_good,
>> +                    terms->term_bad);
>
> Very minor: It improves the readability if you'd split the string after
> the \n and put the "and "in the next line.

Ah. This is because of the message. If I do the other way, then it won't match the output in one of the tests in t/t6030 thus, I am keeping it that way in order to avoid modifying the file t/t6030.

Show 17 quoted lines
>> +             return 0;
>> +     }
>> +
>> +     for (i = 0; i < argc; i++) {
>> +             if (!strcmp(argv[i], "--term-good"))
>> +                     printf("%s\n", terms->term_good);
>> +             else if (!strcmp(argv[i], "--term-bad"))
>> +                     printf("%s\n", terms->term_bad);
>> +             else
>> +                     die(_("invalid argument %s for 'git bisect "
>> +                               "terms'.\nSupported options are: "
>> +                               "--term-good|--term-old and "
>> +                               "--term-bad|--term-new."), argv[i]);
>
> Hm, "return error(...)" and "die(...)" seems to be quasi-equivalent in
> this case. Because I am always looking from a library perspective, I'd
> prefer "return error(...)".
I should use return error()
Show 11 quoted lines
>> @@ -429,6 +492,11 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>>               terms.term_bad = xstrdup(argv[1]);
>>               res = bisect_next_check(&terms, argc == 3 ? argv[2] : NULL);
>>               break;
>> +     case BISECT_TERMS:
>> +             if (argc > 1)
>> +                     die(_("--bisect-terms requires 0 or 1 argument"));
>> +             res = bisect_terms(&terms, argv, argc);
>> +             break;
>
> Also here: "terms" is leaking...
Will have to free it.
> ~Stephan
Stephan Beyer· Dec 6, 2016, 23:05 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 12/27] bisect--helper: `get_terms` & `bisect_terms` shell function in C

Hey Pranit,
On 12/06/2016 10:14 PM, Pranit Bauva wrote:
Show 12 quoted lines
>>> +
>>> +     if (argc == 0) {
>>> +             printf(_("Your current terms are %s for the old state\nand "
>>> +                    "%s for the new state.\n"), terms->term_good,
>>> +                    terms->term_bad);
>>
>> Very minor: It improves the readability if you'd split the string after
>> the \n and put the "and "in the next line.
> 
> Ah. This is because of the message. If I do the other way, then it
> won't match the output in one of the tests in t/t6030 thus, I am
> keeping it that way in order to avoid modifying the file t/t6030.

I think I was unclear here. I was referring to the coding/layouting style, not to the string. I mean like writing:

	printf(_("Your current terms are %s for the old state\n"
	         "and "%s for the new state.\n"),
	       terms->term_good, terms->term_bad);

The string fed to _() is the same, but it is split in a different (imho more readable) way: after the "\n", not after the "and ".

Show 10 quoted lines
>>> +                     die(_("invalid argument %s for 'git bisect "
>>> +                               "terms'.\nSupported options are: "
>>> +                               "--term-good|--term-old and "
>>> +                               "--term-bad|--term-new."), argv[i]);
>>
>> Hm, "return error(...)" and "die(...)" seems to be quasi-equivalent in
>> this case. Because I am always looking from a library perspective, I'd
>> prefer "return error(...)".
> 
> I should use return error()

When you reroll your patches, please also check if you always put _() around your error()s ;) (Hmmm... On the other hand, it might be arguable if translations are useful for errors that only occur when people hack git-bisect or use the bisect--helper directly... This makes me feel like all those errors should be prefixed by some "BUG: " marker since the ordinary user only sees them when there is a bug. But I don't feel in the position to decide or recommend such a thing, so it's just a thought.)

~Stephan
Pranit Bauva· Dec 7, 2016, 12:06 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 12/27] bisect--helper: `get_terms` & `bisect_terms` shell function in C

Hey Stephan,
On Wed, Dec 7, 2016 at 4:35 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
Show 25 quoted lines
> Hey Pranit,
>
> On 12/06/2016 10:14 PM, Pranit Bauva wrote:
>>>> +
>>>> +     if (argc == 0) {
>>>> +             printf(_("Your current terms are %s for the old state\nand "
>>>> +                    "%s for the new state.\n"), terms->term_good,
>>>> +                    terms->term_bad);
>>>
>>> Very minor: It improves the readability if you'd split the string after
>>> the \n and put the "and "in the next line.
>>
>> Ah. This is because of the message. If I do the other way, then it
>> won't match the output in one of the tests in t/t6030 thus, I am
>> keeping it that way in order to avoid modifying the file t/t6030.
>
> I think I was unclear here. I was referring to the coding/layouting
> style, not to the string. I mean like writing:
>
>         printf(_("Your current terms are %s for the old state\n"
>                  "and "%s for the new state.\n"),
>                terms->term_good, terms->term_bad);
>
> The string fed to _() is the same, but it is split in a different (imho
> more readable) way: after the "\n", not after the "and ".
Thanks for clearing it out. This seems a sensible change.
Show 18 quoted lines
>>>> +                     die(_("invalid argument %s for 'git bisect "
>>>> +                               "terms'.\nSupported options are: "
>>>> +                               "--term-good|--term-old and "
>>>> +                               "--term-bad|--term-new."), argv[i]);
>>>
>>> Hm, "return error(...)" and "die(...)" seems to be quasi-equivalent in
>>> this case. Because I am always looking from a library perspective, I'd
>>> prefer "return error(...)".
>>
>> I should use return error()
>
> When you reroll your patches, please also check if you always put _()
> around your error()s ;) (Hmmm... On the other hand, it might be arguable
> if translations are useful for errors that only occur when people hack
> git-bisect or use the bisect--helper directly... This makes me feel like
> all those errors should be prefixed by some "BUG: " marker since the
> ordinary user only sees them when there is a bug. But I don't feel in
> the position to decide or recommend such a thing, so it's just a thought.)

It is seems a good change, I will do it. Let other's comment on what they think in the next re-roll.

Regards, Pranit Bauva

Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 13/27] bisect--helper: `bisect_start` shell function partially in C

Reimplement the `bisect_start` shell function partially in C and add `bisect-start` subcommand to `git bisect--helper` to call it from git-bisect.sh .

The last part is not converted because it calls another shell function `bisect_start` shell function will be completed after the `bisect_next` shell function is ported in C.

Using `--bisect-start` subcommand is a temporary measure to port shell function in C so as to use the existing test suite. As more functions are ported, this subcommand will be retired and will be called by some other methods.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++-
 git-bisect.sh            | 133 +----------------------------
 2 files changed, 217 insertions(+), 133 deletions(-)
Show changes to 2 files +217 −134

builtin/bisect--helper.c, git-bisect.sh

diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 6a5878c..1d3e17f 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -7,6 +7,7 @@
 #include "argv-array.h"
 #include "run-command.h"
 #include "prompt.h"
+#include "quote.h"
 
 static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
 static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
@@ -14,6 +15,8 @@ static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
 static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
 static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
 static GIT_PATH_FUNC(git_path_bisect_head, "BISECT_HEAD")
+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]"),
@@ -24,6 +27,8 @@ static const char * const git_bisect_helper_usage[] = {
 	N_("git bisect--helper --bisect-check-and-set-terms <command> <TERM_GOOD> <TERM_BAD>"),
 	N_("git bisect--helper --bisect-next-check [<term>] <TERM_GOOD> <TERM_BAD"),
 	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>...]"),
 	NULL
 };
 
@@ -403,6 +408,205 @@ static int bisect_terms(struct bisect_terms *terms, const char **argv, int argc)
 	return 0;
 }
 
+static int 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, retval = 0;
+	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 strbuf orig_args = STRBUF_INIT;
+	const char *head;
+	unsigned char sha1[20];
+	FILE *fp = NULL;
+	struct object_id oid;
+
+	if (is_bare_repository())
+		no_checkout = 1;
+
+	for (i = 0; i < argc; i++) {
+		if (!strcmp(argv[i], "--")) {
+			has_double_dash = 1;
+			break;
+		}
+	}
+
+	for (i = 0; i < argc; i++) {
+		const char *commit_id = xstrfmt("%s^{commit}", argv[i]);
+		const char *arg = argv[i];
+		if (!strcmp(argv[i], "--")) {
+			has_double_dash = 1;
+			break;
+		} else if (!strcmp(arg, "--no-checkout")) {
+			no_checkout = 1;
+		} else if (!strcmp(arg, "--term-good") ||
+			 !strcmp(arg, "--term-old")) {
+			must_write_terms = 1;
+			terms->term_good = xstrdup(argv[++i]);
+		} else if (skip_prefix(arg, "--term-good=", &arg)) {
+			must_write_terms = 1;
+			terms->term_good = xstrdup(arg);
+		} else if (skip_prefix(arg, "--term-old=", &arg)) {
+			must_write_terms = 1;
+			terms->term_good = xstrdup(arg);
+		} else if (!strcmp(arg, "--term-bad") ||
+			 !strcmp(arg, "--term-new")) {
+			must_write_terms = 1;
+			terms->term_bad = xstrdup(argv[++i]);
+		} else if (skip_prefix(arg, "--term-bad=", &arg)) {
+			must_write_terms = 1;
+			terms->term_bad = xstrdup(arg);
+		} else if (skip_prefix(arg, "--term-new=", &arg)) {
+			must_write_terms = 1;
+			terms->term_good = xstrdup(arg);
+		} else if (starts_with(arg, "--") &&
+			 !one_of(arg, "--term-good", "--term-bad", NULL)) {
+			die(_("unrecognised option: '%s'"), arg);
+		} else if (get_oid(commit_id, &oid) && has_double_dash) {
+			die(_("'%s' does not appear to be a valid revision"), argv[i]);
+		} else {
+			string_list_append(&revs, oid_to_hex(&oid));
+		}
+	}
+	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.
+	 */
+	must_write_terms |= !!revs.nr;
+	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, sha1, &flags);
+	if (!head)
+		if (get_sha1("HEAD", sha1))
+			die(_("Bad HEAD - I need a HEAD"));
+
+	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) {
+			struct argv_array argv = ARGV_ARRAY_INIT;
+			argv_array_pushl(&argv, "checkout", start_head.buf,
+					 "--", NULL);
+			if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
+				error(_("checking out '%s' failed. Try 'git "
+					"bisect start <valid-branch>'."),
+				      start_head.buf);
+				retval = -1;
+				goto finish;
+			}
+		}
+	} else {
+		if (!get_sha1(head, sha1) &&
+		    !starts_with(head, "refs/heads/")) {
+			strbuf_reset(&start_head);
+			strbuf_addstr(&start_head, sha1_to_hex(sha1));
+		} else if (!get_sha1(head, sha1) &&
+			   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()))
+				die(_("won't bisect on cg-seek'ed tree"));
+			strbuf_addstr(&start_head, head);
+		} else {
+			die(_("Bad HEAD - strange symbolic ref"));
+		}
+	}
+
+	/*
+	 * Get rid of any old bisect state.
+	 */
+	if (bisect_clean_state()) {
+		return -1;
+	}
+	/*
+	 * 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".
+	 */
+
+	/*
+	 * Write new start state
+	 */
+	write_file(git_path_bisect_start(), "%s\n", start_head.buf);
+
+	if (no_checkout) {
+		get_oid(start_head.buf, &oid);
+		if (update_ref(NULL, "BISECT_HEAD", oid.hash, NULL, 0,
+			       UPDATE_REFS_MSG_ON_ERR)) {
+			retval = -1;
+			goto finish;
+		}
+	}
+
+	if (pathspec_pos < argc - 1)
+		sq_quote_argv(&bisect_names, argv + pathspec_pos, 0);
+	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)) {
+			retval = -1;
+			goto finish;
+		}
+	}
+
+	if (must_write_terms)
+		if (write_terms(terms->term_bad, terms->term_good)) {
+			retval = -1;
+			goto finish;
+		}
+
+	fp = fopen(git_path_bisect_log(), "a");
+	if (!fp)
+		return -1;
+
+	if (fprintf(fp, "git bisect start") < 1) {
+		retval = -1;
+		goto finish;
+	}
+
+	sq_quote_argv(&orig_args, argv, 0);
+	if (fprintf(fp, "%s", orig_args.buf) < 0) {
+		retval = -1;
+		goto finish;
+	}
+	if (fprintf(fp, "\n") < 1) {
+		retval = -1;
+		goto finish;
+	}
+	goto finish;
+finish:
+	if (fp)
+		fclose(fp);
+	string_list_clear(&revs, 0);
+	string_list_clear(&states, 0);
+	strbuf_release(&start_head);
+	strbuf_release(&bisect_names);
+	strbuf_release(&orig_args);
+	return retval;
+}
+
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
 	enum {
@@ -414,7 +618,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		BISECT_WRITE,
 		CHECK_AND_SET_TERMS,
 		BISECT_NEXT_CHECK,
-		BISECT_TERMS
+		BISECT_TERMS,
+		BISECT_START
 	} cmdmode = 0;
 	int no_checkout = 0, res = 0;
 	struct option options[] = {
@@ -436,6 +641,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 			 N_("check whether bad or good terms exist"), BISECT_NEXT_CHECK),
 		OPT_CMDMODE(0, "bisect-terms", &cmdmode,
 			 N_("print out the bisect terms"), BISECT_TERMS),
+		OPT_CMDMODE(0, "bisect-start", &cmdmode,
+			 N_("start the bisect session"), BISECT_START),
 		OPT_BOOL(0, "no-checkout", &no_checkout,
 			 N_("update BISECT_HEAD instead of checking out the current commit")),
 		OPT_END()
@@ -443,7 +650,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 	struct bisect_terms terms;
 
 	argc = parse_options(argc, argv, prefix, options,
-			     git_bisect_helper_usage, PARSE_OPT_KEEP_UNKNOWN);
+			     git_bisect_helper_usage,
+			     PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_UNKNOWN);
 
 	if (!cmdmode)
 		usage_with_options(git_bisect_helper_usage, options);
@@ -497,6 +705,11 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 			die(_("--bisect-terms requires 0 or 1 argument"));
 		res = bisect_terms(&terms, argv, argc);
 		break;
+	case BISECT_START:
+		terms.term_good = "good";
+		terms.term_bad = "bad";
+		res = bisect_start(&terms, no_checkout, argv, argc);
+		break;
 	default:
 		die("BUG: unknown subcommand '%d'", cmdmode);
 	}
diff --git a/git-bisect.sh b/git-bisect.sh
index d6c8b5a..f0896b3 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -71,122 +71,7 @@ bisect_autostart() {
 }
 
 bisect_start() {
-	#
-	# Check for one bad and then some good revisions.
-	#
-	has_double_dash=0
-	for arg; do
-		case "$arg" in --) has_double_dash=1; break ;; esac
-	done
-	orig_args=$(git rev-parse --sq-quote "$@")
-	bad_seen=0
-	eval=''
-	must_write_terms=0
-	revs=''
-	if test "z$(git rev-parse --is-bare-repository)" != zfalse
-	then
-		mode=--no-checkout
-	else
-		mode=''
-	fi
-	while [ $# -gt 0 ]; do
-		arg="$1"
-		case "$arg" in
-		--)
-			shift
-			break
-		;;
-		--no-checkout)
-			mode=--no-checkout
-			shift ;;
-		--term-good|--term-old)
-			shift
-			must_write_terms=1
-			TERM_GOOD=$1
-			shift ;;
-		--term-good=*|--term-old=*)
-			must_write_terms=1
-			TERM_GOOD=${1#*=}
-			shift ;;
-		--term-bad|--term-new)
-			shift
-			must_write_terms=1
-			TERM_BAD=$1
-			shift ;;
-		--term-bad=*|--term-new=*)
-			must_write_terms=1
-			TERM_BAD=${1#*=}
-			shift ;;
-		--*)
-			die "$(eval_gettext "unrecognised option: '\$arg'")" ;;
-		*)
-			rev=$(git rev-parse -q --verify "$arg^{commit}") || {
-				test $has_double_dash -eq 1 &&
-				die "$(eval_gettext "'\$arg' does not appear to be a valid revision")"
-				break
-			}
-			revs="$revs $rev"
-			shift
-			;;
-		esac
-	done
-
-	for rev in $revs
-	do
-		# 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.
-		must_write_terms=1
-
-		case $bad_seen in
-		0) state=$TERM_BAD ; bad_seen=1 ;;
-		*) state=$TERM_GOOD ;;
-		esac
-		eval="$eval git bisect--helper --bisect-write '$state' '$rev' '$TERM_GOOD' '$TERM_BAD' 'nolog' &&"
-	done
-	#
-	# Verify HEAD.
-	#
-	head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
-	head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
-	die "$(gettext "Bad HEAD - I need a HEAD")"
-
-	#
-	# Check if we are bisecting.
-	#
-	start_head=''
-	if test -s "$GIT_DIR/BISECT_START"
-	then
-		# Reset to the rev from where we started.
-		start_head=$(cat "$GIT_DIR/BISECT_START")
-		if test "z$mode" != "z--no-checkout"
-		then
-			git checkout "$start_head" -- ||
-			die "$(eval_gettext "Checking out '\$start_head' failed. Try 'git bisect reset <valid-branch>'.")"
-		fi
-	else
-		# Get rev from where we start.
-		case "$head" in
-		refs/heads/*|$_x40)
-			# This error message should only be triggered by
-			# cogito usage, and cogito users should understand
-			# it relates to cg-seek.
-			[ -s "$GIT_DIR/head-name" ] &&
-				die "$(gettext "won't bisect on cg-seek'ed tree")"
-			start_head="${head#refs/heads/}"
-			;;
-		*)
-			die "$(gettext "Bad HEAD - strange symbolic ref")"
-			;;
-		esac
-	fi
-
-	#
-	# Get rid of any old bisect state.
-	#
-	git bisect--helper --bisect-clean-state || exit
+	git bisect--helper --bisect-start $@ || exit
 
 	#
 	# Change state.
@@ -197,24 +82,10 @@ bisect_start() {
 	#
 	trap 'git bisect--helper --bisect-clean-state' 0
 	trap 'exit 255' 1 2 3 15
-
-	#
-	# Write new start state.
-	#
-	echo "$start_head" >"$GIT_DIR/BISECT_START" && {
-		test "z$mode" != "z--no-checkout" ||
-		git update-ref --no-deref BISECT_HEAD "$start_head"
-	} &&
-	git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
-	eval "$eval true" &&
-	if test $must_write_terms -eq 1
-	then
-		git bisect--helper --write-terms "$TERM_BAD" "$TERM_GOOD"
-	fi &&
-	echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
 	#
 	# Check if we can proceed to the next bisect state.
 	#
+	get_terms
 	bisect_auto_next
 
 	trap '-' 0

--
https://github.com/git/git/pull/287
Stephan Beyer· Nov 15, 2016, 23:19 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 13/27] bisect--helper: `bisect_start` shell function partially in C

Hi,
On 10/14/2016 04:14 PM, Pranit Bauva wrote:
Show 10 quoted lines
> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
> index 6a5878c..1d3e17f 100644
> --- a/builtin/bisect--helper.c
> +++ b/builtin/bisect--helper.c
> @@ -24,6 +27,8 @@ static const char * const git_bisect_helper_usage[] = {
>  	N_("git bisect--helper --bisect-check-and-set-terms <command> <TERM_GOOD> <TERM_BAD>"),
>  	N_("git bisect--helper --bisect-next-check [<term>] <TERM_GOOD> <TERM_BAD"),
>  	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>...]"),
Typo: "--bisect start" with space instead of "-"
Show 34 quoted lines
> @@ -403,6 +408,205 @@ static int bisect_terms(struct bisect_terms *terms, const char **argv, int argc)
>  	return 0;
>  }
>  
> +static int 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, retval = 0;
> +	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 strbuf orig_args = STRBUF_INIT;
> +	const char *head;
> +	unsigned char sha1[20];
> +	FILE *fp = NULL;
> +	struct object_id oid;
> +
> +	if (is_bare_repository())
> +		no_checkout = 1;
> +
> +	for (i = 0; i < argc; i++) {
> +		if (!strcmp(argv[i], "--")) {
> +			has_double_dash = 1;
> +			break;
> +		}
> +	}
> +
> +	for (i = 0; i < argc; i++) {
> +		const char *commit_id = xstrfmt("%s^{commit}", argv[i]);
> +		const char *arg = argv[i];
> +		if (!strcmp(argv[i], "--")) {
> +			has_double_dash = 1;

This is without effect since has_double_dash is already set to 1 by the loop above. I think you can remove this line.

Show 13 quoted lines
> +			break;
> +		} else if (!strcmp(arg, "--no-checkout")) {
> +			no_checkout = 1;
> +		} else if (!strcmp(arg, "--term-good") ||
> +			 !strcmp(arg, "--term-old")) {
> +			must_write_terms = 1;
> +			terms->term_good = xstrdup(argv[++i]);
> +		} else if (skip_prefix(arg, "--term-good=", &arg)) {
> +			must_write_terms = 1;
> +			terms->term_good = xstrdup(arg);
> +		} else if (skip_prefix(arg, "--term-old=", &arg)) {
> +			must_write_terms = 1;
> +			terms->term_good = xstrdup(arg);
I think you can join the last two branches:

+ } else if (skip_prefix(arg, "--term-good=", &arg) || + skip_prefix(arg, "--term-old=", &arg)) { + must_write_terms = 1; + terms->term_good = xstrdup(arg);

Show 10 quoted lines
> +		} else if (!strcmp(arg, "--term-bad") ||
> +			 !strcmp(arg, "--term-new")) {
> +			must_write_terms = 1;
> +			terms->term_bad = xstrdup(argv[++i]);
> +		} else if (skip_prefix(arg, "--term-bad=", &arg)) {
> +			must_write_terms = 1;
> +			terms->term_bad = xstrdup(arg);
> +		} else if (skip_prefix(arg, "--term-new=", &arg)) {
> +			must_write_terms = 1;
> +			terms->term_good = xstrdup(arg);
This has to be terms->term_bad = ...
Also, you can join the last two branches, again, ie,

+ } else if (skip_prefix(arg, "--term-bad=", &arg) || + skip_prefix(arg, "--term-new=", &arg)) { + must_write_terms = 1; + terms->term_bad = xstrdup(arg);

> +		} else if (starts_with(arg, "--") &&
> +			 !one_of(arg, "--term-good", "--term-bad", NULL)) {
> +			die(_("unrecognised option: '%s'"), arg);
[...]
Show 9 quoted lines
> +	/*
> +	 * Verify HEAD
> +	 */
> +	head = resolve_ref_unsafe("HEAD", 0, sha1, &flags);
> +	if (!head)
> +		if (get_sha1("HEAD", sha1))
> +			die(_("Bad HEAD - I need a HEAD"));
> +
> +	if (!is_empty_or_missing_file(git_path_bisect_start())) {

You were so eager to re-use the comments from the shell script, but you forgot the "Check if we are bisecting." comment above this line ;-)

Show 5 quoted lines
> +		/* 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) {
> +			struct argv_array argv = ARGV_ARRAY_INIT;
[...]
Show 6 quoted lines
> +	if (must_write_terms)
> +		if (write_terms(terms->term_bad, terms->term_good)) {
> +			retval = -1;
> +			goto finish;
> +		}
> +

bisect_start() is a pretty big function. I think it can easily be decomposed in some smaller parts, for example, the following lines ...

Show 18 quoted lines
> +	fp = fopen(git_path_bisect_log(), "a");
> +	if (!fp)
> +		return -1;
> +
> +	if (fprintf(fp, "git bisect start") < 1) {
> +		retval = -1;
> +		goto finish;
> +	}
> +
> +	sq_quote_argv(&orig_args, argv, 0);
> +	if (fprintf(fp, "%s", orig_args.buf) < 0) {
> +		retval = -1;
> +		goto finish;
> +	}
> +	if (fprintf(fp, "\n") < 1) {
> +		retval = -1;
> +		goto finish;
> +	}
... could be in a function like
static int bisect_append_log(const char **argv)
{
	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) {
		retval = -1;
		goto finish;
	}
	sq_quote_argv(&orig_args, argv, 0);
	if (fprintf(fp, "%s", orig_args.buf) < 0 ||
	    fprintf(fp, "\n") < 1) {
		retval = -1;
		goto finish;
	}
finish:
	if (fp)
		fclose(fp);
	strbuf_release(&orig_args);
	return retval;
}
and then simply call
	retval = bisect_append_log(argv);
in bisect_start()... (This is totally untested.)
If you do not want that for some reason, you should at least fix
> +	if (!fp)
> +		return -1;

to retval = 1; goto finish; such that the other lists and strings are released.

> +	goto finish;
> +finish:
The "goto finish" right above the "finish" label is unnecessary.
Show 13 quoted lines
> +	if (fp)
> +		fclose(fp);
> +	string_list_clear(&revs, 0);
> +	string_list_clear(&states, 0);
> +	strbuf_release(&start_head);
> +	strbuf_release(&bisect_names);
> +	strbuf_release(&orig_args);
> +	return retval;
> +}
> +
>  int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>  {
>  	enum {
By the way, there are two spaghetti-ish ways to get rid of the
	retval = -1;
	goto finish;
line pair:
	goto fail;
and below the "return retval;" add
fail:
	retval = -1;
	goto finish;
and you can feel the touch of His Noodly Appendage. *scnr*

The other way is to keep the "goto finish" I deemed unnecessary (right above the label), and expand it to:

	goto finish;
fail:
	retval = -1;
finish:
	...
Cheers
  Stephan
Pranit Bauva· Nov 16, 2016, 17:09 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 13/27] bisect--helper: `bisect_start` shell function partially in C

Hey Stephan,
On Wed, Nov 16, 2016 at 4:49 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
Show 53 quoted lines
> Hi,
>
> On 10/14/2016 04:14 PM, Pranit Bauva wrote:
>> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
>> index 6a5878c..1d3e17f 100644
>> --- a/builtin/bisect--helper.c
>> +++ b/builtin/bisect--helper.c
>> @@ -24,6 +27,8 @@ static const char * const git_bisect_helper_usage[] = {
>>       N_("git bisect--helper --bisect-check-and-set-terms <command> <TERM_GOOD> <TERM_BAD>"),
>>       N_("git bisect--helper --bisect-next-check [<term>] <TERM_GOOD> <TERM_BAD"),
>>       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>...]"),
>
> Typo: "--bisect start" with space instead of "-"
>
>> @@ -403,6 +408,205 @@ static int bisect_terms(struct bisect_terms *terms, const char **argv, int argc)
>>       return 0;
>>  }
>>
>> +static int 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, retval = 0;
>> +     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 strbuf orig_args = STRBUF_INIT;
>> +     const char *head;
>> +     unsigned char sha1[20];
>> +     FILE *fp = NULL;
>> +     struct object_id oid;
>> +
>> +     if (is_bare_repository())
>> +             no_checkout = 1;
>> +
>> +     for (i = 0; i < argc; i++) {
>> +             if (!strcmp(argv[i], "--")) {
>> +                     has_double_dash = 1;
>> +                     break;
>> +             }
>> +     }
>> +
>> +     for (i = 0; i < argc; i++) {
>> +             const char *commit_id = xstrfmt("%s^{commit}", argv[i]);
>> +             const char *arg = argv[i];
>> +             if (!strcmp(argv[i], "--")) {
>> +                     has_double_dash = 1;
>
> This is without effect since has_double_dash is already set to 1 by the
> loop above. I think you can remove this line.
True. I will remove this line.
Show 33 quoted lines
>> +                     break;
>> +             } else if (!strcmp(arg, "--no-checkout")) {
>> +                     no_checkout = 1;
>> +             } else if (!strcmp(arg, "--term-good") ||
>> +                      !strcmp(arg, "--term-old")) {
>> +                     must_write_terms = 1;
>> +                     terms->term_good = xstrdup(argv[++i]);
>> +             } else if (skip_prefix(arg, "--term-good=", &arg)) {
>> +                     must_write_terms = 1;
>> +                     terms->term_good = xstrdup(arg);
>> +             } else if (skip_prefix(arg, "--term-old=", &arg)) {
>> +                     must_write_terms = 1;
>> +                     terms->term_good = xstrdup(arg);
>
> I think you can join the last two branches:
>
> +               } else if (skip_prefix(arg, "--term-good=", &arg) ||
> +                          skip_prefix(arg, "--term-old=", &arg)) {
> +                       must_write_terms = 1;
> +                       terms->term_good = xstrdup(arg);
>
>> +             } else if (!strcmp(arg, "--term-bad") ||
>> +                      !strcmp(arg, "--term-new")) {
>> +                     must_write_terms = 1;
>> +                     terms->term_bad = xstrdup(argv[++i]);
>> +             } else if (skip_prefix(arg, "--term-bad=", &arg)) {
>> +                     must_write_terms = 1;
>> +                     terms->term_bad = xstrdup(arg);
>> +             } else if (skip_prefix(arg, "--term-new=", &arg)) {
>> +                     must_write_terms = 1;
>> +                     terms->term_good = xstrdup(arg);
>
> This has to be terms->term_bad = ...
My bad.
> Also, you can join the last two branches, again, ie,
Sure!
Show 21 quoted lines
> +               } else if (skip_prefix(arg, "--term-bad=", &arg) ||
> +                          skip_prefix(arg, "--term-new=", &arg)) {
> +                       must_write_terms = 1;
> +                       terms->term_bad = xstrdup(arg);
>
>> +             } else if (starts_with(arg, "--") &&
>> +                      !one_of(arg, "--term-good", "--term-bad", NULL)) {
>> +                     die(_("unrecognised option: '%s'"), arg);
> [...]
>> +     /*
>> +      * Verify HEAD
>> +      */
>> +     head = resolve_ref_unsafe("HEAD", 0, sha1, &flags);
>> +     if (!head)
>> +             if (get_sha1("HEAD", sha1))
>> +                     die(_("Bad HEAD - I need a HEAD"));
>> +
>> +     if (!is_empty_or_missing_file(git_path_bisect_start())) {
>
> You were so eager to re-use the comments from the shell script, but you
> forgot the "Check if we are bisecting." comment above this line ;-)
I will add it back again.
Show 70 quoted lines
>> +             /* 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) {
>> +                     struct argv_array argv = ARGV_ARRAY_INIT;
> [...]
>> +     if (must_write_terms)
>> +             if (write_terms(terms->term_bad, terms->term_good)) {
>> +                     retval = -1;
>> +                     goto finish;
>> +             }
>> +
>
> bisect_start() is a pretty big function.
> I think it can easily be decomposed in some smaller parts, for example,
> the following lines ...
>
>> +     fp = fopen(git_path_bisect_log(), "a");
>> +     if (!fp)
>> +             return -1;
>> +
>> +     if (fprintf(fp, "git bisect start") < 1) {
>> +             retval = -1;
>> +             goto finish;
>> +     }
>> +
>> +     sq_quote_argv(&orig_args, argv, 0);
>> +     if (fprintf(fp, "%s", orig_args.buf) < 0) {
>> +             retval = -1;
>> +             goto finish;
>> +     }
>> +     if (fprintf(fp, "\n") < 1) {
>> +             retval = -1;
>> +             goto finish;
>> +     }
>
> ... could be in a function like
>
> static int bisect_append_log(const char **argv)
> {
>         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) {
>                 retval = -1;
>                 goto finish;
>         }
>
>         sq_quote_argv(&orig_args, argv, 0);
>         if (fprintf(fp, "%s", orig_args.buf) < 0 ||
>             fprintf(fp, "\n") < 1) {
>                 retval = -1;
>                 goto finish;
>         }
>
> finish:
>         if (fp)
>                 fclose(fp);
>         strbuf_release(&orig_args);
>
>         return retval;
> }
>
> and then simply call
>
>         retval = bisect_append_log(argv);
>
> in bisect_start()... (This is totally untested.)
I think this would be a better choice. Thanks!
Show 43 quoted lines
> If you do not want that for some reason, you should at least fix
>
>> +     if (!fp)
>> +             return -1;
>
> to retval = 1; goto finish; such that the other lists and strings are
> released.
>
>> +     goto finish;
>> +finish:
>
> The "goto finish" right above the "finish" label is unnecessary.
>
>> +     if (fp)
>> +             fclose(fp);
>> +     string_list_clear(&revs, 0);
>> +     string_list_clear(&states, 0);
>> +     strbuf_release(&start_head);
>> +     strbuf_release(&bisect_names);
>> +     strbuf_release(&orig_args);
>> +     return retval;
>> +}
>> +
>>  int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>>  {
>>       enum {
>
> By the way, there are two spaghetti-ish ways to get rid of the
>
>         retval = -1;
>         goto finish;
>
> line pair:
>
>         goto fail;
>
> and below the "return retval;" add
>
> fail:
>         retval = -1;
>         goto finish;
>
> and you can feel the touch of His Noodly Appendage. *scnr*
Nice little trick.
Show 8 quoted lines
> The other way is to keep the "goto finish" I deemed unnecessary (right
> above the label), and expand it to:
>
>         goto finish;
> fail:
>         retval = -1;
> finish:
>         ...

Regards, Pranit Bauva

Stephan Beyer· Nov 20, 2016, 20:01 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 13/27] bisect--helper: `bisect_start` shell function partially in C

Hi,
On 10/14/2016 04:14 PM, Pranit Bauva wrote:
Show 45 quoted lines
> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
> index 6a5878c..1d3e17f 100644
> --- a/builtin/bisect--helper.c
> +++ b/builtin/bisect--helper.c
> @@ -403,6 +408,205 @@ static int bisect_terms(struct bisect_terms *terms, const char **argv, int argc)
>  	return 0;
>  }
>  
> +static int 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, retval = 0;
> +	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 strbuf orig_args = STRBUF_INIT;
> +	const char *head;
> +	unsigned char sha1[20];
> +	FILE *fp = NULL;
> +	struct object_id oid;
> +
> +	if (is_bare_repository())
> +		no_checkout = 1;
> +
> +	for (i = 0; i < argc; i++) {
> +		if (!strcmp(argv[i], "--")) {
> +			has_double_dash = 1;
> +			break;
> +		}
> +	}
> +
> +	for (i = 0; i < argc; i++) {
> +		const char *commit_id = xstrfmt("%s^{commit}", argv[i]);
> +		const char *arg = argv[i];
> +		if (!strcmp(argv[i], "--")) {
> +			has_double_dash = 1;
> +			break;
> +		} else if (!strcmp(arg, "--no-checkout")) {
> +			no_checkout = 1;
> +		} else if (!strcmp(arg, "--term-good") ||
> +			 !strcmp(arg, "--term-old")) {
> +			must_write_terms = 1;
> +			terms->term_good = xstrdup(argv[++i]);
All these xstrdup() for the terms here and below will leak memory.

I recommend to use xstrdup() also at (*) below, and use free(terms->term_good) above this line (and for every occurrence below, of course).

> +		} else if (skip_prefix(arg, "--term-good=", &arg)) {
> +			must_write_terms = 1;
> +			terms->term_good = xstrdup(arg);
[...]
Show 7 quoted lines
> @@ -497,6 +705,11 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>  			die(_("--bisect-terms requires 0 or 1 argument"));
>  		res = bisect_terms(&terms, argv, argc);
>  		break;
> +	case BISECT_START:
> +		terms.term_good = "good";
> +		terms.term_bad = "bad";
Here is (*): use xstrdup("good") etc.
And then, as already mentioned for another patch, free(terms.*) below.

I personally am a friend of small functions and would prefer something like as follows... (This is a comment about several patches of your series, not only this one.)

First, replace the current set_terms() by
static void set_terms(struct bisect_terms *terms, const char *bad,
                                                 const char *good)
{
	terms->term_good = xstrdup(good);
	terms->term_bad = xstrdup(bad);
}

ie, without calling write_terms(...). And then replace the *current* set_terms() calls by set_terms(...); write_terms(...); calls.

Second, add
static void get_default_terms(struct bisect_terms *terms)
{
	set_terms(terms, "bad", "good");
}

and use this instead of the two lines quoted above (and all its other occurrences).

Third, use the new set_terms() everywhere instead of settings terms members directly (with the exception of get_terms()).

This sounds like a safer variant (with respect to leaks and handling them) to me than doing it the current way.

~Stephan
Stephan Beyer· Nov 20, 2016, 20:19 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 13/27] bisect--helper: `bisect_start` shell function partially in C

On 11/20/2016 09:01 PM, Stephan Beyer wrote:
Show 10 quoted lines
> First, replace the current set_terms() by
> 
> static void set_terms(struct bisect_terms *terms, const char *bad,
>                                                  const char *good)
> {
> 	terms->term_good = xstrdup(good);
> 	terms->term_bad = xstrdup(bad);
> }
> 
> ie, without calling write_terms(...).

I did not want to confuse you here but I forgot to mention that there should also be freeing code, i.e. initialize your terms to NULL in the beginning of cmd_builtin__helper, and always free them if it is not null. This freeing code could also be in an extra function free_terms() and you call it in set_terms() and for cleanup in the end.

Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 18/27] bisect--helper: `bisect_autostart` shell function in C

Reimplement the `bisect_autostart` shell function in C and add the C implementation from `bisect_next()` which was previously left uncovered. Also add a subcommand `--bisect-autostart` to `git bisect--helper` be called from `bisect_state()` from git-bisect.sh .

Using `--bisect-autostart` subcommand 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 and will be called by `bisect_state()`.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 40 ++++++++++++++++++++++++++++++++++++++++
 git-bisect.sh            | 23 +----------------------
 2 files changed, 41 insertions(+), 22 deletions(-)
Show changes to 2 files +41 −23

builtin/bisect--helper.c, git-bisect.sh

diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 502bf18..1767916 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -30,6 +30,7 @@ static const char * const git_bisect_helper_usage[] = {
 					      "[--no-checkout] [<bad> [<good>...]] [--] [<paths>...]"),
 	N_("git bisect--helper --bisect-next"),
 	N_("git bisect--helper --bisect-auto-next"),
+	N_("git bisect--helper --bisect-autostart"),
 	NULL
 };
 
@@ -38,6 +39,8 @@ struct bisect_terms {
 	const char *term_bad;
 };
 
+static int bisect_autostart(struct bisect_terms *terms);
+
 /*
  * Check whether the string `term` belongs to the set of strings
  * included in the variable arguments.
@@ -422,6 +425,7 @@ static int bisect_next(struct bisect_terms *terms, const char *prefix)
 {
 	int res, no_checkout;
 
+	bisect_autostart(terms);
 	/*
 	 * In case of mistaken revs or checkout error, or signals received,
 	 * "bisect_auto_next" below may exit or misbehave.
@@ -754,6 +758,32 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
 	return retval || bisect_auto_next(terms, NULL);
 }
 
+static int bisect_autostart(struct bisect_terms *terms)
+{
+	if (is_empty_or_missing_file(git_path_bisect_start())) {
+		const char *yesno;
+		const char *argv[] = {NULL};
+		fprintf(stderr, _("You need to start by \"git bisect "
+				  "start\"\n"));
+
+		if (!isatty(0))
+			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"))
+			exit(0);
+
+		return bisect_start(terms, 0, argv, 0);
+	}
+	return 0;
+}
+
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
 	enum {
@@ -767,6 +797,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		BISECT_START,
 		BISECT_NEXT,
 		BISECT_AUTO_NEXT,
+		BISECT_AUTOSTART,
 	} cmdmode = 0;
 	int no_checkout = 0, res = 0;
 	struct option options[] = {
@@ -790,6 +821,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 			 N_("find the next bisection commit"), BISECT_NEXT),
 		OPT_CMDMODE(0, "bisect-auto-next", &cmdmode,
 			 N_("verify the next bisection state then find the next bisection state"), BISECT_AUTO_NEXT),
+		OPT_CMDMODE(0, "bisect-autostart", &cmdmode,
+			 N_("start the bisection if BISECT_START empty or missing"), BISECT_AUTOSTART),
 		OPT_BOOL(0, "no-checkout", &no_checkout,
 			 N_("update BISECT_HEAD instead of checking out the current commit")),
 		OPT_END()
@@ -862,6 +895,13 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		get_terms(&terms);
 		res = bisect_auto_next(&terms, prefix);
 		break;
+	case BISECT_AUTOSTART:
+		if (argc)
+			die(_("--bisect-autostart requires 0 arguments"));
+		terms.term_good = "good";
+		terms.term_bad = "bad";
+		res = bisect_autostart(&terms);
+		break;
 	default:
 		die("BUG: unknown subcommand '%d'", cmdmode);
 	}
diff --git a/git-bisect.sh b/git-bisect.sh
index d574c44..cd56551 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -49,27 +49,6 @@ bisect_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
-			git bisect--helper --bisect-start
-		else
-			exit 1
-		fi
-	}
-}
-
 bisect_skip() {
 	all=''
 	for arg in "$@"
@@ -86,7 +65,7 @@ bisect_skip() {
 }
 
 bisect_state() {
-	bisect_autostart
+	git bisect--helper --bisect-autostart
 	state=$1
 	get_terms
 	git bisect--helper --check-and-set-terms $state $TERM_GOOD $TERM_BAD || exit

--
https://github.com/git/git/pull/287
Stephan Beyer· Nov 20, 2016, 20:15 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 18/27] bisect--helper: `bisect_autostart` shell function in C

Hi,
On 10/14/2016 04:14 PM, Pranit Bauva wrote:
Show 9 quoted lines
> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
> index 502bf18..1767916 100644
> --- a/builtin/bisect--helper.c
> +++ b/builtin/bisect--helper.c
> @@ -422,6 +425,7 @@ static int bisect_next(...)
>  {
>  	int res, no_checkout;
>
> +	bisect_autostart(terms);

You are not checking for return values here. (The shell code simply exited if there is no tty, but you don't.)

Show 13 quoted lines
> @@ -754,6 +758,32 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
>  	return retval || bisect_auto_next(terms, NULL);
>  }
>  
> +static int bisect_autostart(struct bisect_terms *terms)
> +{
> +	if (is_empty_or_missing_file(git_path_bisect_start())) {
> +		const char *yesno;
> +		const char *argv[] = {NULL};
> +		fprintf(stderr, _("You need to start by \"git bisect "
> +				  "start\"\n"));
> +
> +		if (!isatty(0))
isatty(STDIN_FILENO)?
Show 5 quoted lines
> +			return 1;
> +
> +		/*
> +		 * TRANSLATORS: Make sure to include [Y] and [n] in your
> +		 * translation. THe program will only accept English input
Typo "THe"
> +		 * at this point.
> +		 */

Taking "at this point" into consideration, I think the Y and n can be easily translated now that it is in C. I guess, by using...

> +		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"))

... starts_with(yesno, _("n")) || starts_with(yesno, _("N")) here (but not sure). However, this would be an extra patch on top of this series.

> +			exit(0);

Shouldn't this also be "return 1;"? Saying "no" is the same outcome as not having a tty to ask for yes or no.

Show 9 quoted lines
>  int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>  {
>  	enum {
> @@ -790,6 +821,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>  			 N_("find the next bisection commit"), BISECT_NEXT),
>  		OPT_CMDMODE(0, "bisect-auto-next", &cmdmode,
>  			 N_("verify the next bisection state then find the next bisection state"), BISECT_AUTO_NEXT),
> +		OPT_CMDMODE(0, "bisect-autostart", &cmdmode,
> +			 N_("start the bisection if BISECT_START empty or missing"), BISECT_AUTOSTART),
The word "is" is missing.
~Stephan
Pranit Bauva· Dec 6, 2016, 19:47 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 18/27] bisect--helper: `bisect_autostart` shell function in C

Hey Stephan,
On Mon, Nov 21, 2016 at 1:45 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
Show 15 quoted lines
> Hi,
>
> On 10/14/2016 04:14 PM, Pranit Bauva wrote:
>> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
>> index 502bf18..1767916 100644
>> --- a/builtin/bisect--helper.c
>> +++ b/builtin/bisect--helper.c
>> @@ -422,6 +425,7 @@ static int bisect_next(...)
>>  {
>>       int res, no_checkout;
>>
>> +     bisect_autostart(terms);
>
> You are not checking for return values here. (The shell code simply
> exited if there is no tty, but you don't.)
True. I didn't notice it carefully. Thanks for pointing it out.
Show 15 quoted lines
>> @@ -754,6 +758,32 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
>>       return retval || bisect_auto_next(terms, NULL);
>>  }
>>
>> +static int bisect_autostart(struct bisect_terms *terms)
>> +{
>> +     if (is_empty_or_missing_file(git_path_bisect_start())) {
>> +             const char *yesno;
>> +             const char *argv[] = {NULL};
>> +             fprintf(stderr, _("You need to start by \"git bisect "
>> +                               "start\"\n"));
>> +
>> +             if (!isatty(0))
>
> isatty(STDIN_FILENO)?
Seems better.
Show 7 quoted lines
>> +                     return 1;
>> +
>> +             /*
>> +              * TRANSLATORS: Make sure to include [Y] and [n] in your
>> +              * translation. THe program will only accept English input
>
> Typo "THe"
Sure.
Show 13 quoted lines
>> +              * at this point.
>> +              */
>
> Taking "at this point" into consideration, I think the Y and n can be
> easily translated now that it is in C. I guess, by using...
>
>> +             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"))
>
> ... starts_with(yesno, _("n")) || starts_with(yesno, _("N"))
> here (but not sure). However, this would be an extra patch on top of
> this series.
Can add it as an extra patch. Thanks for informing.
>> +                     exit(0);
>
> Shouldn't this also be "return 1;"? Saying "no" is the same outcome as
> not having a tty to ask for yes or no.
Yes.
Show 11 quoted lines
>>  int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>>  {
>>       enum {
>> @@ -790,6 +821,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>>                        N_("find the next bisection commit"), BISECT_NEXT),
>>               OPT_CMDMODE(0, "bisect-auto-next", &cmdmode,
>>                        N_("verify the next bisection state then find the next bisection state"), BISECT_AUTO_NEXT),
>> +             OPT_CMDMODE(0, "bisect-autostart", &cmdmode,
>> +                      N_("start the bisection if BISECT_START empty or missing"), BISECT_AUTOSTART),
>
> The word "is" is missing.
Sure. Thanks for going through these patches very carefully.

Regards, Pranit Bauva

Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 21/27] bisect--helper: retire `--write-terms` subcommand

The `--write-terms` subcommand is no longer used in the shell script and the function `write_terms()` is called from the C implementation of `set_terms()` and `bisect_start()`.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)
Show changes to builtin/bisect--helper.c +1 −11
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index d5fe35b..493034c 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -20,7 +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 --write-terms <bad_term> <good_term>"),
 	N_("git bisect--helper --bisect-reset [<commit>]"),
 	N_("git bisect--helper --bisect-write <state> <revision> <TERM_GOOD> <TERM_BAD> [<nolog>]"),
 	N_("git bisect--helper --bisect-check-and-set-terms <command> <TERM_GOOD> <TERM_BAD>"),
@@ -862,8 +861,7 @@ static int bisect_state(struct bisect_terms *terms, const char **argv,
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
 	enum {
-		WRITE_TERMS = 1,
-		BISECT_RESET,
+		BISECT_RESET = 1,
 		BISECT_WRITE,
 		CHECK_AND_SET_TERMS,
 		BISECT_NEXT_CHECK,
@@ -876,8 +874,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 	} cmdmode = 0;
 	int no_checkout = 0, res = 0;
 	struct option options[] = {
-		OPT_CMDMODE(0, "write-terms", &cmdmode,
-			 N_("write the terms to .git/BISECT_TERMS"), WRITE_TERMS),
 		OPT_CMDMODE(0, "bisect-reset", &cmdmode,
 			 N_("reset the bisection state"), BISECT_RESET),
 		OPT_CMDMODE(0, "bisect-write", &cmdmode,
@@ -913,11 +909,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 
 	switch (cmdmode) {
 	int nolog;
-	case WRITE_TERMS:
-		if (argc != 2)
-			die(_("--write-terms requires two arguments"));
-		res = write_terms(argv[0], argv[1]);
-		break;
 	case BISECT_RESET:
 		if (argc > 1)
 			die(_("--bisect-reset requires either zero or one arguments"));

--
https://github.com/git/git/pull/287
Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 19/27] bisect--helper: `bisect_state` & `bisect_head` shell function in C

Reimplement the `bisect_state` shell function in C and also add a subcommand `--bisect-state` to `git-bisect--helper` to call it from git-bisect.sh .

Using `--bisect-state` subcommand 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 and will be called by some other methods.

`bisect_head` is called from `bisect_state` thus its not required to introduce another subcommand.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++
 git-bisect.sh            | 57 +++-----------------------------
 2 files changed, 91 insertions(+), 52 deletions(-)
Show changes to 2 files +91 −53

builtin/bisect--helper.c, git-bisect.sh

diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 1767916..1481c6d 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -31,6 +31,8 @@ static const char * const git_bisect_helper_usage[] = {
 	N_("git bisect--helper --bisect-next"),
 	N_("git bisect--helper --bisect-auto-next"),
 	N_("git bisect--helper --bisect-autostart"),
+	N_("git bisect--helper --bisect-state (bad|new) [<rev>]"),
+	N_("git bisect--helper --bisect-state (good|old) [<rev>...]"),
 	NULL
 };
 
@@ -784,6 +786,79 @@ static int bisect_autostart(struct bisect_terms *terms)
 	return 0;
 }
 
+static char *bisect_head(void)
+{
+	if (is_empty_or_missing_file(git_path_bisect_head()))
+		return "HEAD";
+	else
+		return "BISECT_HEAD";
+}
+
+static int bisect_state(struct bisect_terms *terms, const char **argv,
+			int argc)
+{
+	const char *state = argv[0];
+
+	get_terms(terms);
+	if (check_and_set_terms(terms, state))
+		return -1;
+
+	if (!argc)
+		die(_("Please call `--bisect-state` with at least one argument"));
+
+	if (argc == 1 && one_of(state, terms->term_good,
+	    terms->term_bad, "skip", NULL)) {
+		const char *bisected_head = xstrdup(bisect_head());
+		const char *hex[1];
+		unsigned char sha1[20];
+
+		if (get_sha1(bisected_head, sha1))
+			die(_("Bad rev input: %s"), bisected_head);
+		if (bisect_write(state, sha1_to_hex(sha1), terms, 0))
+			return -1;
+
+		*hex = xstrdup(sha1_to_hex(sha1));
+		if (check_expected_revs(hex, 1))
+			return -1;
+		return bisect_auto_next(terms, NULL);
+	}
+
+	if ((argc == 2 && !strcmp(state, terms->term_bad)) ||
+			one_of(state, terms->term_good, "skip", NULL)) {
+		int i;
+		struct string_list hex = STRING_LIST_INIT_DUP;
+
+		for (i = 1; i < argc; i++) {
+			unsigned char sha1[20];
+
+			if (get_sha1(argv[i], sha1)) {
+				string_list_clear(&hex, 0);
+				die(_("Bad rev input: %s"), argv[i]);
+			}
+			string_list_append(&hex, sha1_to_hex(sha1));
+		}
+		for (i = 0; i < hex.nr; i++) {
+			const char **hex_string = (const char **) &hex.items[i].string;
+			if(bisect_write(state, *hex_string, terms, 0)) {
+				string_list_clear(&hex, 0);
+				return -1;
+			}
+			if (check_expected_revs(hex_string, 1)) {
+				string_list_clear(&hex, 0);
+				return -1;
+			}
+		}
+		string_list_clear(&hex, 0);
+		return bisect_auto_next(terms, NULL);
+	}
+
+	if (!strcmp(state, terms->term_bad))
+		die(_("'git bisect %s' can take only one argument."),
+		      terms->term_bad);
+
+	return -1;
+}
+
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
 	enum {
@@ -798,6 +873,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		BISECT_NEXT,
 		BISECT_AUTO_NEXT,
 		BISECT_AUTOSTART,
+		BISECT_STATE
 	} cmdmode = 0;
 	int no_checkout = 0, res = 0;
 	struct option options[] = {
@@ -823,6 +899,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 			 N_("verify the next bisection state then find the next bisection state"), BISECT_AUTO_NEXT),
 		OPT_CMDMODE(0, "bisect-autostart", &cmdmode,
 			 N_("start the bisection if BISECT_START empty or missing"), BISECT_AUTOSTART),
+		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_END()
@@ -902,6 +980,14 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		terms.term_bad = "bad";
 		res = bisect_autostart(&terms);
 		break;
+	case BISECT_STATE:
+		if (argc == 0)
+			die(_("--bisect-state requires at least 1 argument"));
+		terms.term_good = "good";
+		terms.term_bad = "bad";
+		get_terms(&terms);
+		res = bisect_state(&terms, argv, argc);
+		break;
 	default:
 		die("BUG: unknown subcommand '%d'", cmdmode);
 	}
diff --git a/git-bisect.sh b/git-bisect.sh
index cd56551..a9eebbb 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -39,16 +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_skip() {
 	all=''
 	for arg in "$@"
@@ -61,44 +51,7 @@ bisect_skip() {
 		esac
 		all="$all $revs"
 	done
-	eval bisect_state 'skip' $all
-}
-
-bisect_state() {
-	git bisect--helper --bisect-autostart
-	state=$1
-	get_terms
-	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
-	git bisect--helper --bisect-auto-next || exit
+	eval git bisect--helper --bisect-state 'skip' $all
 }
 
 bisect_visualize() {
@@ -184,8 +137,8 @@ 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" )
+		# We have to use a subshell because "--bisect-state" can exit.
+		( git bisect--helper --bisect-state $state >"$GIT_DIR/BISECT_RUN" )
 		res=$?
 
 		cat "$GIT_DIR/BISECT_RUN"
@@ -200,7 +153,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
 
@@ -241,7 +194,7 @@ case "$#" in
 	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)

--
https://github.com/git/git/pull/287
Stephan Beyer· Nov 22, 2016, 00:12 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 19/27] bisect--helper: `bisect_state` & `bisect_head` shell function in C

Hi,
On 10/14/2016 04:14 PM, Pranit Bauva wrote:
Show 11 quoted lines
> Reimplement the `bisect_state` shell function in C and also add a
> subcommand `--bisect-state` to `git-bisect--helper` to call it from
> git-bisect.sh .
> 
> Using `--bisect-state` subcommand 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 and will be called by some
> other methods.
> 
> `bisect_head` is called from `bisect_state` thus its not required to
> introduce another subcommand.
Missing comma before "thus", and "it is" (or "it's") instead of "its" :)
Show 15 quoted lines
> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
> index 1767916..1481c6d 100644
> --- a/builtin/bisect--helper.c
> +++ b/builtin/bisect--helper.c
> @@ -784,6 +786,79 @@ static int bisect_autostart(struct bisect_terms *terms)
>  	return 0;
>  }
>  
> +static char *bisect_head(void)
> +{
> +	if (is_empty_or_missing_file(git_path_bisect_head()))
> +		return "HEAD";
> +	else
> +		return "BISECT_HEAD";
> +}

This is very shellish. In C I'd expect something like

static int bisect_head_sha1(unsigned char *sha)
{
	int res;
	if (is_empty_or_missing_file(git_path_bisect_head()))
		res = get_sha1("HEAD", sha);
	else
		res = get_sha1("BISECT_HEAD", sha);
	if (res)
		return error(_("Could not find BISECT_HEAD or HEAD."));
	return 0;
}
Show 12 quoted lines
> +
> +static int bisect_state(struct bisect_terms *terms, const char **argv,
> +			int argc)
> +{
> +	const char *state = argv[0];
> +
> +	get_terms(terms);
> +	if (check_and_set_terms(terms, state))
> +		return -1;
> +
> +	if (!argc)
> +		die(_("Please call `--bisect-state` with at least one argument"));

I think this check should move to cmd_bisect__helper. There are also the other argument number checks.

Show 5 quoted lines
> +
> +	if (argc == 1 && one_of(state, terms->term_good,
> +	    terms->term_bad, "skip", NULL)) {
> +		const char *bisected_head = xstrdup(bisect_head());
> +		const char *hex[1];
Maybe:
		const char *hex;
> +		unsigned char sha1[20];
> +
> +		if (get_sha1(bisected_head, sha1))
> +			die(_("Bad rev input: %s"), bisected_head);
And instead of...
Show 6 quoted lines
> +		if (bisect_write(state, sha1_to_hex(sha1), terms, 0))
> +			return -1;
> +
> +		*hex = xstrdup(sha1_to_hex(sha1));
> +		if (check_expected_revs(hex, 1))
> +			return -1;
... simply:
		hex = xstrdup(sha1_to_hex(sha1));
		if (set_state(terms, state, hex)) {
			free(hex);
			return -1;
		}
		free(hex);
where:
static int set_state(struct bisect_terms *terms, const char *state,
                                                 const char *hex)
{
	if (bisect_write(state, hex, terms, 0))
		return -1;
	if (check_expected_revs(&hex, 1))
		return -1;
	return 0;
}
Show 18 quoted lines
> +		return bisect_auto_next(terms, NULL);
> +	}
> +
> +	if ((argc == 2 && !strcmp(state, terms->term_bad)) ||
> +			one_of(state, terms->term_good, "skip", NULL)) {
> +		int i;
> +		struct string_list hex = STRING_LIST_INIT_DUP;
> +
> +		for (i = 1; i < argc; i++) {
> +			unsigned char sha1[20];
> +
> +			if (get_sha1(argv[i], sha1)) {
> +				string_list_clear(&hex, 0);
> +				die(_("Bad rev input: %s"), argv[i]);
> +			}
> +			string_list_append(&hex, sha1_to_hex(sha1));
> +		}
> +		for (i = 0; i < hex.nr; i++) {
... And replace this:
Show 9 quoted lines
> +			const char **hex_string = (const char **) &hex.items[i].string;
> +			if(bisect_write(state, *hex_string, terms, 0)) {
> +				string_list_clear(&hex, 0);
> +				return -1;
> +			}
> +			if (check_expected_revs(hex_string, 1)) {
> +				string_list_clear(&hex, 0);
> +				return -1;
> +			}
by:
			const char *hex_str = hex.items[i].string;
			if (set_state(terms, state, hex_string)) {
				string_list_clear(&hex, 0);
				return -1;
			}
Show 21 quoted lines
> +		}
> +		string_list_clear(&hex, 0);
> +		return bisect_auto_next(terms, NULL);
> +	}
> +
> +	if (!strcmp(state, terms->term_bad))
> +		die(_("'git bisect %s' can take only one argument."),
> +		      terms->term_bad);
> +
> +	return -1;
> +}
> +
>  int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>  {
>  	enum {
> @@ -823,6 +899,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>  			 N_("verify the next bisection state then find the next bisection state"), BISECT_AUTO_NEXT),
>  		OPT_CMDMODE(0, "bisect-autostart", &cmdmode,
>  			 N_("start the bisection if BISECT_START empty or missing"), BISECT_AUTOSTART),
> +		OPT_CMDMODE(0, "bisect-state", &cmdmode,
> +			 N_("mark the state of ref (or refs)"), BISECT_STATE),
"mark the state of the given revs"
Note that rev != ref
Show 7 quoted lines
> @@ -902,6 +980,14 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>  		terms.term_bad = "bad";
>  		res = bisect_autostart(&terms);
>  		break;
> +	case BISECT_STATE:
> +		if (argc == 0)
> +			die(_("--bisect-state requires at least 1 argument"));
"at least one revision"
Show 9 quoted lines
> diff --git a/git-bisect.sh b/git-bisect.sh
> index cd56551..a9eebbb 100755
> --- a/git-bisect.sh
> +++ b/git-bisect.sh
> @@ -61,44 +51,7 @@ bisect_skip() {
>  		esac
>  		all="$all $revs"
>  	done
> -	eval bisect_state 'skip' $all
[...deleted lines...]
> +	eval git bisect--helper --bisect-state 'skip' $all
I think you don't need "eval" here any longer.
Show 8 quoted lines
> @@ -184,8 +137,8 @@ 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" )
> +		# We have to use a subshell because "--bisect-state" can exit.
> +		( git bisect--helper --bisect-state $state >"$GIT_DIR/BISECT_RUN" )
The new comment is funny, but you don't need a subshell here any longer.
~Stephan
Pranit Bauva· Dec 6, 2016, 22:40 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 19/27] bisect--helper: `bisect_state` & `bisect_head` shell function in C

Hey Stephan,
On Tue, Nov 22, 2016 at 5:42 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
Show 16 quoted lines
> Hi,
>
> On 10/14/2016 04:14 PM, Pranit Bauva wrote:
>> Reimplement the `bisect_state` shell function in C and also add a
>> subcommand `--bisect-state` to `git-bisect--helper` to call it from
>> git-bisect.sh .
>>
>> Using `--bisect-state` subcommand 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 and will be called by some
>> other methods.
>>
>> `bisect_head` is called from `bisect_state` thus its not required to
>> introduce another subcommand.
>
> Missing comma before "thus", and "it is" (or "it's") instead of "its" :)
Sure, will fix.
Show 48 quoted lines
>> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
>> index 1767916..1481c6d 100644
>> --- a/builtin/bisect--helper.c
>> +++ b/builtin/bisect--helper.c
>> @@ -784,6 +786,79 @@ static int bisect_autostart(struct bisect_terms *terms)
>>       return 0;
>>  }
>>
>> +static char *bisect_head(void)
>> +{
>> +     if (is_empty_or_missing_file(git_path_bisect_head()))
>> +             return "HEAD";
>> +     else
>> +             return "BISECT_HEAD";
>> +}
>
> This is very shellish.
> In C I'd expect something like
>
> static int bisect_head_sha1(unsigned char *sha)
> {
>         int res;
>         if (is_empty_or_missing_file(git_path_bisect_head()))
>                 res = get_sha1("HEAD", sha);
>         else
>                 res = get_sha1("BISECT_HEAD", sha);
>
>         if (res)
>                 return error(_("Could not find BISECT_HEAD or HEAD."));
>
>         return 0;
> }
>
>> +
>> +static int bisect_state(struct bisect_terms *terms, const char **argv,
>> +                     int argc)
>> +{
>> +     const char *state = argv[0];
>> +
>> +     get_terms(terms);
>> +     if (check_and_set_terms(terms, state))
>> +             return -1;
>> +
>> +     if (!argc)
>> +             die(_("Please call `--bisect-state` with at least one argument"));
>
> I think this check should move to cmd_bisect__helper. There are also the
> other argument number checks.

Not really. After the whole conversion, the cmdmode will cease to exists while bisect_state will be called directly, thus it is important to check it here.

Show 33 quoted lines
>> +
>> +     if (argc == 1 && one_of(state, terms->term_good,
>> +         terms->term_bad, "skip", NULL)) {
>> +             const char *bisected_head = xstrdup(bisect_head());
>> +             const char *hex[1];
>
> Maybe:
>                 const char *hex;
>
>> +             unsigned char sha1[20];
>> +
>> +             if (get_sha1(bisected_head, sha1))
>> +                     die(_("Bad rev input: %s"), bisected_head);
>
> And instead of...
>
>> +             if (bisect_write(state, sha1_to_hex(sha1), terms, 0))
>> +                     return -1;
>> +
>> +             *hex = xstrdup(sha1_to_hex(sha1));
>> +             if (check_expected_revs(hex, 1))
>> +                     return -1;
>
> ... simply:
>
>                 hex = xstrdup(sha1_to_hex(sha1));
>                 if (set_state(terms, state, hex)) {
>                         free(hex);
>                         return -1;
>                 }
>                 free(hex);
>
> where:

Yes I am planning to convert all places with hex rather than the sha1 but not yet, maybe in an another patch series because currently a lot of things revolve around sha1 and changing its behaviour wouldn't really be a part of a porting patch series.

Show 74 quoted lines
> static int set_state(struct bisect_terms *terms, const char *state,
>                                                  const char *hex)
> {
>         if (bisect_write(state, hex, terms, 0))
>                 return -1;
>         if (check_expected_revs(&hex, 1))
>                 return -1;
>         return 0;
> }
>
>> +             return bisect_auto_next(terms, NULL);
>> +     }
>> +
>> +     if ((argc == 2 && !strcmp(state, terms->term_bad)) ||
>> +                     one_of(state, terms->term_good, "skip", NULL)) {
>> +             int i;
>> +             struct string_list hex = STRING_LIST_INIT_DUP;
>> +
>> +             for (i = 1; i < argc; i++) {
>> +                     unsigned char sha1[20];
>> +
>> +                     if (get_sha1(argv[i], sha1)) {
>> +                             string_list_clear(&hex, 0);
>> +                             die(_("Bad rev input: %s"), argv[i]);
>> +                     }
>> +                     string_list_append(&hex, sha1_to_hex(sha1));
>> +             }
>> +             for (i = 0; i < hex.nr; i++) {
>
> ... And replace this:
>
>> +                     const char **hex_string = (const char **) &hex.items[i].string;
>> +                     if(bisect_write(state, *hex_string, terms, 0)) {
>> +                             string_list_clear(&hex, 0);
>> +                             return -1;
>> +                     }
>> +                     if (check_expected_revs(hex_string, 1)) {
>> +                             string_list_clear(&hex, 0);
>> +                             return -1;
>> +                     }
>
> by:
>
>                         const char *hex_str = hex.items[i].string;
>                         if (set_state(terms, state, hex_string)) {
>                                 string_list_clear(&hex, 0);
>                                 return -1;
>                         }
>
>> +             }
>> +             string_list_clear(&hex, 0);
>> +             return bisect_auto_next(terms, NULL);
>> +     }
>> +
>> +     if (!strcmp(state, terms->term_bad))
>> +             die(_("'git bisect %s' can take only one argument."),
>> +                   terms->term_bad);
>> +
>> +     return -1;
>> +}
>> +
>>  int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>>  {
>>       enum {
>> @@ -823,6 +899,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>>                        N_("verify the next bisection state then find the next bisection state"), BISECT_AUTO_NEXT),
>>               OPT_CMDMODE(0, "bisect-autostart", &cmdmode,
>>                        N_("start the bisection if BISECT_START empty or missing"), BISECT_AUTOSTART),
>> +             OPT_CMDMODE(0, "bisect-state", &cmdmode,
>> +                      N_("mark the state of ref (or refs)"), BISECT_STATE),
>
> "mark the state of the given revs"
>
> Note that rev != ref
Might have been a typo. Will fix.
Show 9 quoted lines
>> @@ -902,6 +980,14 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>>               terms.term_bad = "bad";
>>               res = bisect_autostart(&terms);
>>               break;
>> +     case BISECT_STATE:
>> +             if (argc == 0)
>> +                     die(_("--bisect-state requires at least 1 argument"));
>
> "at least one revision"
Okay, that would make it more specific.
Show 13 quoted lines
>> diff --git a/git-bisect.sh b/git-bisect.sh
>> index cd56551..a9eebbb 100755
>> --- a/git-bisect.sh
>> +++ b/git-bisect.sh
>> @@ -61,44 +51,7 @@ bisect_skip() {
>>               esac
>>               all="$all $revs"
>>       done
>> -     eval bisect_state 'skip' $all
> [...deleted lines...]
>> +     eval git bisect--helper --bisect-state 'skip' $all
>
> I think you don't need "eval" here any longer.
Yes, I wouldn't
Show 8 quoted lines
>> @@ -184,8 +137,8 @@ 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" )
>> +             # We have to use a subshell because "--bisect-state" can exit.
>> +             ( git bisect--helper --bisect-state $state >"$GIT_DIR/BISECT_RUN" )

True, but right now I didn't want to modify that part of the source code to remove the comment. I will remove the comment all together when I port bisect_run()

Regards, Pranit Bauva

Stephan Beyer· Dec 6, 2016, 23:54 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 19/27] bisect--helper: `bisect_state` & `bisect_head` shell function in C

Hi Pranit,
On 12/06/2016 11:40 PM, Pranit Bauva wrote:
Show 20 quoted lines
> On Tue, Nov 22, 2016 at 5:42 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
>> On 10/14/2016 04:14 PM, Pranit Bauva wrote:
>>> +static int bisect_state(struct bisect_terms *terms, const char **argv,
>>> +                     int argc)
>>> +{
>>> +     const char *state = argv[0];
>>> +
>>> +     get_terms(terms);
>>> +     if (check_and_set_terms(terms, state))
>>> +             return -1;
>>> +
>>> +     if (!argc)
>>> +             die(_("Please call `--bisect-state` with at least one argument"));
>>
>> I think this check should move to cmd_bisect__helper. There are also the
>> other argument number checks.
> 
> Not really. After the whole conversion, the cmdmode will cease to
> exists while bisect_state will be called directly, thus it is
> important to check it here.

Okay, that's a point. In that case, you should probably use "return error()" again and the message (mentioning "--bisect-state") does not make sense when --bisect-state ceases to exist.

Show 38 quoted lines
>>> +
>>> +     if (argc == 1 && one_of(state, terms->term_good,
>>> +         terms->term_bad, "skip", NULL)) {
>>> +             const char *bisected_head = xstrdup(bisect_head());
>>> +             const char *hex[1];
>>
>> Maybe:
>>                 const char *hex;
>>
>>> +             unsigned char sha1[20];
>>> +
>>> +             if (get_sha1(bisected_head, sha1))
>>> +                     die(_("Bad rev input: %s"), bisected_head);
>>
>> And instead of...
>>
>>> +             if (bisect_write(state, sha1_to_hex(sha1), terms, 0))
>>> +                     return -1;
>>> +
>>> +             *hex = xstrdup(sha1_to_hex(sha1));
>>> +             if (check_expected_revs(hex, 1))
>>> +                     return -1;
>>
>> ... simply:
>>
>>                 hex = xstrdup(sha1_to_hex(sha1));
>>                 if (set_state(terms, state, hex)) {
>>                         free(hex);
>>                         return -1;
>>                 }
>>                 free(hex);
>>
>> where:
> 
> Yes I am planning to convert all places with hex rather than the sha1
> but not yet, maybe in an another patch series because currently a lot
> of things revolve around sha1 and changing its behaviour wouldn't
> really be a part of a porting patch series.

I was not suggesting a change of behavior, I was suggesting a simple helper function set_state() to get rid of code duplication above and some lines below:

Show 19 quoted lines
>> ... And replace this:
>>
>>> +                     const char **hex_string = (const char **) &hex.items[i].string;
>>> +                     if(bisect_write(state, *hex_string, terms, 0)) {
>>> +                             string_list_clear(&hex, 0);
>>> +                             return -1;
>>> +                     }
>>> +                     if (check_expected_revs(hex_string, 1)) {
>>> +                             string_list_clear(&hex, 0);
>>> +                             return -1;
>>> +                     }
>>
>> by:
>>
>>                         const char *hex_str = hex.items[i].string;
>>                         if (set_state(terms, state, hex_string)) {
>>                                 string_list_clear(&hex, 0);
>>                                 return -1;
>>                         }
See?
Show 15 quoted lines
>>> @@ -184,8 +137,8 @@ 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" )
>>> +             # We have to use a subshell because "--bisect-state" can exit.
>>> +             ( git bisect--helper --bisect-state $state >"$GIT_DIR/BISECT_RUN" )
>>
>> The new comment is funny, but you don't need a subshell here any
>> longer.
> 
> True, but right now I didn't want to modify that part of the source
> code to remove the comment. I will remove the comment all together
> when I port bisect_run()

For most of the patches, I was commenting on the current state, not on the big picture.

Still I think that it is better to remove the comment and the subshell instead of making the comment weird ("yes the builtin can exit, but why do we need a subshell?" vs "yes the shell function calls exit, so we need a subshell because we do not want to exit this shell script")

~Stephan
Pranit Bauva· Dec 8, 2016, 06:43 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 19/27] bisect--helper: `bisect_state` & `bisect_head` shell function in C

Hey Stephan,
On Wed, Dec 7, 2016 at 5:24 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
Show 28 quoted lines
> Hi Pranit,
>
> On 12/06/2016 11:40 PM, Pranit Bauva wrote:
>> On Tue, Nov 22, 2016 at 5:42 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
>>> On 10/14/2016 04:14 PM, Pranit Bauva wrote:
>>>> +static int bisect_state(struct bisect_terms *terms, const char **argv,
>>>> +                     int argc)
>>>> +{
>>>> +     const char *state = argv[0];
>>>> +
>>>> +     get_terms(terms);
>>>> +     if (check_and_set_terms(terms, state))
>>>> +             return -1;
>>>> +
>>>> +     if (!argc)
>>>> +             die(_("Please call `--bisect-state` with at least one argument"));
>>>
>>> I think this check should move to cmd_bisect__helper. There are also the
>>> other argument number checks.
>>
>> Not really. After the whole conversion, the cmdmode will cease to
>> exists while bisect_state will be called directly, thus it is
>> important to check it here.
>
> Okay, that's a point.
> In that case, you should probably use "return error()" again and the
> message (mentioning "--bisect-state") does not make sense when
> --bisect-state ceases to exist.
True. Will change accordingly.
Show 64 quoted lines
>>>> +
>>>> +     if (argc == 1 && one_of(state, terms->term_good,
>>>> +         terms->term_bad, "skip", NULL)) {
>>>> +             const char *bisected_head = xstrdup(bisect_head());
>>>> +             const char *hex[1];
>>>
>>> Maybe:
>>>                 const char *hex;
>>>
>>>> +             unsigned char sha1[20];
>>>> +
>>>> +             if (get_sha1(bisected_head, sha1))
>>>> +                     die(_("Bad rev input: %s"), bisected_head);
>>>
>>> And instead of...
>>>
>>>> +             if (bisect_write(state, sha1_to_hex(sha1), terms, 0))
>>>> +                     return -1;
>>>> +
>>>> +             *hex = xstrdup(sha1_to_hex(sha1));
>>>> +             if (check_expected_revs(hex, 1))
>>>> +                     return -1;
>>>
>>> ... simply:
>>>
>>>                 hex = xstrdup(sha1_to_hex(sha1));
>>>                 if (set_state(terms, state, hex)) {
>>>                         free(hex);
>>>                         return -1;
>>>                 }
>>>                 free(hex);
>>>
>>> where:
>>
>> Yes I am planning to convert all places with hex rather than the sha1
>> but not yet, maybe in an another patch series because currently a lot
>> of things revolve around sha1 and changing its behaviour wouldn't
>> really be a part of a porting patch series.
>
> I was not suggesting a change of behavior, I was suggesting a simple
> helper function set_state() to get rid of code duplication above and
> some lines below:
>
>>> ... And replace this:
>>>
>>>> +                     const char **hex_string = (const char **) &hex.items[i].string;
>>>> +                     if(bisect_write(state, *hex_string, terms, 0)) {
>>>> +                             string_list_clear(&hex, 0);
>>>> +                             return -1;
>>>> +                     }
>>>> +                     if (check_expected_revs(hex_string, 1)) {
>>>> +                             string_list_clear(&hex, 0);
>>>> +                             return -1;
>>>> +                     }
>>>
>>> by:
>>>
>>>                         const char *hex_str = hex.items[i].string;
>>>                         if (set_state(terms, state, hex_string)) {
>>>                                 string_list_clear(&hex, 0);
>>>                                 return -1;
>>>                         }
>
> See?

I can do this change of using set_state() keeping aside the sha1 to hex and such change.

Show 22 quoted lines
>>>> @@ -184,8 +137,8 @@ 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" )
>>>> +             # We have to use a subshell because "--bisect-state" can exit.
>>>> +             ( git bisect--helper --bisect-state $state >"$GIT_DIR/BISECT_RUN" )
>>>
>>> The new comment is funny, but you don't need a subshell here any
>>> longer.
>>
>> True, but right now I didn't want to modify that part of the source
>> code to remove the comment. I will remove the comment all together
>> when I port bisect_run()
> For most of the patches, I was commenting on the current state, not on
> the big picture.
>
> Still I think that it is better to remove the comment and the subshell
> instead of making the comment weird ("yes the builtin can exit, but why
> do we need a subshell?" vs "yes the shell function calls exit, so we
> need a subshell because we do not want to exit this shell script")
Sure I will remove the comment.

Regards, Pranit Bauva

Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 14/27] t6030: no cleanup with bad merge base

The bisection cleanup should be performed with bad merge base so that the user can return to its original position with `git bisect reset`.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 t/t6030-bisect-porcelain.sh | 7 +++++++
 1 file changed, 7 insertions(+)
Show changes to t/t6030-bisect-porcelain.sh +7 −1
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index e62e2a8..8ac77ee 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -911,4 +911,11 @@ test_expect_success 'git bisect reset cleans bisection state properly' '
 	test_path_is_missing "$GIT_DIR/BISECT_START"
 '
 
+test_expect_success 'check whether bisection cleanup is not done with bad merges' '
+	git bisect start $HASH7 $SIDE_HASH7 &&
+	test_expect_failure git bisect bad >out 2>out &&
+	test_i18ngrep "The merge base" out &&
+	test -e .git/BISECT_START
+'
+
 test_done

--
https://github.com/git/git/pull/287
Junio C Hamano· Oct 14, 2016, 21:43 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 14/27] t6030: no cleanup with bad merge base

Pranit Bauva <pranit.bauva@gmail.com> writes:
> +test_expect_success 'check whether bisection cleanup is not done with bad merges' '
> +	git bisect start $HASH7 $SIDE_HASH7 &&
> +	test_expect_failure git bisect bad >out 2>out &&
I think you meant "test_must_fail" here.
Show 8 quoted lines
> +	test_i18ngrep "The merge base" out &&
> +	test -e .git/BISECT_START
> +'
> +
>  test_done
>
> --
> https://github.com/git/git/pull/287
Pranit Bauva· Oct 15, 2016, 08:46 UTC · re: Junio C Hamano · lore

Re: [PATCH v15 14/27] t6030: no cleanup with bad merge base

Hey Junio,
On Sat, Oct 15, 2016 at 3:13 AM, Junio C Hamano <gitster@pobox.com> wrote:
Show 7 quoted lines
> Pranit Bauva <pranit.bauva@gmail.com> writes:
>
>> +test_expect_success 'check whether bisection cleanup is not done with bad merges' '
>> +     git bisect start $HASH7 $SIDE_HASH7 &&
>> +     test_expect_failure git bisect bad >out 2>out &&
>
> I think you meant "test_must_fail" here.

Oh yes! Thanks for pointing it out. I see that you have already submitted a patch to catch this mistake which was previously ignored. Thanks!

Regards, Pranit Bauva

Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 15/27] bisect--helper: `bisect_next` and `bisect_auto_next` shell function in C

Reimplement the `bisect_next` and the `bisect_auto_next` shell function in C and add the subcommands to `git bisect--helper` to call it from git-bisect.sh .

Along with this conversion of `bisect_start` has also finished and thus it has been fully ported to C.

A lot of parts of bisect.c uses exit() and these signals are then trapped in the `bisect_start` function. Since the shell script ceases its existence it would be necessary to convert those exit() calls to return statements so that errors can be reported.

As more and more calls are happening to the subcommands in `git bisect--helper`, more specifically when `bisect_start $rev` is converted to `git bisect--helper --bisect-start $rev` it is necessary to dequote the arguments because of shell to C conversion.

Using `--bisect-next` and `--bisect-auto-start` subcommands 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 and will be called by some other methods.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 bisect.c                 | 128 ++++++++++++++++++++++++--------
 builtin/bisect--helper.c | 189 ++++++++++++++++++++++++++++++++++++++++++++---
 git-bisect.sh            |  74 ++-----------------
 3 files changed, 285 insertions(+), 106 deletions(-)
Show changes to 3 files +285 −107

bisect.c, builtin/bisect--helper.c, git-bisect.sh

diff --git a/bisect.c b/bisect.c
index 45d598d..7c97e85 100644
--- a/bisect.c
+++ b/bisect.c
@@ -618,6 +618,12 @@ static void bisect_rev_setup(struct rev_info *revs, const char *prefix,
 	struct argv_array rev_argv = ARGV_ARRAY_INIT;
 	int i;
 
+	/*
+	 * Since the code is slowly being converted to C, there might be
+	 * instances where the revisions were initialized before. Thus
+	 * we first need to reset it.
+	 */
+	reset_revision_walk();
 	init_revisions(revs, prefix);
 	revs->abbrev = 0;
 	revs->commit_format = CMIT_FMT_UNSPECIFIED;
@@ -638,17 +644,22 @@ static void bisect_rev_setup(struct rev_info *revs, const char *prefix,
 
 static void bisect_common(struct rev_info *revs)
 {
+	/*
+	 * We don't want to clean the bisection state
+	 * as we need to get back to where we started
+	 * by using `git bisect reset`.
+	 */
 	if (prepare_revision_walk(revs))
 		die("revision walk setup failed");
 	if (revs->tree_objects)
 		mark_edges_uninteresting(revs, NULL);
 }
 
-static void exit_if_skipped_commits(struct commit_list *tried,
+static int exit_if_skipped_commits(struct commit_list *tried,
 				    const struct object_id *bad)
 {
 	if (!tried)
-		return;
+		return 0;
 
 	printf("There are only 'skip'ped commits left to test.\n"
 	       "The first %s commit could be any of:\n", term_bad);
@@ -659,7 +670,13 @@ static void exit_if_skipped_commits(struct commit_list *tried,
 	if (bad)
 		printf("%s\n", oid_to_hex(bad));
 	printf(_("We cannot bisect more!\n"));
-	exit(2);
+
+	/*
+	 * We don't want to clean the bisection state
+	 * as we need to get back to where we started
+	 * by using `git bisect reset`.
+	 */
+	return 2;
 }
 
 static int is_expected_rev(const struct object_id *oid)
@@ -700,7 +717,7 @@ static int bisect_checkout(const unsigned char *bisect_rev, int no_checkout)
 		int res;
 		res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
 		if (res)
-			exit(res);
+			return res;
 	}
 
 	argv_show_branch[1] = bisect_rev_hex;
@@ -729,7 +746,7 @@ static struct commit **get_bad_and_good_commits(int *rev_nr)
 	return rev;
 }
 
-static void handle_bad_merge_base(void)
+static int handle_bad_merge_base(void)
 {
 	if (is_expected_rev(current_bad_oid)) {
 		char *bad_hex = oid_to_hex(current_bad_oid);
@@ -750,17 +767,23 @@ static void handle_bad_merge_base(void)
 				"between %s and [%s].\n"),
 				bad_hex, term_bad, term_good, bad_hex, good_hex);
 		}
-		exit(3);
+		/*
+		 * We don't want to clean the bisection state
+		 * as we need to get back to where we started
+		 * by using `git bisect reset`.
+		 */
+		return 3;
 	}
 
 	fprintf(stderr, _("Some %s revs are not ancestor of the %s rev.\n"
 		"git bisect cannot work properly in this case.\n"
 		"Maybe you mistook %s and %s revs?\n"),
 		term_good, term_bad, term_good, term_bad);
-	exit(1);
+	bisect_clean_state();
+	return 1;
 }
 
-static void handle_skipped_merge_base(const unsigned char *mb)
+static int handle_skipped_merge_base(const unsigned char *mb)
 {
 	char *mb_hex = sha1_to_hex(mb);
 	char *bad_hex = oid_to_hex(current_bad_oid);
@@ -773,21 +796,22 @@ static void handle_skipped_merge_base(const unsigned char *mb)
 		"We continue anyway."),
 		bad_hex, good_hex, term_bad, mb_hex, bad_hex);
 	free(good_hex);
+	return 0;
 }
 
 /*
  * "check_merge_bases" checks that merge bases are not "bad" (or "new").
  *
  * - If one is "bad" (or "new"), it means the user assumed something wrong
- * and we must exit with a non 0 error code.
+ * and we must return error with a non 0 error code.
  * - If one is "good" (or "old"), that's good, we have nothing to do.
  * - If one is "skipped", we can't know but we should warn.
  * - If we don't know, we should check it out and ask the user to test.
  */
-static void check_merge_bases(int no_checkout)
+static int check_merge_bases(int no_checkout)
 {
 	struct commit_list *result;
-	int rev_nr;
+	int rev_nr, res = 0;
 	struct commit **rev = get_bad_and_good_commits(&rev_nr);
 
 	result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1);
@@ -795,19 +819,30 @@ static void check_merge_bases(int no_checkout)
 	for (; result; result = result->next) {
 		const unsigned char *mb = result->item->object.oid.hash;
 		if (!hashcmp(mb, current_bad_oid->hash)) {
-			handle_bad_merge_base();
+			res = handle_bad_merge_base();
+			break;
 		} else if (0 <= sha1_array_lookup(&good_revs, mb)) {
 			continue;
 		} else if (0 <= sha1_array_lookup(&skipped_revs, mb)) {
-			handle_skipped_merge_base(mb);
+			res = handle_skipped_merge_base(mb);
+			break;
 		} else {
 			printf(_("Bisecting: a merge base must be tested\n"));
-			exit(bisect_checkout(mb, no_checkout));
+			res = bisect_checkout(mb, no_checkout);
+			/*
+			 * We don't want to clean the bisection state
+			 * as we need to get back to where we started
+			 * by using `git bisect reset`.
+			 */
+			if (!res)
+				exit(0);
+			break;
 		}
 	}
 
 	free(rev);
 	free_commit_list(result);
+	return res;
 }
 
 static int check_ancestors(const char *prefix)
@@ -843,16 +878,21 @@ static int check_ancestors(const char *prefix)
  *
  * If that's not the case, we need to check the merge bases.
  * If a merge base must be tested by the user, its source code will be
- * checked out to be tested by the user and we will exit.
+ * checked out to be tested by the user and we will return.
  */
-static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
+static int check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
 {
 	char *filename = git_pathdup("BISECT_ANCESTORS_OK");
 	struct stat st;
-	int fd;
+	int fd, res = 0;
 
+	/*
+	 * We don't want to clean the bisection state
+	 * as we need to get back to where we started
+	 * by using `git bisect reset`.
+	 */
 	if (!current_bad_oid)
-		die(_("a %s revision is needed"), term_bad);
+		error(_("a %s revision is needed"), term_bad);
 
 	/* Check if file BISECT_ANCESTORS_OK exists. */
 	if (!stat(filename, &st) && S_ISREG(st.st_mode))
@@ -864,7 +904,10 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
 
 	/* Check if all good revs are ancestor of the bad rev. */
 	if (check_ancestors(prefix))
-		check_merge_bases(no_checkout);
+		res = check_merge_bases(no_checkout);
+
+	if (res)
+		goto done;
 
 	/* Create file BISECT_ANCESTORS_OK. */
 	fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
@@ -873,8 +916,11 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
 			      filename);
 	else
 		close(fd);
+
+	goto done;
  done:
 	free(filename);
+	return res;
 }
 
 /*
@@ -933,7 +979,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
 }
 
 /*
- * We use the convention that exiting with an exit code 10 means that
+ * We use the convention to return with an return code 10 means that
  * the bisection process finished successfully.
  * In this case the calling shell script should exit 0.
  *
@@ -944,7 +990,7 @@ int bisect_next_all(const char *prefix, int no_checkout)
 {
 	struct rev_info revs;
 	struct commit_list *tried;
-	int reaches = 0, all = 0, nr, steps;
+	int reaches = 0, all = 0, nr, steps, res;
 	const unsigned char *bisect_rev;
 	char steps_msg[32];
 
@@ -952,7 +998,9 @@ int bisect_next_all(const char *prefix, int no_checkout)
 	if (read_bisect_refs())
 		die(_("reading bisect refs failed"));
 
-	check_good_are_ancestors_of_bad(prefix, no_checkout);
+	res = check_good_are_ancestors_of_bad(prefix, no_checkout);
+	if (res)
+		return res;
 
 	bisect_rev_setup(&revs, prefix, "%s", "^%s", 1);
 	revs.limited = 1;
@@ -964,34 +1012,52 @@ int bisect_next_all(const char *prefix, int no_checkout)
 	revs.commits = managed_skipped(revs.commits, &tried);
 
 	if (!revs.commits) {
+		int res;
 		/*
-		 * We should exit here only if the "bad"
+		 * We should return error here only if the "bad"
 		 * commit is also a "skip" commit.
 		 */
-		exit_if_skipped_commits(tried, NULL);
+		res = exit_if_skipped_commits(tried, NULL);
+		if (res)
+			return res;
 
 		printf(_("%s was both %s and %s\n"),
 		       oid_to_hex(current_bad_oid),
 		       term_good,
 		       term_bad);
-		exit(1);
+
+		/*
+		 * We don't want to clean the bisection state
+		 * as we need to get back to where we started
+		 * by using `git bisect reset`.
+		 */
+		return 1;
 	}
 
 	if (!all) {
 		fprintf(stderr, _("No testable commit found.\n"
 			"Maybe you started with bad path parameters?\n"));
-		exit(4);
+
+		/*
+		 * We don't want to clean the bisection state
+		 * as we need to get back to where we started
+		 * by using `git bisect reset`.
+		 */
+		return 4;
 	}
 
 	bisect_rev = revs.commits->item->object.oid.hash;
 
 	if (!hashcmp(bisect_rev, current_bad_oid->hash)) {
-		exit_if_skipped_commits(tried, current_bad_oid);
+		res = exit_if_skipped_commits(tried, current_bad_oid);
+		if (res)
+			return res;
+
 		printf("%s is the first %s commit\n", sha1_to_hex(bisect_rev),
 			term_bad);
 		show_diff_tree(prefix, revs.commits->item);
 		/* This means the bisection process succeeded. */
-		exit(10);
+		return 10;
 	}
 
 	nr = all - reaches - 1;
@@ -1005,7 +1071,11 @@ int bisect_next_all(const char *prefix, int no_checkout)
 		  "Bisecting: %d revisions left to test after this %s\n",
 		  nr), nr, steps_msg);
 
-	return bisect_checkout(bisect_rev, no_checkout);
+	res = bisect_checkout(bisect_rev, no_checkout);
+	if (res)
+		bisect_clean_state();
+
+	return res;
 }
 
 static inline int log2i(int n)
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 1d3e17f..fcd7574 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -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")
@@ -29,6 +30,8 @@ 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"),
 	NULL
 };
 
@@ -408,6 +411,136 @@ static int bisect_terms(struct bisect_terms *terms, const char **argv, int argc)
 	return 0;
 }
 
+static int register_good_ref(const char *refname,
+			     const struct object_id *oid, int flags,
+			     void *cb_data)
+{
+	struct string_list *good_refs = cb_data;
+	string_list_append(good_refs, oid_to_hex(oid));
+	return 0;
+}
+
+static int bisect_next(struct bisect_terms *terms, const char *prefix)
+{
+	int res, no_checkout;
+
+	/*
+	 * 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".
+	 */
+	if (bisect_next_check(terms, terms->term_good))
+		return -1;
+
+	no_checkout = !is_empty_or_missing_file(git_path_bisect_head());
+
+	/* Perform all bisection computation, display and checkout */
+	res = bisect_next_all(prefix , no_checkout);
+
+	if (res == 10) {
+		FILE *fp = NULL;
+		unsigned char sha1[20];
+		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 retval = 0;
+
+		read_ref(bad_ref, sha1);
+		commit = lookup_commit_reference(sha1);
+		format_commit_message(commit, "%s", &commit_name, &pp);
+		fp = fopen(git_path_bisect_log(), "a");
+		if (!fp) {
+			retval = -1;
+			goto finish_10;
+		}
+		if (fprintf(fp, "# first %s commit: [%s] %s\n",
+			    terms->term_bad, sha1_to_hex(sha1),
+			    commit_name.buf) < 1){
+			retval = -1;
+			goto finish_10;
+		}
+		goto finish_10;
+	finish_10:
+		if (fp)
+			fclose(fp);
+		strbuf_release(&commit_name);
+		free(bad_ref);
+		return retval;
+	}
+	else if (res == 2) {
+		FILE *fp = NULL;
+		struct rev_info revs;
+		struct argv_array rev_argv = ARGV_ARRAY_INIT;
+		struct string_list good_revs = STRING_LIST_INIT_DUP;
+		struct pretty_print_context pp = {0};
+		struct commit *commit;
+		char *term_good = xstrfmt("%s-*", terms->term_good);
+		int i, retval = 0;
+
+		fp = fopen(git_path_bisect_log(), "a");
+		if (!fp) {
+			retval = -1;
+			goto finish_2;
+		}
+		if (fprintf(fp, "# only skipped commits left to test\n") < 1) {
+			retval = -1;
+			goto finish_2;
+		}
+		for_each_glob_ref_in(register_good_ref, term_good,
+				     "refs/bisect/", (void *) &good_revs);
+
+		argv_array_pushl(&rev_argv, "skipped_commits", "refs/bisect/bad", "--not", NULL);
+		for (i = 0; i < good_revs.nr; i++)
+			argv_array_push(&rev_argv, good_revs.items[i].string);
+
+		/* It is important to reset the flags used by revision walks
+		 * as the previous call to bisect_next_all() in turn
+		 * setups a revision walk.
+		 */
+		reset_revision_walk();
+		init_revisions(&revs, NULL);
+		rev_argv.argc = setup_revisions(rev_argv.argc, rev_argv.argv, &revs, NULL);
+		argv_array_clear(&rev_argv);
+		string_list_clear(&good_revs, 0);
+		if (prepare_revision_walk(&revs))
+			die(_("revision walk setup failed\n"));
+
+		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);
+		}
+		goto finish_2;
+	finish_2:
+		if (fp)
+			fclose(fp);
+		string_list_clear(&good_revs, 0);
+		argv_array_clear(&rev_argv);
+		free(term_good);
+		if (retval)
+			return retval;
+		else
+			return res;
+	}
+	return res;
+}
+
+static int bisect_auto_next(struct bisect_terms *terms, const char *prefix)
+{
+	if (!bisect_next_check(terms, NULL))
+		return bisect_next(terms, prefix);
+
+	return 0;
+}
+
 static int bisect_start(struct bisect_terms *terms, int no_checkout,
 			const char **argv, int argc)
 {
@@ -427,15 +560,24 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
 		no_checkout = 1;
 
 	for (i = 0; i < argc; i++) {
-		if (!strcmp(argv[i], "--")) {
+		const char *arg;
+		if (starts_with(argv[i], "'"))
+			arg = sq_dequote(xstrdup(argv[i]));
+		else
+			arg = argv[i];
+		if (!strcmp(arg, "--")) {
 			has_double_dash = 1;
 			break;
 		}
 	}
 
 	for (i = 0; i < argc; i++) {
-		const char *commit_id = xstrfmt("%s^{commit}", argv[i]);
-		const char *arg = argv[i];
+		const char *arg, *commit_id;
+		if (starts_with(argv[i], "'"))
+			arg = sq_dequote(xstrdup(argv[i]));
+		else
+			arg = argv[i];
+		commit_id = xstrfmt("%s^{commit}", arg);
 		if (!strcmp(argv[i], "--")) {
 			has_double_dash = 1;
 			break;
@@ -443,24 +585,31 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
 			no_checkout = 1;
 		} else if (!strcmp(arg, "--term-good") ||
 			 !strcmp(arg, "--term-old")) {
+			if (starts_with(argv[++i], "'"))
+				terms->term_good = sq_dequote(xstrdup(argv[i]));
+			else
+				terms->term_good = xstrdup(argv[i]);
 			must_write_terms = 1;
-			terms->term_good = xstrdup(argv[++i]);
 		} else if (skip_prefix(arg, "--term-good=", &arg)) {
 			must_write_terms = 1;
-			terms->term_good = xstrdup(arg);
+			terms->term_good = arg;
 		} else if (skip_prefix(arg, "--term-old=", &arg)) {
 			must_write_terms = 1;
-			terms->term_good = xstrdup(arg);
+			terms->term_good = arg;
 		} else if (!strcmp(arg, "--term-bad") ||
 			 !strcmp(arg, "--term-new")) {
+			const char *next_arg;
+			if (starts_with(argv[++i], "'"))
+				terms->term_bad = sq_dequote(xstrdup(argv[i]));
+			else
+				terms->term_bad = xstrdup(argv[i]);
 			must_write_terms = 1;
-			terms->term_bad = xstrdup(argv[++i]);
 		} else if (skip_prefix(arg, "--term-bad=", &arg)) {
 			must_write_terms = 1;
-			terms->term_bad = xstrdup(arg);
+			terms->term_bad = arg;
 		} else if (skip_prefix(arg, "--term-new=", &arg)) {
 			must_write_terms = 1;
-			terms->term_good = xstrdup(arg);
+			terms->term_good = arg;
 		} else if (starts_with(arg, "--") &&
 			 !one_of(arg, "--term-good", "--term-bad", NULL)) {
 			die(_("unrecognised option: '%s'"), arg);
@@ -604,7 +753,7 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
 	strbuf_release(&start_head);
 	strbuf_release(&bisect_names);
 	strbuf_release(&orig_args);
-	return retval;
+	return retval || bisect_auto_next(terms, NULL);
 }
 
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
@@ -619,7 +768,9 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		CHECK_AND_SET_TERMS,
 		BISECT_NEXT_CHECK,
 		BISECT_TERMS,
-		BISECT_START
+		BISECT_START,
+		BISECT_NEXT,
+		BISECT_AUTO_NEXT,
 	} cmdmode = 0;
 	int no_checkout = 0, res = 0;
 	struct option options[] = {
@@ -643,6 +794,10 @@ 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 find the next bisection state"), BISECT_AUTO_NEXT),
 		OPT_BOOL(0, "no-checkout", &no_checkout,
 			 N_("update BISECT_HEAD instead of checking out the current commit")),
 		OPT_END()
@@ -710,6 +865,18 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		terms.term_bad = "bad";
 		res = bisect_start(&terms, no_checkout, argv, argc);
 		break;
+	case BISECT_NEXT:
+		if (argc)
+			die(_("--bisect-next requires 0 arguments"));
+		get_terms(&terms);
+		res = bisect_next(&terms, prefix);
+		break;
+	case BISECT_AUTO_NEXT:
+		if (argc)
+			die(_("--bisect-auto-next requires 0 arguments"));
+		get_terms(&terms);
+		res = bisect_auto_next(&terms, prefix);
+		break;
 	default:
 		die("BUG: unknown subcommand '%d'", cmdmode);
 	}
diff --git a/git-bisect.sh b/git-bisect.sh
index f0896b3..d574c44 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -63,34 +63,13 @@ bisect_autostart() {
 			[Nn]*)
 				exit ;;
 			esac
-			bisect_start
+			git bisect--helper --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 "$@"
@@ -109,6 +88,7 @@ bisect_skip() {
 bisect_state() {
 	bisect_autostart
 	state=$1
+	get_terms
 	git bisect--helper --check-and-set-terms $state $TERM_GOOD $TERM_BAD || exit
 	get_terms
 	case "$#,$state" in
@@ -139,45 +119,7 @@ bisect_state() {
 	*)
 		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
+	git bisect--helper --bisect-auto-next || exit
 }
 
 bisect_visualize() {
@@ -221,8 +163,7 @@ bisect_replay () {
 		get_terms
 		case "$command" in
 		start)
-			cmd="bisect_start $rev"
-			eval "$cmd" ;;
+			eval "git bisect--helper --bisect-start $rev" ;;
 		"$TERM_GOOD"|"$TERM_BAD"|skip)
 			git bisect--helper --bisect-write "$command" "$rev" "$TERM_GOOD" "$TERM_BAD" || exit;;
 		terms)
@@ -231,7 +172,7 @@ bisect_replay () {
 			die "$(gettext "?? what are you talking about?")" ;;
 		esac
 	done <"$file"
-	bisect_auto_next
+	git bisect--helper --bisect-auto-next
 }
 
 bisect_run () {
@@ -319,14 +260,15 @@ 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" "$@" ;;
 	skip)
 		bisect_skip "$@" ;;
 	next)
 		# Not sure we want "next" at the UI level anymore.
-		bisect_next "$@" ;;
+		get_terms
+		git bisect--helper --bisect-next "$@" || exit ;;
 	visualize|view)
 		bisect_visualize "$@" ;;
 	reset)

--
https://github.com/git/git/pull/287
Stephan Beyer· Nov 20, 2016, 20:01 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 15/27] bisect--helper: `bisect_next` and `bisect_auto_next` shell function in C

Hi Pranit,

this one is hard to review because you do two or three commits in one here. I think the first commit should be the exit()->return conversion, the second commit is next and autonext, and the third commit is the pretty trivial bisect_start commit ;) However, you did it this way and it's always a hassle to split commit, so I don't really care...

However, I was reviewing this superficially, to be honest. This mail skips the next and autonext part.

On 10/14/2016 04:14 PM, Pranit Bauva wrote:
Show 27 quoted lines
> diff --git a/bisect.c b/bisect.c
> index 45d598d..7c97e85 100644
> --- a/bisect.c
> +++ b/bisect.c
> @@ -843,16 +878,21 @@ static int check_ancestors(const char *prefix)
>   *
>   * If that's not the case, we need to check the merge bases.
>   * If a merge base must be tested by the user, its source code will be
> - * checked out to be tested by the user and we will exit.
> + * checked out to be tested by the user and we will return.
>   */
> -static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
> +static int check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
>  {
>  	char *filename = git_pathdup("BISECT_ANCESTORS_OK");
>  	struct stat st;
> -	int fd;
> +	int fd, res = 0;
>  
> +	/*
> +	 * We don't want to clean the bisection state
> +	 * as we need to get back to where we started
> +	 * by using `git bisect reset`.
> +	 */
>  	if (!current_bad_oid)
> -		die(_("a %s revision is needed"), term_bad);
> +		error(_("a %s revision is needed"), term_bad);
Only error() or return error()?
Show 7 quoted lines
> @@ -873,8 +916,11 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
>  			      filename);
>  	else
>  		close(fd);
> +
> +	goto done;
>   done:

I never understand why you do this. In case of adding a "fail" label (and fail code like "res = -1;") between "goto done" and "done:", it's fine... but without one this is just a nop.

Show 14 quoted lines
> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
> index 1d3e17f..fcd7574 100644
> --- a/builtin/bisect--helper.c
> +++ b/builtin/bisect--helper.c
> @@ -427,15 +560,24 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
>  		no_checkout = 1;
>  
>  	for (i = 0; i < argc; i++) {
> -		if (!strcmp(argv[i], "--")) {
> +		const char *arg;
> +		if (starts_with(argv[i], "'"))
> +			arg = sq_dequote(xstrdup(argv[i]));
> +		else
> +			arg = argv[i];

One is xstrdup'ed, one is not, so there'll be a leak somewhere, and it's an inconsistent leak... I guess it's a bad idea to do it this way ;) (Also below.)

Show 14 quoted lines
> @@ -443,24 +585,31 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
>  			no_checkout = 1;
>  		} else if (!strcmp(arg, "--term-good") ||
>  			 !strcmp(arg, "--term-old")) {
> +			if (starts_with(argv[++i], "'"))
> +				terms->term_good = sq_dequote(xstrdup(argv[i]));
> +			else
> +				terms->term_good = xstrdup(argv[i]);
>  			must_write_terms = 1;
> -			terms->term_good = xstrdup(argv[++i]);
>  		} else if (skip_prefix(arg, "--term-good=", &arg)) {
>  			must_write_terms = 1;
> -			terms->term_good = xstrdup(arg);
> +			terms->term_good = arg;
No ;) (See my other comments (to other patches) for the "terms" leaks.)
[This repeats several times below.]
Show 12 quoted lines
> diff --git a/git-bisect.sh b/git-bisect.sh
> index f0896b3..d574c44 100755
> --- a/git-bisect.sh
> +++ b/git-bisect.sh
> @@ -109,6 +88,7 @@ bisect_skip() {
>  bisect_state() {
>  	bisect_autostart
>  	state=$1
> +	get_terms
>  	git bisect--helper --check-and-set-terms $state $TERM_GOOD $TERM_BAD || exit
>  	get_terms
>  	case "$#,$state" in

I can't say if this change is right or wrong. It looks right, but: How does this relate to the other changes? Is this the right patch for it?

~Stephan
Pranit Bauva· Dec 31, 2016, 10:23 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 15/27] bisect--helper: `bisect_next` and `bisect_auto_next` shell function in C

Hey Stephan,

Extremely sorry I just forgot to reply to this email before. I was preparing from the next iteration when I saw this.

On Mon, Nov 21, 2016 at 1:31 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
Show 7 quoted lines
> Hi Pranit,
>
> this one is hard to review because you do two or three commits in one here.
> I think the first commit should be the exit()->return conversion, the
> second commit is next and autonext, and the third commit is the pretty
> trivial bisect_start commit ;) However, you did it this way and it's
> always a hassle to split commit, so I don't really care...

I had confusion about how to split the commits, but then I then decided to dump it all together so that it compiles (I was finding it difficult to split into meaningful parts which also compiled).

Show 33 quoted lines
> However, I was reviewing this superficially, to be honest. This mail
> skips the next and autonext part.
>
> On 10/14/2016 04:14 PM, Pranit Bauva wrote:
>> diff --git a/bisect.c b/bisect.c
>> index 45d598d..7c97e85 100644
>> --- a/bisect.c
>> +++ b/bisect.c
>> @@ -843,16 +878,21 @@ static int check_ancestors(const char *prefix)
>>   *
>>   * If that's not the case, we need to check the merge bases.
>>   * If a merge base must be tested by the user, its source code will be
>> - * checked out to be tested by the user and we will exit.
>> + * checked out to be tested by the user and we will return.
>>   */
>> -static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
>> +static int check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
>>  {
>>       char *filename = git_pathdup("BISECT_ANCESTORS_OK");
>>       struct stat st;
>> -     int fd;
>> +     int fd, res = 0;
>>
>> +     /*
>> +      * We don't want to clean the bisection state
>> +      * as we need to get back to where we started
>> +      * by using `git bisect reset`.
>> +      */
>>       if (!current_bad_oid)
>> -             die(_("a %s revision is needed"), term_bad);
>> +             error(_("a %s revision is needed"), term_bad);
>
> Only error() or return error()?
It should be return error(). Thanks for pointing it out! :)
Show 11 quoted lines
>> @@ -873,8 +916,11 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
>>                             filename);
>>       else
>>               close(fd);
>> +
>> +     goto done;
>>   done:
>
> I never understand why you do this. In case of adding a "fail" label
> (and fail code like "res = -1;") between "goto done" and "done:", it's
> fine... but without one this is just a nop.
I will just remove that line.
Show 18 quoted lines
>> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
>> index 1d3e17f..fcd7574 100644
>> --- a/builtin/bisect--helper.c
>> +++ b/builtin/bisect--helper.c
>> @@ -427,15 +560,24 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
>>               no_checkout = 1;
>>
>>       for (i = 0; i < argc; i++) {
>> -             if (!strcmp(argv[i], "--")) {
>> +             const char *arg;
>> +             if (starts_with(argv[i], "'"))
>> +                     arg = sq_dequote(xstrdup(argv[i]));
>> +             else
>> +                     arg = argv[i];
>
> One is xstrdup'ed, one is not, so there'll be a leak somewhere, and it's
> an inconsistent leak... I guess it's a bad idea to do it this way ;)
> (Also below.)
Yes. I will use xstrdup() and it does leak.
Show 16 quoted lines
>> @@ -443,24 +585,31 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
>>                       no_checkout = 1;
>>               } else if (!strcmp(arg, "--term-good") ||
>>                        !strcmp(arg, "--term-old")) {
>> +                     if (starts_with(argv[++i], "'"))
>> +                             terms->term_good = sq_dequote(xstrdup(argv[i]));
>> +                     else
>> +                             terms->term_good = xstrdup(argv[i]);
>>                       must_write_terms = 1;
>> -                     terms->term_good = xstrdup(argv[++i]);
>>               } else if (skip_prefix(arg, "--term-good=", &arg)) {
>>                       must_write_terms = 1;
>> -                     terms->term_good = xstrdup(arg);
>> +                     terms->term_good = arg;
>
> No ;) (See my other comments (to other patches) for the "terms" leaks.)
Yes I have addressed this issue.
Show 17 quoted lines
> [This repeats several times below.]
>
>> diff --git a/git-bisect.sh b/git-bisect.sh
>> index f0896b3..d574c44 100755
>> --- a/git-bisect.sh
>> +++ b/git-bisect.sh
>> @@ -109,6 +88,7 @@ bisect_skip() {
>>  bisect_state() {
>>       bisect_autostart
>>       state=$1
>> +     get_terms
>>       git bisect--helper --check-and-set-terms $state $TERM_GOOD $TERM_BAD || exit
>>       get_terms
>>       case "$#,$state" in
>
> I can't say if this change is right or wrong. It looks right, but: How
> does this relate to the other changes? Is this the right patch for it?
This line is because of the following:
 * TERM_BAD and TERM_GOOD are global but in the coming patch they
would be removed as global variables.
 * To compensate for that, I will write out the state of TERM_BAD and
TERM_GOOD every time it is updated in the file BISECT_TERMS.
 * So we will be reading it from there.
 * It is quite possible that this is completely redundant as for now
but I really don't care to check for each case because I have removed
the shell function bisect_state() afterwards and then this line won't
create a problem there because we are using `struct bisect_terms`
there.
Stephan Beyer· Nov 21, 2016, 21:35 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 15/27] bisect--helper: `bisect_next` and `bisect_auto_next` shell function in C

Hi Pranit,

in this mail I review the "second part" of your patch: the transition of bisect_next and bisect_auto_next to C.

On 10/14/2016 04:14 PM, Pranit Bauva wrote:
Show 27 quoted lines
> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
> index 1d3e17f..fcd7574 100644
> --- a/builtin/bisect--helper.c
> +++ b/builtin/bisect--helper.c
> @@ -408,6 +411,136 @@ static int bisect_terms(struct bisect_terms *terms, const char **argv, int argc)
>  	return 0;
>  }
>  
> +static int register_good_ref(const char *refname,
> +			     const struct object_id *oid, int flags,
> +			     void *cb_data)
> +{
> +	struct string_list *good_refs = cb_data;
> +	string_list_append(good_refs, oid_to_hex(oid));
> +	return 0;
> +}
> +
> +static int bisect_next(struct bisect_terms *terms, const char *prefix)
> +{
> +	int res, no_checkout;
> +
> +	/*
> +	 * 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".
> +	 */
The comment above makes no sense here, or does it?
Show 7 quoted lines
> +	if (bisect_next_check(terms, terms->term_good))
> +		return -1;
> +
> +	no_checkout = !is_empty_or_missing_file(git_path_bisect_head());
> +
> +	/* Perform all bisection computation, display and checkout */
> +	res = bisect_next_all(prefix , no_checkout);
Style: there is a space left of the comma.
Show 95 quoted lines
> +
> +	if (res == 10) {
> +		FILE *fp = NULL;
> +		unsigned char sha1[20];
> +		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 retval = 0;
> +
> +		read_ref(bad_ref, sha1);
> +		commit = lookup_commit_reference(sha1);
> +		format_commit_message(commit, "%s", &commit_name, &pp);
> +		fp = fopen(git_path_bisect_log(), "a");
> +		if (!fp) {
> +			retval = -1;
> +			goto finish_10;
> +		}
> +		if (fprintf(fp, "# first %s commit: [%s] %s\n",
> +			    terms->term_bad, sha1_to_hex(sha1),
> +			    commit_name.buf) < 1){
> +			retval = -1;
> +			goto finish_10;
> +		}
> +		goto finish_10;
> +	finish_10:
> +		if (fp)
> +			fclose(fp);
> +		strbuf_release(&commit_name);
> +		free(bad_ref);
> +		return retval;
> +	}
> +	else if (res == 2) {
> +		FILE *fp = NULL;
> +		struct rev_info revs;
> +		struct argv_array rev_argv = ARGV_ARRAY_INIT;
> +		struct string_list good_revs = STRING_LIST_INIT_DUP;
> +		struct pretty_print_context pp = {0};
> +		struct commit *commit;
> +		char *term_good = xstrfmt("%s-*", terms->term_good);
> +		int i, retval = 0;
> +
> +		fp = fopen(git_path_bisect_log(), "a");
> +		if (!fp) {
> +			retval = -1;
> +			goto finish_2;
> +		}
> +		if (fprintf(fp, "# only skipped commits left to test\n") < 1) {
> +			retval = -1;
> +			goto finish_2;
> +		}
> +		for_each_glob_ref_in(register_good_ref, term_good,
> +				     "refs/bisect/", (void *) &good_revs);
> +
> +		argv_array_pushl(&rev_argv, "skipped_commits", "refs/bisect/bad", "--not", NULL);
> +		for (i = 0; i < good_revs.nr; i++)
> +			argv_array_push(&rev_argv, good_revs.items[i].string);
> +
> +		/* It is important to reset the flags used by revision walks
> +		 * as the previous call to bisect_next_all() in turn
> +		 * setups a revision walk.
> +		 */
> +		reset_revision_walk();
> +		init_revisions(&revs, NULL);
> +		rev_argv.argc = setup_revisions(rev_argv.argc, rev_argv.argv, &revs, NULL);
> +		argv_array_clear(&rev_argv);
> +		string_list_clear(&good_revs, 0);
> +		if (prepare_revision_walk(&revs))
> +			die(_("revision walk setup failed\n"));
> +
> +		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);
> +		}
> +		goto finish_2;
> +	finish_2:
> +		if (fp)
> +			fclose(fp);
> +		string_list_clear(&good_revs, 0);
> +		argv_array_clear(&rev_argv);
> +		free(term_good);
> +		if (retval)
> +			return retval;
> +		else
> +			return res;
> +	}
> +	return res;
> +}

It would be much nicer if you put the (res == 10) branch and the (res == 2) branch into separate functions and just call them. Then you also won't need ugly label naming like finish_10 or finish_2. I'd also (again) recommend to use goto fail instead of setting retval to -1 separately each time.

I'd also recommend to use a separate function to append to the bisect log file. There is a lot of duplicated opening, checking, closing code; IIRC such a function would also already be handy for some of the previous patches.

Show 8 quoted lines
> +
> +static int bisect_auto_next(struct bisect_terms *terms, const char *prefix)
> +{
> +	if (!bisect_next_check(terms, NULL))
> +		return bisect_next(terms, prefix);
> +
> +	return 0;
> +}

Hmm, the handling of the return values is a little confusing. However, if I understand the sh source correctly, it always returns success, no matter if bisect_next failed or not. I do not know if you had something special in mind here.

Show 9 quoted lines
>  int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
> @@ -643,6 +794,10 @@ 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 find the next bisection state"), BISECT_AUTO_NEXT),
The next bisection *state* is found?
Show 9 quoted lines
> diff --git a/git-bisect.sh b/git-bisect.sh
> index f0896b3..d574c44 100755
> --- a/git-bisect.sh
> +++ b/git-bisect.sh
> @@ -139,45 +119,7 @@ bisect_state() {
>  	*)
>  		usage ;;
>  	esac
> -	bisect_auto_next
[...deleted lines...]
> +	git bisect--helper --bisect-auto-next || exit
Why is the "|| exit" necessary?
Show 15 quoted lines
> @@ -319,14 +260,15 @@ 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" "$@" ;;
>  	skip)
>  		bisect_skip "$@" ;;
>  	next)
>  		# Not sure we want "next" at the UI level anymore.
> -		bisect_next "$@" ;;
> +		get_terms
> +		git bisect--helper --bisect-next "$@" || exit ;;
Why is the "|| exit" necessary? ;)

Furthermore: Where is the bisect_autostart call from bisect_next() sh source gone? Was it not necessary?

~Stephan
Pranit Bauva· Dec 31, 2016, 10:43 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 15/27] bisect--helper: `bisect_next` and `bisect_auto_next` shell function in C

Hey Stephan,
On Tue, Nov 22, 2016 at 3:05 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
Show 35 quoted lines
> Hi Pranit,
>
> in this mail I review the "second part" of your patch: the transition of
> bisect_next and bisect_auto_next to C.
>
> On 10/14/2016 04:14 PM, Pranit Bauva wrote:
>> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
>> index 1d3e17f..fcd7574 100644
>> --- a/builtin/bisect--helper.c
>> +++ b/builtin/bisect--helper.c
>> @@ -408,6 +411,136 @@ static int bisect_terms(struct bisect_terms *terms, const char **argv, int argc)
>>       return 0;
>>  }
>>
>> +static int register_good_ref(const char *refname,
>> +                          const struct object_id *oid, int flags,
>> +                          void *cb_data)
>> +{
>> +     struct string_list *good_refs = cb_data;
>> +     string_list_append(good_refs, oid_to_hex(oid));
>> +     return 0;
>> +}
>> +
>> +static int bisect_next(struct bisect_terms *terms, const char *prefix)
>> +{
>> +     int res, no_checkout;
>> +
>> +     /*
>> +      * 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".
>> +      */
>
> The comment above makes no sense here, or does it?

No it doesn't right now. I had this initally because I was trying to keep bisect_clean_state() call inside this function but then I realized it should be placed elsewhere. I will remove this.

Show 9 quoted lines
>> +     if (bisect_next_check(terms, terms->term_good))
>> +             return -1;
>> +
>> +     no_checkout = !is_empty_or_missing_file(git_path_bisect_head());
>> +
>> +     /* Perform all bisection computation, display and checkout */
>> +     res = bisect_next_all(prefix , no_checkout);
>
> Style: there is a space left of the comma.

Nice catch! ;) I have gone through this patch so many times, yet I have forgot to notice this.

Show 101 quoted lines
>> +
>> +     if (res == 10) {
>> +             FILE *fp = NULL;
>> +             unsigned char sha1[20];
>> +             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 retval = 0;
>> +
>> +             read_ref(bad_ref, sha1);
>> +             commit = lookup_commit_reference(sha1);
>> +             format_commit_message(commit, "%s", &commit_name, &pp);
>> +             fp = fopen(git_path_bisect_log(), "a");
>> +             if (!fp) {
>> +                     retval = -1;
>> +                     goto finish_10;
>> +             }
>> +             if (fprintf(fp, "# first %s commit: [%s] %s\n",
>> +                         terms->term_bad, sha1_to_hex(sha1),
>> +                         commit_name.buf) < 1){
>> +                     retval = -1;
>> +                     goto finish_10;
>> +             }
>> +             goto finish_10;
>> +     finish_10:
>> +             if (fp)
>> +                     fclose(fp);
>> +             strbuf_release(&commit_name);
>> +             free(bad_ref);
>> +             return retval;
>> +     }
>> +     else if (res == 2) {
>> +             FILE *fp = NULL;
>> +             struct rev_info revs;
>> +             struct argv_array rev_argv = ARGV_ARRAY_INIT;
>> +             struct string_list good_revs = STRING_LIST_INIT_DUP;
>> +             struct pretty_print_context pp = {0};
>> +             struct commit *commit;
>> +             char *term_good = xstrfmt("%s-*", terms->term_good);
>> +             int i, retval = 0;
>> +
>> +             fp = fopen(git_path_bisect_log(), "a");
>> +             if (!fp) {
>> +                     retval = -1;
>> +                     goto finish_2;
>> +             }
>> +             if (fprintf(fp, "# only skipped commits left to test\n") < 1) {
>> +                     retval = -1;
>> +                     goto finish_2;
>> +             }
>> +             for_each_glob_ref_in(register_good_ref, term_good,
>> +                                  "refs/bisect/", (void *) &good_revs);
>> +
>> +             argv_array_pushl(&rev_argv, "skipped_commits", "refs/bisect/bad", "--not", NULL);
>> +             for (i = 0; i < good_revs.nr; i++)
>> +                     argv_array_push(&rev_argv, good_revs.items[i].string);
>> +
>> +             /* It is important to reset the flags used by revision walks
>> +              * as the previous call to bisect_next_all() in turn
>> +              * setups a revision walk.
>> +              */
>> +             reset_revision_walk();
>> +             init_revisions(&revs, NULL);
>> +             rev_argv.argc = setup_revisions(rev_argv.argc, rev_argv.argv, &revs, NULL);
>> +             argv_array_clear(&rev_argv);
>> +             string_list_clear(&good_revs, 0);
>> +             if (prepare_revision_walk(&revs))
>> +                     die(_("revision walk setup failed\n"));
>> +
>> +             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);
>> +             }
>> +             goto finish_2;
>> +     finish_2:
>> +             if (fp)
>> +                     fclose(fp);
>> +             string_list_clear(&good_revs, 0);
>> +             argv_array_clear(&rev_argv);
>> +             free(term_good);
>> +             if (retval)
>> +                     return retval;
>> +             else
>> +                     return res;
>> +     }
>> +     return res;
>> +}
>
> It would be much nicer if you put the (res == 10) branch and the
> (res == 2) branch into separate functions and just call them.
> Then you also won't need ugly label naming like finish_10 or finish_2.
> I'd also (again) recommend to use goto fail instead of setting retval to
> -1 separately each time.
Yes, that seems a better way to go! Thanks! :)
> I'd also recommend to use a separate function to append to the bisect
> log file. There is a lot of duplicated opening, checking, closing code;
> IIRC such a function would also already be handy for some of the
> previous patches.
True. I have made that function. Will reuse it here. Thanks! :)
Show 13 quoted lines
>> +
>> +static int bisect_auto_next(struct bisect_terms *terms, const char *prefix)
>> +{
>> +     if (!bisect_next_check(terms, NULL))
>> +             return bisect_next(terms, prefix);
>> +
>> +     return 0;
>> +}
>
> Hmm, the handling of the return values is a little confusing. However,
> if I understand the sh source correctly, it always returns success, no
> matter if bisect_next failed or not. I do not know if you had something
> special in mind here.

Umm. Shell code used to die() and thus exit with an error code. Thus it is important to return the exit code. Handling return values was *very confusing* for me too while writing this commit ;) A lot of behaviour is changed in this commit regarding return values.

Show 11 quoted lines
>>  int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>> @@ -643,6 +794,10 @@ 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 find the next bisection state"), BISECT_AUTO_NEXT),
>
> The next bisection *state* is found?
checkout is more appropriate. I don't remember why I used "find".
Show 13 quoted lines
>> diff --git a/git-bisect.sh b/git-bisect.sh
>> index f0896b3..d574c44 100755
>> --- a/git-bisect.sh
>> +++ b/git-bisect.sh
>> @@ -139,45 +119,7 @@ bisect_state() {
>>       *)
>>               usage ;;
>>       esac
>> -     bisect_auto_next
> [...deleted lines...]
>> +     git bisect--helper --bisect-auto-next || exit
>
> Why is the "|| exit" necessary?

Since it returning exit code, we need to quit, see earlier comment. Earlier in shell script we used die() but now we are using return error() thus we need to make it exit here too.

Show 17 quoted lines
>> @@ -319,14 +260,15 @@ 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" "$@" ;;
>>       skip)
>>               bisect_skip "$@" ;;
>>       next)
>>               # Not sure we want "next" at the UI level anymore.
>> -             bisect_next "$@" ;;
>> +             get_terms
>> +             git bisect--helper --bisect-next "$@" || exit ;;
>
> Why is the "|| exit" necessary? ;)
Same as before.
>
> Furthermore:
> Where is the bisect_autostart call from bisect_next() sh source gone?
> Was it not necessary?

It is necessary, but I couldn't just incorporate it in one commit. The functions are called in a circular fashion. Thus I skipped bisect_autostart() as for now but then when I managed to port bisect_autostart(), I called that function from this.

Regards, Pranit Bauva

Stephan Beyer· Jan 1, 2017, 16:27 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 15/27] bisect--helper: `bisect_next` and `bisect_auto_next` shell function in C

Hi Pranit,
On 12/31/2016 11:43 AM, Pranit Bauva wrote:
Show 15 quoted lines
>>> +
>>> +static int bisect_auto_next(struct bisect_terms *terms, const char *prefix)
>>> +{
>>> +     if (!bisect_next_check(terms, NULL))
>>> +             return bisect_next(terms, prefix);
>>> +
>>> +     return 0;
>>> +}
>>
>> Hmm, the handling of the return values is a little confusing. However,
>> if I understand the sh source correctly, it always returns success, no
>> matter if bisect_next failed or not. I do not know if you had something
>> special in mind here.
> 
> Umm. Shell code used to die() and thus exit with an error code.

The invoked bisect_next shell code called "exit", right... you had to replace this by passing return values. I get it. Thank you!

Show 13 quoted lines
>>>  int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>>> @@ -643,6 +794,10 @@ 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 find the next bisection state"), BISECT_AUTO_NEXT),
>>
>> The next bisection *state* is found?
> 
> checkout is more appropriate. I don't remember why I used "find".
"checkout the next bisection commit" maybe?
Thanks,
  Stephan
Pranit Bauva· Jan 1, 2017, 17:41 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 15/27] bisect--helper: `bisect_next` and `bisect_auto_next` shell function in C

Hey Stephan,
On Sun, Jan 1, 2017 at 9:57 PM, Stephan Beyer <s-beyer@gmx.net> wrote:
Show 15 quoted lines
>>>>  int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>>>> @@ -643,6 +794,10 @@ 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 find the next bisection state"), BISECT_AUTO_NEXT),
>>>
>>> The next bisection *state* is found?
>>
>> checkout is more appropriate. I don't remember why I used "find".
>
> "checkout the next bisection commit" maybe?
Seems better. Thanks!

Regards, Pranit Bauva

Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 07/27] bisect--helper: `bisect_reset` shell function in C

Reimplement `bisect_reset` shell function in C and add a `--bisect-reset` subcommand to `git bisect--helper` to call it from git-bisect.sh .

Using `bisect_reset` subcommand is a temporary measure to port shell functions to C so as to use the existing test suite. As more functions are ported, this subcommand would be retired but its implementation will be called by some other method.

Note: --bisect-clean-state subcommand has not been retired as there are
still a function namely `bisect_start()` which still uses this
subcommand.
Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++-
 git-bisect.sh            | 28 ++--------------------------
 2 files changed, 49 insertions(+), 27 deletions(-)
Show changes to 2 files +49 −28

builtin/bisect--helper.c, git-bisect.sh

diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 4254d61..d84ba86 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -3,17 +3,22 @@
 #include "parse-options.h"
 #include "bisect.h"
 #include "refs.h"
+#include "dir.h"
+#include "argv-array.h"
+#include "run-command.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 GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
 static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
+static GIT_PATH_FUNC(git_path_bisect_head, "BISECT_HEAD")
 
 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>]"),
 	NULL
 };
 
@@ -84,12 +89,47 @@ static int write_terms(const char *bad, const char *good)
 	return (res < 0) ? -1 : 0;
 }
 
+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 {
+		unsigned char sha1[20];
+		if (get_sha1_committish(commit, sha1))
+			return error(_("'%s' is not a valid commit"), commit);
+		strbuf_addstr(&branch, commit);
+	}
+
+	if (!file_exists(git_path_bisect_head())) {
+		struct argv_array argv = ARGV_ARRAY_INIT;
+		argv_array_pushl(&argv, "checkout", branch.buf, "--", NULL);
+		if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
+			error(_("Could not check out original HEAD '%s'. Try "
+				"'git bisect reset <commit>'."), branch.buf);
+			strbuf_release(&branch);
+			argv_array_clear(&argv);
+			return -1;
+		}
+		argv_array_clear(&argv);
+	}
+
+	strbuf_release(&branch);
+	return bisect_clean_state();
+}
+
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
 	enum {
 		NEXT_ALL = 1,
 		WRITE_TERMS,
-		BISECT_CLEAN_STATE
+		BISECT_CLEAN_STATE,
+		BISECT_RESET
 	} cmdmode = 0;
 	int no_checkout = 0;
 	struct option options[] = {
@@ -99,6 +139,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 			 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, "bisect-reset", &cmdmode,
+			 N_("reset the bisection state"), BISECT_RESET),
 		OPT_BOOL(0, "no-checkout", &no_checkout,
 			 N_("update BISECT_HEAD instead of checking out the current commit")),
 		OPT_END()
@@ -121,6 +163,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		if (argc != 0)
 			die(_("--bisect-clean-state requires no arguments"));
 		return bisect_clean_state();
+	case BISECT_RESET:
+		if (argc > 1)
+			die(_("--bisect-reset requires either zero or one arguments"));
+		return bisect_reset(argc ? argv[0] : NULL);
 	default:
 		die("BUG: unknown subcommand '%d'", cmdmode);
 	}
diff --git a/git-bisect.sh b/git-bisect.sh
index f1202df..442397b 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -409,35 +409,11 @@ bisect_visualize() {
 	eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
 }
 
-bisect_reset() {
-	test -s "$GIT_DIR/BISECT_START" || {
-		gettextln "We are not bisecting."
-		return
-	}
-	case "$#" in
-	0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
-	1) git rev-parse --quiet --verify "$1^{commit}" >/dev/null || {
-			invalid="$1"
-			die "$(eval_gettext "'\$invalid' is not a valid commit")"
-		}
-		branch="$1" ;;
-	*)
-		usage ;;
-	esac
-
-	if ! test -f "$GIT_DIR/BISECT_HEAD" && ! git checkout "$branch" --
-	then
-		die "$(eval_gettext "Could not check out original HEAD '\$branch'.
-Try 'git bisect reset <commit>'.")"
-	fi
-	git bisect--helper --bisect-clean-state || exit
-}
-
 bisect_replay () {
 	file="$1"
 	test "$#" -eq 1 || die "$(gettext "No logfile given")"
 	test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
-	bisect_reset
+	git bisect--helper --bisect-reset || exit
 	while read git bisect command rev
 	do
 		test "$git $bisect" = "git bisect" || test "$git" = "git-bisect" || continue
@@ -627,7 +603,7 @@ case "$#" in
 	visualize|view)
 		bisect_visualize "$@" ;;
 	reset)
-		bisect_reset "$@" ;;
+		git bisect--helper --bisect-reset "$@" ;;
 	replay)
 		bisect_replay "$@" ;;
 	log)

--
https://github.com/git/git/pull/287
Stephan Beyer· Nov 16, 2016, 23:23 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 07/27] bisect--helper: `bisect_reset` shell function in C

Hi,
On 10/14/2016 04:14 PM, Pranit Bauva wrote:
Show 15 quoted lines
> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
> index 4254d61..d84ba86 100644
> --- a/builtin/bisect--helper.c
> +++ b/builtin/bisect--helper.c
> @@ -84,12 +89,47 @@ static int write_terms(const char *bad, const char *good)
>  	return (res < 0) ? -1 : 0;
>  }
>  
> +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");
I think this string should be marked for translation.
> +			return 0;
> +		}
> +		strbuf_rtrim(&branch);
[...]
Show 7 quoted lines
> @@ -121,6 +163,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>  		if (argc != 0)
>  			die(_("--bisect-clean-state requires no arguments"));
>  		return bisect_clean_state();
> +	case BISECT_RESET:
> +		if (argc > 1)
> +			die(_("--bisect-reset requires either zero or one arguments"));

This error message is imho a little unspecific (but this might not be an issue because bisect--helper commands are not really exposed to the user). Maybe "--bisect-reset requires either no argument or a commit."?

> +		return bisect_reset(argc ? argv[0] : NULL);
>  	default:
>  		die("BUG: unknown subcommand '%d'", cmdmode);
>  	}
~Stephan
Pranit Bauva· Nov 17, 2016, 03:56 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 07/27] bisect--helper: `bisect_reset` shell function in C

Hey Stephan,
On Thu, Nov 17, 2016 at 4:53 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
Show 20 quoted lines
> Hi,
>
> On 10/14/2016 04:14 PM, Pranit Bauva wrote:
>> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
>> index 4254d61..d84ba86 100644
>> --- a/builtin/bisect--helper.c
>> +++ b/builtin/bisect--helper.c
>> @@ -84,12 +89,47 @@ static int write_terms(const char *bad, const char *good)
>>       return (res < 0) ? -1 : 0;
>>  }
>>
>> +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");
>
> I think this string should be marked for translation.
True. Thanks!
Show 15 quoted lines
>> +                     return 0;
>> +             }
>> +             strbuf_rtrim(&branch);
> [...]
>> @@ -121,6 +163,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>>               if (argc != 0)
>>                       die(_("--bisect-clean-state requires no arguments"));
>>               return bisect_clean_state();
>> +     case BISECT_RESET:
>> +             if (argc > 1)
>> +                     die(_("--bisect-reset requires either zero or one arguments"));
>
> This error message is imho a little unspecific (but this might not be an
> issue because bisect--helper commands are not really exposed to the
> user). Maybe "--bisect-reset requires either no argument or a commit."?
That sounds good.
>> +             return bisect_reset(argc ? argv[0] : NULL);
>>       default:
>>               die("BUG: unknown subcommand '%d'", cmdmode);
>>       }

Regards, Pranit Bauva

Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 17/27] bisect--helper: retire `--next-all` subcommand

The `--next-all` subcommand is no longer used in the shell script and the function `bisect_next_all()` is called from the C implementation of `bisect_next()`.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)
Show changes to builtin/bisect--helper.c +1 −8
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 45d9336..502bf18 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -20,7 +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-reset [<commit>]"),
 	N_("git bisect--helper --bisect-write <state> <revision> <TERM_GOOD> <TERM_BAD> [<nolog>]"),
@@ -758,8 +757,7 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
 	enum {
-		NEXT_ALL = 1,
-		WRITE_TERMS,
+		WRITE_TERMS = 1,
 		BISECT_RESET,
 		CHECK_EXPECTED_REVS,
 		BISECT_WRITE,
@@ -772,8 +770,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 	} cmdmode = 0;
 	int no_checkout = 0, res = 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-reset", &cmdmode,
@@ -809,8 +805,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 
 	switch (cmdmode) {
 	int nolog;
-	case NEXT_ALL:
-		return bisect_next_all(prefix, no_checkout);
 	case WRITE_TERMS:
 		if (argc != 2)
 			die(_("--write-terms requires two arguments"));

--
https://github.com/git/git/pull/287
Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 24/27] bisect--helper: retire `--bisect-write` subcommand

The `--bisect-write` subcommand is no longer used in the shell script and the function `bisect_write()` is called from the C implementation.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 13 -------------
 1 file changed, 13 deletions(-)
Show changes to builtin/bisect--helper.c +0 −14
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index b367d8d..7f676e2 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -21,7 +21,6 @@ static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
 
 static const char * const git_bisect_helper_usage[] = {
 	N_("git bisect--helper --bisect-reset [<commit>]"),
-	N_("git bisect--helper --bisect-write <state> <revision> <TERM_GOOD> <TERM_BAD> [<nolog>]"),
 	N_("git bisect--helper --bisect-check-and-set-terms <command> <TERM_GOOD> <TERM_BAD>"),
 	N_("git bisect--helper --bisect-next-check [<term>] <TERM_GOOD> <TERM_BAD"),
 	N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"),
@@ -990,7 +989,6 @@ 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,
@@ -1006,8 +1004,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 	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,
@@ -1042,20 +1038,11 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		usage_with_options(git_bisect_helper_usage, options);
 
 	switch (cmdmode) {
-	int nolog;
 	case BISECT_RESET:
 		if (argc > 1)
 			die(_("--bisect-reset requires either zero or one arguments"));
 		res = bisect_reset(argc ? argv[0] : NULL);
 		break;
-	case BISECT_WRITE:
-		if (argc != 4 && argc != 5)
-			die(_("--bisect-write requires either 4 or 5 arguments"));
-		nolog = (argc == 5) && !strcmp(argv[4], "nolog");
-		terms.term_good = xstrdup(argv[2]);
-		terms.term_bad = xstrdup(argv[3]);
-		res = bisect_write(argv[0], argv[1], &terms, nolog);
-		break;
 	case CHECK_AND_SET_TERMS:
 		if (argc != 3)
 			die(_("--check-and-set-terms requires 3 arguments"));

--
https://github.com/git/git/pull/287
Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 03/27] bisect--helper: `write_terms` shell function in C

Reimplement the `write_terms` shell function in C and add a `write-terms` subcommand to `git bisect--helper` to call it from git-bisect.sh . Also remove the subcommand `--check-term-format` as it can now be called from inside the function write_terms() C implementation.

Also `|| exit` is added when calling write-terms subcommand from git-bisect.sh so as to exit whenever there is an error.

Using `--write-terms` subcommand 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 and its implementation will be called by some other method.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 36 +++++++++++++++++++++++++++++-------
 git-bisect.sh            | 22 +++++++---------------
 2 files changed, 36 insertions(+), 22 deletions(-)
Show changes to 2 files +36 −23

builtin/bisect--helper.c, git-bisect.sh

diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index a47f1f2..65cf519 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -4,9 +4,11 @@
 #include "bisect.h"
 #include "refs.h"
 
+static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
+
 static const char * const git_bisect_helper_usage[] = {
 	N_("git bisect--helper --next-all [--no-checkout]"),
-	N_("git bisect--helper --check-term-format <term> <orig_term>"),
+	N_("git bisect--helper --write-terms <bad_term> <good_term>"),
 	NULL
 };
 
@@ -57,18 +59,38 @@ static int check_term_format(const char *term, const char *orig_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;
+}
+
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
 	enum {
 		NEXT_ALL = 1,
-		CHECK_TERM_FMT
+		WRITE_TERMS
 	} 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, "check-term-format", &cmdmode,
-			 N_("check format of the term"), CHECK_TERM_FMT),
+		OPT_CMDMODE(0, "write-terms", &cmdmode,
+			 N_("write the terms to .git/BISECT_TERMS"), WRITE_TERMS),
 		OPT_BOOL(0, "no-checkout", &no_checkout,
 			 N_("update BISECT_HEAD instead of checking out the current commit")),
 		OPT_END()
@@ -83,10 +105,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 	switch (cmdmode) {
 	case NEXT_ALL:
 		return bisect_next_all(prefix, no_checkout);
-	case CHECK_TERM_FMT:
+	case WRITE_TERMS:
 		if (argc != 2)
-			die(_("--check-term-format requires two arguments"));
-		return check_term_format(argv[0], argv[1]);
+			die(_("--write-terms requires two arguments"));
+		return write_terms(argv[0], argv[1]);
 	default:
 		die("BUG: unknown subcommand '%d'", cmdmode);
 	}
diff --git a/git-bisect.sh b/git-bisect.sh
index a727c59..9ef6cb8 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -209,7 +209,7 @@ bisect_start() {
 	eval "$eval true" &&
 	if test $must_write_terms -eq 1
 	then
-		write_terms "$TERM_BAD" "$TERM_GOOD"
+		git bisect--helper --write-terms "$TERM_BAD" "$TERM_GOOD"
 	fi &&
 	echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
 	#
@@ -557,18 +557,6 @@ get_terms () {
 	fi
 }
 
-write_terms () {
-	TERM_BAD=$1
-	TERM_GOOD=$2
-	if test "$TERM_BAD" = "$TERM_GOOD"
-	then
-		die "$(gettext "please use two different terms")"
-	fi
-	git bisect--helper --check-term-format "$TERM_BAD" bad || exit
-	git bisect--helper --check-term-format "$TERM_GOOD" good || exit
-	printf '%s\n%s\n' "$TERM_BAD" "$TERM_GOOD" >"$GIT_DIR/BISECT_TERMS"
-}
-
 check_and_set_terms () {
 	cmd="$1"
 	case "$cmd" in
@@ -582,13 +570,17 @@ check_and_set_terms () {
 		bad|good)
 			if ! test -s "$GIT_DIR/BISECT_TERMS"
 			then
-				write_terms bad good
+				TERM_BAD=bad
+				TERM_GOOD=good
+				git bisect--helper --write-terms "$TERM_BAD" "$TERM_GOOD" || exit
 			fi
 			;;
 		new|old)
 			if ! test -s "$GIT_DIR/BISECT_TERMS"
 			then
-				write_terms new old
+				TERM_BAD=new
+				TERM_GOOD=old
+				git bisect--helper --write-terms "$TERM_BAD" "$TERM_GOOD" || exit
 			fi
 			;;
 		esac ;;

--
https://github.com/git/git/pull/287
Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 02/27] bisect: rewrite `check_term_format` shell function in C

Reimplement the `check_term_format` shell function in C and add a `--check-term-format` subcommand to `git bisect--helper` to call it from git-bisect.sh

Using `--check-term-format` subcommand 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 and its implementation will be called by some other method/subcommand. For eg. In conversion of write_terms() of git-bisect.sh, the subcommand will be removed and instead check_term_format() will be called in its C implementation while a new subcommand will be introduced for write_terms().

Helped-by: Johannes Schindelein <Johannes.Schindelein@gmx.de>
Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++-
 git-bisect.sh            | 31 ++-----------------------
 2 files changed, 61 insertions(+), 30 deletions(-)
Show changes to 2 files +61 −31

builtin/bisect--helper.c, git-bisect.sh

diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 8111c91..a47f1f2 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -2,19 +2,73 @@
 #include "cache.h"
 #include "parse-options.h"
 #include "bisect.h"
+#include "refs.h"
 
 static const char * const git_bisect_helper_usage[] = {
 	N_("git bisect--helper --next-all [--no-checkout]"),
+	N_("git bisect--helper --check-term-format <term> <orig_term>"),
 	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", "replay", "log", "run", 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;
+}
+
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
-	enum { NEXT_ALL = 1 } cmdmode = 0;
+	enum {
+		NEXT_ALL = 1,
+		CHECK_TERM_FMT
+	} 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, "check-term-format", &cmdmode,
+			 N_("check format of the term"), CHECK_TERM_FMT),
 		OPT_BOOL(0, "no-checkout", &no_checkout,
 			 N_("update BISECT_HEAD instead of checking out the current commit")),
 		OPT_END()
@@ -29,6 +83,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 	switch (cmdmode) {
 	case NEXT_ALL:
 		return bisect_next_all(prefix, no_checkout);
+	case CHECK_TERM_FMT:
+		if (argc != 2)
+			die(_("--check-term-format requires two arguments"));
+		return check_term_format(argv[0], argv[1]);
 	default:
 		die("BUG: unknown subcommand '%d'", cmdmode);
 	}
diff --git a/git-bisect.sh b/git-bisect.sh
index ae3cb01..a727c59 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -564,38 +564,11 @@ write_terms () {
 	then
 		die "$(gettext "please use two different terms")"
 	fi
-	check_term_format "$TERM_BAD" bad
-	check_term_format "$TERM_GOOD" good
+	git bisect--helper --check-term-format "$TERM_BAD" bad || exit
+	git bisect--helper --check-term-format "$TERM_GOOD" good || exit
 	printf '%s\n%s\n' "$TERM_BAD" "$TERM_GOOD" >"$GIT_DIR/BISECT_TERMS"
 }
 
-check_term_format () {
-	term=$1
-	git check-ref-format refs/bisect/"$term" ||
-	die "$(eval_gettext "'\$term' is not a valid term")"
-	case "$term" in
-	help|start|terms|skip|next|reset|visualize|replay|log|run)
-		die "$(eval_gettext "can't use the builtin command '\$term' as a term")"
-		;;
-	bad|new)
-		if test "$2" != bad
-		then
-			# 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.
-			die "$(eval_gettext "can't change the meaning of term '\$term'")"
-		fi
-		;;
-	good|old)
-		if test "$2" != good
-		then
-			die "$(eval_gettext "can't change the meaning of term '\$term'")"
-		fi
-		;;
-	esac
-}
-
 check_and_set_terms () {
 	cmd="$1"
 	case "$cmd" in

--
https://github.com/git/git/pull/287
Stephan Beyer· Nov 14, 2016, 22:20 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 02/27] bisect: rewrite `check_term_format` shell function in C

Hi,

I saw in the recent "What's cooking" mail that this is still waiting for review, so I thought I could interfere and help reviewing it from a non-git-developer point of view. But only two commits for today. The first one seems fine. The second one makes me write this mail ;-)

On 10/14/2016 04:14 PM, Pranit Bauva wrote:
> +static int check_term_format(const char *term, const char *orig_term)
> +{
[...]
> +	if (one_of(term, "help", "start", "skip", "next", "reset",
> +			"visualize", "replay", "log", "run", NULL))
[... vs ...]
Show 6 quoted lines
> -check_term_format () {
> -	term=$1
> -	git check-ref-format refs/bisect/"$term" ||
> -	die "$(eval_gettext "'\$term' is not a valid term")"
> -	case "$term" in
> -	help|start|terms|skip|next|reset|visualize|replay|log|run)
Is there a reasons why "terms" has been dropped from the list?

Best Stephan

Pranit Bauva· Nov 15, 2016, 05:16 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 02/27] bisect: rewrite `check_term_format` shell function in C

Hey Stephan,
On Tue, Nov 15, 2016 at 3:50 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
Show 7 quoted lines
> Hi,
>
> I saw in the recent "What's cooking" mail that this is still waiting
> for review, so I thought I could interfere and help reviewing it from a
> non-git-developer point of view.
> But only two commits for today. The first one seems fine. The second
> one makes me write this mail ;-)
Thanks a lot!
Show 15 quoted lines
> On 10/14/2016 04:14 PM, Pranit Bauva wrote:
>> +static int check_term_format(const char *term, const char *orig_term)
>> +{
> [...]
>> +     if (one_of(term, "help", "start", "skip", "next", "reset",
>> +                     "visualize", "replay", "log", "run", NULL))
> [... vs ...]
>> -check_term_format () {
>> -     term=$1
>> -     git check-ref-format refs/bisect/"$term" ||
>> -     die "$(eval_gettext "'\$term' is not a valid term")"
>> -     case "$term" in
>> -     help|start|terms|skip|next|reset|visualize|replay|log|run)
>
> Is there a reasons why "terms" has been dropped from the list?
It is a mistake. I will rectify.. Thanks!

Regards, Pranit Bauva

Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 26/27] bisect--helper: retire `--bisect-auto-next` subcommand

The `--bisect-auto-next` subcommand is no longer used in the shell script and the function `bisect_auto_next` is called from the C implementation.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 10 ----------
 1 file changed, 10 deletions(-)
Show changes to builtin/bisect--helper.c +0 −11
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 8653fde..d6c2efc 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -27,7 +27,6 @@ static const char * const git_bisect_helper_usage[] = {
 	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>...]"),
 	N_("git bisect--helper --bisect-replay <filename>"),
@@ -993,7 +992,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		BISECT_TERMS,
 		BISECT_START,
 		BISECT_NEXT,
-		BISECT_AUTO_NEXT,
 		BISECT_STATE,
 		BISECT_LOG,
 		BISECT_REPLAY
@@ -1012,8 +1010,6 @@ 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 find the next bisection state"), 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,
@@ -1069,12 +1065,6 @@ 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)
-			die(_("--bisect-auto-next requires 0 arguments"));
-		get_terms(&terms);
-		res = bisect_auto_next(&terms, prefix);
-		break;
 	case BISECT_STATE:
 		if (argc == 0)
 			die(_("--bisect-state requires at least 1 argument"));

--
https://github.com/git/git/pull/287
Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 16/27] bisect--helper: retire `--bisect-clean-state` subcommand

The `bisect-clean-state` subcommand is no longer used in the shell script while the C code uses `bisect_clean_state()` thus remove the subcommand.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 9 ---------
 1 file changed, 9 deletions(-)
Show changes to builtin/bisect--helper.c +0 −10
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index fcd7574..45d9336 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -22,7 +22,6 @@ 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 <state> <revision> <TERM_GOOD> <TERM_BAD> [<nolog>]"),
 	N_("git bisect--helper --bisect-check-and-set-terms <command> <TERM_GOOD> <TERM_BAD>"),
@@ -761,7 +760,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 	enum {
 		NEXT_ALL = 1,
 		WRITE_TERMS,
-		BISECT_CLEAN_STATE,
 		BISECT_RESET,
 		CHECK_EXPECTED_REVS,
 		BISECT_WRITE,
@@ -778,8 +776,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 			 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, "bisect-reset", &cmdmode,
 			 N_("reset the bisection state"), BISECT_RESET),
 		OPT_CMDMODE(0, "check-expected-revs", &cmdmode,
@@ -820,11 +816,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 			die(_("--write-terms requires two arguments"));
 		res = write_terms(argv[0], argv[1]);
 		break;
-	case BISECT_CLEAN_STATE:
-		if (argc != 0)
-			die(_("--bisect-clean-state requires no arguments"));
-		res = bisect_clean_state();
-		break;
 	case BISECT_RESET:
 		if (argc > 1)
 			die(_("--bisect-reset requires either zero or one arguments"));

--
https://github.com/git/git/pull/287
Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 08/27] bisect--helper: `is_expected_rev` & `check_expected_revs` shell function in C

Reimplement `is_expected_rev` & `check_expected_revs` shell function in C and add a `--check-expected-revs` subcommand to `git bisect--helper` to call it from git-bisect.sh .

Using `--check-expected-revs` subcommand is a temporary measure to port shell functions to C so as to use the existing test suite. As more functions are ported, this subcommand would be retired but its implementation will be called by some other method.

Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 33 ++++++++++++++++++++++++++++++++-
 git-bisect.sh            | 20 ++------------------
 2 files changed, 34 insertions(+), 19 deletions(-)
Show changes to 2 files +34 −20

builtin/bisect--helper.c, git-bisect.sh

diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index d84ba86..c542e8b 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -123,13 +123,40 @@ static int bisect_reset(const char *commit)
 	return bisect_clean_state();
 }
 
+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 int 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());
+			return 0;
+		}
+	}
+	return 0;
+}
+
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
 	enum {
 		NEXT_ALL = 1,
 		WRITE_TERMS,
 		BISECT_CLEAN_STATE,
-		BISECT_RESET
+		BISECT_RESET,
+		CHECK_EXPECTED_REVS
 	} cmdmode = 0;
 	int no_checkout = 0;
 	struct option options[] = {
@@ -141,6 +168,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 			 N_("cleanup the bisection state"), BISECT_CLEAN_STATE),
 		OPT_CMDMODE(0, "bisect-reset", &cmdmode,
 			 N_("reset the bisection state"), BISECT_RESET),
+		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()
@@ -167,6 +196,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		if (argc > 1)
 			die(_("--bisect-reset requires either zero or one arguments"));
 		return bisect_reset(argc ? argv[0] : NULL);
+	case CHECK_EXPECTED_REVS:
+		return check_expected_revs(argv, argc);
 	default:
 		die("BUG: unknown subcommand '%d'", cmdmode);
 	}
diff --git a/git-bisect.sh b/git-bisect.sh
index 442397b..c3e43248 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -237,22 +237,6 @@ bisect_write() {
 	test -n "$nolog" || echo "git bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
 }
 
-is_expected_rev() {
-	test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
-	test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
-}
-
-check_expected_revs() {
-	for _rev in "$@"; do
-		if ! is_expected_rev "$_rev"
-		then
-			rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
-			rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
-			return
-		fi
-	done
-}
-
 bisect_skip() {
 	all=''
 	for arg in "$@"
@@ -280,7 +264,7 @@ bisect_state() {
 		rev=$(git rev-parse --verify "$bisected_head") ||
 			die "$(eval_gettext "Bad rev input: \$bisected_head")"
 		bisect_write "$state" "$rev"
-		check_expected_revs "$rev" ;;
+		git bisect--helper --check-expected-revs "$rev" ;;
 	2,"$TERM_BAD"|*,"$TERM_GOOD"|*,skip)
 		shift
 		hash_list=''
@@ -294,7 +278,7 @@ bisect_state() {
 		do
 			bisect_write "$state" "$rev"
 		done
-		check_expected_revs $hash_list ;;
+		git bisect--helper --check-expected-revs $hash_list ;;
 	*,"$TERM_BAD")
 		die "$(eval_gettext "'git bisect \$TERM_BAD' can take only one argument.")" ;;
 	*)

--
https://github.com/git/git/pull/287
Stephan Beyer· Nov 16, 2016, 23:47 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 08/27] bisect--helper: `is_expected_rev` & `check_expected_revs` shell function in C

Hi,
On 10/14/2016 04:14 PM, Pranit Bauva wrote:
Show 19 quoted lines
> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
> index d84ba86..c542e8b 100644
> --- a/builtin/bisect--helper.c
> +++ b/builtin/bisect--helper.c
> @@ -123,13 +123,40 @@ static int bisect_reset(const char *commit)
>  	return bisect_clean_state();
>  }
>  
> +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;
> +}
I am not sure it does what it should.
I would expect the following behavior from this function:
 - file does not exist (or is "broken") => return 0
 - actual_hex != expected_hex => return 0
 - otherwise return 1
If I am not wrong, the code does the following instead:
 - file does not exist (or is "broken") => return 0
 - actual_hex != expected_hex => return 1
 - otherwise => return 0
Show 13 quoted lines
> +static int 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());
> +			return 0;
> +		}
> +	}
> +	return 0;
> +}

Here I am not sure what the function *should* do. However, I see that it basically mimics the behavior of the shell function (assuming is_expected_rev() is implemented correctly).

I don't understand why the return value is int and not void. To avoid a "return 0;" line when calling this function?

Show 6 quoted lines
> @@ -167,6 +196,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>  		if (argc > 1)
>  			die(_("--bisect-reset requires either zero or one arguments"));
>  		return bisect_reset(argc ? argv[0] : NULL);
> +	case CHECK_EXPECTED_REVS:
> +		return check_expected_revs(argv, argc);

I note that you check the correct number of arguments for some subcommands and you do not check it for some other subcommands like this one. (I don't care, I just want to mention it.)

>  	default:
>  		die("BUG: unknown subcommand '%d'", cmdmode);
>  	}
~Stephan
Pranit Bauva· Dec 6, 2016, 19:33 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 08/27] bisect--helper: `is_expected_rev` & `check_expected_revs` shell function in C

Hey Stephan,
On Thu, Nov 17, 2016 at 5:17 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
Show 34 quoted lines
> Hi,
>
> On 10/14/2016 04:14 PM, Pranit Bauva wrote:
>> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
>> index d84ba86..c542e8b 100644
>> --- a/builtin/bisect--helper.c
>> +++ b/builtin/bisect--helper.c
>> @@ -123,13 +123,40 @@ static int bisect_reset(const char *commit)
>>       return bisect_clean_state();
>>  }
>>
>> +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;
>> +}
>
> I am not sure it does what it should.
>
> I would expect the following behavior from this function:
>  - file does not exist (or is "broken") => return 0
>  - actual_hex != expected_hex => return 0
>  - otherwise return 1
>
> If I am not wrong, the code does the following instead:
>  - file does not exist (or is "broken") => return 0
>  - actual_hex != expected_hex => return 1
>  - otherwise => return 0
Yeah, you are right. I should update this. Thanks for pointing it out.
Show 20 quoted lines
>> +static int 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());
>> +                     return 0;
>> +             }
>> +     }
>> +     return 0;
>> +}
>
> Here I am not sure what the function *should* do. However, I see that it
> basically mimics the behavior of the shell function (assuming
> is_expected_rev() is implemented correctly).
>
> I don't understand why the return value is int and not void. To avoid a
> "return 0;" line when calling this function?

Initially I thought I would be using the return value but now I realize that it is meaningless to do so. Using void seems better. :)

Show 10 quoted lines
>> @@ -167,6 +196,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
>>               if (argc > 1)
>>                       die(_("--bisect-reset requires either zero or one arguments"));
>>               return bisect_reset(argc ? argv[0] : NULL);
>> +     case CHECK_EXPECTED_REVS:
>> +             return check_expected_revs(argv, argc);
>
> I note that you check the correct number of arguments for some
> subcommands and you do not check it for some other subcommands like this
> one. (I don't care, I just want to mention it.)

Here we should be able to accept any number of arguments. I think it would be good to add a non-zero check though just to maintain the uniformity. Though this is something programmer needs to be careful about rather than the user.

Show 5 quoted lines
>>       default:
>>               die("BUG: unknown subcommand '%d'", cmdmode);
>>       }
>
> ~Stephan

Regards, Pranit Bauva

Pranit Bauva· Dec 16, 2016, 19:00 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 08/27] bisect--helper: `is_expected_rev` & `check_expected_revs` shell function in C

Hey Stephan,
On Wed, Dec 7, 2016 at 1:03 AM, Pranit Bauva <pranit.bauva@gmail.com> wrote:
Show 5 quoted lines
>> I don't understand why the return value is int and not void. To avoid a
>> "return 0;" line when calling this function?
>
> Initially I thought I would be using the return value but now I
> realize that it is meaningless to do so. Using void seems better. :)

I just recollected when I was creating the next iteration of this series that I will need that int.

>>> +     case CHECK_EXPECTED_REVS:
>>> +             return check_expected_revs(argv, argc);
See this.

Regards, Pranit Bauva

Stephan Beyer· Dec 17, 2016, 19:42 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 08/27] bisect--helper: `is_expected_rev` & `check_expected_revs` shell function in C

Hi Pranit,
On 12/16/2016 08:00 PM, Pranit Bauva wrote:
Show 14 quoted lines
> On Wed, Dec 7, 2016 at 1:03 AM, Pranit Bauva <pranit.bauva@gmail.com> wrote:
>>> I don't understand why the return value is int and not void. To avoid a
>>> "return 0;" line when calling this function?
>>
>> Initially I thought I would be using the return value but now I
>> realize that it is meaningless to do so. Using void seems better. :)
> 
> I just recollected when I was creating the next iteration of this
> series that I will need that int.
> 
>>>> +     case CHECK_EXPECTED_REVS:
>>>> +             return check_expected_revs(argv, argc);
> 
> See this.

This does not show that you need the "int", it just shows that you use the return value of the function. But this return value is (in the original shell code as well as in your v15) always 0. That is a sign that the "void" return value makes more sense. Of course, then the line above must be changed to

+     case CHECK_EXPECTED_REVS:
+             check_expected_revs(argv, argc);
+             return 0;

By the way, it also seems that the original function name "check_expected_revs" was not the best choice (because the function always returns 0, but the "check" implies that some semantically true or false is returned). On the other hand, it might also be useful (I cannot tell) to return different values in check_expected_revs() — to signal the caller that something changed or something did not change — but then ignore the return value.

Best
  Stephan
Pranit Bauva· Dec 16, 2016, 19:35 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 08/27] bisect--helper: `is_expected_rev` & `check_expected_revs` shell function in C

Hey Stephan,
On Thu, Nov 17, 2016 at 5:17 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
Show 34 quoted lines
> Hi,
>
> On 10/14/2016 04:14 PM, Pranit Bauva wrote:
>> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
>> index d84ba86..c542e8b 100644
>> --- a/builtin/bisect--helper.c
>> +++ b/builtin/bisect--helper.c
>> @@ -123,13 +123,40 @@ static int bisect_reset(const char *commit)
>>       return bisect_clean_state();
>>  }
>>
>> +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;
>> +}
>
> I am not sure it does what it should.
>
> I would expect the following behavior from this function:
>  - file does not exist (or is "broken") => return 0
>  - actual_hex != expected_hex => return 0
>  - otherwise return 1
>
> If I am not wrong, the code does the following instead:
>  - file does not exist (or is "broken") => return 0
>  - actual_hex != expected_hex => return 1
>  - otherwise => return 0

It seems that I didn't carefully see what the shell code is (or apparently did a mistake in understanding it ;)). I think the C version does exactly what the shell version does. Can you confirm it once again, please?

Regards, Pranit Bauva

Stephan Beyer· Dec 17, 2016, 19:55 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 08/27] bisect--helper: `is_expected_rev` & `check_expected_revs` shell function in C

Hi Pranit,
On 12/16/2016 08:35 PM, Pranit Bauva wrote:
Show 38 quoted lines
> On Thu, Nov 17, 2016 at 5:17 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
>> On 10/14/2016 04:14 PM, Pranit Bauva wrote:
>>> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
>>> index d84ba86..c542e8b 100644
>>> --- a/builtin/bisect--helper.c
>>> +++ b/builtin/bisect--helper.c
>>> @@ -123,13 +123,40 @@ static int bisect_reset(const char *commit)
>>>       return bisect_clean_state();
>>>  }
>>>
>>> +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;
>>> +}
>>
>> I am not sure it does what it should.
>>
>> I would expect the following behavior from this function:
>>  - file does not exist (or is "broken") => return 0
>>  - actual_hex != expected_hex => return 0
>>  - otherwise return 1
>>
>> If I am not wrong, the code does the following instead:
>>  - file does not exist (or is "broken") => return 0
>>  - actual_hex != expected_hex => return 1
>>  - otherwise => return 0
> 
> It seems that I didn't carefully see what the shell code is (or
> apparently did a mistake in understanding it ;)). I think the C
> version does exactly what the shell version does. Can you confirm it
> once again, please?
I check again...
The shell code does the following:
 - file does not exist or is not a regular file => return false
 - actual_hex != expected_hex => return false
 - otherwise => return true
(false means a value != 0, true means 0)
Your code does the following:
 - file cannot be read or is broken => return 0
 - actual_hex != expected_hex => return 0
 - otherwise => return 1

So you are very right, it seems I had a weird thinko (I probably missed the "!" in front of strcmp or something) :)

Sorry for bothering,
  Stephan
Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 25/27] bisect--helper: retire `--bisect-autostart` subcommand

The `--bisect-autostart` subcommand is no longer used in the shell script and the function `bisect_autostart()` is called from the C implementation.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 11 -----------
 1 file changed, 11 deletions(-)
Show changes to builtin/bisect--helper.c +0 −12
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 7f676e2..8653fde 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -28,7 +28,6 @@ static const char * const git_bisect_helper_usage[] = {
 					      "[--no-checkout] [<bad> [<good>...]] [--] [<paths>...]"),
 	N_("git bisect--helper --bisect-next"),
 	N_("git bisect--helper --bisect-auto-next"),
-	N_("git bisect--helper --bisect-autostart"),
 	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>"),
@@ -995,7 +994,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		BISECT_START,
 		BISECT_NEXT,
 		BISECT_AUTO_NEXT,
-		BISECT_AUTOSTART,
 		BISECT_STATE,
 		BISECT_LOG,
 		BISECT_REPLAY
@@ -1016,8 +1014,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 			 N_("find the next bisection commit"), BISECT_NEXT),
 		OPT_CMDMODE(0, "bisect-auto-next", &cmdmode,
 			 N_("verify the next bisection state then find the next bisection state"), BISECT_AUTO_NEXT),
-		OPT_CMDMODE(0, "bisect-autostart", &cmdmode,
-			 N_("start the bisection if BISECT_START empty or missing"), BISECT_AUTOSTART),
 		OPT_CMDMODE(0, "bisect-state", &cmdmode,
 			 N_("mark the state of ref (or refs)"), BISECT_STATE),
 		OPT_CMDMODE(0, "bisect-log", &cmdmode,
@@ -1079,13 +1075,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		get_terms(&terms);
 		res = bisect_auto_next(&terms, prefix);
 		break;
-	case BISECT_AUTOSTART:
-		if (argc)
-			die(_("--bisect-autostart requires 0 arguments"));
-		terms.term_good = "good";
-		terms.term_bad = "bad";
-		res = bisect_autostart(&terms);
-		break;
 	case BISECT_STATE:
 		if (argc == 0)
 			die(_("--bisect-state requires at least 1 argument"));

--
https://github.com/git/git/pull/287
Pranit Bauva· Oct 14, 2016, 15:12 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 01/27] bisect--helper: use OPT_CMDMODE instead of OPT_BOOL

Hey everyone,

It took me some time to get to the next version as I was a bit preoccupied with my assignments and exams. The diff between the v14[1] and v15[2] can be found here[3] because gmail web client will wrap the lines. Thanks Junio for the reviews in v14. I have tried to solves every issue raised in the previous round.

The major changes in this series are:
 * Use the char * instead of struct strbuf in struct bisect_terms.
 * Use goto mechanism for error handling.
 * Use get_sha1_committish() instead of just get_oid() because there
is also ^{commit} at the end of the call from shell.
 * Use skip_prefix() where ever required.
 * Use return error() instead of die() where ever possible.
 * Restructure a part in bisect_start() to make it more readable.
 * Change the comments in bisect.c to use return instead of exit
because the code does it.
 * Introduce a test to check for cleanups in after bad merge base to
make the patch 15/28 more understood.
 * Also keeping comments in bisect.c to show at which places there
would be cleanups and where not.
 * Restructure bisect_replay() and get_next_word().

[1]: http://public-inbox.org/git/01020156b73fe5b4-5dc768ab-b73b-4a21-ab92-018e2a7aa6f7-000000@eu-west-1.amazonses.com/T/#m7c26060fcf95abbd19f93742d7317eef87b915a1 [2]: http://public-inbox.org/git/01020157c38b19e0-81123fa5-5d9d-4f64-8f1b-ff336e83ebe4-000000@eu-west-1.amazonses.com/T/#u [3]: http://paste.ubuntu.com/23323581/

Regards, Pranit Bauva

Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 11/27] bisect--helper: `bisect_next_check` & bisect_voc 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 .

Also reimplement `bisect_voc` shell function in C and call it from `bisect_next_check` implementation in C.

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.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++-
 git-bisect.sh            |  60 ++------------------------
 2 files changed, 113 insertions(+), 57 deletions(-)
Show changes to 2 files +113 −58

builtin/bisect--helper.c, git-bisect.sh

diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index c6c11e3..317d671 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -6,6 +6,7 @@
 #include "dir.h"
 #include "argv-array.h"
 #include "run-command.h"
+#include "prompt.h"
 
 static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
 static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
@@ -21,6 +22,7 @@ static const char * const git_bisect_helper_usage[] = {
 	N_("git bisect--helper --bisect-reset [<commit>]"),
 	N_("git bisect--helper --bisect-write <state> <revision> <TERM_GOOD> <TERM_BAD> [<nolog>]"),
 	N_("git bisect--helper --bisect-check-and-set-terms <command> <TERM_GOOD> <TERM_BAD>"),
+	N_("git bisect--helper --bisect-next-check [<term>] <TERM_GOOD> <TERM_BAD"),
 	NULL
 };
 
@@ -245,6 +247,102 @@ static int check_and_set_terms(struct bisect_terms *terms, const char *cmd)
 	return 0;
 }
 
+static int mark_good(const char *refname, const struct object_id *oid,
+		     int flag, void *cb_data)
+{
+	int *m_good = (int *)cb_data;
+	*m_good = 0;
+	return 1;
+}
+
+static char *bisect_voc(char *revision_type)
+{
+	if (!strcmp(revision_type, "bad"))
+		return "bad|new";
+	if (!strcmp(revision_type, "good"))
+		return "good|old";
+
+	return NULL;
+}
+
+static int bisect_next_check(const struct bisect_terms *terms,
+			     const char *current_term)
+{
+	int missing_good = 1, missing_bad = 1, retval = 0;
+	char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad);
+	char *good_glob = xstrfmt("%s-*", terms->term_good);
+	char *bad_syn, *good_syn;
+
+	if (ref_exists(bad_ref))
+		missing_bad = 0;
+
+	for_each_glob_ref_in(mark_good, good_glob, "refs/bisect/",
+			     (void *) &missing_good);
+
+	if (!missing_good && !missing_bad)
+		goto finish;
+
+	if (!current_term) {
+		retval = -1;
+		goto finish;
+	}
+
+	if (missing_good && !missing_bad && current_term &&
+	    !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.
+		 */
+		fprintf(stderr, _("Warning: bisecting only with a %s commit\n"),
+			terms->term_bad);
+		if (!isatty(0))
+			goto finish;
+		/*
+		 * 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")) {
+			retval = -1;
+			goto finish;
+		}
+
+		goto finish;
+	}
+	bad_syn = xstrdup(bisect_voc("bad"));
+	good_syn = xstrdup(bisect_voc("good"));
+	if (!is_empty_or_missing_file(git_path_bisect_start())) {
+		error(_("You need to give me at least one %s and "
+			"%s revision. You can use \"git bisect %s\" "
+			"and \"git bisect %s\" for that. \n"),
+			bad_syn, good_syn, bad_syn, good_syn);
+		retval = -1;
+		goto finish;
+	}
+	else {
+		error(_("You need to start by \"git bisect start\". You "
+			"then need to give me at least one %s and %s "
+			"revision. You can use \"git bisect %s\" and "
+			"\"git bisect %s\" for that.\n"),
+			good_syn, bad_syn, bad_syn, good_syn);
+		retval = -1;
+		goto finish;
+	}
+	goto finish;
+finish:
+	if (!bad_ref)
+		free(bad_ref);
+	if (!good_glob)
+		free(good_glob);
+	if (!bad_syn)
+		free(bad_syn);
+	if (!good_syn)
+		free(good_syn);
+	return retval;
+}
+
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
 	enum {
@@ -254,7 +352,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		BISECT_RESET,
 		CHECK_EXPECTED_REVS,
 		BISECT_WRITE,
-		CHECK_AND_SET_TERMS
+		CHECK_AND_SET_TERMS,
+		BISECT_NEXT_CHECK
 	} cmdmode = 0;
 	int no_checkout = 0, res = 0;
 	struct option options[] = {
@@ -272,6 +371,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 			 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_BOOL(0, "no-checkout", &no_checkout,
 			 N_("update BISECT_HEAD instead of checking out the current commit")),
 		OPT_END()
@@ -321,6 +422,13 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		terms.term_bad = xstrdup(argv[2]);
 		res = check_and_set_terms(&terms, argv[0]);
 		break;
+	case BISECT_NEXT_CHECK:
+		if (argc != 2 && argc != 3)
+			die(_("--bisect-next-check requires 2 or 3 arguments"));
+		terms.term_good = xstrdup(argv[0]);
+		terms.term_bad = xstrdup(argv[1]);
+		res = bisect_next_check(&terms, argc == 3 ? argv[2] : NULL);
+		break;
 	default:
 		die("BUG: unknown subcommand '%d'", cmdmode);
 	}
diff --git a/git-bisect.sh b/git-bisect.sh
index bdf2227..fe6c9d0 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -271,59 +271,14 @@ bisect_state() {
 	bisect_auto_next
 }
 
-bisect_next_check() {
-	missing_good= missing_bad=
-	git show-ref -q --verify refs/bisect/$TERM_BAD || missing_bad=t
-	test -n "$(git for-each-ref "refs/bisect/$TERM_GOOD-*")" || missing_good=t
-
-	case "$missing_good,$missing_bad,$1" in
-	,,*)
-		: have both $TERM_GOOD and $TERM_BAD - ok
-		;;
-	*,)
-		# do not have both but not asked to fail - just report.
-		false
-		;;
-	t,,"$TERM_GOOD")
-		# have bad (or new) but not good (or old).  we could bisect although
-		# this is less optimum.
-		eval_gettextln "Warning: bisecting only with a \$TERM_BAD commit." >&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 "Are you sure [Y/n]? " >&2
-			read yesno
-			case "$yesno" in [Nn]*) exit 1 ;; esac
-		fi
-		: bisect without $TERM_GOOD...
-		;;
-	*)
-		bad_syn=$(bisect_voc bad)
-		good_syn=$(bisect_voc good)
-		if test -s "$GIT_DIR/BISECT_START"
-		then
-
-			eval_gettextln "You need to give me at least one \$bad_syn and one \$good_syn revision.
-(You can use \"git bisect \$bad_syn\" and \"git bisect \$good_syn\" for that.)" >&2
-		else
-			eval_gettextln "You need to start by \"git bisect start\".
-You then need to give me at least one \$good_syn and one \$bad_syn revision.
-(You can use \"git bisect \$bad_syn\" and \"git bisect \$good_syn\" for that.)" >&2
-		fi
-		exit 1 ;;
-	esac
-}
-
 bisect_auto_next() {
-	bisect_next_check && bisect_next || :
+	git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD && bisect_next || :
 }
 
 bisect_next() {
 	case "$#" in 0) ;; *) usage ;; esac
 	bisect_autostart
-	bisect_next_check $TERM_GOOD
+	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)
@@ -355,7 +310,7 @@ bisect_next() {
 }
 
 bisect_visualize() {
-	bisect_next_check fail
+	git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit
 
 	if test $# = 0
 	then
@@ -409,7 +364,7 @@ bisect_replay () {
 }
 
 bisect_run () {
-	bisect_next_check fail
+	git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit
 
 	while true
 	do
@@ -482,13 +437,6 @@ get_terms () {
 	fi
 }
 
-bisect_voc () {
-	case "$1" in
-	bad) echo "bad|new" ;;
-	good) echo "good|old" ;;
-	esac
-}
-
 bisect_terms () {
 	get_terms
 	if ! test -s "$GIT_DIR/BISECT_TERMS"

--
https://github.com/git/git/pull/287
Stephan Beyer· Nov 17, 2016, 20:59 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 11/27] bisect--helper: `bisect_next_check` & bisect_voc shell function in C

Hi Pranit,
On 10/14/2016 04:14 PM, Pranit Bauva wrote:
> Also reimplement `bisect_voc` shell function in C and call it from
> `bisect_next_check` implementation in C.
Please don't! ;D
Show 9 quoted lines
> +static char *bisect_voc(char *revision_type)
> +{
> +	if (!strcmp(revision_type, "bad"))
> +		return "bad|new";
> +	if (!strcmp(revision_type, "good"))
> +		return "good|old";
> +
> +	return NULL;
> +}
Why not simply use something like this:
static const char *voc[] = {
	"bad|new",
	"good|old",
};
Then...
Show 7 quoted lines
> +static int bisect_next_check(const struct bisect_terms *terms,
> +			     const char *current_term)
> +{
> +	int missing_good = 1, missing_bad = 1, retval = 0;
> +	char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad);
> +	char *good_glob = xstrfmt("%s-*", terms->term_good);
> +	char *bad_syn, *good_syn;
...you don't need bad_syn and good_syn...
> +	bad_syn = xstrdup(bisect_voc("bad"));
> +	good_syn = xstrdup(bisect_voc("good"));
...and hence not these two lines...
Show 5 quoted lines
> +	if (!is_empty_or_missing_file(git_path_bisect_start())) {
> +		error(_("You need to give me at least one %s and "
> +			"%s revision. You can use \"git bisect %s\" "
> +			"and \"git bisect %s\" for that. \n"),
> +			bad_syn, good_syn, bad_syn, good_syn);
...and write
			voc[0], voc[1], voc[0], voc[1]);
instead...
Show 9 quoted lines
> +		retval = -1;
> +		goto finish;
> +	}
> +	else {
> +		error(_("You need to start by \"git bisect start\". You "
> +			"then need to give me at least one %s and %s "
> +			"revision. You can use \"git bisect %s\" and "
> +			"\"git bisect %s\" for that.\n"),
> +			good_syn, bad_syn, bad_syn, good_syn);
...and here
			voc[1], voc[0], voc[0], voc[1]);
...
Show 13 quoted lines
> +		retval = -1;
> +		goto finish;
> +	}
> +	goto finish;
> +finish:
> +	if (!bad_ref)
> +		free(bad_ref);
> +	if (!good_glob)
> +		free(good_glob);
> +	if (!bad_syn)
> +		free(bad_syn);
> +	if (!good_syn)
> +		free(good_syn);
...and you can remove the 4 lines above.
> +	return retval;
> +}

Besides that, there are again some things that I've already mentioned and that can be applied here, too, for example, not capitalizing TERM_GOOD and TERM_BAD, the goto fail simplification, the terms memory leak.

Cheers Stephan

Pranit Bauva· Dec 6, 2016, 18:39 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 11/27] bisect--helper: `bisect_next_check` & bisect_voc shell function in C

Hey Stephan,
Sorry for the late replies. My end semester exams just got over.
On Fri, Nov 18, 2016 at 2:29 AM, Stephan Beyer <s-beyer@gmx.net> wrote:
Show 27 quoted lines
>
> Hi Pranit,
>
> On 10/14/2016 04:14 PM, Pranit Bauva wrote:
> > Also reimplement `bisect_voc` shell function in C and call it from
> > `bisect_next_check` implementation in C.
>
> Please don't! ;D
>
> > +static char *bisect_voc(char *revision_type)
> > +{
> > +     if (!strcmp(revision_type, "bad"))
> > +             return "bad|new";
> > +     if (!strcmp(revision_type, "good"))
> > +             return "good|old";
> > +
> > +     return NULL;
> > +}
>
> Why not simply use something like this:
>
> static const char *voc[] = {
>         "bad|new",
>         "good|old",
> };
>
> Then...
This probably seems a good idea.
Show 61 quoted lines
> > +static int bisect_next_check(const struct bisect_terms *terms,
> > +                          const char *current_term)
> > +{
> > +     int missing_good = 1, missing_bad = 1, retval = 0;
> > +     char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad);
> > +     char *good_glob = xstrfmt("%s-*", terms->term_good);
> > +     char *bad_syn, *good_syn;
>
> ...you don't need bad_syn and good_syn...
>
> > +     bad_syn = xstrdup(bisect_voc("bad"));
> > +     good_syn = xstrdup(bisect_voc("good"));
>
> ...and hence not these two lines...
>
> > +     if (!is_empty_or_missing_file(git_path_bisect_start())) {
> > +             error(_("You need to give me at least one %s and "
> > +                     "%s revision. You can use \"git bisect %s\" "
> > +                     "and \"git bisect %s\" for that. \n"),
> > +                     bad_syn, good_syn, bad_syn, good_syn);
>
> ...and write
>                         voc[0], voc[1], voc[0], voc[1]);
> instead...
>
> > +             retval = -1;
> > +             goto finish;
> > +     }
> > +     else {
> > +             error(_("You need to start by \"git bisect start\". You "
> > +                     "then need to give me at least one %s and %s "
> > +                     "revision. You can use \"git bisect %s\" and "
> > +                     "\"git bisect %s\" for that.\n"),
> > +                     good_syn, bad_syn, bad_syn, good_syn);
>
> ...and here
>                         voc[1], voc[0], voc[0], voc[1]);
> ...
>
> > +             retval = -1;
> > +             goto finish;
> > +     }
> > +     goto finish;
> > +finish:
> > +     if (!bad_ref)
> > +             free(bad_ref);
> > +     if (!good_glob)
> > +             free(good_glob);
> > +     if (!bad_syn)
> > +             free(bad_syn);
> > +     if (!good_syn)
> > +             free(good_syn);
>
> ...and you can remove the 4 lines above.
>
> > +     return retval;
> > +}
>
> Besides that, there are again some things that I've already mentioned
> and that can be applied here, too, for example, not capitalizing
> TERM_GOOD and TERM_BAD, the goto fail simplification, the terms memory leak.
Your suggestion simplifies things, I will use that.

Regards, Pranit Bauva

Pranit Bauva· Oct 14, 2016, 14:14 UTC · re: Pranit Bauva · lore

[PATCH v15 27/27] bisect--helper: remove the dequote in bisect_start()

Dequoting the arguments was introduced in 25b48b5c to port the function `bisect_next()` but after the `bisect_replay()` porting, the dequoting is carried out itself when it passes the arguments to `bisect_start()` in a simpler way thus dequoting again isn't required. So remove the extra "deqouting" code introduced by the commit 25b48b5c.

Mentored-by: Lars Schneider <larsxschneider@gmail.com>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
---
 builtin/bisect--helper.c | 26 ++++++--------------------
 1 file changed, 6 insertions(+), 20 deletions(-)
Show changes to builtin/bisect--helper.c +6 −21
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index d6c2efc..8cd6527 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -561,24 +561,16 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
 		no_checkout = 1;
 
 	for (i = 0; i < argc; i++) {
-		const char *arg;
-		if (starts_with(argv[i], "'"))
-			arg = sq_dequote(xstrdup(argv[i]));
-		else
-			arg = argv[i];
-		if (!strcmp(arg, "--")) {
+		if (!strcmp(argv[i], "--")) {
 			has_double_dash = 1;
 			break;
 		}
 	}
 
 	for (i = 0; i < argc; i++) {
-		const char *arg, *commit_id;
-		if (starts_with(argv[i], "'"))
-			arg = sq_dequote(xstrdup(argv[i]));
-		else
-			arg = argv[i];
-		commit_id = xstrfmt("%s^{commit}", arg);
+		const char  *commit_id;
+		const char *arg = argv[i];
+		commit_id = xstrfmt("%s^{commit}", argv[i]);
 		if (!strcmp(argv[i], "--")) {
 			has_double_dash = 1;
 			break;
@@ -586,11 +578,8 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
 			no_checkout = 1;
 		} else if (!strcmp(arg, "--term-good") ||
 			 !strcmp(arg, "--term-old")) {
-			if (starts_with(argv[++i], "'"))
-				terms->term_good = sq_dequote(xstrdup(argv[i]));
-			else
-				terms->term_good = xstrdup(argv[i]);
 			must_write_terms = 1;
+			terms->term_good = xstrdup(argv[++i]);
 		} else if (skip_prefix(arg, "--term-good=", &arg)) {
 			must_write_terms = 1;
 			terms->term_good = arg;
@@ -599,10 +588,7 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
 			terms->term_good = arg;
 		} else if (!strcmp(arg, "--term-bad") ||
 			 !strcmp(arg, "--term-new")) {
-			if (starts_with(argv[++i], "'"))
-				terms->term_bad = sq_dequote(xstrdup(argv[i]));
-			else
-				terms->term_bad = xstrdup(argv[i]);
+			terms->term_bad = xstrdup(argv[++i]);
 			must_write_terms = 1;
 		} else if (skip_prefix(arg, "--term-bad=", &arg)) {
 			must_write_terms = 1;

--
https://github.com/git/git/pull/287
Junio C Hamano· Oct 27, 2016, 16:59 UTC · re: Pranit Bauva · lore

Re: [PATCH v15 01/27] bisect--helper: use OPT_CMDMODE instead of OPT_BOOL

Cc'ed those who touched either "git-bisect.sh" or "builtin/bisect-helper.c" in our relatively recent past.

Does any of you (and others on the list) have time and inclination to review this series?

Thanks.
Christian Couder· Oct 27, 2016, 20:14 UTC · re: Junio C Hamano · lore

Re: [PATCH v15 01/27] bisect--helper: use OPT_CMDMODE instead of OPT_BOOL

On Thu, Oct 27, 2016 at 6:59 PM, Junio C Hamano <gitster@pobox.com> wrote:
Show 5 quoted lines
> Cc'ed those who touched either "git-bisect.sh" or "builtin/bisect-helper.c"
> in our relatively recent past.
>
> Does any of you (and others on the list) have time and inclination
> to review this series?

As part of my mentoring Pranit for the GSoC I already took a look at those patches some months ago on GitHub.

I could take another look at them but my eyes will not be fresh anymore, so I don know if it will be valuable.

Matthieu Moy· Oct 28, 2016, 06:02 UTC · re: Junio C Hamano · lore

Re: [PATCH v15 01/27] bisect--helper: use OPT_CMDMODE instead of OPT_BOOL

Junio C Hamano <gitster@pobox.com> writes:
Show 5 quoted lines
> Cc'ed those who touched either "git-bisect.sh" or "builtin/bisect-helper.c"
> in our relatively recent past.
>
> Does any of you (and others on the list) have time and inclination
> to review this series?

Unfortunately, I have essentially zero-bandwidth to do that in the near future :-(.

-- 
Matthieu Moy
http://www-verimag.imag.fr/~moy/
Stephan Beyer· Nov 15, 2016, 21:40 UTC · re: Junio C Hamano · lore

Re: [PATCH v15 01/27] bisect--helper: use OPT_CMDMODE instead of OPT_BOOL

Hi,
On 10/27/2016 06:59 PM, Junio C Hamano wrote:
> Does any of you (and others on the list) have time and inclination
> to review this series?

Me, currently. ;) Besides the things I'm mentioning in respective patch e-mails, I wonder why several bisect--helper commands are prefixed by "bisect"; I'm talking about:

	git bisect--helper --bisect-clean-state
	git bisect--helper --bisect-reset
	git bisect--helper --bisect-write
	git bisect--helper --bisect-check-and-set-terms
	git bisect--helper --bisect-next-check
	git bisect--helper --bisect-terms
	git bisect--helper --bisect-start
	etc.
instead of
	git bisect--helper --clean-state
	git bisect--helper --reset
	git bisect--helper --write
	git bisect--helper --check-and-set-terms
	git bisect--helper --next-check
	git bisect--helper --terms
	git bisect--helper --start
	etc.

Well, I know *why* they have these names: because the shell function names are simply reused. But I don't know why these prefixes are kept in the bisect--helper command options. On the other hand, these command names are not exposed to the user and may hence not be that important.(?)

~Stephan
Junio C Hamano· Nov 16, 2016, 00:18 UTC · re: Stephan Beyer · lore

Re: [PATCH v15 01/27] bisect--helper: use OPT_CMDMODE instead of OPT_BOOL

Stephan Beyer <s-beyer@gmx.net> writes:
Show 20 quoted lines
> Besides the things I'm mentioning in respective patch e-mails, I wonder
> why several bisect--helper commands are prefixed by "bisect"; I'm
> talking about:
>
> 	git bisect--helper --bisect-clean-state
>...
> 	git bisect--helper --bisect-start
> 	etc.
>
> instead of
>
> 	git bisect--helper --clean-state
>...
> 	git bisect--helper --start
> 	etc.
>
> Well, I know *why* they have these names: because the shell function
> names are simply reused. But I don't know why these prefixes are kept in
> the bisect--helper command options. On the other hand, these command
> names are not exposed to the user and may hence not be that important.(?)
That's a good point ;-) 

These are not intended to be end-user entry points, so names that are bit longer than necessary does not bother me too much. Hopefully the longer-term endgame would be not to need a separate "bisect-helper" binary at all but to have a "git bisect" binary making these requests as subroutine calls, and at that point, the names of the functions would want to have "bisect" prefix.

← back to recent threads