From: Jonathan Nieder <jrnieder@gmail.com>
To: Stephen Oberholtzer <stevie@qrpff.net>
Cc: git@vger.kernel.org, Junio C Hamano <gitster@pobox.com>,
Christian Couder <chriscool@tuxfamily.org>
Subject: Re: [RFC PATCH] bisect run: allow inverting meaning of exit code
Date: Fri, 3 Jan 2020 17:22:30 -0800 [thread overview]
Message-ID: <20200104012230.GD130883@google.com> (raw)
In-Reply-To: <20200103043027.4537-1-stevie@qrpff.net>
(+cc: Christian Couder, bisect expert)
Hi!
Stephen Oberholtzer wrote:
> NOTE: This is obviously not ready for merging; I just wanted to
> get feedback.
Thanks for writing. I like where you're going.
> In particular, I expect some bikeshedding on the specific option
> names (-r, --invert, --expect). I'm probably going to change
> `--expect` to `--success`, in fact.
>
> If we can come to a consensus on the names (and, of course, on the
> feature itself), I'll clean up the tests, remove the debug output,
> update the documentation, then resubmit.
>
> >8------------------------------------------------------8<
>
> If your automated test naturally yields zero for _old_/_good_,
> 1-124 or 126-127 for _new_/_bad_, then you're set.
>
> If that logic is reversed, however, it's a bit more of a pain: you
> can't just stick a `sh -c !` in front of your command, because that
> doesn't account for exit codes 125 or 129-255.
>
> This commit enhances `git bisect run` as follows:
>
> * '--' can be used as an option list terminator,
> just as everywhere else.
Could this part go in a separate commit? That way, it can go in
more quickly while we figure out the rest. :)
> * The treatment of the exit code can be selected via an option:
>
> - No option, of course, treats 0 as _old_/_good_
> - `-r` (for reverse) treats 0 as _new_/_bad_
> - `--invert` is the long form for `-r`
> - `--expect=<term>` treats 0 as <term>
Initial thoughts:
- it's not immediately clear to me that this is common enough
to need the short-and-sweet name "-r". Could it have a long
form only?
- I think I agree with you that "git bisect run --expect=5" might
be more clearly written as "git bisect run --success=5". Or even
something explicitly referring to exit status, like
--success-status=5.
- This has an interesting relationship to the "alternate terms"
feature (--term-old / --term-new). I don't know if there's a
good way to make that more explicit --- maybe just some notes
with examples in the relevant parts of the manpage?
- the name --invert doesn't make it obvious to me what it is
inverting: good versus bad, ones complement of the status
code, revision range passed to "git bisect start"?
I'm even tempted to call it something like '-!', to make the
allusion to ! in shells more explicit. (But that's probably not a
great idea, since quoting ! correctly in interactive shells can be
difficult.)
Are there other commands we can try to make this consistent with?
"find" supports arbitrary expressions involving '!' and '-not'.
"git grep" has --invert-match: perhaps a name --invert-status
would be clear enough?
> You're not allowed to specify more than one expectation.
Usual convention would be "last specified option wins".
> Note that this lets one specify `--expect=good` as an explicit
> selection of the default behavior. This is intentional.
>
> Signed-off-by: Stephen Oberholtzer <stevie@qrpff.net>
> ---
> git-bisect.sh | 33 +++++++++-
> t/t6071-bisect-run.sh | 142 ++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 172 insertions(+), 3 deletions(-)
> create mode 100755 t/t6071-bisect-run.sh
As you said, this needs docs. Writing docs often helps make the UI a
bit better since it forces one to think about the various ways a tool
would be used in practice.
> diff --git a/git-bisect.sh b/git-bisect.sh
> index efee12b8b1..dbeb213846 100755
> --- a/git-bisect.sh
> +++ b/git-bisect.sh
> @@ -26,7 +26,7 @@ git bisect replay <logfile>
> replay bisection log.
> git bisect log
> show bisect log.
> -git bisect run <cmd>...
> +git bisect run [--expect=<term> | -r | --invert] [--] <cmd>...
> use <cmd>... to automatically bisect.
>
> Please use "git help bisect" to get the full man page.'
> @@ -238,6 +238,31 @@ bisect_replay () {
> bisect_run () {
> git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit
>
> + SUCCESS_TERM=$TERM_GOOD
> + FAIL_TERM=$TERM_BAD
> + INVERT_SET=
> + while [ "$1" != "${1#-}" ]; do
Might be simpler to do 'while true', and in the *) case to break.
> + case "$1" in
> + --)
> + shift
> + break ;;
> + --expect=$TERM_GOOD)
> + [ -z "$INVERT_SET" ] || die "$(gettext "bisect run: multiple expect options specified")"
> + INVERT_SET=1 ;;
> + -r|--invert|--expect=$TERM_BAD)
> + [ -z "$INVERT_SET" ] || die "$(gettext "bisect run: multiple expect options specified")"
> + SUCCESS_TERM=$TERM_BAD
> + FAIL_TERM=$TERM_GOOD
> + INVERT_SET=1 ;;
> + --expect=*)
> + # how to localize part 2?
> + die "$(printf "$(gettext "bisect run: invalid --expect value, use --expect=%s or --expect=%s")" "$TERM_GOOD" "$TERM_BAD")" ;;
It's more idiomatic to use eval_gettext here. See
"git grep -e die -- '*.sh'" for some examples.
> + *)
> + die "$(printf "$(gettext "bisect run: invalid option: %s")" "$1")" ;;
> + esac
> + shift
> + done
> +
> test -n "$*" || die "$(gettext "bisect run failed: no command provided.")"
>
> while true
> @@ -262,11 +287,13 @@ exit code \$res from '\$command' is < 0 or >= 128" >&2
> state='skip'
> elif [ $res -gt 0 ]
> then
> - state="$TERM_BAD"
> + state="$FAIL_TERM"
> else
> - state="$TERM_GOOD"
> + state="$SUCCESS_TERM"
> fi
>
> + echo "exit code $res means this commit is $state"
> +
> # We have to use a subshell because "bisect_state" can exit.
> ( bisect_state $state >"$GIT_DIR/BISECT_RUN" )
> res=$?
> diff --git a/t/t6071-bisect-run.sh b/t/t6071-bisect-run.sh
> new file mode 100755
> index 0000000000..2708e0f854
> --- /dev/null
> +++ b/t/t6071-bisect-run.sh
> @@ -0,0 +1,142 @@
> +# verify that unrecognized options are rejected by 'git bisect run'
> +#!/bin/sh
> +
> +# the linter's not smart enough to handle set -e
> +GIT_TEST_CHAIN_LINT=0
> +
> +test_description='Tests git bisect run'
> +
> +exec </dev/null
> +
> +. ./test-lib.sh
> +
> +{ test_expect_success 'Setting up repo for "git bisect run" tests.' "$(cat)" ; } <<'SETUP'
Tests put the commands to be passed to test_expect_success in a
single-quoted argument, with explicit &&-chaining. ("set -e" has
enough exceptions that it hasn't been worth using for this. The test
harness is able to detect a missing "&&" so this is less error-prone
than it sounds.) See t/README and any recently added test scripts in
t/ (try "git log -p --diff-filter=A -- t") for more details.
Thanks for a clear and pleasant description of the problem being
solved. I would like to say "here's a simple shell construct to use
instead of ! that makes this all redundant" but lacking that, for what
it's worth this set of new options seems like a good idea to me. :)
Sincerely,
Jonathan
(the rest left unsnipped for reference)
> +(
> +# I don't know how they managed it, but the git test engine
> +# somehow ignores the effect of 'set -e'.
> +set -eu || exit 1
> +# set -e canary
> +false
> +# hopefully, next year, we get -o pipefail!
> +echo '.DEFAULT: dummy
> +.PHONY: dummy
> +dummy:
> + true
> +' > Makefile
> +make
> +echo '0' >path0
> +git update-index --add -- Makefile path0
> +git commit -q -m 'initial commit'
> +git tag working0
> +# make some commits that don't cause problems
> +for x in `test_seq 1 20`; do
> + echo "$x" >path0
> + git update-index --replace -- path0
> + git commit -q -m "working commit $x"
> + git tag "working$x"
> +done
> +# break the makefile
> +sed -i.bak -e 's/true/false/' Makefile
> +rm -f Makefile.bak
> +! make
> +git update-index --replace -- Makefile
> +git commit -q -m "First broken commit"
> +git tag broken0
> +# make some more commits that still FTBFS
> +echo "exit code was $?; flags are $-"
> +for x in `test_seq 1 20`; do
> + echo "$x" >path0
> + git update-index --replace -- path0
> + git commit -q -m "broken build $x"
> + git tag "broken$x"
> +done
> +# repair it
> +git checkout working0 -- Makefile
> +make
> +git update-index --replace -- Makefile
> +git commit -q -m "First repaired commit"
> +git tag fixed0
> +# make some more commits with the bugfix
> +for x in `test_seq 1 20`; do
> + echo "$x" >path0
> + git update-index --replace -- path0
> + git commit -q -m "fixed build $x"
> + git tag "fixed$x"
> +done
> +#sh -c 'bash -i <> /dev/tty >&0 2>&1'
> +)
> +
> +SETUP
> +
> +test_expect_success 'setup first bisect' 'git bisect start && git bisect good working0 && git bisect bad broken9'
> +
> +test_expect_failure() {
> + shift
> + #echo arguments are "$*"
> + test_must_fail "$@"
> +}
> +
> +# okay, let's do some negative testing
> +
> +OLDPATH="$PATH"
> +
> +PATH="$PATH:."
> +
> +test_expect_success 'setup this-is-not-a-valid-option' '
> + echo "#/bin/sh" > --this-is-not-a-valid-option &&
> + chmod a+x -- --this-is-not-a-valid-option &&
> + --this-is-not-a-valid-option'
> +
> +test_expect_failure 'git bisect run: reject unrecognized options' git bisect run --this-is-not-a-valid-option
> +
> +PATH="$OLDPATH"
> +
> +test_expect_failure 'git bisect run: reject invalid values for --expect' git bisect run --expect=invalid make
> +
> +# okay, all of these settings are mutually exclusive (for sanity's sake, even with themselves)
> +for a in --expect=bad --expect=good -r --invert; do
> + for b in --expect=bad --expect=good -r --invert; do
> + test_expect_failure 'git bisect run: reject multiple --expect options' git bisect run $a $b make
> + done
> +done
> +
> +# finally, verify that '--' is honored (note that will mess things up and require a bisect reset)
> +PATH="$PATH:."
> +
> +test_expect_success 'git bisect run: honor --' 'git bisect run -- --this-is-not-a-valid-option'
> +
> +PATH="$OLDPATH"
> +
> +for expect_syntax in '' --expect=good; do
> +
> + # now we have to undo the bisect run
> + test_expect_success 'restarting bisection' 'git bisect reset && git bisect start && git bisect good working0 && git bisect bad broken9'
> +
> + test_expect_success "running bisection ($expect_syntax)" "
> +git bisect run $expect_syntax make &&
> +git log --oneline &&
> + # we should have determined that broken0 is the first bad version
> + test_cmp_rev broken0 refs/bisect/bad &&
> + # and that version should be the one checked out
> + test_cmp_rev broken0 HEAD
> +"
> +done
> +
> +
> +# NOW, test the reverse: find when we fixed it again
> +
> +for expect_syntax in -r --invert --expect=fixed; do
> +
> + test_expect_success 'restarting bisection' 'git bisect reset && git bisect start --term-old=broken --term-new=fixed && git bisect broken broken20 && git bisect fixed fixed20'
> + test_expect_success "running bisection ($expect_syntax)" "
> + git bisect run $expect_syntax make &&
> + git log --oneline &&
> + test_cmp_rev fixed0 refs/bisect/fixed &&
> + test_cmp_rev fixed0 HEAD
> + "
> +done
> +
> +test_expect_failure 'sanity check error message with custom terms' git bisect run --expect=invalid make
> +
> +
> +test_done
> --
> 2.20.1
>
next prev parent reply other threads:[~2020-01-04 1:22 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-01-03 4:30 [RFC PATCH] bisect run: allow inverting meaning of exit code Stephen Oberholtzer
2020-01-04 1:22 ` Jonathan Nieder [this message]
2020-01-04 5:37 ` Stephen Oberholtzer
2020-01-04 3:27 ` Junio C Hamano
2020-01-04 6:22 ` Stephen Oberholtzer
2020-01-06 1:16 ` Junio C Hamano
2020-01-07 0:10 ` Stephen Oberholtzer
2020-01-07 1:42 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20200104012230.GD130883@google.com \
--to=jrnieder@gmail.com \
--cc=chriscool@tuxfamily.org \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=stevie@qrpff.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox