Git development
 help / color / mirror / Atom feed
From: Junio C Hamano <gitster@pobox.com>
To: RISHAV DEWAN <rishavdewan10@gmail.com>
Cc: git@vger.kernel.org, Johannes Schindelin <Johannes.Schindelin@gmx.de>
Subject: Re: [PATCH v1] config: fix case-insensitive match for old-style [section.subsection]
Date: Tue, 30 Jun 2026 13:12:48 -0700	[thread overview]
Message-ID: <xmqqldbvrdmn.fsf@gitster.g> (raw)
In-Reply-To: <20260630124850.2498-1-rishavdewan10@gmail.com> (RISHAV DEWAN's message of "Tue, 30 Jun 2026 18:18:50 +0530")

RISHAV DEWAN <rishavdewan10@gmail.com> writes:

Thanks.

Although this was originally CC'ed to me, according to "git blame",
most of the code involved in the "bug" (i.e., matches() and its
caller) seems to be work by Dscho around 2018, so I'll redirect this
review in that direction ;-0.

> When writing to an old-style "[section.subsection]" header, the
> config file parser always folds the subsection to lower case
> (get_base_var() lowercases unconditionally), while the key given on
> the command line keeps whatever case the caller typed
> (do_parse_config_key() deliberately leaves the subsection segment
> untouched). matches() compared these two forms with a plain strcmp(),
> so a caller passing an upper-cased subsection (e.g. 'git config
> section.Subsection.key value2' against a file containing
> '[section.subsection]') never matched the existing key and a
> duplicate line was appended instead of replacing the value. This was
> a known, documented limitation (see the now-removed BUGS section of
> git-config.adoc).
>
> Track whether the currently active section was parsed case-
> insensitively (old-style) or case-sensitively (new-style, quoted) in
> struct config_store_data, and have matches() use that information to
> compare the subsection segment of the key accordingly, instead of an
> unconditional case-sensitive strcmp().
>
> Signed-off-by: RISHAV DEWAN <rishavdewan10@gmail.com>
> ---
>  Documentation/git-config.adoc | 21 ---------------------
>  config.c                      | 18 +++++++++++++++++-
>  t/t1300-config.sh             |  2 --
>  3 files changed, 17 insertions(+), 24 deletions(-)
>
> diff --git a/Documentation/git-config.adoc b/Documentation/git-config.adoc
> index 57af010ade..11dfd802b4 100644
> --- a/Documentation/git-config.adoc
> +++ b/Documentation/git-config.adoc
> @@ -634,27 +634,6 @@ http.sslverify false
>  
>  include::config.adoc[]
>  
> -BUGS
> -----
> -When using the deprecated `[section.subsection]` syntax, changing a value
> -will result in adding a multi-line key instead of a change, if the subsection
> -is given with at least one uppercase character. For example when the config
> -looks like
> -
> ---------
> -  [section.subsection]
> -    key = value1
> ---------
> -
> -and running `git config section.Subsection.key value2` will result in
> -
> ---------
> -  [section.subsection]
> -    key = value1
> -    key = value2
> ---------
> -
> -
>  GIT
>  ---
>  Part of the linkgit:git[1] suite
> diff --git a/config.c b/config.c
> index 6a0de86e3a..7f086fcd75 100644
> --- a/config.c
> +++ b/config.c
> @@ -2594,6 +2594,7 @@ struct config_store_data {
>  	} *parsed;
>  	unsigned int parsed_nr, parsed_alloc, *seen, seen_nr, seen_alloc;
>  	unsigned int key_seen:1, section_seen:1, is_keys_section:1;
> +	unsigned int subsection_case_sensitive:1;
>  };
>  #define CONFIG_STORE_INIT { 0 }
>  
> @@ -2613,7 +2614,21 @@ static void config_store_data_clear(struct config_store_data *store)
>  static int matches(const char *key, const char *value,
>  		   const struct config_store_data *store)
>  {
> -	if (strcmp(key, store->key))
> +	/*
> +	 * The subsection part of "key" (key[0..store->baselen)) was parsed
> +	 * out of the config file using the case sensitivity of whichever
> +	 * section header it came from (see store_aux_event()): old-style
> +	 * "[section.subsection]" headers are folded to lower case while
> +	 * parsing, so they must be compared case-insensitively against
> +	 * store->key, which preserves whatever case the caller passed on
> +	 * the command line. New-style "[section "Subsection"]" headers keep
> +	 * their case, so they need an exact, case-sensitive comparison.
> +	 */
> +	int (*cmpfn)(const char *, const char *, size_t) =
> +		store->subsection_case_sensitive ? strncasecmp : strncmp;
> +
> +	if (cmpfn(key, store->key, store->baselen) ||
> +	    strcmp(key + store->baselen, store->key + store->baselen))
>  		return 0; /* not ours */
>  	if (store->fixed_value && value)
>  		return !strcmp(store->fixed_value, value);
> @@ -2654,6 +2669,7 @@ static int store_aux_event(enum config_event_t type, size_t begin, size_t end,
>  			!cmpfn(cs->var.buf, store->key, store->baselen);
>  		if (store->is_keys_section) {
>  			store->section_seen = 1;
> +			store->subsection_case_sensitive = cs->subsection_case_sensitive;
>  			ALLOC_GROW(store->seen, store->seen_nr + 1,
>  				   store->seen_alloc);
>  			store->seen[store->seen_nr] = store->parsed_nr;
> diff --git a/t/t1300-config.sh b/t/t1300-config.sh
> index 87ca11a127..eaa3b83990 100755
> --- a/t/t1300-config.sh
> +++ b/t/t1300-config.sh
> @@ -1499,7 +1499,6 @@ test_expect_success 'old-fashioned settings are case insensitive' '
>  	EOF
>  	q_to_tab >testConfig_expect <<-EOF &&
>  	[V.A]
> -	r = value1
>  	Qr = value2
>  	EOF
>  	git config -f testConfig_actual "V.A.r" value2 &&
> @@ -1511,7 +1510,6 @@ test_expect_success 'old-fashioned settings are case insensitive' '
>  	EOF
>  	q_to_tab >testConfig_expect <<-EOF &&
>  	[V.A]
> -	r = value1
>  	Qr = value2
>  	EOF
>  	git config -f testConfig_actual "v.A.r" value2 &&

      reply	other threads:[~2026-06-30 20:12 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-30 12:48 [PATCH v1] config: fix case-insensitive match for old-style [section.subsection] RISHAV DEWAN
2026-06-30 20:12 ` Junio C Hamano [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=xmqqldbvrdmn.fsf@gitster.g \
    --to=gitster@pobox.com \
    --cc=Johannes.Schindelin@gmx.de \
    --cc=git@vger.kernel.org \
    --cc=rishavdewan10@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox