From: Jonatan Holmgren <jonatan@jontes.page>
To: git@vger.kernel.org
Cc: peff@peff.net, gitster@pobox.com,
"D . Ben Knoble" <benknoble@gmail.com>,
"brian m . carlson" <sandals@crustytoothpaste.net>,
Jonatan Holmgren <jonatan@jontes.page>
Subject: [PATCH 3/3] alias: support non-alphanumeric names via subsection syntax
Date: Tue, 10 Feb 2026 23:27:45 +0100 [thread overview]
Message-ID: <20260210222745.78575-4-jonatan@jontes.page> (raw)
In-Reply-To: <20260210222745.78575-1-jonatan@jontes.page>
Git alias names are limited to ASCII alphanumeric characters and
dashes because aliases are implemented as config variable names, which
are validated by iskeychar(). This prevents non-English speakers from
creating aliases in their native languages.
Add support for arbitrary alias names by using config subsections:
[alias "förgrena"]
command = branch
The subsection name is matched as-is (case-sensitive byte comparison),
while the existing definition without a subsection (e.g.,
"[alias] co = checkout") remains case-insensitive for backward
compatibility. This uses existing config infrastructure since
subsections already support arbitrary bytes, and avoids introducing
Unicode normalization.
Also teach the help subsystem about the new syntax so that "git help
-a" properly lists subsection aliases and the autocorrect feature can
suggest them. Use utf8_strwidth() instead of strlen() for column
alignment so that non-ASCII alias names display correctly.
Suggested-by: Jeff King <peff@peff.net>
Signed-off-by: Jonatan Holmgren <jonatan@jontes.page>
---
Documentation/config/alias.adoc | 43 ++++++++++++++++++-----
alias.c | 39 +++++++++++++++++----
help.c | 14 ++++++--
t/t0014-alias.sh | 60 +++++++++++++++++++++++++++++++++
4 files changed, 138 insertions(+), 18 deletions(-)
diff --git a/Documentation/config/alias.adoc b/Documentation/config/alias.adoc
index 80ce17d2de..9184f7cb37 100644
--- a/Documentation/config/alias.adoc
+++ b/Documentation/config/alias.adoc
@@ -1,12 +1,39 @@
alias.*::
- Command aliases for the linkgit:git[1] command wrapper - e.g.
- after defining `alias.last = cat-file commit HEAD`, the invocation
- `git last` is equivalent to `git cat-file commit HEAD`. To avoid
- confusion and troubles with script usage, aliases that
- hide existing Git commands are ignored except for deprecated
- commands. Arguments are split by
- spaces, the usual shell quoting and escaping are supported.
- A quote pair or a backslash can be used to quote them.
+alias.*.command::
+ Command aliases for the linkgit:git[1] command wrapper. Aliases
+ can be defined using two syntaxes:
++
+--
+1. Without a subsection, e.g., `[alias] co = checkout`. The alias
+ name is limited to ASCII alphanumeric characters and `-`,
+ and is matched case-insensitively.
+2. With a subsection, e.g., `[alias "name"] command = value`. The
+ alias name can contain any characters including UTF-8, and is
+ matched case-sensitively as raw bytes.
+--
++
+Examples:
++
+----
+# Without subsection (ASCII alphanumeric and dash only)
+[alias]
+ co = checkout
+ st = status
+
+# With subsection (allows any characters, including UTF-8)
+[alias "hämta"]
+ command = fetch
+[alias "gömma"]
+ command = stash
+----
++
+E.g. after defining `alias.last = cat-file commit HEAD`, the invocation
+`git last` is equivalent to `git cat-file commit HEAD`. To avoid
+confusion and troubles with script usage, aliases that
+hide existing Git commands are ignored except for deprecated
+commands. Arguments are split by
+spaces, the usual shell quoting and escaping are supported.
+A quote pair or a backslash can be used to quote them.
+
Note that the first word of an alias does not necessarily have to be a
command. It can be a command-line option that will be passed into the
diff --git a/alias.c b/alias.c
index 1f8e13610c..ae1fe1efc6 100644
--- a/alias.c
+++ b/alias.c
@@ -17,15 +17,37 @@ static int config_alias_cb(const char *key, const char *value,
const struct config_context *ctx UNUSED, void *d)
{
struct config_alias_data *data = d;
- const char *p;
+ const char *subsection, *subkey;
+ size_t subsection_len;
- if (!skip_prefix(key, "alias.", &p))
+ if (parse_config_key(key, "alias", &subsection, &subsection_len,
+ &subkey) < 0)
return 0;
+ /*
+ * Two config syntaxes:
+ * - alias.name = value (without subsection, case-insensitive)
+ * - [alias "name"]
+ * command = value (with subsection, case-sensitive)
+ */
+ if (subsection) {
+ if (strcmp(subkey, "command"))
+ return 0;
+ if (!value)
+ return config_error_nonbool(key);
+ }
+
if (data->alias) {
- if (!strcasecmp(p, data->alias)) {
- if (!value)
- return config_error_nonbool(key);
+ int match;
+
+ if (subsection)
+ match = (strlen(data->alias) == subsection_len &&
+ !strncmp(data->alias, subsection,
+ subsection_len));
+ else
+ match = !strcasecmp(data->alias, subkey);
+
+ if (match) {
FREE_AND_NULL(data->v);
return git_config_string(&data->v,
key, value);
@@ -33,10 +55,13 @@ static int config_alias_cb(const char *key, const char *value,
} else if (data->list) {
struct string_list_item *item;
- item = string_list_append(data->list, p);
+ if (subsection)
+ item = string_list_append_nodup(data->list,
+ xmemdupz(subsection, subsection_len));
+ else
+ item = string_list_append(data->list, subkey);
if (value)
item->util = xstrdup(value);
- /* if !value, item->util remains NULL but item is still added */
}
return 0;
diff --git a/help.c b/help.c
index eccd0c22f8..d7c6011780 100644
--- a/help.c
+++ b/help.c
@@ -21,6 +21,7 @@
#include "fsmonitor-ipc.h"
#include "repository.h"
#include "alias.h"
+#include "utf8.h"
#ifndef NO_CURL
#include "git-curl-compat.h" /* For LIBCURL_VERSION only */
@@ -108,7 +109,7 @@ static void print_command_list(const struct cmdname_help *cmds,
for (i = 0; cmds[i].name; i++) {
if (cmds[i].category & mask) {
- size_t len = strlen(cmds[i].name);
+ size_t len = utf8_strwidth(cmds[i].name);
printf(" %s ", cmds[i].name);
if (longest > len)
mput_char(' ', longest - len);
@@ -492,7 +493,7 @@ static void list_all_cmds_help_aliases(int longest)
string_list_sort(&alias_list);
for (i = 0; i < alias_list.nr; i++) {
- size_t len = strlen(alias_list.items[i].string);
+ size_t len = utf8_strwidth(alias_list.items[i].string);
if (longest < len)
longest = len;
}
@@ -591,8 +592,15 @@ static int git_unknown_cmd_config(const char *var, const char *value,
/* Also use aliases for command lookup */
if (!parse_config_key(var, "alias", &subsection, &subsection_len,
&key)) {
- if (!subsection)
+ if (subsection) {
+ /* [alias "name"] command = value */
+ if (!strcmp(key, "command"))
+ add_cmdname(&cfg->aliases, subsection,
+ subsection_len);
+ } else {
+ /* alias.name = value */
add_cmdname(&cfg->aliases, key, strlen(key));
+ }
}
return 0;
diff --git a/t/t0014-alias.sh b/t/t0014-alias.sh
index 07a53e7366..5d1f7730e6 100755
--- a/t/t0014-alias.sh
+++ b/t/t0014-alias.sh
@@ -112,4 +112,64 @@ test_expect_success 'cannot alias-shadow a sample of regular builtins' '
done
'
+test_expect_success 'subsection syntax works' '
+ test_config alias.testnew.command "!echo ran-subsection" &&
+ git testnew >output &&
+ test_grep "ran-subsection" output
+'
+
+test_expect_success 'subsection syntax only accepts command key' '
+ test_config alias.invalid.notcommand value &&
+ test_must_fail git invalid 2>error &&
+ test_grep -i "not a git command" error
+'
+
+test_expect_success 'subsection syntax requires value for command' '
+ test_when_finished "git config --remove-section alias.noval" &&
+ cat >>.git/config <<-\EOF &&
+ [alias "noval"]
+ command
+ EOF
+ test_must_fail git noval 2>error &&
+ test_grep "alias.noval.command" error
+'
+
+test_expect_success 'simple syntax is case-insensitive' '
+ test_config alias.LegacyCase "!echo ran-legacy" &&
+ git legacycase >output &&
+ test_grep "ran-legacy" output
+'
+
+test_expect_success 'subsection syntax is case-sensitive' '
+ test_config alias.SubCase.command "!echo ran-upper" &&
+ test_config alias.subcase.command "!echo ran-lower" &&
+ git SubCase >upper.out &&
+ git subcase >lower.out &&
+ test_grep "ran-upper" upper.out &&
+ test_grep "ran-lower" lower.out
+'
+
+test_expect_success 'UTF-8 alias with Swedish characters' '
+ test_config alias."förgrena".command "!echo ran-swedish" &&
+ git förgrena >output &&
+ test_grep "ran-swedish" output
+'
+
+test_expect_success 'UTF-8 alias with CJK characters' '
+ test_config alias."分支".command "!echo ran-cjk" &&
+ git 分支 >output &&
+ test_grep "ran-cjk" output
+'
+
+test_expect_success 'alias with spaces in name' '
+ test_config alias."test name".command "!echo ran-spaces" &&
+ git "test name" >output &&
+ test_grep "ran-spaces" output
+'
+
+test_expect_success 'subsection aliases listed in help -a' '
+ test_config alias."förgrena".command "!echo test" &&
+ git help -a | grep "förgrena"
+'
+
test_done
--
2.53.0
next prev parent reply other threads:[~2026-02-10 22:28 UTC|newest]
Thread overview: 88+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-08 15:30 [RFC] Support UTF-8 characters in Git alias names Jonatan Holmgren
2026-02-08 16:07 ` D. Ben Knoble
2026-02-08 23:21 ` brian m. carlson
2026-02-09 14:55 ` Junio C Hamano
2026-02-09 15:19 ` Jonatan Holmgren
2026-02-09 17:59 ` Junio C Hamano
2026-02-09 22:40 ` brian m. carlson
2026-02-09 23:14 ` Junio C Hamano
2026-02-10 0:45 ` Ben Knoble
2026-02-10 1:04 ` Junio C Hamano
2026-02-10 6:59 ` Jeff King
2026-02-09 7:36 ` Jeff King
2026-02-09 13:59 ` Theodore Tso
2026-02-09 22:01 ` [PATCH v1] alias: support UTF-8 characters via subsection syntax Jonatan Holmgren
2026-02-10 7:44 ` Jeff King
2026-02-10 8:30 ` Torsten Bögershausen
2026-02-10 16:35 ` Junio C Hamano
2026-02-10 18:31 ` [PATCH v2 0/2] support UTF-8 in alias names Jonatan Holmgren
2026-02-10 18:31 ` [PATCH v2 1/2] help: use list_aliases() for alias listing and lookup Jonatan Holmgren
2026-02-10 19:27 ` Junio C Hamano
2026-02-10 18:31 ` [PATCH v2 2/2] alias: support non-alphanumeric names via subsection syntax Jonatan Holmgren
2026-02-10 19:47 ` Junio C Hamano
2026-02-10 22:29 ` Jonatan Holmgren
2026-02-23 9:29 ` Kristoffer Haugsbakk
2026-02-23 16:07 ` Kristoffer Haugsbakk
2026-02-23 20:22 ` Junio C Hamano
2026-02-23 20:25 ` Kristoffer Haugsbakk
2026-02-24 10:27 ` Patrick Steinhardt
2026-02-10 22:27 ` [PATCH 0/3] support UTF-8 in alias names Jonatan Holmgren
2026-02-10 22:27 ` [PATCH 1/3] help: use list_aliases() for alias listing Jonatan Holmgren
2026-02-10 23:17 ` Junio C Hamano
2026-02-10 22:27 ` [PATCH 2/3] alias: prepare for subsection aliases Jonatan Holmgren
2026-02-10 22:27 ` Jonatan Holmgren [this message]
2026-02-11 21:18 ` [PATCH v4 0/3] support UTF-8 in alias names Jonatan Holmgren
2026-02-11 21:18 ` [PATCH v4 1/3] help: use list_aliases() for alias listing Jonatan Holmgren
2026-02-11 22:29 ` Junio C Hamano
2026-02-11 21:18 ` [PATCH v4 2/3] alias: prepare for subsection aliases Jonatan Holmgren
2026-02-11 21:53 ` Junio C Hamano
2026-02-11 21:18 ` [PATCH v4 3/3] alias: support non-alphanumeric names via subsection syntax Jonatan Holmgren
2026-02-11 22:28 ` Junio C Hamano
2026-02-12 11:16 ` Richard Kerry
2026-02-12 15:34 ` Jonatan Holmgren
2026-02-12 18:52 ` Jonatan Holmgren
2026-02-12 10:27 ` [PATCH v4 0/3] support UTF-8 in alias names Torsten Bögershausen
2026-02-12 15:35 ` Jonatan Holmgren
2026-02-16 16:15 ` [PATCH v5 0/4] support uTF-8 " Jonatan Holmgren
2026-02-16 16:15 ` [PATCH v5 1/4] help: use list_aliases() for alias listing Jonatan Holmgren
2026-02-16 16:15 ` [PATCH v5 2/4] alias: prepare for subsection aliases Jonatan Holmgren
2026-02-16 16:15 ` [PATCH v5 3/4] alias: support non-alphanumeric names via subsection syntax Jonatan Holmgren
2026-02-16 16:15 ` [PATCH v5 4/4] completion: fix zsh alias listing for subsection aliases Jonatan Holmgren
2026-02-16 18:32 ` D. Ben Knoble
2026-02-17 20:01 ` Junio C Hamano
2026-02-18 14:52 ` [PATCH v6 0/4] support UTF-8 in alias names Jonatan Holmgren
2026-02-18 14:52 ` [PATCH v6 1/4] help: use list_aliases() for alias listing Jonatan Holmgren
2026-02-18 14:52 ` [PATCH v6 2/4] alias: prepare for subsection aliases Jonatan Holmgren
2026-02-18 16:21 ` Kristoffer Haugsbakk
2026-02-18 14:52 ` [PATCH v6 3/4] alias: support non-alphanumeric names via subsection syntax Jonatan Holmgren
2026-02-18 14:52 ` [PATCH v6 4/4] completion: fix zsh alias listing for subsection aliases Jonatan Holmgren
2026-02-18 21:57 ` [PATCH v7 0/4] support UTF-8 in alias names Jonatan Holmgren
2026-02-18 21:57 ` [PATCH v7 1/4] help: use list_aliases() for alias listing Jonatan Holmgren
2026-02-24 22:19 ` Jacob Keller
2026-02-24 22:41 ` Junio C Hamano
2026-02-25 20:45 ` Junio C Hamano
2026-02-26 23:33 ` Jacob Keller
2026-02-24 22:21 ` Jacob Keller
2026-02-18 21:57 ` [PATCH v7 2/4] alias: prepare for subsection aliases Jonatan Holmgren
2026-02-18 21:57 ` [PATCH v7 3/4] alias: support non-alphanumeric names via subsection syntax Jonatan Holmgren
2026-02-24 10:55 ` Kristoffer Haugsbakk
2026-02-24 14:48 ` Jonatan Holmgren
2026-02-24 23:23 ` Kristoffer Haugsbakk
2026-02-18 21:57 ` [PATCH v7 4/4] completion: fix zsh alias listing for subsection aliases Jonatan Holmgren
2026-02-19 18:17 ` [PATCH v7 0/4] support UTF-8 in alias names Junio C Hamano
2026-02-19 18:54 ` Jonatan Holmgren
2026-02-24 17:12 ` [PATCH 0/2] Fix small issues in alias subsection handling Jonatan Holmgren
2026-02-24 17:12 ` [PATCH 1/2] doc: fix list continuation in alias subsection example Jonatan Holmgren
2026-02-24 19:11 ` Junio C Hamano
2026-02-24 19:14 ` Kristoffer Haugsbakk
2026-02-24 20:23 ` Junio C Hamano
2026-02-24 17:12 ` [PATCH 2/2] alias: treat empty subsection [alias ""] as plain [alias] Jonatan Holmgren
2026-02-26 17:00 ` [PATCH 0/2] Fix small issues in alias subsection handling Junio C Hamano
2026-02-26 20:53 ` [PATCH v2 0/3] " Jonatan Holmgren
2026-02-26 20:53 ` [PATCH v2 1/3] doc: fix list continuation in alias subsection example Jonatan Holmgren
2026-03-03 9:41 ` Kristoffer Haugsbakk
2026-03-03 15:13 ` [PATCH v2 1/3] doc: fix list continuation in alias subsection example! Jonatan Holmgren
2026-02-26 20:53 ` [PATCH v2 2/3] alias: treat empty subsection [alias ""] as plain [alias] Jonatan Holmgren
2026-02-26 20:53 ` [PATCH v2 3/3] git, help: fix memory leaks in alias listing Jonatan Holmgren
2026-02-26 21:08 ` [PATCH v2 0/3] Fix small issues in alias subsection handling Junio C Hamano
2026-03-03 15:12 ` [PATCH] doc: fix list continuation in alias.adoc Jonatan Holmgren
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=20260210222745.78575-4-jonatan@jontes.page \
--to=jonatan@jontes.page \
--cc=benknoble@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=peff@peff.net \
--cc=sandals@crustytoothpaste.net \
/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