git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v2] parse-options: don't emit "ambiguous option" for aliases

From
Duy Nguyen <pclouds@gmail.com>
Date
Apr 18, 2019, 09:29 UTC
Message-ID
<20190418092940.GA13484@ash>
In-Reply-To
<xmqq4l6wjfyw.fsf@gitster-ct.c.googlers.com>
On Thu, Apr 18, 2019 at 09:48:39AM +0900, Junio C Hamano wrote:
Show 7 quoted lines
> > But on the other hand I can see it's a bit more work to teach
> > parse-options OPT_ALIAS to say "--recursive is just an alias of
> > --recurse-submodules" and chances of --recursive-hard coming up are
> > probably very low.
> 
> The "bit more work" is something that is worth doing in this case, I
> think.

I had a look at it. Linking two options together is not exactly easy because the way we organize option data. And I also had difficulty defining the exact semantics of this OPT_ALIAS.

So an alternative is simply outsource the ambiguity decision back to git-clone. If the same situation appears again elsewhere, we'll need to sit back and fix it for real. But this way we don't potentially introduce any new traps.

parse_options_extended() also makes it easier to extend parseopt functionality without changing all call sites. One thing I have in mind, which could maybe use this interface, is to remove parse_options_{start,step,end} from public API, avoid expose the parse_options_step internal result code. The caller just need to pass a callback to handle unknown options.

-- 8< --
diff --git a/builtin/clone.c b/builtin/clone.c
index 50bde99618..041cd43ddc 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -145,6 +145,29 @@ static struct option builtin_clone_options[] = {
 	OPT_END()
 };
 
