All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v17 0/7] bisect: Add support for --no-checkout option
@ 2011-08-04 12:00 Jon Seymour
  2011-08-04 12:00 ` [PATCH v17 1/7] bisect: move argument parsing before state modification Jon Seymour
                   ` (6 more replies)
  0 siblings, 7 replies; 12+ messages in thread
From: Jon Seymour @ 2011-08-04 12:00 UTC (permalink / raw)
  To: git; +Cc: chriscool, gitster, j6t, jnareb, jrnieder, Jon Seymour

Motivation
==========
For some bisection tasks, checking out the commit at each stage of the bisection process is unecessary or undesirable.

This series adds support for a --no-checkout option to git-bisect.

If specified on a start command, --no-checkout causes 'git bisect' to update BISECT_HEAD at each stage of the bisection process instead of checking out the commit at that point. 

One application of the --no-checkout option is to find, within a partially damaged repository, a commit that has at least one parent whose graph is fully reachable in the sense of 'git pack-objects'.

For example:

	git bisect start BISECT_HEAD <some-known-good-commit> <boundary-commits> --no-checkout
	git bisect run sh -c '
	       GOOD=$(git for-each-ref "--format=%(objectname)" refs/bisect/good-*) &&
	       git rev-list --objects BISECT_HEAD --not $GOOD >tmp.$$ &&
	       git pack-objects --stdout >/dev/null <tmp.$$
	       rc=$?
	       rm -f tmp.$$
	       test $rc = 0'

<some-known-good-commit> is a known good commit, for which the test passes.
<boundary-commits> are commits chosen to prevent the bisection visiting missing or corrupt commit objects.

Assuming this git bisect run completes successfully, bisect/bad will refer to a commit which has at least one parent that is fully reachable in the sense of 'git pack-objects'.

Patch Synopsis
==============

Remediation
-----------
Patch 1/7 changes existing behaviour in the case that an invalid revision argument is supplied to 'git bisect start'. In particular, in this case, bisection state is neither created or modified if argument validation fails. Previously, existing bisection state would be cleared even if the revision arguments were subsequently determined to be invalid. 	

Patch 2/7 remediates a potential flaw that might hide a failure in a chain of pasted statements.

Patch 3/7 adds a test which documents the existing behaviour of git bisect in the presence of tree damage.

New Function
------------
Patch 4/7 modifies the C code that supports bisection.
Patch 5/7 modifies porcelain to enable option exposed by 4/7.
Patch 6/7 adds some tests.
Patch 7/7 adds some documentation.

Revision History
----------------
v17:
	Removed 2 trailing semi-colons.
	Fix errors found by Junio in documented example and test.
	Fixed instances of "; then" in new and modified code.
v16:
	Use --no-def with update-ref -d. 
	Ensure update-ref BISECT_HEAD is created after BISECT_START and destroyed before BISECT_START. (Christian Couder)
	dash compatability (Jonathan Nieder).
	Documentation and test tweaks (Junio Hamano).
v15:
	Fixed reset behaviour in --no-checkout case. Added one test for same.
	Simplified implementation so that no-checkout mode is inferred by presence of 
	$GIT_DIR/BISECT_HEAD eliminating the need for a separate BISECT_MODE control file.
	Patch 8/8 from v13/14 was redistributed and squashed into earlier commits.
	Style and documentation edits based on feedback from Christian Coulder.
v14:
	Reverted --bisect-mode aspect of v13 change so C code matches v11.
v13:
	Following suggestions from Junio:
	 * Replaced BISECT_NO_CHECKOUT control file with BISECT_MODE. 
	 * Changed name of internal option on bisect--helper from --no-checkout to --bisect-mode=checkout|update-ref.
	 * Changed --no-checkout bisections to update BISECT_HEAD instead of HEAD.	
v11:
	Removed support for --update-ref=<ref>, per Junio's preference.
v10:
	Changed the way deferred statements are connected. Reverted some whitespace minimization.
v8:
	Further feedback from Christian Couder. Support --update-ref <ref>.
v6: 
	This series includes numerous improvements suggested by Christian Couder.
Reworks: 
	"bisect: allow git bisect to be used with repos containing damaged trees." 
	Replaced --ignore-checkout-failure with --no-checkout option suggested by Junio.

Future series
-------------
* Implement full support for bisection in bare repositories.
* Fix whitespace, "; then" issues in git-bisect.h (patch that applies on v17 is available).

Jon Seymour (7):
  bisect: move argument parsing before state modification.
  bisect: use && to connect statements that are deferred with eval.
  bisect: add tests to document expected behaviour in presence of
    broken trees.
  bisect: introduce support for --no-checkout option.
  bisect: introduce --no-checkout support into porcelain.
  bisect: add tests for the --no-checkout option.
  bisect: add documentation for --no-checkout option.

 Documentation/git-bisect.txt |   32 +++++++++-
 bisect.c                     |   33 +++++++---
 bisect.h                     |    2 +-
 builtin/bisect--helper.c     |    7 ++-
 git-bisect.sh                |  116 +++++++++++++++++++++-------------
 t/t6030-bisect-porcelain.sh  |  144 +++++++++++++++++++++++++++++++++++++++++-
 6 files changed, 271 insertions(+), 63 deletions(-)

