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

Re: [PATCH v2 2/2] parseopt: check for duplicate long names and numerical options

From
Jeff King <peff@peff.net>
Date
Feb 28, 2026, 10:58 UTC
Message-ID
<20260228105849.GA3626520@coredump.intra.peff.net>
In-Reply-To
<6b674316-9a6e-4f57-b32c-f1824869ba7e@web.de>
On Sat, Feb 28, 2026 at 10:19:16AM +0100, René Scharfe wrote:
> Perform the slightly expensive string duplicate check only when showing
> the usage to keep the cost of normal invocations low.  t0012-help.sh
> covers it.

Nice, this seems like the perfect compromise to me. We get a runtime switch that kicks in at the moment we want, and we don't even have to pollute the world with a new switch or environment variable.

Show 12 quoted lines
> +static void parse_options_check_harder(const struct option *opts)
> +{
> +	struct strset long_names = STRSET_INIT;
> +	for (; opts->type != OPTION_END; opts++) {
> +		if (opts->long_name) {
> +			if (!strset_add(&long_names, opts->long_name))
> +				optbug(opts, "long name already used");
> +		}
> +	}
> +	BUG_if_bug("invalid 'struct option'");
> +	strset_clear(&long_names);
> +}

I confirmed on my silly pathological case that invoking rev-parse with a real option shows no slowdown, and we now pay the same 10ms cost to show "-h".

Your other email made me wonder how the sorted-array solution might perform (patch below). It shaves off 2ms of those 10. Probably not worth caring about for "-h" output (which is already spending another 5-10ms to generate the output, versus a normal parse).

-Peff
-- >8 --
diff --git a/parse-options.c b/parse-options.c
index 0214c106d4..1ea7efd5a3 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -721,17 +721,39 @@ static void parse_options_check(const struct option *opts)
 	BUG_if_bug("invalid 'struct option'");
 }
 
+static int qsort_strcmp(const void *va, const void *vb)
+{
+	const char *a = *(const char **)va;
+	const char *b = *(const char **)vb;
+	return strcmp(a, b);
+}
+
 static void parse_options_check_harder(const struct option *opts)
 {
-	struct strset long_names = STRSET_INIT;
-	for (; opts->type != OPTION_END; opts++) {
-		if (opts->long_name) {
-			if (!strset_add(&long_names, opts->long_name))
-				optbug(opts, "long name already used");
-		}
+	const struct option *p;
+	const char **long_names;
+	size_t i, len;
+
+	len = 0;
+	for (p = opts; p->type != OPTION_END; p++) {
+		if (p->long_name)
+			len++;
 	}
-	BUG_if_bug("invalid 'struct option'");
-	strset_clear(&long_names);
+
+	ALLOC_ARRAY(long_names, len);
+	i = 0;
+	for (p = opts; p->type != OPTION_END; p++) {
+		if (p->long_name)
+			long_names[i++] = p->long_name;
+	}
+
+	QSORT(long_names, len, qsort_strcmp);
+	for (i = 1; i < len; i++) {
+		if (!strcmp(long_names[i], long_names[i-1]))
+			BUG("long name %s used twice", long_names[i]);
+	}
+
+	free(long_names);
 }
 
 static int has_subcommands(const struct option *options)
Previous: René ScharfeNext: René Scharfe
Message 8 of 12 in “[Bug] duplicated long-form options go unnoticed”
  1. Junio C HamanoFeb 27, 2026
  2. 2/2 parseopt: check for duplicate long names and numerical optionsRené Scharfe, Feb 27, 2026
  3. Jeff KingFeb 27, 2026
  4. Jeff KingFeb 27, 2026
  5. Junio C HamanoFeb 27, 2026
  6. René ScharfeFeb 28, 2026
  7. 2/2 parseopt: check for duplicate long names and numerical optionsRené Scharfe, Feb 28, 2026
  8. Jeff KingFeb 28, 2026
  9. René ScharfeFeb 28, 2026
  10. Jeff KingMar 2, 2026
  11. Junio C HamanoMar 1, 2026
  12. 1/2 pack-objects: remove duplicate --stdin-packs definitionRené Scharfe, Feb 27, 2026

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.