[PATCH v4 0/2] bisect: add --reset-when-found to leave when done

"Harald Nordgren via GitGitGadget" <[email protected]> Sat, 01 Aug 2026 09:44:03 +0000
Newsgroups org.kernel.vger.git
Message-ID <[email protected]>
Add a --reset-when-found option to git bisect that resets the bisect session
when culprit is found.

Changes in v4:

 * Simplify translation calls.
 * Avoid git subshell calls in tests, that can bury errors.

Changes in v3:

 * Rename --auto-reset to --reset-when-found, including internal names.
 * Defer git bisect run cleanup until captured output is printed and
   BISECT_RUN is closed. Drop the open-descriptor preparatory change,
   retaining the existing filename-based output handling.

Changes in v2:

 * Add option --auto-reset[=<where>] with option to go to final commit as
   well as original.
 * Refactored tests.

Harald Nordgren (2):
  bisect: let bisect_reset() optionally check out quietly
  bisect: add --reset-when-found to leave when done

 Documentation/git-bisect.adoc |  14 +++-
 bisect.c                      |   2 +
 builtin/bisect.c              | 148 +++++++++++++++++++++++++++++-----
 t/t6030-bisect-porcelain.sh   | 121 +++++++++++++++++++++++++++
 4 files changed, 264 insertions(+), 21 deletions(-)


base-commit: a97fcc37c2bc6340a8d7ce78dedf227aac4e9aa7
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2335%2FHaraldNordgren%2Fbisect-auto-reset-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2335/HaraldNordgren/bisect-auto-reset-v4
Pull-Request: https://github.com/git/git/pull/2335

