Skip to content

Commit 6d159f5

Browse files
committed
Merge branch 'rs/parse-options-negation-help'
"git cmd -h" learned to signal which options can be negated by listing such options like "--[no-]opt". * rs/parse-options-negation-help: parse-options: simplify usage_padding() parse-options: no --[no-]no-... parse-options: factor out usage_indent() and usage_padding() parse-options: show negatability of options in short help t1502: test option negation t1502: move optionspec help output to a file t1502, docs: disallow --no-help subtree: disallow --no-{help,quiet,debug,branch,message}
2 parents cd9da15 + 311c8ff commit 6d159f5

File tree

10 files changed

+198
-125
lines changed

10 files changed

+198
-125
lines changed

Documentation/git-rev-parse.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ some-command [<options>] <args>...
398398

399399
some-command does foo and bar!
400400
--
401-
h,help show the help
401+
h,help! show the help
402402

403403
foo some nifty option --foo
404404
bar= some cool option --bar with an argument
@@ -424,10 +424,10 @@ usage: some-command [<options>] <args>...
424424
some-command does foo and bar!
425425

426426
-h, --help show the help
427-
--foo some nifty option --foo
428-
--bar ... some cool option --bar with an argument
429-
--baz <arg> another cool option --baz with a named argument
430-
--qux[=<path>] qux may take a path argument but has meaning by itself
427+
--[no-]foo some nifty option --foo
428+
--[no-]bar ... some cool option --bar with an argument
429+
--[no-]baz <arg> another cool option --baz with a named argument
430+
--[no-]qux[=<path>] qux may take a path argument but has meaning by itself
431431

432432
An option group Header
433433
-C[...] option C with an optional argument

contrib/subtree/git-subtree.sh

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,19 @@ git subtree split --prefix=<prefix> [<commit>]
3333
git subtree pull --prefix=<prefix> <repository> <ref>
3434
git subtree push --prefix=<prefix> <repository> <refspec>
3535
--
36-
h,help show the help
37-
q,quiet quiet
38-
d,debug show debug messages
36+
h,help! show the help
37+
q,quiet! quiet
38+
d,debug! show debug messages
3939
P,prefix= the name of the subdir to split out
4040
options for 'split' (also: 'push')
4141
annotate= add a prefix to commit message of new commits
42-
b,branch= create a new branch from the split subtree
42+
b,branch!= create a new branch from the split subtree
4343
ignore-joins ignore prior --rejoin commits
4444
onto= try connecting new tree to an existing one
4545
rejoin merge the new branch back into HEAD
4646
options for 'add' and 'merge' (also: 'pull', 'split --rejoin', and 'push --rejoin')
4747
squash merge subtree changes as a single commit
48-
m,message= use the given message as the commit message for the merge commit
48+
m,message!= use the given message as the commit message for the merge commit
4949
"
5050

5151
indent=0

contrib/subtree/t/t7900-subtree.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ test_expect_success 'shows short help text for -h' '
7171
test_expect_code 129 git subtree -h >out 2>err &&
7272
test_must_be_empty err &&
7373
grep -e "^ *or: git subtree pull" out &&
74-
grep -e --annotate out
74+
grep -F -e "--[no-]annotate" out
7575
'
7676

7777
#

parse-options.c

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,14 +1023,37 @@ static int usage_argh(const struct option *opts, FILE *outfile)
10231023
return utf8_fprintf(outfile, s, opts->argh ? _(opts->argh) : _("..."));
10241024
}
10251025

1026-
#define USAGE_OPTS_WIDTH 24
1027-
#define USAGE_GAP 2
1026+
static int usage_indent(FILE *outfile)
1027+
{
1028+
return fprintf(outfile, " ");
1029+
}
1030+
1031+
#define USAGE_OPTS_WIDTH 26
1032+
1033+
static void usage_padding(FILE *outfile, size_t pos)
1034+
{
1035+
if (pos < USAGE_OPTS_WIDTH)
1036+
fprintf(outfile, "%*s", USAGE_OPTS_WIDTH - (int)pos, "");
1037+
else
1038+
fprintf(outfile, "\n%*s", USAGE_OPTS_WIDTH, "");
1039+
}
1040+
1041+
static const struct option *find_option_by_long_name(const struct option *opts,
1042+
const char *long_name)
1043+
{
1044+
for (; opts->type != OPTION_END; opts++) {
1045+
if (opts->long_name && !strcmp(opts->long_name, long_name))
1046+
return opts;
1047+
}
1048+
return NULL;
1049+
}
10281050

