alias: restore support for simple dotted aliases

Historically, config entries like alias.foo.bar expanded the alias "foo.bar". The subsection-based alias syntax introduced in ac1f12a9de (alias: support non-alphanumeric names via subsection syntax, 2026-02-18) broke that behavior by treating such entries as if they were subsection syntax. Restore support for the old dotted form by falling back to the full name when the final key is not "command". Add tests covering execution and help output for simple dotted aliases. Reported-by: Michael Grossfeld <michael.grossfeld@amd.com> Helped-by: Jeff King <peff@peff.net> Signed-off-by: Jonatan Holmgren <jonatan@jontes.page> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jonatan Holmgren committed Apr 24, 2026 at 18:17 UTC 21186cf9bbe5f1d8a039286027ebb4f33c7f85d1
3 files changed +34 -3
alias.c
+14 -2
@@ -34,8 +34,20 @@ static int config_alias_cb(const char *var, const char *value,
34 if (subsection && !subsection_len)
35 subsection = NULL;
36
37 - if (subsection && strcmp(key, "command"))
38 - return 0;
37 + if (subsection && strcmp(key, "command")) {
38 + /*
39 + * We have historically supported the "alias.name" form when
40 + * "name" happens to contain dots (e.g., alias.foo.bar to allow
41 + * "git foo.bar". But our parsing above would split that into
42 + * subsection "foo".
43 + *
44 + * If we do not understand the final key in a subsection-style
45 + * variable, fall back to treating it as a two-level alias.
46 + */
47 + key = var + strlen("alias.");
48 + subsection = NULL;
49 + subsection_len = 0;
50 + }
51
52 if (data->alias) {
53 int match;
help.c
+8 -1
@@ -593,14 +593,21 @@ static int git_unknown_cmd_config(const char *var, const char *value,
593 /* Also use aliases for command lookup */
594 if (!parse_config_key(var, "alias", &subsection, &subsection_len,
595 &key)) {
596 + size_t key_len = strlen(key);
597 +
598 if (subsection) {
599 /* [alias "name"] command = value */
600 if (!strcmp(key, "command"))
601 add_cmdname(&cfg->aliases, subsection,
602 subsection_len);
603 + else {
604 + key = var + strlen("alias.");
605 + key_len = strlen(key);
606 + add_cmdname(&cfg->aliases, key, key_len);
607 + }
608 } else {
609 /* alias.name = value */
603 - add_cmdname(&cfg->aliases, key, strlen(key));
610 + add_cmdname(&cfg->aliases, key, key_len);
611 }
612 }
613
t/t0014-alias.sh
+12
@@ -128,6 +128,12 @@ test_expect_success 'subsection syntax works' '
128 test_grep "ran-subsection" output
129 '
130
131 +test_expect_success 'simple dotted alias syntax still works' '
132 + test_config alias.simple.dotted "!echo ran-simple-dotted" &&
133 + git simple.dotted >output &&
134 + test_grep "ran-simple-dotted" output
135 +'
136 +
137 test_expect_success 'subsection syntax only accepts command key' '
138 test_config alias.invalid.notcommand value &&
139 test_must_fail git invalid 2>error &&
@@ -183,6 +189,12 @@ test_expect_success 'subsection aliases listed in help -a' '
189 test_grep "förgrena" output
190 '
191
192 +test_expect_success 'simple dotted aliases listed in help -a' '
193 + test_config alias.simple.listed "!echo test" &&
194 + git help -a >output &&
195 + test_grep "simple.listed" output
196 +'
197 +
198 test_expect_success 'empty subsection treated as no subsection' '
199 test_config "alias..something" "!echo foobar" &&
200 git something >actual &&