parse-options: allow positivation of options starting, with no-

Long options can be negated by adding no- right after the leading two dashes. This is useful e.g. to override options set by aliases. For options that are defined to start with no- already, this looks a bit funny. Allow such options to also be negated by removing the prefix. The following thirteen options are affected: apply --no-add bisect--helper --no-checkout checkout-index --no-create clone --no-checkout --no-hardlinks commit --no-verify --no-post-rewrite format-patch --no-binary hash-object --no-filters read-tree --no-sparse-checkout revert --no-commit show-branch --no-name update-ref --no-deref The following five are NOT affected because they are defined with PARSE_OPT_NONEG or the non-negated version is defined as well: branch --no-merged format-patch --no-stat --no-numbered update-index --no-assume-unchanged --no-skip-worktree Signed-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx> Signed-off-by: Junio C Hamano <gitster@pobox.com>

René Scharfe committed Feb 25, 2012 at 20:14 UTC 0f1930c58754a821bd51087ca4676e6a69cd4d2b
3 files changed +20 -12
Documentation/technical/api-parse-options.txt
+2 -1
@@ -39,7 +39,8 @@ The parse-options API allows:
39 * Short options may be bundled, e.g. `-a -b` can be specified as `-ab`.
40
41 * Boolean long options can be 'negated' (or 'unset') by prepending
42 - `no-`, e.g. `\--no-abbrev` instead of `\--abbrev`.
42 + `no-`, e.g. `\--no-abbrev` instead of `\--abbrev`. Conversely,
43 + options that begin with `no-` can be 'negated' by removing it.
44
45 * Options and non-option arguments can clearly be separated using the `\--`
46 option, e.g. `-a -b \--option \-- \--this-is-a-file` indicates that
parse-options.c
+17 -10
@@ -193,13 +193,14 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
193 arg_end = arg + strlen(arg);
194
195 for (; options->type != OPTION_END; options++) {
196 - const char *rest;
197 - int flags = 0;
196 + const char *rest, *long_name = options->long_name;
197 + int flags = 0, opt_flags = 0;
198
199 - if (!options->long_name)
199 + if (!long_name)
200 continue;
201
202 - rest = skip_prefix(arg, options->long_name);
202 +again:
203 + rest = skip_prefix(arg, long_name);
204 if (options->type == OPTION_ARGUMENT) {
205 if (!rest)
206 continue;
@@ -212,7 +213,7 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
213 }
214 if (!rest) {
215 /* abbreviated? */
215 - if (!strncmp(options->long_name, arg, arg_end - arg)) {
216 + if (!strncmp(long_name, arg, arg_end - arg)) {
217 is_abbreviated:
218 if (abbrev_option) {
219 /*
@@ -227,7 +228,7 @@ is_abbreviated:
228 if (!(flags & OPT_UNSET) && *arg_end)
229 p->opt = arg_end + 1;
230 abbrev_option = options;
230 - abbrev_flags = flags;
231 + abbrev_flags = flags ^ opt_flags;
232 continue;
233 }
234 /* negation allowed? */
@@ -239,12 +240,18 @@ is_abbreviated:
240 goto is_abbreviated;
241 }
242 /* negated? */
242 - if (strncmp(arg, "no-", 3))
243 + if (prefixcmp(arg, "no-")) {
244 + if (!prefixcmp(long_name, "no-")) {
245 + long_name += 3;
246 + opt_flags |= OPT_UNSET;
247 + goto again;
248 + }
249 continue;
250 + }
251 flags |= OPT_UNSET;
245 - rest = skip_prefix(arg + 3, options->long_name);
252 + rest = skip_prefix(arg + 3, long_name);
253 /* abbreviated and negated? */
247 - if (!rest && !prefixcmp(options->long_name, arg + 3))
254 + if (!rest && !prefixcmp(long_name, arg + 3))
255 goto is_abbreviated;
256 if (!rest)
257 continue;
@@ -254,7 +261,7 @@ is_abbreviated:
261 continue;
262 p->opt = rest + 1;
263 }
257 - return get_value(p, options, flags);
264 + return get_value(p, options, flags ^ opt_flags);
265 }
266
267 if (ambiguous_option)
t/t0040-parse-options.sh
+1 -1
@@ -107,7 +107,7 @@ test_expect_success 'OPT_BOOL() negation #2' 'check boolean: 0 -D --no-no-doubt'
107 test_expect_success 'OPT_BOOL() no negation #1' 'check_unknown --fear'
108 test_expect_success 'OPT_BOOL() no negation #2' 'check_unknown --no-no-fear'
109
110 -test_expect_failure 'OPT_BOOL() positivation' 'check boolean: 0 -D --doubt'
110 +test_expect_success 'OPT_BOOL() positivation' 'check boolean: 0 -D --doubt'
111
112 cat > expect << EOF
113 boolean: 2