Add a new core.abbreviatedOptions which corresponds to the newly added
GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS test mode. This allows
e.g. script authors to enact the same sort of paranoia about option
abbreviation as our own test suite now uses by default.

I'm renaming GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS to
GIT_TEST_ABBREVIATED_OPTIONS for consistency with the new
configuration variable, and I've picked the shorter
core.abbreviatedOptions instead of core.disallowAbbreviatedOptions
because it's easier to reason about a variable that's true by default
than about one that has a negation in its name.

While I'm at it, factor out the check for the environment variable
into a parse_options_config() which similar to the code added in
62c23938fa ("tests: add a special setup where rebase.useBuiltin is
off", 2018-11-14) will first check the environment variable, and then
the configuration.

Using git_env_bool() means it's a bit of a pain to distinguish between
the case where GIT_TEST_ABBREVIATED_OPTIONS is set to "true" or
"false" v.s. being empty, hence the
test_when_finished/export/sane_unset dance in the test that's being
added, but I think that's more sane than introducing a "bool but not
in the same way as the rest" GIT_TEST_* variable.

Signed-off-by: Ævar Arnfjörð Bjarmason <ava...@gmail.com>
---
 Documentation/config/core.txt | 12 ++++++++++++
 parse-options.c               | 19 +++++++++++++++----
 t/README                      |  4 ++--
 t/t0040-parse-options.sh      | 22 +++++++++++++++++-----
 t/test-lib.sh                 |  6 +++---
 5 files changed, 49 insertions(+), 14 deletions(-)

diff --git a/Documentation/config/core.txt b/Documentation/config/core.txt
index 7e9b6c8f4c..5a42ec3940 100644
--- a/Documentation/config/core.txt
+++ b/Documentation/config/core.txt
@@ -1,3 +1,15 @@
+core.abbreviatedOptions::
+       Defaults to `true` which allows options to be abbreviated as
+       long as they aren't ambiguous, e.g. for linkgit:git-init[1]
+       the `--bare` option can be abbreviated as `--bar`, `--ba` or
+       even `--b` since no other option starts with those
+       prefixes. However, if such an option were added in the future
+       any use of these abbreviations would break.
++
+By setting this to false (e.g. in scripts) you can defend against such
+future breakages by enforcing that options must always be fully
+provided.
+
 core.fileMode::
        Tells Git if the executable bit of files in the working tree
        is to be honored.
diff --git a/parse-options.c b/parse-options.c
index acc3a93660..d6a291f705 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -6,7 +6,7 @@
 #include "color.h"
 #include "utf8.h"
 
-static int disallow_abbreviated_options;
+static int abbreviated_options = 1;
 
 #define OPT_SHORT 1
 #define OPT_UNSET 2
@@ -346,7 +346,7 @@ static enum parse_opt_result parse_long_opt(
                return get_value(p, options, all_opts, flags ^ opt_flags);
        }
 
-       if (disallow_abbreviated_options && (ambiguous_option || abbrev_option))
+       if (!abbreviated_options && (ambiguous_option || abbrev_option))
                die("disallowed abbreviated or ambiguous option '%.*s'",
                    (int)(arg_end - arg), arg);
 
@@ -456,6 +456,17 @@ static void parse_options_check(const struct option *opts)
                exit(128);
 }
 
+static void parse_options_config(void)
+{
+       int env = git_env_bool("GIT_TEST_ABBREVIATED_OPTIONS", -1);
+       if (env != -1) {
+               abbreviated_options = env;
+               return;
+       }
+       git_config_get_bool("core.abbreviatedoptions", &abbreviated_options);
+       return;
+}
+
 void parse_options_start(struct parse_opt_ctx_t *ctx,
                         int argc, const char **argv, const char *prefix,
                         const struct option *options, int flags)
@@ -480,6 +491,7 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
            (flags & PARSE_OPT_KEEP_ARGV0))
                BUG("Can't keep argv0 if you don't have it");
        parse_options_check(options);