-- 
1.7.6.353.g50d6f

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v17 1/7] bisect: move argument parsing before state modification.
  2011-08-04 12:00 [PATCH v17 0/7] bisect: Add support for --no-checkout option Jon Seymour
@ 2011-08-04 12:00 ` Jon Seymour
  2011-09-07  6:16   ` Christian Couder
  2011-08-04 12:00 ` [PATCH v17 2/7] bisect: use && to connect statements that are deferred with eval Jon Seymour
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 12+ messages in thread
From: Jon Seymour @ 2011-08-04 12:00 UTC (permalink / raw)
  To: git; +Cc: chriscool, gitster, j6t, jnareb, jrnieder, Jon Seymour

Currently 'git bisect start' modifies some state prior to checking
that its arguments are valid.

This change moves argument validation before state modification
with the effect that state modification does not occur
unless argument validations succeeds.

An existing test is changed to check that new bisect state
is not created if arguments are invalid.

A new test is added to check that existing bisect state
is not modified if arguments are invalid.

Signed-off-by: Jon Seymour <jon.seymour@gmail.com>
---
 git-bisect.sh               |   66 +++++++++++++++++++++---------------------
 t/t6030-bisect-porcelain.sh |   14 +++++++--
 2 files changed, 44 insertions(+), 36 deletions(-)

diff --git a/git-bisect.sh b/git-bisect.sh
index b2186a8..20f6dd5 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -60,6 +60,39 @@ 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=''
+	while [ $# -gt 0 ]; do
+	    arg="$1"
+	    case "$arg" in
+	    --)
+		shift
+		break
+		;;
+	    *)
+		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
+		}
+		case $bad_seen in
+		0) state='bad' ; bad_seen=1 ;;
+		*) state='good' ;;
+		esac
+		eval="$eval bisect_write '$state' '$rev' 'nolog'; "
+		shift
+		;;
+	    esac
+	done
+
+	#
 	# Verify HEAD.
 	#
 	head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
@@ -98,39 +131,6 @@ bisect_start() {
 	bisect_clean_state || exit
 
 	#
-	# 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=''
-	while [ $# -gt 0 ]; do
-	    arg="$1"
-	    case "$arg" in
-	    --)
-		shift
-		break
-		;;
-	    *)
-		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
-		}
-		case $bad_seen in
-		0) state='bad' ; bad_seen=1 ;;
-		*) state='good' ;;
-		esac
-		eval="$eval bisect_write '$state' '$rev' 'nolog'; "
-		shift
-		;;
-	    esac
-	done
-
-	#
 	# Change state.
 	# In case of mistaken revs or checkout error, or signals received,
 	# "bisect_auto_next" below may exit or misbehave.
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index b5063b6..b3d1b14 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -138,15 +138,23 @@ test_expect_success 'bisect start: back in good branch' '
 	grep "* other" branch.output > /dev/null
 '
 
-test_expect_success 'bisect start: no ".git/BISECT_START" if junk rev' '
-	git bisect start $HASH4 $HASH1 -- &&
-	git bisect good &&
+test_expect_success 'bisect start: no ".git/BISECT_START" created if junk rev' '
+	git bisect reset &&
 	test_must_fail git bisect start $HASH4 foo -- &&
 	git branch > branch.output &&
 	grep "* other" branch.output > /dev/null &&
 	test_must_fail test -e .git/BISECT_START
 '
 
+test_expect_success 'bisect start: existing ".git/BISECT_START" not modified if junk rev' '
+	git bisect start $HASH4 $HASH1 -- &&
+	git bisect good &&
+	cp .git/BISECT_START saved &&
+	test_must_fail git bisect start $HASH4 foo -- &&
+	git branch > branch.output &&
+	grep "* (no branch)" branch.output > /dev/null &&
+	test_cmp saved .git/BISECT_START
+'
 test_expect_success 'bisect start: no ".git/BISECT_START" if mistaken rev' '
 	git bisect start $HASH4 $HASH1 -- &&
 	git bisect good &&
-- 
1.7.6.353.g50d6f

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v17 2/7] bisect: use && to connect statements that are deferred with eval.
  2011-08-04 12:00 [PATCH v17 0/7] bisect: Add support for --no-checkout option Jon Seymour
  2011-08-04 12:00 ` [PATCH v17 1/7] bisect: move argument parsing before state modification Jon Seymour
@ 2011-08-04 12:00 ` Jon Seymour
  2011-08-04 12:00 ` [PATCH v17 3/7] bisect: add tests to document expected behaviour in presence of broken trees Jon Seymour
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Jon Seymour @ 2011-08-04 12:00 UTC (permalink / raw)
  To: git; +Cc: chriscool, gitster, j6t, jnareb, jrnieder, Jon Seymour

Christian Couder pointed out that the existing eval strategy
swallows an initial non-zero return. Using && to connect
the statements should fix this.

Signed-off-by: Jon Seymour <jon.seymour@gmail.com>
---
 git-bisect.sh |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/git-bisect.sh b/git-bisect.sh
index 20f6dd5..a44ffe1 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -86,7 +86,7 @@ bisect_start() {
 		0) state='bad' ; bad_seen=1 ;;
 		*) state='good' ;;
 		esac
-		eval="$eval bisect_write '$state' '$rev' 'nolog'; "
+		eval="$eval bisect_write '$state' '$rev' 'nolog' &&"
 		shift
 		;;
 	    esac
@@ -145,7 +145,7 @@ bisect_start() {
 	#
 	echo "$start_head" >"$GIT_DIR/BISECT_START" &&
 	git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
-	eval "$eval" &&
+	eval "$eval true" &&
 	echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
 	#
 	# Check if we can proceed to the next bisect state.
-- 
1.7.6.353.g50d6f

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v17 3/7] bisect: add tests to document expected behaviour in presence of broken trees.
  2011-08-04 12:00 [PATCH v17 0/7] bisect: Add support for --no-checkout option Jon Seymour
  2011-08-04 12:00 ` [PATCH v17 1/7] bisect: move argument parsing before state modification Jon Seymour
  2011-08-04 12:00 ` [PATCH v17 2/7] bisect: use && to connect statements that are deferred with eval Jon Seymour
@ 2011-08-04 12:00 ` Jon Seymour
  2011-08-04 12:01 ` [PATCH v17 4/7] bisect: introduce support for --no-checkout option Jon Seymour
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Jon Seymour @ 2011-08-04 12:00 UTC (permalink / raw)
  To: git; +Cc: chriscool, gitster, j6t, jnareb, jrnieder, Jon Seymour

If the repo is broken, we expect bisect to fail.

Signed-off-by: Jon Seymour <jon.seymour@gmail.com>
---
 t/t6030-bisect-porcelain.sh |   48 +++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 48 insertions(+), 0 deletions(-)

diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index b3d1b14..9ae2de8 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -581,5 +581,53 @@ test_expect_success 'erroring out when using bad path parameters' '
 '
 
 #
+# This creates a broken branch which cannot be checked out because
+# the tree created has been deleted.
 #
+# H1-H2-H3-H4-H5-H6-H7  <--other
+#            \
+#             S5-S6'-S7'-S8'-S9  <--broken
+#
+# Commits marked with ' have a missing tree.
+#
+test_expect_success 'broken branch creation' '
+	git bisect reset &&
+	git checkout -b broken $HASH4 &&
+	git tag BROKEN_HASH4 $HASH4 &&
+	add_line_into_file "5(broken): first line on a broken branch" hello2 &&
+	git tag BROKEN_HASH5 &&
+	mkdir missing &&
+	:> missing/MISSING &&
+	git add missing/MISSING &&
+	git commit -m "6(broken): Added file that will be deleted"
+	git tag BROKEN_HASH6 &&
+	add_line_into_file "7(broken): second line on a broken branch" hello2 &&
+	git tag BROKEN_HASH7 &&
+	add_line_into_file "8(broken): third line on a broken branch" hello2 &&
+	git tag BROKEN_HASH8 &&
+	git rm missing/MISSING &&
+	git commit -m "9(broken): Remove missing file"
+	git tag BROKEN_HASH9 &&
+	rm .git/objects/39/f7e61a724187ab767d2e08442d9b6b9dab587d
+'
+
+echo "" > expected.ok
+cat > expected.missing-tree.default <<EOF
+fatal: unable to read tree 39f7e61a724187ab767d2e08442d9b6b9dab587d
+EOF
+
+test_expect_success 'bisect fails if tree is broken on start commit' '
+	git bisect reset &&
+	test_must_fail git bisect start BROKEN_HASH7 BROKEN_HASH4 2>error.txt &&
+	test_cmp expected.missing-tree.default error.txt
+'
+
+test_expect_success 'bisect fails if tree is broken on trial commit' '
+	git bisect reset &&
+	test_must_fail git bisect start BROKEN_HASH9 BROKEN_HASH4 2>error.txt &&
+	git reset --hard broken &&
+	git checkout broken &&
+	test_cmp expected.missing-tree.default error.txt
+'
+
 test_done
-- 
1.7.6.353.g50d6f

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v17 4/7] bisect: introduce support for --no-checkout option.
  2011-08-04 12:00 [PATCH v17 0/7] bisect: Add support for --no-checkout option Jon Seymour
                   ` (2 preceding siblings ...)
  2011-08-04 12:00 ` [PATCH v17 3/7] bisect: add tests to document expected behaviour in presence of broken trees Jon Seymour
@ 2011-08-04 12:01 ` Jon Seymour
  2011-08-04 12:01 ` [PATCH v17 5/7] bisect: introduce --no-checkout support into porcelain Jon Seymour
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Jon Seymour @ 2011-08-04 12:01 UTC (permalink / raw)
  To: git; +Cc: chriscool, gitster, j6t, jnareb, jrnieder, Jon Seymour

If --no-checkout is specified, then the bisection process uses:

	git update-ref --no-deref HEAD <trial>

at each trial instead of:

	git checkout <trial>

Improved-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Jon Seymour <jon.seymour@gmail.com>
---
 bisect.c                 |   33 ++++++++++++++++++++++-----------
 bisect.h                 |    2 +-
 builtin/bisect--helper.c |    7 +++++--
 3 files changed, 28 insertions(+), 14 deletions(-)

diff --git a/bisect.c b/bisect.c
index dd7e8ed..c7b7d79 100644
--- a/bisect.c
+++ b/bisect.c
@@ -24,6 +24,7 @@ struct argv_array {
 
 static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
 static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
+static const char *argv_update_ref[] = {"update-ref", "--no-deref", "BISECT_HEAD", NULL, NULL};
 
 /* bits #0-15 in revision.h */
 
@@ -707,16 +708,23 @@ static void mark_expected_rev(char *bisect_rev_hex)
 		die("closing file %s: %s", filename, strerror(errno));
 }
 
-static int bisect_checkout(char *bisect_rev_hex)
+static int bisect_checkout(char *bisect_rev_hex, int no_checkout)
 {
 	int res;
 
 	mark_expected_rev(bisect_rev_hex);
 
 	argv_checkout[2] = bisect_rev_hex;
-	res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
-	if (res)
-		exit(res);
+	if (no_checkout) {
+		argv_update_ref[3] = bisect_rev_hex;
+		if (run_command_v_opt(argv_update_ref, RUN_GIT_CMD))
+			die("update-ref --no-deref HEAD failed on %s",
+			    bisect_rev_hex);
+	} else {
+		res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
+		if (res)
+			exit(res);
+	}
 
 	argv_show_branch[1] = bisect_rev_hex;
 	return run_command_v_opt(argv_show_branch, RUN_GIT_CMD);
@@ -788,7 +796,7 @@ static void handle_skipped_merge_base(const unsigned char *mb)
  * - 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(void)
+static void check_merge_bases(int no_checkout)
 {
 	struct commit_list *result;
 	int rev_nr;
@@ -806,7 +814,7 @@ static void check_merge_bases(void)
 			handle_skipped_merge_base(mb);
 		} else {
 			printf("Bisecting: a merge base must be tested\n");
-			exit(bisect_checkout(sha1_to_hex(mb)));
+			exit(bisect_checkout(sha1_to_hex(mb), no_checkout));
 		}
 	}
 
@@ -849,7 +857,7 @@ static int check_ancestors(const char *prefix)
  * 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.
  */
-static void check_good_are_ancestors_of_bad(const char *prefix)
+static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
 {
 	const char *filename = git_path("BISECT_ANCESTORS_OK");
 	struct stat st;
@@ -868,7 +876,7 @@ static void check_good_are_ancestors_of_bad(const char *prefix)
 
 	/* Check if all good revs are ancestor of the bad rev. */
 	if (check_ancestors(prefix))
-		check_merge_bases();
+		check_merge_bases(no_checkout);
 
 	/* Create file BISECT_ANCESTORS_OK. */
 	fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
@@ -908,8 +916,11 @@ static void show_diff_tree(const char *prefix, struct commit *commit)
  * We use the convention that exiting with an exit code 10 means that
  * the bisection process finished successfully.
  * In this case the calling shell script should exit 0.
+ *
+ * If no_checkout is non-zero, the bisection process does not
+ * checkout the trial commit but instead simply updates BISECT_HEAD.
  */
-int bisect_next_all(const char *prefix)
+int bisect_next_all(const char *prefix, int no_checkout)
 {
 	struct rev_info revs;
 	struct commit_list *tried;
@@ -920,7 +931,7 @@ int bisect_next_all(const char *prefix)
 	if (read_bisect_refs())
 		die("reading bisect refs failed");
 
-	check_good_are_ancestors_of_bad(prefix);
+	check_good_are_ancestors_of_bad(prefix, no_checkout);
 
 	bisect_rev_setup(&revs, prefix, "%s", "^%s", 1);
 	revs.limited = 1;
@@ -966,6 +977,6 @@ int bisect_next_all(const char *prefix)
 	       "(roughly %d step%s)\n", nr, (nr == 1 ? "" : "s"),
 	       steps, (steps == 1 ? "" : "s"));
 
-	return bisect_checkout(bisect_rev_hex);
+	return bisect_checkout(bisect_rev_hex, no_checkout);
 }
 
diff --git a/bisect.h b/bisect.h
index 0862ce5..22f2e4d 100644
--- a/bisect.h
+++ b/bisect.h
@@ -27,7 +27,7 @@ struct rev_list_info {
 	const char *header_prefix;
 };
 
-extern int bisect_next_all(const char *prefix);
+extern int bisect_next_all(const char *prefix, int no_checkout);
 
 extern int estimate_bisect_steps(int all);
 
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 5b22639..8d325a5 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -4,16 +4,19 @@
 #include "bisect.h"
 
 static const char * const git_bisect_helper_usage[] = {
-	"git bisect--helper --next-all",
+	"git bisect--helper --next-all [--no-checkout]",
 	NULL
 };
 
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
 	int next_all = 0;
+	int no_checkout = 0;
 	struct option options[] = {
 		OPT_BOOLEAN(0, "next-all", &next_all,
 			    "perform 'git bisect next'"),
+		OPT_BOOLEAN(0, "no-checkout", &no_checkout,
+			    "update BISECT_HEAD instead of checking out the current commit"),
 		OPT_END()
 	};
 
@@ -24,5 +27,5 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 		usage_with_options(git_bisect_helper_usage, options);
 
 	/* next-all */
-	return bisect_next_all(prefix);
+	return bisect_next_all(prefix, no_checkout);
 }
