{"thread":{"id":"11326","subject":"[PATCH 4/7] parse-options: let OPT__ABBREV ignore arguments.","startedAt":"2007-12-17T18:23:10Z","lastAt":"2007-12-18T02:00:42Z","messageCount":13,"participants":["Pierre Habouzit","Junio C Hamano","Wayne Davison"],"isPatch":true,"patchVersion":1,"patchTotal":7},"messages":[{"id":"63452","messageId":"1197915797-30679-1-git-send-email-madcoder@debian.org","threadId":"11326","inReplyTo":null,"subject":"[proposal] make parse-options nicer wrt optional arguments (supersedes all my recent posts on the matter)","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2007-12-17T18:23:10Z","receivedAt":"2007-12-17T18:23:10Z","isPatch":false,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"Here is a series that aims at fixing the various issues with\nparse-options that were raised recently.\n\n* preliminary patch:\n  [PATCH 1/7] parse-options: Make callbacks take flags instead of boolean `unset`\n\n* teach git parse-options to allow callbacks to ignore arguments that\n  don't seem to be theirs, refactors:\n  [PATCH 2/7] parse-options: allow callbacks to ignore arguments they don't need to use.\n  [PATCH 3/7] parse-options: Let the integer/string cases be callbacks as well.\n  [PATCH 4/7] parse-options: let OPT__ABBREV ignore arguments.\n\n* Document this (my previous proposal + Junio's squashed):\n  [PATCH 5/7] parse-options: Add a gitcli(5) man page.\n\n* Implement my `{}` proposal, a sed -e s/{}/_/ will replace {} with _\n  as a wildcard. Contains documentation for this placeholder.\n  [PATCH 6/7] parse-options: have a `use default value` wildcard.\n\n* Somehow unrelated patch, but still parse-option related (resend):\n  [PATCH 7/7] git-tag: fix -l switch handling regression.\n\n\n\nThis has been pushed as my ph/parseopt branch on\ngit://git.madism.org/git.git.\n"},{"id":"63457","messageId":"1197915797-30679-2-git-send-email-madcoder@debian.org","threadId":"11326","inReplyTo":"1197915797-30679-1-git-send-email-madcoder@debian.org","subject":"[PATCH 1/7] parse-options: Make callbacks take flags instead of boolean `unset`","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2007-12-17T18:23:11Z","receivedAt":"2007-12-17T18:23:11Z","isPatch":true,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"Signed-off-by: Pierre Habouzit <madcoder@debian.org>\n---\n builtin-branch.c       |    2 +-\n builtin-commit.c       |    4 ++--\n builtin-fast-export.c  |    4 ++--\n builtin-for-each-ref.c |    2 +-\n builtin-tag.c          |    2 +-\n parse-options.c        |   37 ++++++++++++++++---------------------\n parse-options.h        |    7 ++++++-\n 7 files changed, 29 insertions(+), 29 deletions(-)\n\ndiff --git a/builtin-branch.c b/builtin-branch.c\nindex 089cae5..677eee5 100644\n--- a/builtin-branch.c\n+++ b/builtin-branch.c\n@@ -531,7 +531,7 @@ static void rename_branch(const char *oldname, const char *newname, int force)\n \t\tdie(\"Branch is renamed, but update of config-file failed\");\n }\n \n-static int opt_parse_with_commit(const struct option *opt, const char *arg, int unset)\n+static int opt_parse_with_commit(const struct option *opt, const char *arg, int flags)\n {\n \tunsigned char sha1[20];\n \tstruct commit *commit;\ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex 0a91013..ca18a5c 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -52,10 +52,10 @@ static int no_edit, initial_commit, in_merge;\n const char *only_include_assumed;\n struct strbuf message;\n \n-static int opt_parse_m(const struct option *opt, const char *arg, int unset)\n+static int opt_parse_m(const struct option *opt, const char *arg, int flags)\n {\n \tstruct strbuf *buf = opt->value;\n-\tif (unset)\n+\tif (flags & PARSE_OPT_UNSET)\n \t\tstrbuf_setlen(buf, 0);\n \telse {\n \t\tstrbuf_addstr(buf, arg);\ndiff --git a/builtin-fast-export.c b/builtin-fast-export.c\nindex ef27eee..9f914b9 100755\n--- a/builtin-fast-export.c\n+++ b/builtin-fast-export.c\n@@ -26,9 +26,9 @@ static int progress;\n static enum { VERBATIM, WARN, STRIP, ABORT } signed_tag_mode = ABORT;\n \n static int parse_opt_signed_tag_mode(const struct option *opt,\n-\t\t\t\t     const char *arg, int unset)\n+\t\t\t\t     const char *arg, int flags)\n {\n-\tif (unset || !strcmp(arg, \"abort\"))\n+\tif (flags & PARSE_OPT_UNSET || !strcmp(arg, \"abort\"))\n \t\tsigned_tag_mode = ABORT;\n \telse if (!strcmp(arg, \"verbatim\") || !strcmp(arg, \"ignore\"))\n \t\tsigned_tag_mode = VERBATIM;\ndiff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\nindex f36a43c..3eecfe9 100644\n--- a/builtin-for-each-ref.c\n+++ b/builtin-for-each-ref.c\n@@ -802,7 +802,7 @@ static struct ref_sort *default_sort(void)\n \treturn sort;\n }\n \n-int opt_parse_sort(const struct option *opt, const char *arg, int unset)\n+int opt_parse_sort(const struct option *opt, const char *arg, int flags)\n {\n \tstruct ref_sort **sort_tail = opt->value;\n \tstruct ref_sort *s;\ndiff --git a/builtin-tag.c b/builtin-tag.c\nindex 274901a..fd44b2e 100644\n--- a/builtin-tag.c\n+++ b/builtin-tag.c\n@@ -348,7 +348,7 @@ struct msg_arg {\n \tstruct strbuf buf;\n };\n \n-static int parse_msg_arg(const struct option *opt, const char *arg, int unset)\n+static int parse_msg_arg(const struct option *opt, const char *arg, int flags)\n {\n \tstruct msg_arg *msg = opt->value;\n \ndiff --git a/parse-options.c b/parse-options.c\nindex e12b428..8f70e5d 100644\n--- a/parse-options.c\n+++ b/parse-options.c\n@@ -1,9 +1,6 @@\n #include \"git-compat-util.h\"\n #include \"parse-options.h\"\n \n-#define OPT_SHORT 1\n-#define OPT_UNSET 2\n-\n struct optparse_t {\n \tconst char **argv;\n \tint argc;\n@@ -29,9 +26,9 @@ static inline const char *skip_prefix(const char *str, const char *prefix)\n \n static int opterror(const struct option *opt, const char *reason, int flags)\n {\n-\tif (flags & OPT_SHORT)\n+\tif (flags & PARSE_OPT_SHORT)\n \t\treturn error(\"switch `%c' %s\", opt->short_name, reason);\n-\tif (flags & OPT_UNSET)\n+\tif (flags & PARSE_OPT_UNSET)\n \t\treturn error(\"option `no-%s' %s\", opt->long_name, reason);\n \treturn error(\"option `%s' %s\", opt->long_name, reason);\n }\n@@ -40,14 +37,14 @@ static int get_value(struct optparse_t *p,\n                      const struct option *opt, int flags)\n {\n \tconst char *s, *arg;\n-\tconst int unset = flags & OPT_UNSET;\n+\tconst int unset = flags & PARSE_OPT_UNSET;\n \n \tif (unset && p->opt)\n \t\treturn opterror(opt, \"takes no value\", flags);\n \tif (unset && (opt->flags & PARSE_OPT_NONEG))\n \t\treturn opterror(opt, \"isn't available\", flags);\n \n-\tif (!(flags & OPT_SHORT) && p->opt) {\n+\tif (!(flags & PARSE_OPT_SHORT) && p->opt) {\n \t\tswitch (opt->type) {\n \t\tcase OPTION_CALLBACK:\n \t\t\tif (!(opt->flags & PARSE_OPT_NOARG))\n@@ -99,15 +96,13 @@ static int get_value(struct optparse_t *p,\n \t\treturn 0;\n \n \tcase OPTION_CALLBACK:\n-\t\tif (unset)\n-\t\t\treturn (*opt->callback)(opt, NULL, 1);\n-\t\tif (opt->flags & PARSE_OPT_NOARG)\n-\t\t\treturn (*opt->callback)(opt, NULL, 0);\n+\t\tif (unset || (opt->flags & PARSE_OPT_NOARG))\n+\t\t\treturn (*opt->callback)(opt, NULL, flags);\n \t\tif (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-'))\n-\t\t\treturn (*opt->callback)(opt, NULL, 0);\n+\t\t\treturn (*opt->callback)(opt, NULL, flags);\n \t\tif (!arg)\n \t\t\treturn opterror(opt, \"requires a value\", flags);\n-\t\treturn (*opt->callback)(opt, get_arg(p), 0);\n+\t\treturn (*opt->callback)(opt, get_arg(p), flags);\n \n \tcase OPTION_INTEGER:\n \t\tif (unset) {\n@@ -135,7 +130,7 @@ static int parse_short_opt(struct optparse_t *p, const struct option *options)\n \tfor (; options->type != OPTION_END; options++) {\n \t\tif (options->short_name == *p->opt) {\n \t\t\tp->opt = p->opt[1] ? p->opt + 1 : NULL;\n-\t\t\treturn get_value(p, options, OPT_SHORT);\n+\t\t\treturn get_value(p, options, PARSE_OPT_SHORT);\n \t\t}\n \t}\n \treturn error(\"unknown switch `%c'\", *p->opt);\n@@ -173,7 +168,7 @@ is_abbreviated:\n \t\t\t\t\tambiguous_option = abbrev_option;\n \t\t\t\t\tambiguous_flags = abbrev_flags;\n \t\t\t\t}\n-\t\t\t\tif (!(flags & OPT_UNSET) && *arg_end)\n+\t\t\t\tif (!(flags & PARSE_OPT_UNSET) && *arg_end)\n \t\t\t\t\tp->opt = arg_end + 1;\n \t\t\t\tabbrev_option = options;\n \t\t\t\tabbrev_flags = flags;\n@@ -181,13 +176,13 @@ is_abbreviated:\n \t\t\t}\n \t\t\t/* negated and abbreviated very much? */\n \t\t\tif (!prefixcmp(\"no-\", arg)) {\n-\t\t\t\tflags |= OPT_UNSET;\n+\t\t\t\tflags |= PARSE_OPT_UNSET;\n \t\t\t\tgoto is_abbreviated;\n \t\t\t}\n \t\t\t/* negated? */\n \t\t\tif (strncmp(arg, \"no-\", 3))\n \t\t\t\tcontinue;\n-\t\t\tflags |= OPT_UNSET;\n+\t\t\tflags |= PARSE_OPT_UNSET;\n \t\t\trest = skip_prefix(arg + 3, options->long_name);\n \t\t\t/* abbreviated and negated? */\n \t\t\tif (!rest && !prefixcmp(options->long_name, arg + 3))\n@@ -207,9 +202,9 @@ is_abbreviated:\n \t\treturn error(\"Ambiguous option: %s \"\n \t\t\t\"(could be --%s%s or --%s%s)\",\n \t\t\targ,\n-\t\t\t(ambiguous_flags & OPT_UNSET) ?  \"no-\" : \"\",\n+\t\t\t(ambiguous_flags & PARSE_OPT_UNSET) ?  \"no-\" : \"\",\n \t\t\tambiguous_option->long_name,\n-\t\t\t(abbrev_flags & OPT_UNSET) ?  \"no-\" : \"\",\n+\t\t\t(abbrev_flags & PARSE_OPT_UNSET) ?  \"no-\" : \"\",\n \t\t\tabbrev_option->long_name);\n \tif (abbrev_option)\n \t\treturn get_value(p, abbrev_option, abbrev_flags);\n@@ -351,12 +346,12 @@ void usage_with_options(const char * const *usagestr,\n /*----- some often used options -----*/\n #include \"cache.h\"\n \n-int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset)\n+int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int flags)\n {\n \tint v;\n \n \tif (!arg) {\n-\t\tv = unset ? 0 : DEFAULT_ABBREV;\n+\t\tv = flags & PARSE_OPT_UNSET ? 0 : DEFAULT_ABBREV;\n \t} else {\n \t\tv = strtol(arg, (char **)&arg, 10);\n \t\tif (*arg)\ndiff --git a/parse-options.h b/parse-options.h\nindex 102ac31..ae6b3ca 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -27,8 +27,13 @@ enum parse_opt_option_flags {\n \tPARSE_OPT_HIDDEN  = 8,\n };\n \n+enum parse_opt_cbflags {\n+\tPARSE_OPT_SHORT   = 1,\n+\tPARSE_OPT_UNSET   = 2,\n+};\n+\n struct option;\n-typedef int parse_opt_cb(const struct option *, const char *arg, int unset);\n+typedef int parse_opt_cb(const struct option *, const char *arg, int flags);\n \n /*\n  * `type`::\n-- \n1.5.4.rc0.1148.ga3ab1-dirty\n"},{"id":"63451","messageId":"1197915797-30679-3-git-send-email-madcoder@debian.org","threadId":"11326","inReplyTo":"1197915797-30679-2-git-send-email-madcoder@debian.org","subject":"[PATCH 2/7] parse-options: allow callbacks to ignore arguments they don't need to use.","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2007-12-17T18:23:12Z","receivedAt":"2007-12-17T18:23:12Z","isPatch":true,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"Signed-off-by: Pierre Habouzit <madcoder@debian.org>\n---\n parse-options.c |   22 +++++++++++++++-------\n parse-options.h |    7 +++++++\n 2 files changed, 22 insertions(+), 7 deletions(-)\n\ndiff --git a/parse-options.c b/parse-options.c\nindex 8f70e5d..d716ccc 100644\n--- a/parse-options.c\n+++ b/parse-options.c\n@@ -38,7 +38,10 @@ static int get_value(struct optparse_t *p,\n {\n \tconst char *s, *arg;\n \tconst int unset = flags & PARSE_OPT_UNSET;\n+\tint may_ign = 0, res;\n \n+\tif (!unset && !p->opt && (opt->flags & PARSE_OPT_OPTARG))\n+\t\tmay_ign = PARSE_OPT_MAY_IGN;\n \tif (unset && p->opt)\n \t\treturn opterror(opt, \"takes no value\", flags);\n \tif (unset && (opt->flags & PARSE_OPT_NONEG))\n@@ -86,7 +89,7 @@ static int get_value(struct optparse_t *p,\n \t\t\t*(const char **)opt->value = NULL;\n \t\t\treturn 0;\n \t\t}\n-\t\tif (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-')) {\n+\t\tif (may_ign && (!arg || *arg == '-')) {\n \t\t\t*(const char **)opt->value = (const char *)opt->defval;\n \t\t\treturn 0;\n \t\t}\n@@ -98,18 +101,23 @@ static int get_value(struct optparse_t *p,\n \tcase OPTION_CALLBACK:\n \t\tif (unset || (opt->flags & PARSE_OPT_NOARG))\n \t\t\treturn (*opt->callback)(opt, NULL, flags);\n-\t\tif (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-'))\n-\t\t\treturn (*opt->callback)(opt, NULL, flags);\n-\t\tif (!arg)\n+\t\tif (!may_ign && !arg)\n \t\t\treturn opterror(opt, \"requires a value\", flags);\n-\t\treturn (*opt->callback)(opt, get_arg(p), flags);\n+\t\tif (may_ign && arg && arg[0] == '-' && arg[1])\n+\t\t\treturn (*opt->callback)(opt, NULL, flags);\n+\t\tres = (*opt->callback)(opt, arg, flags);\n+\t\tif (!may_ign && res == PARSE_OPT_IGNORE)\n+\t\t\tdie(\"should not happen: MAY_IGN unset, but arg was IGNOREd\");\n+\t\tif (res == PARSE_OPT_IGNORE)\n+\t\t\tget_arg(p);\n+\t\treturn res;\n \n \tcase OPTION_INTEGER:\n \t\tif (unset) {\n \t\t\t*(int *)opt->value = 0;\n \t\t\treturn 0;\n \t\t}\n-\t\tif (opt->flags & PARSE_OPT_OPTARG && (!arg || !isdigit(*arg))) {\n+\t\tif (may_ign && (!arg || !isdigit(*arg))) {\n \t\t\t*(int *)opt->value = opt->defval;\n \t\t\treturn 0;\n \t\t}\n@@ -251,7 +259,7 @@ int parse_options(int argc, const char **argv, const struct option *options,\n \t\t\tusage_with_options_internal(usagestr, options, 1);\n \t\tif (!strcmp(arg + 2, \"help\"))\n \t\t\tusage_with_options(usagestr, options);\n-\t\tif (parse_long_opt(&args, arg + 2, options))\n+\t\tif (parse_long_opt(&args, arg + 2, options) < 0)\n \t\t\tusage_with_options(usagestr, options);\n \t}\n \ndiff --git a/parse-options.h b/parse-options.h\nindex ae6b3ca..eeb40a4 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -27,9 +27,16 @@ enum parse_opt_option_flags {\n \tPARSE_OPT_HIDDEN  = 8,\n };\n \n+enum parse_opt_cbres {\n+\tPARSE_OPT_ERR     = -1,\n+\tPARSE_OPT_OK      =  0,\n+\tPARSE_OPT_IGNORE  =  1,\n+};\n+\n enum parse_opt_cbflags {\n \tPARSE_OPT_SHORT   = 1,\n \tPARSE_OPT_UNSET   = 2,\n+\tPARSE_OPT_MAY_IGN = 4,\n };\n \n struct option;\n-- \n1.5.4.rc0.1148.ga3ab1-dirty\n"},{"id":"63456","messageId":"1197915797-30679-4-git-send-email-madcoder@debian.org","threadId":"11326","inReplyTo":"1197915797-30679-3-git-send-email-madcoder@debian.org","subject":"[PATCH 3/7] parse-options: Let the integer/string cases be callbacks as well.","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2007-12-17T18:23:13Z","receivedAt":"2007-12-17T18:23:13Z","isPatch":true,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"Signed-off-by: Pierre Habouzit <madcoder@debian.org>\n---\n parse-options.c |  115 ++++++++++++++++++++++++++++---------------------------\n 1 files changed, 59 insertions(+), 56 deletions(-)\n\ndiff --git a/parse-options.c b/parse-options.c\nindex d716ccc..f3f0f2a 100644\n--- a/parse-options.c\n+++ b/parse-options.c\n@@ -7,15 +7,14 @@ struct optparse_t {\n \tconst char *opt;\n };\n \n-static inline const char *get_arg(struct optparse_t *p)\n+static inline void use_arg(struct optparse_t *p)\n {\n \tif (p->opt) {\n-\t\tconst char *res = p->opt;\n \t\tp->opt = NULL;\n-\t\treturn res;\n+\t} else {\n+\t\tp->argc--;\n+\t\t++p->argv;\n \t}\n-\tp->argc--;\n-\treturn *++p->argv;\n }\n \n static inline const char *skip_prefix(const char *str, const char *prefix)\n@@ -33,15 +32,62 @@ static int opterror(const struct option *opt, const char *reason, int flags)\n \treturn error(\"option `%s' %s\", opt->long_name, reason);\n }\n \n+static int parse_opt_string(const struct option *opt,\n+                            const char *arg, int flags)\n+{\n+\t*(const char **)opt->value = flags & PARSE_OPT_UNSET ? NULL : arg;\n+\treturn 0;\n+}\n+\n+static int parse_opt_integer(const struct option *opt,\n+                             const char *arg, int flags)\n+{\n+\tint v = flags & PARSE_OPT_UNSET ? 0 : opt->defval;\n+\tif (arg) {\n+\t\tv = strtol(arg, (char **)&arg, 10);\n+\t\tif (*arg) {\n+\t\t\tif (flags & PARSE_OPT_MAY_IGN) {\n+\t\t\t\t*(int *)opt->value = opt->defval;\n+\t\t\t\treturn PARSE_OPT_IGNORE;\n+\t\t\t}\n+\t\t\treturn opterror(opt, \"expects a numerical value\", 0);\n+\t\t}\n+\t}\n+\t*(int *)opt->value = v;\n+\treturn 0;\n+}\n+\n+static int run_callback(struct optparse_t *p, parse_opt_cb *cb,\n+\t\t\t\t\t\tconst struct option *opt, int flags)\n+{\n+\tconst char *arg = p->opt ? p->opt : (p->argc > 1 ? p->argv[1] : NULL);\n+\tint may_ign = 0;\n+\n+\tif (!p->opt && (opt->flags & PARSE_OPT_OPTARG))\n+\t\tmay_ign = PARSE_OPT_MAY_IGN;\n+\tif ((flags & PARSE_OPT_UNSET) || (opt->flags & PARSE_OPT_NOARG))\n+\t\treturn (*cb)(opt, NULL, flags);\n+\tif (!may_ign && !arg)\n+\t\treturn opterror(opt, \"requires a value\", flags);\n+\tif (may_ign && arg && arg[0] == '-' && arg[1])\n+\t\treturn (*cb)(opt, NULL, flags);\n+\tswitch ((*cb)(opt, arg, flags | may_ign)) {\n+\tcase PARSE_OPT_OK:\n+\t\tuse_arg(p);\n+\t\treturn PARSE_OPT_OK;\n+\tcase PARSE_OPT_IGNORE:\n+\t\tif (!may_ign)\n+\t\t\tdie(\"should not happen: MAY_IGN unset, but arg was IGNOREd\");\n+\t\treturn PARSE_OPT_IGNORE;\n+\tdefault:\n+\t\treturn PARSE_OPT_ERR;\n+\t}\n+}\n+\n static int get_value(struct optparse_t *p,\n                      const struct option *opt, int flags)\n {\n-\tconst char *s, *arg;\n \tconst int unset = flags & PARSE_OPT_UNSET;\n-\tint may_ign = 0, res;\n-\n-\tif (!unset && !p->opt && (opt->flags & PARSE_OPT_OPTARG))\n-\t\tmay_ign = PARSE_OPT_MAY_IGN;\n \tif (unset && p->opt)\n \t\treturn opterror(opt, \"takes no value\", flags);\n \tif (unset && (opt->flags & PARSE_OPT_NONEG))\n@@ -63,7 +109,6 @@ static int get_value(struct optparse_t *p,\n \t\t}\n \t}\n \n-\targ = p->opt ? p->opt : (p->argc > 1 ? p->argv[1] : NULL);\n \tswitch (opt->type) {\n \tcase OPTION_BIT:\n \t\tif (unset)\n@@ -71,63 +116,21 @@ static int get_value(struct optparse_t *p,\n \t\telse\n \t\t\t*(int *)opt->value |= opt->defval;\n \t\treturn 0;\n-\n \tcase OPTION_BOOLEAN:\n \t\t*(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;\n \t\treturn 0;\n-\n \tcase OPTION_SET_INT:\n \t\t*(int *)opt->value = unset ? 0 : opt->defval;\n \t\treturn 0;\n-\n \tcase OPTION_SET_PTR:\n \t\t*(void **)opt->value = unset ? NULL : (void *)opt->defval;\n \t\treturn 0;\n-\n \tcase OPTION_STRING:\n-\t\tif (unset) {\n-\t\t\t*(const char **)opt->value = NULL;\n-\t\t\treturn 0;\n-\t\t}\n-\t\tif (may_ign && (!arg || *arg == '-')) {\n-\t\t\t*(const char **)opt->value = (const char *)opt->defval;\n-\t\t\treturn 0;\n-\t\t}\n-\t\tif (!arg)\n-\t\t\treturn opterror(opt, \"requires a value\", flags);\n-\t\t*(const char **)opt->value = get_arg(p);\n-\t\treturn 0;\n-\n+\t\treturn run_callback(p, &parse_opt_string, opt, flags);\n \tcase OPTION_CALLBACK:\n-\t\tif (unset || (opt->flags & PARSE_OPT_NOARG))\n-\t\t\treturn (*opt->callback)(opt, NULL, flags);\n-\t\tif (!may_ign && !arg)\n-\t\t\treturn opterror(opt, \"requires a value\", flags);\n-\t\tif (may_ign && arg && arg[0] == '-' && arg[1])\n-\t\t\treturn (*opt->callback)(opt, NULL, flags);\n-\t\tres = (*opt->callback)(opt, arg, flags);\n-\t\tif (!may_ign && res == PARSE_OPT_IGNORE)\n-\t\t\tdie(\"should not happen: MAY_IGN unset, but arg was IGNOREd\");\n-\t\tif (res == PARSE_OPT_IGNORE)\n-\t\t\tget_arg(p);\n-\t\treturn res;\n-\n+\t\treturn run_callback(p, opt->callback, opt, flags);\n \tcase OPTION_INTEGER:\n-\t\tif (unset) {\n-\t\t\t*(int *)opt->value = 0;\n-\t\t\treturn 0;\n-\t\t}\n-\t\tif (may_ign && (!arg || !isdigit(*arg))) {\n-\t\t\t*(int *)opt->value = opt->defval;\n-\t\t\treturn 0;\n-\t\t}\n-\t\tif (!arg)\n-\t\t\treturn opterror(opt, \"requires a value\", flags);\n-\t\t*(int *)opt->value = strtol(get_arg(p), (char **)&s, 10);\n-\t\tif (*s)\n-\t\t\treturn opterror(opt, \"expects a numerical value\", flags);\n-\t\treturn 0;\n-\n+\t\treturn run_callback(p, &parse_opt_integer, opt, flags);\n \tdefault:\n \t\tdie(\"should not happen, someone must be hit on the forehead\");\n \t}\n-- \n1.5.4.rc0.1148.ga3ab1-dirty\n"},{"id":"63449","messageId":"1197915797-30679-5-git-send-email-madcoder@debian.org","threadId":"11326","inReplyTo":"1197915797-30679-4-git-send-email-madcoder@debian.org","subject":"[PATCH 4/7] parse-options: let OPT__ABBREV ignore arguments.","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2007-12-17T18:23:14Z","receivedAt":"2007-12-17T18:23:14Z","isPatch":true,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"Signed-off-by: Pierre Habouzit <madcoder@debian.org>\n---\n parse-options.c |   16 +++++++++-------\n 1 files changed, 9 insertions(+), 7 deletions(-)\n\ndiff --git a/parse-options.c b/parse-options.c\nindex f3f0f2a..679a963 100644\n--- a/parse-options.c\n+++ b/parse-options.c\n@@ -359,19 +359,21 @@ void usage_with_options(const char * const *usagestr,\n \n int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int flags)\n {\n-\tint v;\n-\n-\tif (!arg) {\n-\t\tv = flags & PARSE_OPT_UNSET ? 0 : DEFAULT_ABBREV;\n-\t} else {\n+\tint v = flags & PARSE_OPT_UNSET ? 0 : DEFAULT_ABBREV;\n+\tif (arg) {\n \t\tv = strtol(arg, (char **)&arg, 10);\n-\t\tif (*arg)\n+\t\tif (*arg) {\n+\t\t\tif (flags & PARSE_OPT_MAY_IGN) {\n+\t\t\t\t*(int *)opt->value = DEFAULT_ABBREV;\n+\t\t\t\treturn PARSE_OPT_IGNORE;\n+\t\t\t}\n \t\t\treturn opterror(opt, \"expects a numerical value\", 0);\n+\t\t}\n \t\tif (v && v < MINIMUM_ABBREV)\n \t\t\tv = MINIMUM_ABBREV;\n \t\telse if (v > 40)\n \t\t\tv = 40;\n \t}\n-\t*(int *)(opt->value) = v;\n+\t*(int *)opt->value = v;\n \treturn 0;\n }\n-- \n1.5.4.rc0.1148.ga3ab1-dirty\n"},{"id":"63453","messageId":"1197915797-30679-6-git-send-email-madcoder@debian.org","threadId":"11326","inReplyTo":"1197915797-30679-5-git-send-email-madcoder@debian.org","subject":"[PATCH 5/7] parse-options: Add a gitcli(5) man page.","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2007-12-17T18:23:15Z","receivedAt":"2007-12-17T18:23:15Z","isPatch":true,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"This page should hold every information about the git ways to parse command\nlines, and best practices to be used for scripting.\n\nSigned-off-by: Pierre Habouzit <madcoder@debian.org>\n---\n Documentation/Makefile   |    2 +-\n Documentation/gitcli.txt |  113 ++++++++++++++++++++++++++++++++++++++++++++++\n Makefile                 |    1 +\n 3 files changed, 115 insertions(+), 1 deletions(-)\n create mode 100644 Documentation/gitcli.txt\n\ndiff --git a/Documentation/Makefile b/Documentation/Makefile\nindex 76df06c..c4486d3 100644\n--- a/Documentation/Makefile\n+++ b/Documentation/Makefile\n@@ -2,7 +2,7 @@ MAN1_TXT= \\\n \t$(filter-out $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \\\n \t\t$(wildcard git-*.txt)) \\\n \tgitk.txt\n-MAN5_TXT=gitattributes.txt gitignore.txt gitmodules.txt\n+MAN5_TXT=gitattributes.txt gitignore.txt gitcli.txt gitmodules.txt\n MAN7_TXT=git.txt\n \n MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)\ndiff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt\nnew file mode 100644\nindex 0000000..b7dcf9c\n--- /dev/null\n+++ b/Documentation/gitcli.txt\n@@ -0,0 +1,113 @@\n+gitcli(5)\n+=========\n+\n+NAME\n+----\n+gitcli - git command line interface and conventions\n+\n+SYNOPSIS\n+--------\n+gitcli\n+\n+\n+DESCRIPTION\n+-----------\n+\n+This manual describes best practice in how to use git CLI.  Here are\n+the rules that you should follow when you are scripting git:\n+\n+ * it's preferred to use the non dashed form of git commands, which means that\n+   you should prefer `\"git foo\"` to `\"git-foo\"`.\n+\n+ * splitting short options to separate words (prefer `\"git foo -a -b\"`\n+   to `\"git foo -ab\"`, the latter may not even work).\n+\n+ * when a command line option takes an argument, use the 'sticked' form.  In\n+   other words, write `\"git foo -oArg\"` instead of `\"git foo -o Arg\"` for short\n+   options, and `\"git foo --long-opt=Arg\"` instead of `\"git foo --long-opt Arg\"`\n+   for long options.  An option that takes optional option-argument must be\n+   written in the 'sticked' form.\n+\n+ * when you give a revision parameter to a command, make sure the parameter is\n+   not ambiguous with a name of a file in the work tree.  E.g. do not write\n+   `\"git log -1 HEAD\"` but write `\"git log -1 HEAD --\"`; the former will not work\n+   if you happen to have a file called `HEAD` in the work tree.\n+\n+\n+ENHANCED CLI\n+------------\n+From the git 1.5.4 series and further, many git commands (not all of them at the\n+time of the writing though) come with an enhanced option parser.\n+\n+Here is an exhaustive list of the facilities provided by this option parser.\n+\n+\n+Magic Options\n+~~~~~~~~~~~~~\n+Commands which have the enhanced option parser activated all understand a\n+couple of magic command line options:\n+\n+-h::\n+\tgives a pretty printed usage of the command.\n++\n+---------------------------------------------\n+$ git describe -h\n+usage: git-describe [options] <committish>*\n+\n+    --contains            find the tag that comes after the commit\n+    --debug               debug search strategy on stderr\n+    --all                 use any ref in .git/refs\n+    --tags                use any tag in .git/refs/tags\n+    --abbrev [<n>]        use <n> digits to display SHA-1s\n+    --candidates <n>      consider <n> most recent tags (default: 10)\n+---------------------------------------------\n+\n+--help-all::\n+\tSome git commands take options that are only used for plumbing or that\n+\tare deprecated, and such options are hidden from the default usage. This\n+\toption gives the full list of options.\n+\n+\n+Negating options\n+~~~~~~~~~~~~~~~~\n+Options with long option names can be negated by prefixing `\"--no-\"`. For\n+example, `\"git branch\"` has the option `\"--track\"` which is 'on' by default. You\n+can use `\"--no-track\"` to override that behaviour. The same goes for `\"--color\"`\n+and `\"--no-color\"`.\n+\n+\n+Aggregating short options\n+~~~~~~~~~~~~~~~~~~~~~~~~~\n+Commands that support the enhanced option parser allow you to aggregate short\n+options. This means that you can for example use `\"git rm -rf\"` or\n+`\"git clean -fdx\"`.\n+\n+\n+Separating argument from the option\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+You can write the mandatory option parameter to an option as a separate\n+word on the command line.  That means that all the following uses work:\n+\n+----------------------------\n+$ git foo --long-opt=Arg\n+$ git foo --long-opt Arg\n+$ git foo -oArg\n+$ git foo -o Arg\n+----------------------------\n+\n+However, this is *NOT* allowed for switches with an optionnal value, where the\n+'sticked' form must be used:\n+----------------------------\n+$ git describe --abbrev HEAD     # correct\n+$ git describe --abbrev=10 HEAD  # correct\n+$ git describe --abbrev 10 HEAD  # NOT WHAT YOU MEANT\n+----------------------------\n+\n+\n+Documentation\n+-------------\n+Documentation by Pierre Habouzit.\n+\n+GIT\n+---\n+Part of the gitlink:git[7] suite\ndiff --git a/Makefile b/Makefile\nindex 7776077..eda7b1a 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1172,6 +1172,7 @@ check-docs::\n \t\tdocumented,gitattributes | \\\n \t\tdocumented,gitignore | \\\n \t\tdocumented,gitmodules | \\\n+\t\tdocumented,gitcli | \\\n \t\tdocumented,git-tools | \\\n \t\tsentinel,not,matching,is,ok ) continue ;; \\\n \t\tesac; \\\n-- \n1.5.4.rc0.1148.ga3ab1-dirty\n"},{"id":"63454","messageId":"1197915797-30679-7-git-send-email-madcoder@debian.org","threadId":"11326","inReplyTo":"1197915797-30679-6-git-send-email-madcoder@debian.org","subject":"[PATCH 6/7] parse-options: have a `use default value` wildcard.","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2007-12-17T18:23:16Z","receivedAt":"2007-12-17T18:23:16Z","isPatch":true,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"Signed-off-by: Pierre Habouzit <madcoder@debian.org>\n---\n Documentation/gitcli.txt |   20 +++++++++++++++-----\n parse-options.c          |   10 ++++++++--\n 2 files changed, 23 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt\nindex b7dcf9c..a304072 100644\n--- a/Documentation/gitcli.txt\n+++ b/Documentation/gitcli.txt\n@@ -95,14 +95,24 @@ $ git foo -oArg\n $ git foo -o Arg\n ----------------------------\n \n-However, this is *NOT* allowed for switches with an optionnal value, where the\n-'sticked' form must be used:\n+However, this may become ambiguous for switches with an optional value. The\n+enhanced option parser provides a placeholder `{}` that tells to the option\n+parser that it should not try to find an argument to this switch.  Though if\n+you use '{}' sticked to the option, `{}` is passed as the value.\n ----------------------------\n-$ git describe --abbrev HEAD     # correct\n-$ git describe --abbrev=10 HEAD  # correct\n-$ git describe --abbrev 10 HEAD  # NOT WHAT YOU MEANT\n+# all the following uses work\n+$ git describe --abbrev HEAD\n+$ git describe --abbrev {} HEAD\n+$ git describe --abbrev=10 HEAD\n+$ git describe --abbrev 10 HEAD\n+\n+# doesn't work\n+$ git describe --abbrev={} HEAD\n ----------------------------\n \n+Note that an optional switch will never try to use the next token as an\n+argument if it starts with a dash and is not `-`.\n+\n \n Documentation\n -------------\ndiff --git a/parse-options.c b/parse-options.c\nindex 679a963..8734bb1 100644\n--- a/parse-options.c\n+++ b/parse-options.c\n@@ -69,8 +69,14 @@ static int run_callback(struct optparse_t *p, parse_opt_cb *cb,\n \t\treturn (*cb)(opt, NULL, flags);\n \tif (!may_ign && !arg)\n \t\treturn opterror(opt, \"requires a value\", flags);\n-\tif (may_ign && arg && arg[0] == '-' && arg[1])\n-\t\treturn (*cb)(opt, NULL, flags);\n+\tif (may_ign && arg) {\n+\t\tif (arg[0] == '-' && arg[1])\n+\t\t\treturn (*cb)(opt, NULL, flags);\n+\t\tif (!strcmp(arg, \"{}\")) {\n+\t\t\tuse_arg(p);\n+\t\t\treturn (*cb)(opt, NULL, flags);\n+\t\t}\n+\t}\n \tswitch ((*cb)(opt, arg, flags | may_ign)) {\n \tcase PARSE_OPT_OK:\n \t\tuse_arg(p);\n-- \n1.5.4.rc0.1148.ga3ab1-dirty\n"},{"id":"63455","messageId":"1197915797-30679-8-git-send-email-madcoder@debian.org","threadId":"11326","inReplyTo":"1197915797-30679-7-git-send-email-madcoder@debian.org","subject":"[PATCH 7/7] git-tag: fix -l switch handling regression.","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2007-12-17T18:23:17Z","receivedAt":"2007-12-17T18:23:17Z","isPatch":true,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"Signed-off-by: Pierre Habouzit <madcoder@debian.org>\n---\n builtin-tag.c |   12 +++++-------\n 1 files changed, 5 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin-tag.c b/builtin-tag.c\nindex fd44b2e..c7a1563 100644\n--- a/builtin-tag.c\n+++ b/builtin-tag.c\n@@ -16,7 +16,7 @@\n static const char * const git_tag_usage[] = {\n \t\"git-tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]\",\n \t\"git-tag -d <tagname>...\",\n-\t\"git-tag [-n [<num>]] -l [<pattern>]\",\n+\t\"git-tag -l [-n [<num>]] [<pattern>]\",\n \t\"git-tag -v <tagname>...\",\n \tNULL\n };\n@@ -370,13 +370,11 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \tstruct ref_lock *lock;\n \n \tint annotate = 0, sign = 0, force = 0, lines = 0,\n-\t\t\t\t\tdelete = 0, verify = 0;\n-\tchar *list = NULL, *msgfile = NULL, *keyid = NULL;\n-\tconst char *no_pattern = \"NO_PATTERN\";\n+\t\tlist = 0, delete = 0, verify = 0;\n+\tchar *msgfile = NULL, *keyid = NULL;\n \tstruct msg_arg msg = { 0, STRBUF_INIT };\n \tstruct option options[] = {\n-\t\t{ OPTION_STRING, 'l', NULL, &list, \"pattern\", \"list tag names\",\n-\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t) no_pattern },\n+\t\tOPT_INTEGER('l', NULL, &list, \"list tag names\"),\n \t\t{ OPTION_INTEGER, 'n', NULL, &lines, NULL,\n \t\t\t\t\"print n lines of each tag message\",\n \t\t\t\tPARSE_OPT_OPTARG, NULL, 1 },\n@@ -408,7 +406,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tannotate = 1;\n \n \tif (list)\n-\t\treturn list_tags(list == no_pattern ? NULL : list, lines);\n+\t\treturn list_tags(argv[0], lines);\n \tif (delete)\n \t\treturn for_each_tag_name(argv, delete_tag);\n \tif (verify)\n-- \n1.5.4.rc0.1148.ga3ab1-dirty\n"},{"id":"63458","messageId":"20071217185416.GD22554@artemis.madism.org","threadId":"11326","inReplyTo":"1197915797-30679-2-git-send-email-madcoder@debian.org","subject":"Re: [PATCH 1/7] parse-options: Make callbacks take flags instead of boolean `unset`","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2007-12-17T18:54:16Z","receivedAt":"2007-12-17T18:54:16Z","isPatch":true,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"And of course here is the MadBug #1, to be squashed:\n---\n builtin-rev-parse.c |    4 ++--\n 1 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin-rev-parse.c b/builtin-rev-parse.c\nindex 20d1789..3e8ee62 100644\n--- a/builtin-rev-parse.c\n+++ b/builtin-rev-parse.c\n@@ -210,10 +210,10 @@ static int try_difference(const char *arg)\n \treturn 0;\n }\n \n-static int parseopt_dump(const struct option *o, const char *arg, int unset)\n+static int parseopt_dump(const struct option *o, const char *arg, int flags)\n {\n \tstruct strbuf *parsed = o->value;\n-\tif (unset)\n+\tif (flags & PARSE_OPT_UNSET)\n \t\tstrbuf_addf(parsed, \" --no-%s\", o->long_name);\n \telse if (o->short_name)\n \t\tstrbuf_addf(parsed, \" -%c\", o->short_name);\n-- \n1.5.4.rc0.1151.g102b0\n\n"},{"id":"63459","messageId":"20071217185652.GE22554@artemis.madism.org","threadId":"11326","inReplyTo":"1197915797-30679-8-git-send-email-madcoder@debian.org","subject":"Re: [PATCH 7/7] git-tag: fix -l switch handling regression.","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2007-12-17T18:56:52Z","receivedAt":"2007-12-17T18:56:52Z","isPatch":true,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"  And I managed to resend the broken version, hurray myself.\n\n> +\t\tOPT_INTEGER('l', NULL, &list, \"list tag names\"),\n                OPT_BOOLEAN\n\n\n\nBoth these last minute fixes are applied to my public git.git.\n\nLet's now write 1000 times: I will run the test-suite before I send\npatches, I will rune the test-suite before I send patches, …\n-- \n·O·  Pierre Habouzit\n··O                                                madcoder@debian.org\nOOO                                                http://www.madism.org\n"},{"id":"63460","messageId":"20071217190328.GF22554@artemis.madism.org","threadId":"11326","inReplyTo":"20071217185652.GE22554@artemis.madism.org","subject":"Re: [PATCH 7/7] git-tag: fix -l switch handling regression.","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2007-12-17T19:03:28Z","receivedAt":"2007-12-17T19:03:28Z","isPatch":true,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"On Mon, Dec 17, 2007 at 06:56:52PM +0000, Pierre Habouzit wrote:\n>   And I managed to resend the broken version, hurray myself.\n> \n> > +\t\tOPT_INTEGER('l', NULL, &list, \"list tag names\"),\n>                 OPT_BOOLEAN\n> \n> \n> \n> Both these last minute fixes are applied to my public git.git.\n> \n> Let's now write 1000 times: I will run the test-suite before I send\n> patches, I will rune the test-suite before I send patches, …\n\n  oh and t7004 doesn't pass anymore because of the:\n\n  git -n xxx -l or git -n \"\" -l tests. If we really want to allow that\n(but it _REALLY_ feels wrong to me) we have to make '-l' a callback that\ngroks non integers as 0. Else the test also has to be fixed, I'm not\nsure what to do here.\n\n\n-- \n·O·  Pierre Habouzit\n··O                                                madcoder@debian.org\nOOO                                                http://www.madism.org\n"},{"id":"63469","messageId":"7vtzmh6pwn.fsf@gitster.siamese.dyndns.org","threadId":"11326","inReplyTo":"20071217190328.GF22554@artemis.madism.org","subject":"Re: [PATCH 7/7] git-tag: fix -l switch handling regression.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-12-17T20:13:12Z","receivedAt":"2007-12-17T20:13:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Pierre Habouzit <madcoder@debian.org> writes:\n\n> On Mon, Dec 17, 2007 at 06:56:52PM +0000, Pierre Habouzit wrote:\n>>   And I managed to resend the broken version, hurray myself.\n>> \n>> > +\t\tOPT_INTEGER('l', NULL, &list, \"list tag names\"),\n>>                 OPT_BOOLEAN\n>> \n>> \n>> \n>> Both these last minute fixes are applied to my public git.git.\n>> \n>> Let's now write 1000 times: I will run the test-suite before I send\n>> patches, I will rune the test-suite before I send patches, …\n>\n>   oh and t7004 doesn't pass anymore because of the:\n>\n>   git -n xxx -l or git -n \"\" -l tests. If we really want to allow that\n> (but it _REALLY_ feels wrong to me) we have to make '-l' a callback that\n> groks non integers as 0. Else the test also has to be fixed, I'm not\n> sure what to do here.\n\nI did not understand what \"git tag -n xxx\" was meant to do, either.\nTime to run blame and ask the responsible party?\n\nI suspect \"-n ''\" there might be meant as a way to spell the \"no\nargument here -- use our default\" instruction.  It looks slightly nicer\nthan that '{}' but not quite.\n"},{"id":"63549","messageId":"20071218020042.GA9044@blorf.net","threadId":"11326","inReplyTo":"1197915797-30679-6-git-send-email-madcoder@debian.org","subject":"Re: [PATCH 5/7] parse-options: Add a gitcli(5) man page.","fromName":"Wayne Davison","fromEmail":"wayne@opencoder.net","sentAt":"2007-12-18T02:00:42Z","receivedAt":"2007-12-18T02:00:42Z","isPatch":true,"sender":{"key":"wayne@opencoder.net","avatar":"https://gravatar.com/avatar/d55d81825271b1bfe65e57e4e04297d4119aa03c6a1c71d2ff2812b9b4be9f45?d=mp&s=160"},"body":"On Mon, Dec 17, 2007 at 07:23:15PM +0100, Pierre Habouzit wrote:\n> + * when a command line option takes an argument, use the 'sticked' form.\n\nA minor issue:  the word \"sticked\" reads very strangely to me (in this\nspot and several others in your text).  I think something like joined or\nattached (or even abutted) would be better, as seen in this altered text\nfor the spot cited above (with a few other improvements thrown in):\n\n * when a command line option takes an argument, it is best to use the\n   'joined' form.  In other words, write `\"git foo -oArg\"` instead of\n   `\"git foo -o Arg\"` for short options, and `\"git foo --long-opt=Arg\"`\n   instead of `\"git foo --long-opt Arg\"` for long options.  If an option\n   takes an optional option-argument, it MUST be written using the\n   'joined' form when providing the option-argument.\n\n..wayne..\n"}]}