Re: [PATCH v6 2/2] bisect: add --reset-when-found to leave when done

Junio C Hamano <[email protected]> Sun, 02 Aug 2026 17:08:36 -0700
Newsgroups org.kernel.vger.git
Message-ID <[email protected]>
"Harald Nordgren via GitGitGadget" <[email protected]> writes:

> @@ -269,7 +276,79 @@ static int bisect_reset(const char *commit, bool quiet)
>  	}
>  
>  	strbuf_release(&branch);
> -	return bisect_clean_state();
> +	return 0;
> +}

Let's make a mental note that the intenral "bisect_reset()" no
longer calls bisect_clean_state(), so those that call this function
would eventually need to call it to compensate.

> @@ -682,7 +761,8 @@ static int bisect_successful(struct bisect_terms *terms)
>  	return res;
>  }
>  
> -static enum bisect_error bisect_next(struct bisect_terms *terms, const char *prefix)
> +static enum bisect_error bisect_next(struct bisect_terms *terms,
> +				     const char *prefix)
>  {
>  	enum bisect_error res;
>  
> @@ -705,7 +785,8 @@ static enum bisect_error bisect_next(struct bisect_terms *terms, const char *pre
>  	return res;
>  }
>  
> -static enum bisect_error bisect_auto_next(struct bisect_terms *terms, const char *prefix)
> +static enum bisect_error bisect_auto_next(struct bisect_terms *terms,
> +					  const char *prefix)
>  {
>  	if (bisect_next_check(terms, NULL)) {
>  		bisect_print_status(terms);

The above two hunks are pure style clean-ups.  When having others to
review a 500+ line patch, you would want to omit them or move them
to a separate preliminary clean-up step, to avoid distracting them.

> @@ -1246,13 +1344,36 @@ static int bisect_run(struct bisect_terms *terms, int argc, const char **argv)
>  {
>  	int res = BISECT_OK;
>  	struct strbuf command = STRBUF_INIT;
> +	const char *reset_when_found_arg;
>  	const char *new_state;
>  	int temporary_stdout_fd, saved_stdout;
>  	int is_first_run = 1;
> +	enum reset_when_found_mode reset_when_found = RESET_WHEN_FOUND_NONE;
>  
>  	if (bisect_next_check(terms, NULL))
>  		return BISECT_FAILED;
>  
> +	if (argc && !strcmp(argv[0], "--reset-when-found")) {
> +		reset_when_found = RESET_WHEN_FOUND_TO_ORIGINAL;
> +	} else if (argc && skip_prefix(argv[0], "--reset-when-found=",
> +				    &reset_when_found_arg)) {
> +		if (parse_reset_when_found(reset_when_found_arg,
> +					   &reset_when_found))
> +			return BISECT_FAILED;
> +	}
> +
> +	if (reset_when_found != RESET_WHEN_FOUND_NONE &&
> +	    refs_ref_exists(get_main_ref_store(the_repository), "BISECT_HEAD"))
> +		return error(_("options '%s' and '%s' cannot be used together"),
> +			     "--reset-when-found", "--no-checkout");
> +
> +	if (reset_when_found != RESET_WHEN_FOUND_NONE) {
> +		write_file(git_path_bisect_reset_when_found(), "%s\n",
> +			   reset_when_found_mode_name(reset_when_found));
> +		argc--;
> +		argv++;
> +	}
> +
>  	if (!argc) {
>  		error(_("bisect run failed: no command provided."));
>  		return BISECT_FAILED;
> @@ -1327,7 +1448,6 @@ static int bisect_run(struct bisect_terms *terms, int argc, const char **argv)
>  			res = BISECT_OK;
>  		} else if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {
>  			printf(_("bisect found first '%s' commit\n"), terms->term_bad);
> -			res = BISECT_OK;

Now whoever called bisect_run() can react to 1st-bad-found but it is
their responsibility to report that overall bisect was OK to their
callers.

> @@ -1344,10 +1464,15 @@ static int bisect_run(struct bisect_terms *terms, int argc, const char **argv)
>  static int cmd_bisect__reset(int argc, const char **argv, const char *prefix UNUSED,
>  			     struct repository *repo UNUSED)
>  {
> +	int res;
> +
>  	if (argc > 1)
>  		return error(_("'%s' requires either no argument or a commit"),
>  			     "git bisect reset");
> -	return bisect_reset(argc ? argv[0] : NULL, false);
> +	res = bisect_reset(argc ? argv[0] : NULL, false);
> +	if (res)
> +		return res;
> +	return bisect_clean_state();
>  }

Everything contained in this patch to enable --reset-when-finished
are exactly as expected and very understandable to me, but this bit
was a bit hard to grok.  Let me think aloud to see if I can explain
it.

 * Lower level bisect_reset() used to almost always called
   clean_state(), but except when it returned error().

 * Now bisect_reset() never calls clean_state().

Hence, somebody has to call it in the new code.  The above change is
an example of doing exactly that.  If bisect_reset() returns an
error, we refrain from cleaning the state.  If it succeeds, we clean
the state.

Earlier we saw that bisect_reset_when_found() does the same thing.
If bisect_reset() did not fail, it called clean_state().

Both make sense.

>  static int cmd_bisect__terms(int argc, const char **argv, const char *prefix UNUSED,
> @@ -1489,7 +1614,8 @@ int cmd_bisect(int argc,
>  		    !one_of(argv[0], terms.term_good, terms.term_bad, NULL))
>  			usage_msg_optf(_("unknown command: '%s'"), git_bisect_usage,
>  				       options, argv[0]);
> -		res = bisect_state(&terms, argc, argv);
> +		else
> +			res = bisect_state(&terms, argc, argv);
>  		free_terms(&terms);
>  	} else {
>  		argc--;

What is this change about?  We used to see if the given terms
(bad/good) are sensible and otherwise barfed with usage_msg_optf()
that never returns, so we did without "else".  With "else" you are
making it more explicit.  The value of such a change is debatable.
Some would say that, just like 'if ... die()', it is already
explicit enough that 'if ... usage()' never returns and does not
require an "else".  Some would say new readers may not know die()
and usage() do not return, so "else" makes it more explicit.  My
stance is that we should not optimize our code for total newbies
[*], so I may have a mild preference for the original over the
updated version, but it is minor.  In other words, I would not mind
if an author wrote this either way in new code.

However.

If an author is adding a new feature, I would recommend against
making such a change that would only force reviewers to read more
and think more about the change.  Do not waste reviewers' attention,
which is a precious resource, to something much less relevant for
the goal of your topic.

> @@ -1497,5 +1623,15 @@ int cmd_bisect(int argc,
>  		res = fn(argc, argv, prefix, repo);
>  	}
>  
> +	if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {
> +		enum reset_when_found_mode mode;
> +
> +		if (read_reset_when_found(&mode))
> +			res = BISECT_FAILED;
> +		else if (mode != RESET_WHEN_FOUND_NONE &&
> +			 bisect_reset_when_found(mode))
> +			res = BISECT_FAILED;
> +	}

Are there "dead end" states, other than '1st-bad-found', in which we
can no longer make any progress?  One thing that comes to mind is
"you said this one is good, but that contradicts what you said about
its ancestor that you said is bad".  I wonder if we want to do
anything special here, just as this part of the code handles the
'1st-bad-found' state, for such "dead end" states.

This is just for my own education, as I am pondering possible future
extensions.

>  	return is_bisect_success(res) ? 0 : -res;
>  }

Overall, the patch looks very nicely done, except for a few minor
nits that made my reading hiccup while I was reviewing this round.

Thanks.