-- 
1.7.6.353.g50d6f

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v17 5/7] bisect: introduce --no-checkout support into porcelain.
  2011-08-04 12:00 [PATCH v17 0/7] bisect: Add support for --no-checkout option Jon Seymour
                   ` (3 preceding siblings ...)
  2011-08-04 12:01 ` [PATCH v17 4/7] bisect: introduce support for --no-checkout option Jon Seymour
@ 2011-08-04 12:01 ` Jon Seymour
  2011-08-04 12:01 ` [PATCH v17 6/7] bisect: add tests for the --no-checkout option Jon Seymour
  2011-08-04 12:01 ` [PATCH v17 7/7] bisect: add documentation for " Jon Seymour
  6 siblings, 0 replies; 12+ messages in thread
From: Jon Seymour @ 2011-08-04 12:01 UTC (permalink / raw)
  To: git; +Cc: chriscool, gitster, j6t, jnareb, jrnieder, Jon Seymour

git-bisect can now perform bisection of a history without performing
a checkout at each stage of the bisection process. Instead, HEAD is updated.

One use-case for this function is allow git bisect to be used with
damaged repositories where git checkout would fail because the tree
referenced by the commit is damaged.

It can also be used in other cases where actual checkout of the tree
is not required to progress the bisection.

Improved-by: Christian Couder <chriscool@tuxfamily.org>
Improved-by: Junio C Hamano <gitster@pobox.com>
Improved-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Jon Seymour <jon.seymour@gmail.com>
---
 git-bisect.sh |   48 +++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 37 insertions(+), 11 deletions(-)

diff --git a/git-bisect.sh b/git-bisect.sh
index a44ffe1..b9c18dd 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -3,7 +3,7 @@
 USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]'
 LONG_USAGE='git bisect help
         print this long help message.
-git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
+git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...]
         reset bisect state and start bisection.
 git bisect bad [<rev>]
         mark <rev> a known-bad revision.
@@ -34,6 +34,16 @@ require_work_tree
 _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
 _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
 
+bisect_head()
+{
+	if test -f "$GIT_DIR/BISECT_HEAD"
+	then
+		echo BISECT_HEAD
+	else
+		echo HEAD
+	fi
+}
+
 bisect_autostart() {
 	test -s "$GIT_DIR/BISECT_START" || {
 		(
@@ -69,6 +79,7 @@ bisect_start() {
 	orig_args=$(git rev-parse --sq-quote "$@")
 	bad_seen=0
 	eval=''
+	mode=''
 	while [ $# -gt 0 ]; do
 	    arg="$1"
 	    case "$arg" in
@@ -76,6 +87,11 @@ bisect_start() {
 		shift
 		break
 		;;
+	    --no-checkout)
+		mode=--no-checkout
+		shift ;;
+	    --*)
+		die "$(eval_gettext "unrecognised option: '\$arg'")" ;;
 	    *)
 		rev=$(git rev-parse -q --verify "$arg^{commit}") || {
 		    test $has_double_dash -eq 1 &&
@@ -107,7 +123,10 @@ bisect_start() {
 	then
 		# Reset to the rev from where we started.
 		start_head=$(cat "$GIT_DIR/BISECT_START")
-		git checkout "$start_head" -- || exit
+		if test "z$mode" != "z--no-checkout"
+		then
+		    git checkout "$start_head" --
+		fi
 	else
 		# Get rev from where we start.
 		case "$head" in
@@ -143,7 +162,10 @@ bisect_start() {
 	#
 	# Write new start state.
 	#
-	echo "$start_head" >"$GIT_DIR/BISECT_START" &&
+	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" &&
 	echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
@@ -206,8 +228,8 @@ bisect_state() {
 	0,*)
 		die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
 	1,bad|1,good|1,skip)
-		rev=$(git rev-parse --verify HEAD) ||
-			die "$(gettext "Bad rev input: HEAD")"
+		rev=$(git rev-parse --verify $(bisect_head)) ||
+			die "$(gettext "Bad rev input: $(bisect_head)")"
 		bisect_write "$state" "$rev"
 		check_expected_revs "$rev" ;;
 	2,bad|*,good|*,skip)
@@ -291,7 +313,7 @@ bisect_next() {
 	bisect_next_check good
 
 	# Perform all bisection computation, display and checkout
-	git bisect--helper --next-all
+	git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
 	res=$?
 
         # Check if we should exit because bisection is finished
@@ -340,12 +362,15 @@ bisect_reset() {
 	*)
 	    usage ;;
 	esac
-	if git checkout "$branch" -- ; then
-		bisect_clean_state
-	else
-		die "$(eval_gettext "Could not check out original HEAD '\$branch'.
+	if ! test -f "$GIT_DIR/BISECT_HEAD"
+	then
+		if ! git checkout "$branch" --
+		then
+			die "$(eval_gettext "Could not check out original HEAD '\$branch'.
 Try 'git bisect reset <commit>'.")"
+		fi
 	fi
+	bisect_clean_state
 }
 
 bisect_clean_state() {
@@ -362,7 +387,8 @@ bisect_clean_state() {
 	rm -f "$GIT_DIR/BISECT_RUN" &&
 	# 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"
 }
 
-- 
1.7.6.353.g50d6f

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v17 6/7] bisect: add tests for the --no-checkout option.
  2011-08-04 12:00 [PATCH v17 0/7] bisect: Add support for --no-checkout option Jon Seymour
                   ` (4 preceding siblings ...)
  2011-08-04 12:01 ` [PATCH v17 5/7] bisect: introduce --no-checkout support into porcelain Jon Seymour
@ 2011-08-04 12:01 ` Jon Seymour
  2011-08-04 12:01 ` [PATCH v17 7/7] bisect: add documentation for " Jon Seymour
  6 siblings, 0 replies; 12+ messages in thread
From: Jon Seymour @ 2011-08-04 12:01 UTC (permalink / raw)
  To: git; +Cc: chriscool, gitster, j6t, jnareb, jrnieder, Jon Seymour

These tests verify that git-bisect --no-checkout can successfully
bisect commit histories that reference damaged trees.

Signed-off-by: Jon Seymour <jon.seymour@gmail.com>
---
 t/t6030-bisect-porcelain.sh |   82 +++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 82 insertions(+), 0 deletions(-)

diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 9ae2de8..4fb7d11 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -126,6 +126,18 @@ test_expect_success 'bisect reset removes packed refs' '
 	test -z "$(git for-each-ref "refs/heads/bisect")"
 '
 
+test_expect_success 'bisect reset removes bisect state after --no-checkout' '
+	git bisect reset &&
+	git bisect start --no-checkout &&
+	git bisect good $HASH1 &&
+	git bisect bad $HASH3 &&
+	git bisect next &&
+	git bisect reset &&
+	test -z "$(git for-each-ref "refs/bisect/*")" &&
+	test -z "$(git for-each-ref "refs/heads/bisect")" &&
+	test -z "$(git for-each-ref "BISECT_HEAD")"
+'
+
 test_expect_success 'bisect start: back in good branch' '
 	git branch > branch.output &&
 	grep "* other" branch.output > /dev/null &&
@@ -630,4 +642,74 @@ test_expect_success 'bisect fails if tree is broken on trial commit' '
 	test_cmp expected.missing-tree.default error.txt
 '
 
+check_same()
+{
+	echo "Checking $1 is the same as $2" &&
+	git rev-parse "$1" > expected.same &&
+	git rev-parse "$2" > expected.actual &&
+	test_cmp expected.same expected.actual
+}
+
+test_expect_success 'bisect: --no-checkout - start commit bad' '
+	git bisect reset &&
+	git bisect start BROKEN_HASH7 BROKEN_HASH4 --no-checkout &&
+	check_same BROKEN_HASH6 BISECT_HEAD &&
+	git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - trial commit bad' '
+	git bisect reset &&
+	git bisect start broken BROKEN_HASH4 --no-checkout &&
+	check_same BROKEN_HASH6 BISECT_HEAD &&
+	git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - target before breakage' '
+	git bisect reset &&
+	git bisect start broken BROKEN_HASH4 --no-checkout &&
+	check_same BROKEN_HASH6 BISECT_HEAD &&
+	git bisect bad BISECT_HEAD &&
+	check_same BROKEN_HASH5 BISECT_HEAD &&
+	git bisect bad BISECT_HEAD &&
+	check_same BROKEN_HASH5 bisect/bad &&
+	git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - target in breakage' '
+	git bisect reset &&
+	git bisect start broken BROKEN_HASH4 --no-checkout &&
+	check_same BROKEN_HASH6 BISECT_HEAD &&
+	git bisect bad BISECT_HEAD &&
+	check_same BROKEN_HASH5 BISECT_HEAD &&
+	git bisect good BISECT_HEAD &&
+	check_same BROKEN_HASH6 bisect/bad &&
+	git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - target after breakage' '
+	git bisect reset &&
+	git bisect start broken BROKEN_HASH4 --no-checkout &&
+	check_same BROKEN_HASH6 BISECT_HEAD &&
+	git bisect good BISECT_HEAD &&
+	check_same BROKEN_HASH8 BISECT_HEAD &&
+	git bisect good BISECT_HEAD &&
+	check_same BROKEN_HASH9 bisect/bad &&
+	git bisect reset
+'
+
+test_expect_success 'bisect: demonstrate identification of damage boundary' "
+	git bisect reset &&
+	git checkout broken &&
+	git bisect start broken master --no-checkout &&
+	git bisect run sh -c '
+		GOOD=\$(git for-each-ref \"--format=%(objectname)\" refs/bisect/good-*) &&
+		git rev-list --objects BISECT_HEAD --not \$GOOD >tmp.\$\$ &&
+		git pack-objects --stdout >/dev/null < tmp.\$\$
+		rc=\$?
+		rm -f tmp.\$\$
+		test \$rc = 0' &&
+	check_same BROKEN_HASH6 bisect/bad &&
+	git bisect reset
+"
+
 test_done
-- 
1.7.6.353.g50d6f

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v17 7/7] bisect: add documentation for --no-checkout option.
  2011-08-04 12:00 [PATCH v17 0/7] bisect: Add support for --no-checkout option Jon Seymour
                   ` (5 preceding siblings ...)
  2011-08-04 12:01 ` [PATCH v17 6/7] bisect: add tests for the --no-checkout option Jon Seymour
@ 2011-08-04 12:01 ` Jon Seymour
  6 siblings, 0 replies; 12+ messages in thread
