mirror of
https://github.com/git/git
synced 2024-08-27 19:49:21 +00:00
Merge branch 'js/bisect-no-checkout'
* js/bisect-no-checkout: bisect: add support for bisecting bare repositories bisect: further style nitpicks bisect: replace "; then" with "\n<tab>*then" bisect: cleanup whitespace errors in git-bisect.sh. bisect: add documentation for --no-checkout option. bisect: add tests for the --no-checkout option. bisect: introduce --no-checkout support into porcelain. bisect: introduce support for --no-checkout option. bisect: add tests to document expected behaviour in presence of broken trees. bisect: use && to connect statements that are deferred with eval. bisect: move argument parsing before state modification.
This commit is contained in:
commit
da68bf3376
|
@ -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,19 @@ 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.
|
||||
+
|
||||
If the repository is bare, `--no-checkout` is assumed.
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
|
||||
|
@ -343,6 +356,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],
|
||||
|
|
33
bisect.c
33
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);
|
||||
}
|
||||
|
||||
|
|
2
bisect.h
2
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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
341
git-bisect.sh
341
git-bisect.sh
|
@ -2,38 +2,47 @@
|
|||
|
||||
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>...]
|
||||
reset bisect state and start bisection.
|
||||
print this long help message.
|
||||
git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...]
|
||||
reset bisect state and start bisection.
|
||||
git bisect bad [<rev>]
|
||||
mark <rev> a known-bad revision.
|
||||
mark <rev> a known-bad revision.
|
||||
git bisect good [<rev>...]
|
||||
mark <rev>... known-good revisions.
|
||||
mark <rev>... known-good revisions.
|
||||
git bisect skip [(<rev>|<range>)...]
|
||||
mark <rev>... untestable revisions.
|
||||
mark <rev>... untestable revisions.
|
||||
git bisect next
|
||||
find next bisection to test and check it out.
|
||||
find next bisection to test and check it out.
|
||||
git bisect reset [<commit>]
|
||||
finish bisection search and go back to commit.
|
||||
finish bisection search and go back to commit.
|
||||
git bisect visualize
|
||||
show bisect status in gitk.
|
||||
show bisect status in gitk.
|
||||
git bisect replay <logfile>
|
||||
replay bisection log.
|
||||
replay bisection log.
|
||||
git bisect log
|
||||
show bisect log.
|
||||
show bisect log.
|
||||
git bisect run <cmd>...
|
||||
use <cmd>... to automatically bisect.
|
||||
use <cmd>... to automatically bisect.
|
||||
|
||||
Please use "git help bisect" to get the full man page.'
|
||||
|
||||
OPTIONS_SPEC=
|
||||
. git-sh-setup
|
||||
. git-sh-i18n
|
||||
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" || {
|
||||
(
|
||||
|
@ -45,7 +54,7 @@ bisect_autostart() {
|
|||
# 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
|
||||
gettext "Do you want me to do it for you [Y/n]? " >&2
|
||||
read yesno
|
||||
case "$yesno" in
|
||||
[Nn]*)
|
||||
|
@ -59,6 +68,50 @@ 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=''
|
||||
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 ;;
|
||||
--*)
|
||||
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
|
||||
}
|
||||
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.
|
||||
#
|
||||
|
@ -74,7 +127,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
|
||||
|
@ -97,39 +153,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,
|
||||
|
@ -143,9 +166,12 @@ 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" &&
|
||||
eval "$eval true" &&
|
||||
echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
|
||||
#
|
||||
# Check if we can proceed to the next bisect state.
|
||||
|
@ -176,7 +202,8 @@ is_expected_rev() {
|
|||
|
||||
check_expected_revs() {
|
||||
for _rev in "$@"; do
|
||||
if ! is_expected_rev "$_rev"; then
|
||||
if ! is_expected_rev "$_rev"
|
||||
then
|
||||
rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
|
||||
rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
|
||||
return
|
||||
|
@ -185,18 +212,18 @@ check_expected_revs() {
|
|||
}
|
||||
|
||||
bisect_skip() {
|
||||
all=''
|
||||
all=''
|
||||
for arg in "$@"
|
||||
do
|
||||
case "$arg" in
|
||||
*..*)
|
||||
revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
|
||||
*)
|
||||
revs=$(git rev-parse --sq-quote "$arg") ;;
|
||||
esac
|
||||
all="$all $revs"
|
||||
done
|
||||
eval bisect_state 'skip' $all
|
||||
case "$arg" in
|
||||
*..*)
|
||||
revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
|
||||
*)
|
||||
revs=$(git rev-parse --sq-quote "$arg") ;;
|
||||
esac
|
||||
all="$all $revs"
|
||||
done
|
||||
eval bisect_state 'skip' $all
|
||||
}
|
||||
|
||||
bisect_state() {
|
||||
|
@ -206,8 +233,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,10 +318,10 @@ 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
|
||||
# Check if we should exit because bisection is finished
|
||||
test $res -eq 10 && exit 0
|
||||
|
||||
# Check for an error in the bisection process
|
||||
|
@ -309,7 +336,8 @@ bisect_visualize() {
|
|||
if test $# = 0
|
||||
then
|
||||
if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
|
||||
type gitk >/dev/null 2>&1; then
|
||||
type gitk >/dev/null 2>&1
|
||||
then
|
||||
set gitk
|
||||
else
|
||||
set git log
|
||||
|
@ -333,19 +361,20 @@ bisect_reset() {
|
|||
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" ;;
|
||||
invalid="$1"
|
||||
die "$(eval_gettext "'\$invalid' is not a valid commit")"
|
||||
}
|
||||
branch="$1" ;;
|
||||
*)
|
||||
usage ;;
|
||||
usage ;;
|
||||
esac
|
||||
if git checkout "$branch" -- ; then
|
||||
bisect_clean_state
|
||||
else
|
||||
|
||||
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
|
||||
bisect_clean_state
|
||||
}
|
||||
|
||||
bisect_clean_state() {
|
||||
|
@ -362,7 +391,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"
|
||||
}
|
||||
|
||||
|
@ -374,7 +404,8 @@ bisect_replay () {
|
|||
while read git bisect command rev
|
||||
do
|
||||
test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
|
||||
if test "$git" = "git-bisect"; then
|
||||
if test "$git" = "git-bisect"
|
||||
then
|
||||
rev="$command"
|
||||
command="$bisect"
|
||||
fi
|
||||
|
@ -392,65 +423,71 @@ bisect_replay () {
|
|||
}
|
||||
|
||||
bisect_run () {
|
||||
bisect_next_check fail
|
||||
bisect_next_check fail
|
||||
|
||||
while true
|
||||
do
|
||||
command="$@"
|
||||
eval_gettext "running \$command"; echo
|
||||
"$@"
|
||||
res=$?
|
||||
while true
|
||||
do
|
||||
command="$@"
|
||||
eval_gettext "running \$command"; echo
|
||||
"$@"
|
||||
res=$?
|
||||
|
||||
# Check for really bad run error.
|
||||
if [ $res -lt 0 -o $res -ge 128 ]; then
|
||||
(
|
||||
eval_gettext "bisect run failed:
|
||||
# Check for really bad run error.
|
||||
if [ $res -lt 0 -o $res -ge 128 ]
|
||||
then
|
||||
(
|
||||
eval_gettext "bisect run failed:
|
||||
exit code \$res from '\$command' is < 0 or >= 128" &&
|
||||
echo
|
||||
) >&2
|
||||
exit $res
|
||||
fi
|
||||
echo
|
||||
) >&2
|
||||
exit $res
|
||||
fi
|
||||
|
||||
# Find current state depending on run success or failure.
|
||||
# A special exit code of 125 means cannot test.
|
||||
if [ $res -eq 125 ]; then
|
||||
state='skip'
|
||||
elif [ $res -gt 0 ]; then
|
||||
state='bad'
|
||||
else
|
||||
state='good'
|
||||
fi
|
||||
# Find current state depending on run success or failure.
|
||||
# A special exit code of 125 means cannot test.
|
||||
if [ $res -eq 125 ]
|
||||
then
|
||||
state='skip'
|
||||
elif [ $res -gt 0 ]
|
||||
then
|
||||
state='bad'
|
||||
else
|
||||
state='good'
|
||||
fi
|
||||
|
||||
# We have to use a subshell because "bisect_state" can exit.
|
||||
( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
|
||||
res=$?
|
||||
# We have to use a subshell because "bisect_state" can exit.
|
||||
( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
|
||||
res=$?
|
||||
|
||||
cat "$GIT_DIR/BISECT_RUN"
|
||||
cat "$GIT_DIR/BISECT_RUN"
|
||||
|
||||
if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
|
||||
> /dev/null; then
|
||||
(
|
||||
gettext "bisect run cannot continue any more" &&
|
||||
echo
|
||||
) >&2
|
||||
exit $res
|
||||
fi
|
||||
if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
|
||||
> /dev/null
|
||||
then
|
||||
(
|
||||
gettext "bisect run cannot continue any more" &&
|
||||
echo
|
||||
) >&2
|
||||
exit $res
|
||||
fi
|
||||
|
||||
if [ $res -ne 0 ]; then
|
||||
(
|
||||
eval_gettext "bisect run failed:
|
||||
if [ $res -ne 0 ]
|
||||
then
|
||||
(
|
||||
eval_gettext "bisect run failed:
|
||||
'bisect_state \$state' exited with error code \$res" &&
|
||||
echo
|
||||
) >&2
|
||||
exit $res
|
||||
fi
|
||||
echo
|
||||
) >&2
|
||||
exit $res
|
||||
fi
|
||||
|
||||
if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
|
||||
gettext "bisect run success"; echo
|
||||
exit 0;
|
||||
fi
|
||||
if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null
|
||||
then
|
||||
gettext "bisect run success"; echo
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
bisect_log () {
|
||||
|
@ -460,33 +497,33 @@ bisect_log () {
|
|||
|
||||
case "$#" in
|
||||
0)
|
||||
usage ;;
|
||||
usage ;;
|
||||
*)
|
||||
cmd="$1"
|
||||
shift
|
||||
case "$cmd" in
|
||||
help)
|
||||
git bisect -h ;;
|
||||
start)
|
||||
bisect_start "$@" ;;
|
||||
bad|good)
|
||||
bisect_state "$cmd" "$@" ;;
|
||||
skip)
|
||||
bisect_skip "$@" ;;
|
||||
next)
|
||||
# Not sure we want "next" at the UI level anymore.
|
||||
bisect_next "$@" ;;
|
||||
visualize|view)
|
||||
bisect_visualize "$@" ;;
|
||||
reset)
|
||||
bisect_reset "$@" ;;
|
||||
replay)
|
||||
bisect_replay "$@" ;;
|
||||
log)
|
||||
bisect_log ;;
|
||||
run)
|
||||
bisect_run "$@" ;;
|
||||
*)
|
||||
usage ;;
|
||||
esac
|
||||
cmd="$1"
|
||||
shift
|
||||
case "$cmd" in
|
||||
help)
|
||||
git bisect -h ;;
|
||||
start)
|
||||
bisect_start "$@" ;;
|
||||
bad|good)
|
||||
bisect_state "$cmd" "$@" ;;
|
||||
skip)
|
||||
bisect_skip "$@" ;;
|
||||
next)
|
||||
# Not sure we want "next" at the UI level anymore.
|
||||
bisect_next "$@" ;;
|
||||
visualize|view)
|
||||
bisect_visualize "$@" ;;
|
||||
reset)
|
||||
bisect_reset "$@" ;;
|
||||
replay)
|
||||
bisect_replay "$@" ;;
|
||||
log)
|
||||
bisect_log ;;
|
||||
run)
|
||||
bisect_run "$@" ;;
|
||||
*)
|
||||
usage ;;
|
||||
esac
|
||||
esac
|
||||
|
|
2
git.c
2
git.c
|
@ -334,7 +334,7 @@ static void handle_internal_command(int argc, const char **argv)
|
|||
{ "annotate", cmd_annotate, RUN_SETUP },
|
||||
{ "apply", cmd_apply, RUN_SETUP_GENTLY },
|
||||
{ "archive", cmd_archive },
|
||||
{ "bisect--helper", cmd_bisect__helper, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "bisect--helper", cmd_bisect__helper, RUN_SETUP },
|
||||
{ "blame", cmd_blame, RUN_SETUP },
|
||||
{ "branch", cmd_branch, RUN_SETUP },
|
||||
{ "bundle", cmd_bundle, RUN_SETUP_GENTLY },
|
||||
|
|
|
@ -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 &&
|
||||
|
@ -138,15 +150,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 &&
|
||||
|
@ -572,6 +592,155 @@ test_expect_success 'erroring out when using bad path parameters' '
|
|||
grep "bad path parameters" error.txt
|
||||
'
|
||||
|
||||
test_expect_success 'test bisection on bare repo - --no-checkout specified' '
|
||||
git clone --bare . bare.nocheckout &&
|
||||
(
|
||||
cd bare.nocheckout &&
|
||||
git bisect start --no-checkout &&
|
||||
git bisect good $HASH1 &&
|
||||
git bisect bad $HASH4 &&
|
||||
git bisect run eval \
|
||||
"test \$(git rev-list BISECT_HEAD ^$HASH2 --max-count=1 | wc -l) = 0" \
|
||||
>../nocheckout.log &&
|
||||
git bisect reset
|
||||
) &&
|
||||
grep "$HASH3 is the first bad commit" nocheckout.log
|
||||
'
|
||||
|
||||
|
||||
test_expect_success 'test bisection on bare repo - --no-checkout defaulted' '
|
||||
git clone --bare . bare.defaulted &&
|
||||
(
|
||||
cd bare.defaulted &&
|
||||
git bisect start &&
|
||||
git bisect good $HASH1 &&
|
||||
git bisect bad $HASH4 &&
|
||||
git bisect run eval \
|
||||
"test \$(git rev-list BISECT_HEAD ^$HASH2 --max-count=1 | wc -l) = 0" \
|
||||
>../defaulted.log &&
|
||||
git bisect reset
|
||||
) &&
|
||||
grep "$HASH3 is the first bad commit" defaulted.log
|
||||
'
|
||||
|
||||
#
|
||||
# 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
|
||||
'
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue