Subject: Re: [PATCH 2/7] bundle-uri: create base key-value pair parsing

From: Junio C Hamano <[email-masked]>
Date: Mon, 22 Aug 2022 11:20:58 -0700

"Derrick Stolee via GitGitGadget" <[email-masked]> writes:

> diff --git a/Documentation/config.txt b/Documentation/config.txt
> index e376d547ce0..4280af6992e 100644
> --- a/Documentation/config.txt
> +++ b/Documentation/config.txt
> @@ -387,6 +387,8 @@ include::config/branch.txt[]
>  
>  include::config/browser.txt[]
>  
> +include::config/bundle.txt[]
> +

The file that records a list of bundles may borrow the format of git
config files, but will we store their contents in configuration
files in the receiving (or originating) repository?  With the
presence of fields like "bundle.version", I somehow doubt it.

Should "git config --help" list them?

> diff --git a/Documentation/config/bundle.txt b/Documentation/config/bundle.txt
> new file mode 100644
> index 00000000000..3515bfe38d1
> --- /dev/null
> +++ b/Documentation/config/bundle.txt

If the answer is "no", then this file looks out of place.

> diff --git a/bundle-uri.c b/bundle-uri.c
> index ceeef0b6641..ade7eccce39 100644
> --- a/bundle-uri.c
> +++ b/bundle-uri.c
> @@ -66,6 +66,80 @@ int for_all_bundles_in_list(struct bundle_list *list,
>  	return 0;
>  }
>  
> +/**
> + * Given a key-value pair, update the state of the given bundle list.
> + * Returns 0 if the key-value pair is understood. Returns 1 if the key
> + * is not understood or the value is malformed.

Let's stick to the "error is negative" if we do not have a strong
reason not to.

> + */
> +MAYBE_UNUSED
> +static int bundle_list_update(const char *key, const char *value,
> +			      struct bundle_list *list)
> +{
> +	const char *pkey, *dot;
> +	struct strbuf id = STRBUF_INIT;
> +	struct remote_bundle_info lookup = REMOTE_BUNDLE_INFO_INIT;
> +	struct remote_bundle_info *bundle;
> +
> +	if (!skip_prefix(key, "bundle.", &pkey))
> +		return 1;
> +	dot = strchr(pkey, '.');
> +	if (!dot) {
> +		if (!strcmp(pkey, "version")) {
> +			int version = atoi(value);

Can atoi() safely fail?  Are we happy of pkey that says "1A" and we
parse it as "1"?

> +			if (version != 1)
> +				return 1;
> +
> +			list->version = version;
> +			return 0;
> +		}

Is it OK for a bundle list described in the config-file format to
have "bundle.version" twice, giving different values?  It feels
counter-intuitive to apply the "last one wins" rule that is usual
for configuration files.

> +		if (!strcmp(pkey, "mode")) {
> +			if (!strcmp(value, "all"))
> +				list->mode = BUNDLE_MODE_ALL;
> +			else if (!strcmp(value, "any"))
> +				list->mode = BUNDLE_MODE_ANY;
> +			else
> +				return 1;
> +			return 0;
> +		}

Likewise for bundle.mode

> +		/* Ignore other unknown global keys. */
> +		return 0;
> +	}
> +
> +	strbuf_add(&id, pkey, dot - pkey);
> +	dot++;
> +
> +	/*
> +	 * Check for an existing bundle with this <id>, or create one
> +	 * if necessary.
> +	 */
> +	lookup.id = id.buf;
> +	hashmap_entry_init(&lookup.ent, strhash(lookup.id));
> +	if (!(bundle = hashmap_get_entry(&list->bundles, &lookup, ent, NULL))) {
> +		CALLOC_ARRAY(bundle, 1);
> +		bundle->id = strbuf_detach(&id, NULL);
> +		strbuf_init(&bundle->file, 0);
> +		hashmap_entry_init(&bundle->ent, strhash(bundle->id));
> +		hashmap_add(&list->bundles, &bundle->ent);
> +	}
> +	strbuf_release(&id);
> +
> +	if (!strcmp(dot, "uri")) {
> +		free(bundle->uri);
> +		bundle->uri = xstrdup(value);
> +		return 0;
> +	}

This explicitly implements "the last one wins".  Would it really
make sense for a server to serve a bundle list that says redundant
and wasteful pieces of information, i.e.

    [bundle "1"]
	url = one
	url = two

It is not like doing so would allow us to reuse an otherwise mostly
good file by appending new information and that would be a performance
or storage win.  So I am not quite sure why we want "the last one wins"
rule here.  It instead looks like something we want to sanity check
and complain about.

> +	/*
> +	 * At this point, we ignore any information that we don't
> +	 * understand, assuming it to be hints for a heuristic the client
> +	 * does not currently understand.
> +	 */

This is sensible.

> +	return 0;
> +}
> +
>  static int find_temp_filename(struct strbuf *name)
>  {
>  	int fd;