From: Jon Seymour @ 2011-08-04 12:01 UTC (permalink / raw)
  To: git; +Cc: chriscool, gitster, j6t, jnareb, jrnieder, Jon Seymour

Signed-off-by: Jon Seymour <jon.seymour@gmail.com>
---
 Documentation/git-bisect.txt |   32 +++++++++++++++++++++++++++++++-
 1 files changed, 31 insertions(+), 1 deletions(-)

diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt
index ab60a18..41e6ca8 100644
--- a/Documentation/git-bisect.txt
+++ b/Documentation/git-bisect.txt
@@ -17,7 +17,7 @@ The command takes various subcommands, and different options depending
 on the subcommand:
 
  git bisect help
- git bisect start [<bad> [<good>...]] [--] [<paths>...]
+ git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<paths>...]
  git bisect bad [<rev>]
  git bisect good [<rev>...]
  git bisect skip [(<rev>|<range>)...]
@@ -263,6 +263,17 @@ rewind the tree to the pristine state.  Finally the script should exit
 with the status of the real test to let the "git bisect run" command loop
 determine the eventual outcome of the bisect session.
 
+OPTIONS
+-------
+--no-checkout::
++
+Do not checkout the new working tree at each iteration of the bisection
+process. Instead just update a special reference named 'BISECT_HEAD' to make
+it point to the commit that should be tested.
++
+This option may be useful when the test you would perform in each step
+does not require a checked out tree.
+
 EXAMPLES
 --------
 
@@ -343,6 +354,25 @@ $ git bisect run sh -c "make || exit 125; ~/check_test_case.sh"
 This shows that you can do without a run script if you write the test
 on a single line.
 
+* Locate a good region of the object graph in a damaged repository
++
+------------
+$ git bisect start HEAD <known-good-commit> [ <boundary-commit> ... ] --no-checkout
+$ git bisect run sh -c '
+	GOOD=$(git for-each-ref "--format=%(objectname)" refs/bisect/good-*) &&
+	git rev-list --objects BISECT_HEAD --not $GOOD >tmp.$$ &&
+	git pack-objects --stdout >/dev/null <tmp.$$
+	rc=$?
+	rm -f tmp.$$
+	test $rc = 0'
+
+------------
++
+In this case, when 'git bisect run' finishes, bisect/bad will refer to a commit that
+has at least one parent whose reachable graph is fully traversable in the sense
+required by 'git pack objects'.
+
+
 SEE ALSO
 --------
 link:git-bisect-lk2009.html[Fighting regressions with git bisect],
-- 
1.7.6.353.g50d6f

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH v17 1/7] bisect: move argument parsing before state modification.
  2011-08-04 12:00 ` [PATCH v17 1/7] bisect: move argument parsing before state modification Jon Seymour
@ 2011-09-07  6:16   ` Christian Couder
  2011-09-07 11:29     ` Junio C Hamano
  2011-09-07 18:07     ` Johannes Sixt
  0 siblings, 2 replies; 12+ messages in thread
From: Christian Couder @ 2011-09-07  6:16 UTC (permalink / raw)
  To: Jon Seymour; +Cc: git, gitster, j6t, jnareb, jrnieder

Hi,

On Thursday 04 August 2011 14:00:57 Jon Seymour wrote:
> Currently 'git bisect start' modifies some state prior to checking
> that its arguments are valid.
> 
> This change moves argument validation before state modification
> with the effect that state modification does not occur
> unless argument validations succeeds.

This thread:

http://thread.gmane.org/gmane.comp.version-control.git/180733/

made me wonder if we introduced a bug with this patch.

If we start bisecting like this:

$ git bisect start HEAD HEAD~20

and then we decide that it was not optimum and we want to start again like 
this:

$ git bisect start HEAD HEAD~6

then issuing the latter command might not work as it did before this patch.
 
Before this patch the latter command would do a "git checkout $start_head" 
before the repeated rev=$(git rev-parse -q --verify "$arg^{commit}") to 
convert arguments into sha1. And after this patch the order is reversed.

This means that before this patch "HEAD" in the arguments to "git bisect 
start" would refer to $start_head because the "git checkout $start_head" 
changes HEAD. After this patch "HEAD" in the arguments to "git bisect start" 
would refer to the current HEAD.

For example before this patch, if I issue "git bisect start HEAD HEAD~8" twice 
I get:

$ git bisect start HEAD HEAD~8
Bisecting: 15 revisions left to test after this (roughly 4 steps)
[67c116bb26b4ee31889e5ee15d6a9d3b7e972b7b] Merge branch 'jk/pager-with-
external-command'
$ git bisect start HEAD HEAD~8
Previous HEAD position was 67c116b... Merge branch 'jk/pager-with-external-
command'
Switched to branch 'master'
Bisecting: 15 revisions left to test after this (roughly 4 steps)
[67c116bb26b4ee31889e5ee15d6a9d3b7e972b7b] Merge branch 'jk/pager-with-
external-command'

so the same commit to test is checked out.

After this patch I get:

$ git bisect start HEAD HEAD~8
Bisecting: 15 revisions left to test after this (roughly 4 steps)
[67c116bb26b4ee31889e5ee15d6a9d3b7e972b7b] Merge branch 'jk/pager-with-
external-command'
$ git bisect start HEAD HEAD~8
Previous HEAD position was 67c116b... Merge branch 'jk/pager-with-external-
command'
Switched to branch 'master'
Bisecting: 15 revisions left to test after this (roughly 4 steps)
[e5cfcb04e0acc5f3b51e6d69487028315b33e4c9] Merge branch 'mh/attr'