+       parse_options_config();
 }
 
 static void show_negated_gitcomp(const struct option *opts, int nr_noopts)
@@ -714,9 +726,8 @@ int parse_options(int argc, const char **argv, const char 
*prefix,
 {
        struct parse_opt_ctx_t ctx;
 
-       disallow_abbreviated_options =
-               git_env_bool("GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS", 0);
 
+       parse_options_config();
        parse_options_start(&ctx, argc, argv, prefix, options, flags);
        switch (parse_options_step(&ctx, options, usagestr)) {
        case PARSE_OPT_HELP:
diff --git a/t/README b/t/README
index 9ed3051a1c..1d628baf31 100644
--- a/t/README
+++ b/t/README
@@ -399,9 +399,9 @@ GIT_TEST_SIDEBAND_ALL=<boolean>, when true, overrides the
 fetch-pack to not request sideband-all (even if the server advertises
 sideband-all).
 
-GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=<boolean>, when true (which is
+GIT_TEST_ABBREVIATED_OPTIONS=<boolean>, when false (which is
 the default when running tests), errors out when an abbreviated option
-is used.
+is used. Overrides the core.abbreviatedOptions setting.
 
 Naming Tests
 ------------
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index 5f6a16336d..19685d1582 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -203,27 +203,39 @@ file: (not set)
 EOF
 
 test_expect_success 'unambiguously abbreviated option' '
-       GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+       GIT_TEST_ABBREVIATED_OPTIONS=true \
        test-tool parse-options --int 2 --boolean --no-bo >output 2>output.err 
&&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
 test_expect_success 'unambiguously abbreviated option with "="' '
-       GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+       GIT_TEST_ABBREVIATED_OPTIONS=true \
        test-tool parse-options --expect="integer: 2" --int=2
 '
 
 test_expect_success 'ambiguously abbreviated option' '
-       test_expect_code 129 env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+       test_expect_code 129 env GIT_TEST_ABBREVIATED_OPTIONS=true \
        test-tool parse-options --strin 123
 '
 
 test_expect_success 'non ambiguous option (after two options it abbreviates)' '
-       GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+       GIT_TEST_ABBREVIATED_OPTIONS=true \
        test-tool parse-options --expect="string: 123" --st 123
 '
 
+test_expect_success 'abbreviated options configured with 
core.abbreviatedOptions' '
+       test_when_finished "
+               rm -rf A B C &&
+               GIT_TEST_ABBREVIATED_OPTIONS=$GIT_TEST_ABBREVIATED_OPTIONS &&
+               export GIT_TEST_ABBREVIATED_OPTIONS
+       " &&
+       git init --bare A &&
+       test_must_fail git init --ba B &&
+       sane_unset GIT_TEST_ABBREVIATED_OPTIONS &&
+       git -c core.abbreviatedOptions=true init --ba C
+'
+
 cat >typo.err <<\EOF
 error: did you mean `--boolean` (with two dashes ?)
 EOF
@@ -329,7 +341,7 @@ file: (not set)
 EOF
 
 test_expect_success 'negation of OPT_NONEG flags is not ambiguous' '
-       GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+       GIT_TEST_ABBREVIATED_OPTIONS=true \
        test-tool parse-options --no-ambig >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
diff --git a/t/test-lib.sh b/t/test-lib.sh
index e550009411..4b0dd8a1a9 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -58,9 +58,9 @@ fi
 export PERL_PATH SHELL_PATH
 
 # Disallow the use of abbreviated options in the test suite by default
-test -n "$GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS" || {
-       GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=true
-       export GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS
+test -n "$GIT_TEST_ABBREVIATED_OPTIONS" || {
+       GIT_TEST_ABBREVIATED_OPTIONS=false
+       export GIT_TEST_ABBREVIATED_OPTIONS
 }
 
 ################################################################
-- 
2.21.0.360.g471c308f928

Reply via email to