+/*
+ * Avoid ambiguation error between --recursive and --recurse-submodule
+ * because they are the same. --recurs can be expanded to any of them
+ * and it still works.
+ */
+static int is_abbrev_ambiguous(const struct option *prev,
+			       const struct option *next)
+{
+	const struct option *opts[] = { prev, next };
+	int i, found = 0;
+
+	for (i = 0; i < ARRAY_SIZE(opts); i++) {
+		if (!opts[i]->long_name)
+			continue;
+		if (!strcmp(opts[i]->long_name, "recursive"))
+			found |= 1 << 0;
+		if (!strcmp(opts[i]->long_name, "recurse-submodules"))
+			found |= 1 << 1;
+	}
+
+	return found != 3;
+}
+
 static const char *get_repo_path_1(struct strbuf *path, int *is_bundle)
 {
 	static char *suffix[] = { "/.git", "", ".git/.git", ".git" };
@@ -905,14 +928,18 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 	struct remote *remote;
 	int err = 0, complete_refs_before_fetch = 1;
 	int submodule_progress;
+	struct parseopt_options parseopts = { 0 };
 
 	struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
 
 	fetch_if_missing = 0;
 
 	packet_trace_identity("clone");
-	argc = parse_options(argc, argv, prefix, builtin_clone_options,
-			     builtin_clone_usage, 0);
+	parseopts.usagestr = builtin_clone_usage;
+	parseopts.is_abbrev_ambiguous = is_abbrev_ambiguous;
+	argc = parse_options_extended(argc, argv, prefix,
+				      builtin_clone_options,
+				      &parseopts);
 
 	if (argc > 2)
 		usage_msg_opt(_("Too many arguments."),
diff --git a/parse-options.c b/parse-options.c
index cec74522e5..c0354e5a92 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -294,7 +294,8 @@ static enum parse_opt_result parse_long_opt(
 			if (!(p->flags & PARSE_OPT_KEEP_UNKNOWN) &&
 			    !strncmp(long_name, arg, arg_end - arg)) {
 is_abbreviated:
-				if (abbrev_option) {
+				if (abbrev_option &&
+				    p->is_abbrev_ambiguous(abbrev_option, options)) {
 					/*
 					 * If this is abbreviated, it is
 					 * ambiguous. So when there is no
@@ -450,6 +451,12 @@ static void parse_options_check(const struct option *opts)
 		exit(128);
 }
 
+static int abbrev_is_always_ambiguous(const struct option *prev,
+				      const struct option *next)
+{
+	return 1;
+}
+
 void parse_options_start(struct parse_opt_ctx_t *ctx,
 			 int argc, const char **argv, const char *prefix,
 			 const struct option *options, int flags)
@@ -466,6 +473,7 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
 	ctx->prefix = prefix;
 	ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);
 	ctx->flags = flags;
+	ctx->is_abbrev_ambiguous = abbrev_is_always_ambiguous;
 	if ((flags & PARSE_OPT_KEEP_UNKNOWN) &&
 	    (flags & PARSE_OPT_STOP_AT_NON_OPTION) &&
 	    !(flags & PARSE_OPT_ONE_SHOT))
@@ -702,13 +710,17 @@ int parse_options_end(struct parse_opt_ctx_t *ctx)
 	return ctx->cpidx + ctx->argc;
 }
 
-int parse_options(int argc, const char **argv, const char *prefix,
-		  const struct option *options, const char * const usagestr[],
-		  int flags)
+int parse_options_extended(int argc, const char **argv, const char *prefix,
+			   const struct option *options,
+			   const struct parseopt_options *parseopts)
 {
 	struct parse_opt_ctx_t ctx;
+	const char * const * usagestr = parseopts->usagestr;
+	int flags = parseopts->flags;
 
 	parse_options_start(&ctx, argc, argv, prefix, options, flags);
+	if (parseopts->is_abbrev_ambiguous)
+		ctx.is_abbrev_ambiguous = parseopts->is_abbrev_ambiguous;
 	switch (parse_options_step(&ctx, options, usagestr)) {
 	case PARSE_OPT_HELP:
 	case PARSE_OPT_ERROR:
@@ -734,6 +746,17 @@ int parse_options(int argc, const char **argv, const char *prefix,
 	return parse_options_end(&ctx);
 }
 
+int parse_options(int argc, const char **argv, const char *prefix,
+		  const struct option *options, const char * const usagestr[],
+		  int flags)
+{
+	struct parseopt_options parseopts = {0};
+
+	parseopts.flags = flags;
+	parseopts.usagestr = usagestr;
+	return parse_options_extended(argc, argv, prefix, options, &parseopts);
+}
+
 static int usage_argh(const struct option *opts, FILE *outfile)
 {
 	const char *s;
diff --git a/parse-options.h b/parse-options.h
index 74cce4e7fc..31ebf4572c 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -198,6 +198,17 @@ int parse_options(int argc, const char **argv, const char *prefix,
 		  const struct option *options,
 		  const char * const usagestr[], int flags);
 
+struct parseopt_options {
+	int flags;
+	const char * const * usagestr;
+	int (*is_abbrev_ambiguous)(const struct option *prev,
+				   const struct option *next);
+};
+
+int parse_options_extended(int argc, const char **argv, const char *prefix,
+			   const struct option *options,
+			   const struct parseopt_options *parseopts);
+
 NORETURN void usage_with_options(const char * const *usagestr,
 				 const struct option *options);
 
@@ -256,6 +267,7 @@ struct parse_opt_ctx_t {
 	const char *opt;
 	int flags;
 	const char *prefix;
+	int (*is_abbrev_ambiguous)(const struct option *, const struct option *);
 };
 
 void parse_options_start(struct parse_opt_ctx_t *ctx,
-- 8< --

> 
> Thanks.
Previous: Junio C HamanoNext: Junio C Hamano
Message 30 of 52 in “Do not use abbreviated options in tests”
  1. 0/8 Do not use abbreviated options in testsJohannes Schindelin via GitGitGadget, Mar 25, 2019
  2. 1/8 tests (rebase): spell out the `--keep-empty` optionJohannes Schindelin via GitGitGadget, Mar 25, 2019
  3. 3/8 t7810: do not abbreviate `--no-exclude-standard` nor `--invert-match`Johannes Schindelin via GitGitGadget, Mar 25, 2019
  4. 5/8 tests (push): do not abbreviate the `--follow-tags` optionJohannes Schindelin via GitGitGadget, Mar 25, 2019
  5. 4/8 t5531: avoid using an abbreviated optionJohannes Schindelin via GitGitGadget, Mar 25, 2019
  6. 2/8 tests (rebase): spell out the `--force-rebase` optionJohannes Schindelin via GitGitGadget, Mar 25, 2019
  7. 8/8 tests: disallow the use of abbreviated options (by default)Johannes Schindelin via GitGitGadget, Mar 25, 2019
  8. Denton LiuMar 25, 2019
  9. Ævar Arnfjörð BjarmasonMar 25, 2019
  10. Johannes SchindelinApr 12, 2019
  11. Johannes SchindelinApr 12, 2019
  12. Ævar Arnfjörð BjarmasonMar 25, 2019
  13. Johannes SchindelinApr 12, 2019
  14. 6/8 tests (status): spell out the `--find-renames` option in fullJohannes Schindelin via GitGitGadget, Mar 25, 2019
  15. 7/8 tests (pack-objects): use the full, unabbreviated `--revs` optionJohannes Schindelin via GitGitGadget, Mar 25, 2019
  16. 0/2 allow for configuring option abbreviation + fixÆvar Arnfjörð Bjarmason, Mar 25, 2019
  17. 1/2 parse-options: allow for configuring option abbreviationÆvar Arnfjörð Bjarmason, Mar 25, 2019
  18. Eric SunshineMar 25, 2019
  19. Ævar Arnfjörð BjarmasonMar 25, 2019
  20. Duy NguyenMar 26, 2019
  21. Ævar Arnfjörð BjarmasonMar 26, 2019
  22. Duy NguyenMar 26, 2019
  23. Ævar Arnfjörð BjarmasonMar 26, 2019
  24. Junio C HamanoApr 1, 2019
  25. Johannes SchindelinApr 12, 2019
  26. 2/2 parse-options: don't emit "ambiguous option" for aliasesÆvar Arnfjörð Bjarmason, Mar 25, 2019
  27. parse-options: don't emit "ambiguous option" for aliasesÆvar Arnfjörð Bjarmason, Apr 17, 2019
  28. Duy NguyenApr 17, 2019
  29. Junio C HamanoApr 18, 2019
  30. Duy NguyenApr 18, 2019
  31. Junio C HamanoApr 19, 2019
  32. parse-options: don't emit "ambiguous option" for aliasesNguyễn Thái Ngọc Duy, Apr 22, 2019
  33. Duy NguyenApr 22, 2019
  34. parse-options: don't emit "ambiguous option" for aliasesNguyễn Thái Ngọc Duy, Apr 29, 2019
  35. Junio C HamanoMay 7, 2019
  36. Duy NguyenMay 7, 2019
  37. Junio C HamanoApr 2, 2019
  38. 0/8 Do not use abbreviated options in testsJohannes Schindelin via GitGitGadget, Apr 12, 2019
  39. 2/8 tests (rebase): spell out the `--force-rebase` optionJohannes Schindelin via GitGitGadget, Apr 12, 2019
  40. 4/8 t5531: avoid using an abbreviated optionJohannes Schindelin via GitGitGadget, Apr 12, 2019
  41. 6/8 tests (status): spell out the `--find-renames` option in fullJohannes Schindelin via GitGitGadget, Apr 12, 2019
  42. 5/8 tests (push): do not abbreviate the `--follow-tags` optionJohannes Schindelin via GitGitGadget, Apr 12, 2019
  43. 1/8 tests (rebase): spell out the `--keep-empty` optionJohannes Schindelin via GitGitGadget, Apr 12, 2019
  44. 3/8 t7810: do not abbreviate `--no-exclude-standard` nor `--invert-match`Johannes Schindelin via GitGitGadget, Apr 12, 2019
  45. 8/8 tests: disallow the use of abbreviated options (by default)Johannes Schindelin via GitGitGadget, Apr 12, 2019
  46. Junio C HamanoApr 14, 2019
  47. Junio C HamanoApr 15, 2019
  48. Johannes SchindelinApr 15, 2019
  49. Junio C HamanoApr 15, 2019
  50. Johannes SchindelinApr 17, 2019
  51. Johannes SchindelinApr 15, 2019
  52. 7/8 tests (pack-objects): use the full, unabbreviated `--revs` optionJohannes Schindelin via GitGitGadget, Apr 12, 2019

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.