so a different commit is checked out after the second "git bisect start HEAD 
HEAD~8".

Best regards,
Christian.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v17 1/7] bisect: move argument parsing before state modification.
  2011-09-07  6:16   ` Christian Couder
@ 2011-09-07 11:29     ` Junio C Hamano
  2011-09-07 18:07     ` Johannes Sixt
  1 sibling, 0 replies; 12+ messages in thread
From: Junio C Hamano @ 2011-09-07 11:29 UTC (permalink / raw)
  To: Christian Couder; +Cc: Jon Seymour, git, j6t, jnareb, jrnieder

Christian Couder <chriscool@tuxfamily.org> writes:

> If we start bisecting like this:
>
> $ git bisect start HEAD HEAD~20
>
> and then we decide that it was not optimum and we want to start again like 
> this:
>
> $ git bisect start HEAD HEAD~6
>
> then issuing the latter command might not work as it did before this patch.

I didn't even know the above was supposed to work. It may have worked, but
I think it was not by design but merely by accident. Besides, wouldn't it
be the same to say "git bisect good" to further limit the suspect range,
instead of giving the second, seemingly nested, "bisect start" that is not
really nesting, which only confuses the users?

Having said that, if you can resurrect the old behaviour without too much
code churn, I wouldn't mind to look at the patch, but I am not sure if it
is worth it.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v17 1/7] bisect: move argument parsing before state modification.
  2011-09-07  6:16   ` Christian Couder
  2011-09-07 11:29     ` Junio C Hamano
@ 2011-09-07 18:07     ` Johannes Sixt
  2011-09-08  1:23       ` Jon Seymour
  1 sibling, 1 reply; 12+ messages in thread
From: Johannes Sixt @ 2011-09-07 18:07 UTC (permalink / raw)
  To: Christian Couder; +Cc: Jon Seymour, git, gitster, jnareb, jrnieder

Am 07.09.2011 08:16, schrieb Christian Couder:
> If we start bisecting like this:
> 
> $ git bisect start HEAD HEAD~20
> 
> and then we decide that it was not optimum and we want to start again like 
> this:
> 
> $ git bisect start HEAD HEAD~6
> 
> then issuing the latter command might not work as it did before this patch.
>  
> Before this patch the latter command would do a "git checkout $start_head" 
> before the repeated rev=$(git rev-parse -q --verify "$arg^{commit}") to 
> convert arguments into sha1. And after this patch the order is reversed.
> 
> This means that before this patch "HEAD" in the arguments to "git bisect 
> start" would refer to $start_head because the "git checkout $start_head" 
> changes HEAD. After this patch "HEAD" in the arguments to "git bisect start" 
> would refer to the current HEAD.

But isn't this an improvement? HEAD denotes the current head. After the
first 'bisect start HEAD HEAD~20', HEAD is somewhere in the middle, not
the original HEAD anymore; I would *expect* that a different commit is
checked out if I just repeat the command.

IOW, I think the new behavior is *much* better than the old behavior.

-- Hannes

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v17 1/7] bisect: move argument parsing before state modification.
  2011-09-07 18:07     ` Johannes Sixt
@ 2011-09-08  1:23       ` Jon Seymour
  0 siblings, 0 replies; 12+ messages in thread
From: Jon Seymour @ 2011-09-08  1:23 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: Christian Couder, git, gitster, jnareb, jrnieder

On Thu, Sep 8, 2011 at 4:07 AM, Johannes Sixt <j6t@kdbg.org> wrote:
> Am 07.09.2011 08:16, schrieb Christian Couder:

> IOW, I think the new behavior is *much* better than the old behavior.
>

There is perhaps no surprise that I agree with Hannes. Certainly, it
seemed saner to me to do argument validation before state update. [
Also, the earlier iterations of the --no-checkout series needed the
new behaviour. Not sure if that is still true, but I suspect it is ].

jon.

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2011-09-08  1:23 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-08-04 12:00 [PATCH v17 0/7] bisect: Add support for --no-checkout option Jon Seymour
2011-08-04 12:00 ` [PATCH v17 1/7] bisect: move argument parsing before state modification Jon Seymour
2011-09-07  6:16   ` Christian Couder
2011-09-07 11:29     ` Junio C Hamano
2011-09-07 18:07     ` Johannes Sixt
2011-09-08  1:23       ` Jon Seymour
2011-08-04 12:00 ` [PATCH v17 2/7] bisect: use && to connect statements that are deferred with eval Jon Seymour
2011-08-04 12:00 ` [PATCH v17 3/7] bisect: add tests to document expected behaviour in presence of broken trees Jon Seymour
2011-08-04 12:01 ` [PATCH v17 4/7] bisect: introduce support for --no-checkout option Jon Seymour
2011-08-04 12:01 ` [PATCH v17 5/7] bisect: introduce --no-checkout support into porcelain Jon Seymour
2011-08-04 12:01 ` [PATCH v17 6/7] bisect: add tests for the --no-checkout option Jon Seymour
2011-08-04 12:01 ` [PATCH v17 7/7] bisect: add documentation for " Jon Seymour

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.