Range-diff vs v3:

 1:  59920c51ae = 1:  e39670edf4 bisect: let bisect_reset() optionally check out quietly
 2:  542f4b2c80 ! 2:  f5f370df1b bisect: add --reset-when-found to leave when done
     @@ builtin/bisect.c: static enum bisect_error bisect_start(struct bisect_terms *ter
       		}
       	}
      +	if (reset_when_found != RESET_WHEN_FOUND_NONE && no_checkout) {
     -+		res = error(_("'--reset-when-found' cannot be used with '--no-checkout'"));
     ++		res = error(_("options '%s' and '%s' cannot be used together"),
     ++			    "--reset-when-found", "--no-checkout");
      +		goto finish;
      +	}
       	pathspec_pos = i;
     @@ builtin/bisect.c: static int bisect_run(struct bisect_terms *terms, int argc, co
      +
      +	if (reset_when_found != RESET_WHEN_FOUND_NONE) {
      +		if (refs_ref_exists(get_main_ref_store(the_repository), "BISECT_HEAD"))
     -+			return error(_("'--reset-when-found' cannot be used with '--no-checkout'"));
     ++			return error(_("options '%s' and '%s' cannot be used together"),
     ++				     "--reset-when-found", "--no-checkout");
      +		write_file(git_path_bisect_reset_when_found(), "%s\n",
      +			   reset_when_found_mode_name(reset_when_found));
      +		argc--;
     @@ t/t6030-bisect-porcelain.sh: test_bisect_usage () {
       }
       
      +test_bisect_state_file () {
     -+	test_path_is_file "$(git rev-parse --git-path "$1")"
     ++	local file &&
     ++	file=$(git rev-parse --git-path "$1") &&
     ++	test_path_is_file "$file"
      +}
      +
      +test_bisect_state_missing () {
     -+	test_path_is_missing "$(git rev-parse --git-path "$1")"
     ++	local file &&
     ++	file=$(git rev-parse --git-path "$1") &&
     ++	test_path_is_missing "$file"
      +}
      +
      +bisect_start_and_finish () {
     @@ t/t6030-bisect-porcelain.sh: test_expect_success '"git bisect run" simple case'
       '
       
      +test_expect_success '"git bisect start --reset-when-found" defaults to original' '
     -+	test_when_finished "git bisect reset; git checkout main" &&
     ++	test_when_finished "git bisect reset && git checkout main" &&
      +	git checkout main &&
      +	bisect_start_and_finish --reset-when-found &&
     -+	test "$HASH4" = "$(git rev-parse HEAD)" &&
     -+	test main = "$(git branch --show-current)" &&
     ++	actual=$(git rev-parse HEAD) &&
     ++	test "$HASH4" = "$actual" &&
     ++	actual=$(git branch --show-current) &&
     ++	test main = "$actual" &&
      +	test_bisect_state_missing BISECT_START &&
      +
      +	bisect_start_and_finish --reset-when-found=original &&
     -+	test "$HASH4" = "$(git rev-parse HEAD)" &&
     -+	test main = "$(git branch --show-current)" &&
     ++	actual=$(git rev-parse HEAD) &&
     ++	test "$HASH4" = "$actual" &&
     ++	actual=$(git branch --show-current) &&
     ++	test main = "$actual" &&
      +	test_bisect_state_missing BISECT_START
      +'
      +
      +test_expect_success '"git bisect start --reset-when-found=found" leaves first bad checked out' '
     -+	test_when_finished "git bisect reset; git checkout main" &&
     ++	test_when_finished "git bisect reset && git checkout main" &&
      +	bisect_start_and_finish --reset-when-found=found &&
     -+	test "$HASH3" = "$(git rev-parse HEAD)" &&
     ++	actual=$(git rev-parse HEAD) &&
     ++	test "$HASH3" = "$actual" &&
      +	test_bisect_state_missing BISECT_START
      +'
      +
      +test_expect_success '"git bisect run --reset-when-found" defaults to original' '
     -+	test_when_finished "git bisect reset; git checkout main" &&
     ++	test_when_finished "git bisect reset && git checkout main" &&
      +	bisect_run_reset_when_found --reset-when-found &&
     -+	test "$HASH4" = "$(git rev-parse HEAD)" &&
     -+	test main = "$(git branch --show-current)" &&
     ++	actual=$(git rev-parse HEAD) &&
     ++	test "$HASH4" = "$actual" &&
     ++	actual=$(git branch --show-current) &&
     ++	test main = "$actual" &&
      +	test_bisect_state_missing BISECT_START
      +'
      +
      +test_expect_success '"git bisect run --reset-when-found=found" leaves first bad checked out' '
     -+	test_when_finished "git bisect reset; git checkout main" &&
     ++	test_when_finished "git bisect reset && git checkout main" &&
      +	bisect_run_reset_when_found --reset-when-found=found &&
     -+	test "$HASH3" = "$(git rev-parse HEAD)" &&
     ++	actual=$(git rev-parse HEAD) &&
     ++	test "$HASH3" = "$actual" &&
      +	test_bisect_state_missing BISECT_START
      +'
      +
      +test_expect_success '--reset-when-found rejects an unknown reset target' '
     -+	test_when_finished "git bisect reset; git checkout main" &&
     ++	test_when_finished "git bisect reset && git checkout main" &&
      +	test_reset_when_found_fails \
      +		"invalid value for.*--reset-when-found.*unknown" BISECT_START \
      +		git bisect start --reset-when-found=unknown $HASH4 $HASH2 &&
     @@ t/t6030-bisect-porcelain.sh: test_expect_success '"git bisect run" simple case'
      +test_expect_success '--reset-when-found cannot be used with --no-checkout' '
      +	test_when_finished "git bisect reset" &&
      +	test_reset_when_found_fails \
     -+		"cannot be used with.*--no-checkout" BISECT_START \
     ++		"options .*--reset-when-found.* and .*--no-checkout.* cannot be used together" BISECT_START \
      +		git bisect start --reset-when-found=original --no-checkout $HASH4 $HASH2 &&
      +
      +	git bisect start --no-checkout $HASH4 $HASH2 &&
      +	test_reset_when_found_fails \
     -+		"cannot be used with.*--no-checkout" BISECT_RESET_WHEN_FOUND \
     ++		"options .*--reset-when-found.* and .*--no-checkout.* cannot be used together" BISECT_RESET_WHEN_FOUND \
      +		git bisect run --reset-when-found=found true
      +'
      +
     @@ t/t6030-bisect-porcelain.sh: test_expect_success '"git bisect run" simple case'
      +'
      +
      +test_expect_success '--reset-when-found does not leak into a later bisection' '
     -+	test_when_finished "git bisect reset; git checkout main" &&
     ++	test_when_finished "git bisect reset && git checkout main" &&
      +	bisect_start_and_finish --reset-when-found &&
      +
      +	git bisect start $HASH4 $HASH2 &&

-- 
gitgitgadget