10291051
static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *ctx,
10301052
const char * const *usagestr,
10311053
const struct option *opts,
10321054
int full, int err)
10331055
{
1056+
const struct option *all_opts = opts;
10341057
FILE *outfile = err ? stderr : stdout;
10351058
int need_newline;
10361059

@@ -1111,8 +1134,8 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t
11111134

11121135
for (; opts->type != OPTION_END; opts++) {
11131136
size_t pos;
1114-
int pad;
11151137
const char *cp, *np;
1138+
const char *positive_name = NULL;
11161139

11171140
if (opts->type == OPTION_SUBCOMMAND)
11181141
continue;
@@ -1131,7 +1154,7 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t
11311154
need_newline = 0;
11321155
}
11331156

1134-
pos = fprintf(outfile, " ");
1157+
pos = usage_indent(outfile);
11351158
if (opts->short_name) {
11361159
if (opts->flags & PARSE_OPT_NODASH)
11371160
pos += fprintf(outfile, "%c", opts->short_name);
@@ -1140,38 +1163,46 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t
11401163
}
11411164
if (opts->long_name && opts->short_name)
11421165
pos += fprintf(outfile, ", ");
1143-
if (opts->long_name)
1144-
pos += fprintf(outfile, "--%s", opts->long_name);
1166+
if (opts->long_name) {
1167+
const char *long_name = opts->long_name;
1168+
if ((opts->flags & PARSE_OPT_NONEG) ||
1169+
skip_prefix(long_name, "no-", &positive_name))
1170+
pos += fprintf(outfile, "--%s", long_name);
1171+
else
1172+
pos += fprintf(outfile, "--[no-]%s", long_name);
1173+
}
1174+
11451175
if (opts->type == OPTION_NUMBER)
11461176
pos += utf8_fprintf(outfile, _("-NUM"));
11471177

11481178
if ((opts->flags & PARSE_OPT_LITERAL_ARGHELP) ||
11491179
!(opts->flags & PARSE_OPT_NOARG))
11501180
pos += usage_argh(opts, outfile);
11511181

1152-
if (pos == USAGE_OPTS_WIDTH + 1)
1153-
pad = -1;
1154-
else if (pos <= USAGE_OPTS_WIDTH)
1155-
pad = USAGE_OPTS_WIDTH - pos;
1156-
else {
1157-
fputc('\n', outfile);
1158-
pad = USAGE_OPTS_WIDTH;
1159-
}
11601182
if (opts->type == OPTION_ALIAS) {
1161-
fprintf(outfile, "%*s", pad + USAGE_GAP, "");
1183+
usage_padding(outfile, pos);
11621184
fprintf_ln(outfile, _("alias of --%s"),
11631185
(const char *)opts->value);
11641186
continue;
11651187
}
11661188

11671189
for (cp = _(opts->help); *cp; cp = np) {
11681190
np = strchrnul(cp, '\n');
1169-
fprintf(outfile,
1170-
"%*s%.*s\n", pad + USAGE_GAP, "",
1171-
(int)(np - cp), cp);
1191+
usage_padding(outfile, pos);
1192+
fprintf(outfile, "%.*s\n", (int)(np - cp), cp);
11721193
if (*np)
11731194
np++;
1174-
pad = USAGE_OPTS_WIDTH;
1195+
pos = 0;
1196+
}
1197+
1198+
if (positive_name) {
1199+
if (find_option_by_long_name(all_opts, positive_name))
1200+
continue;
1201+
pos = usage_indent(outfile);
1202+
pos += fprintf(outfile, "--%s", positive_name);
1203+
usage_padding(outfile, pos);
1204+
fprintf_ln(outfile, _("opposite of --no-%s"),
1205+
positive_name);
11751206
}
11761207
}
11771208
fputc('\n', outfile);

t/t0040-parse-options.sh

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,30 +13,35 @@ usage: test-tool parse-options <options>
1313
1414
A helper function for the parse-options API.
1515
16-
--yes get a boolean
16+
--[no-]yes get a boolean
1717
-D, --no-doubt begins with 'no-'
18+
--doubt opposite of --no-doubt
1819
-B, --no-fear be brave
19-
-b, --boolean increment by one
20-
-4, --or4 bitwise-or boolean with ...0100
21-
--neg-or4 same as --no-or4
20+
-b, --[no-]boolean increment by one
21+
-4, --[no-]or4 bitwise-or boolean with ...0100
22+
--[no-]neg-or4 same as --no-or4
2223
23-
-i, --integer <n> get a integer
24+
-i, --[no-]integer <n>
25+
get a integer
2426
-j <n> get a integer, too
2527
-m, --magnitude <n> get a magnitude
26-
--set23 set integer to 23
28+
--[no-]set23 set integer to 23
2729
--mode1 set integer to 1 (cmdmode option)
2830
--mode2 set integer to 2 (cmdmode option)
29-
-L, --length <str> get length of <str>
30-
-F, --file <file> set file to <file>
31+
-L, --[no-]length <str>
32+
get length of <str>
33+
-F, --[no-]file <file>
34+
set file to <file>
3135
3236
String options
33-
-s, --string <string> get a string
34-
--string2 <str> get another string
35-
--st <st> get another string (pervert ordering)
37+
-s, --[no-]string <string>
38+
get a string
39+
--[no-]string2 <str> get another string
40+
--[no-]st <st> get another string (pervert ordering)
3641
-o <str> get another string
3742
--longhelp help text of this entry
3843
spans multiple lines
39-
--list <str> add str to list
44+
--[no-]list <str> add str to list
4045
4146
Magic arguments
4247
-NUM set integer to NUM
@@ -45,16 +50,17 @@ Magic arguments
4550
--no-ambiguous negative ambiguity
4651
4752
Standard options
48-
--abbrev[=<n>] use <n> digits to display object names
49-
-v, --verbose be verbose
50-
-n, --dry-run dry run
51-
-q, --quiet be quiet
52-
--expect <string> expected output in the variable dump
53+
--[no-]abbrev[=<n>] use <n> digits to display object names
54+
-v, --[no-]verbose be verbose
55+
-n, --[no-]dry-run dry run
56+
-q, --[no-]quiet be quiet
57+
--[no-]expect <string>
58+
expected output in the variable dump
5359
5460
Alias
55-
-A, --alias-source <string>
61+
-A, --[no-]alias-source <string>
5662
get a string
57-
-Z, --alias-target <string>
63+
-Z, --[no-]alias-target <string>
5864
alias of --alias-source
5965
6066
EOF

0 commit comments

Comments
 (0)