{"thread":{"id":"66204","subject":"[GSoC Patch 0/2] add unicode support to git repo structure","startedAt":"2026-08-21T13:54:50Z","lastAt":"2026-08-23T17:22:02Z","messageCount":7,"participants":["K Jayatheerth","Junio C Hamano","Oswald Buddenhagen","Tuomas Ahola"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"551019","messageId":"20260821135410.429698-1-jayatheerthkulkarni2005@gmail.com","threadId":"66204","inReplyTo":null,"subject":"[GSoC Patch 0/2] add unicode support to git repo structure","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-21T13:53:43Z","receivedAt":"2026-08-21T13:54:50Z","isPatch":true,"body":"Currently, `git repo structure` always renders its table output using plain\nASCII characters (`|`, `-`, `*`), regardless of the user's locale. This\nseries introduces Unicode box-drawing characters (`│`, `├`, `─`, `┼`, `┤`)\nand bullet points (`•`) when a UTF-8 locale is detected, providing a cleaner\nand more visually distinct hierarchical output on modern terminals while\ngracefully falling back to the existing ASCII formatting otherwise.\n\nSummary of changes:\n\n- Patch 1 (gettext: fall back to env-derived charset when unset):\n  Fixes an issue where `is_utf8_locale()` would leave `charset` unset (NULL)\n  when `git_setup_gettext()` returns early upon missing the locale directory\n  (such as in uninstalled development builds). Because `is_encoding_utf8(NULL)`\n  defaults to 1, `is_utf8_locale()` would mistakenly report a UTF-8 locale\n  even under non-UTF-8 environments like `LC_ALL=C`. This patch enables the\n  environment-derived fallback (`LC_ALL`, `LC_CTYPE`, `LANG`) whenever `charset`\n  is unset, regardless of `NO_GETTEXT`.\n\n- Patch 2 (repo: add Unicode support for `repo structure` output):\n  Updates `builtin/repo.c` (`stats_table_setup_structure()` and\n  `stats_table_print_structure()`) to query `is_utf8_locale()` and emit\n  Unicode box-drawing borders and bullets when running under a UTF-8 locale,\n  falling back to ASCII when not in UTF-8. In `t/t1901-repo-structure.sh`,\n  tests follow the established `t/lib-git-svn.sh` convention for discovering\n  `GIT_TEST_UTF8_LOCALE` to define a `UTF8_LOCALE` test prerequisite, existing\n  tests are pinned to `LC_ALL=C` to reliably test the ASCII fallback path,\n  and new tests are added to verify the UTF-8 output rendering (including\n  when running with a missing locale directory).\n\nK Jayatheerth (2):\n  gettext: fall back to env-derived charset when unset\n  repo: add Unicode support for `repo structure` output\n\n builtin/repo.c            | 110 ++++++++++++++++-------------\n gettext.c                 |  32 +++++----\n t/t1901-repo-structure.sh | 141 +++++++++++++++++++++++++++++++++++++-\n 3 files changed, 221 insertions(+), 62 deletions(-)\n\n-- \n2.55.GIT\n"},{"id":"551020","messageId":"20260821135410.429698-2-jayatheerthkulkarni2005@gmail.com","threadId":"66204","inReplyTo":"20260821135410.429698-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch 1/2] gettext: fall back to env-derived charset when unset","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-21T13:53:44Z","receivedAt":"2026-08-21T13:55:10Z","isPatch":true,"body":"`is_utf8_locale()` relies on the static `charset` variable, which is\nnormally initialized by `init_gettext_charset()`. That initialization\nonly happens when `git_setup_gettext()` successfully locates the locale\ndirectory.\n\nWhen running directly from the source tree without `make install`, or in\nother environments where the locale directory is unavailable,\n`git_setup_gettext()` returns early, leaving `charset` unset (NULL).\nBecause `is_encoding_utf8(NULL)` defaults to 1, `is_utf8_locale()` would\nmistakenly report a UTF-8 locale even in non-UTF-8 environments (e.g.\nunder `LC_ALL=C`).\n\nThe fallback that derives the charset from `LC_ALL`, `LC_CTYPE`, or\n`LANG` was previously compiled only under `NO_GETTEXT`. That left\ngettext-enabled builds without a fallback when `charset` remains\nuninitialized.\n\nMake the fallback conditional on `charset` being unset rather than on\n`NO_GETTEXT`. This ensures `is_utf8_locale()` accurately inspects the\nenvironment-derived charset regardless of whether gettext support is\nenabled.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n gettext.c | 32 ++++++++++++++++++--------------\n 1 file changed, 18 insertions(+), 14 deletions(-)\n\ndiff --git a/gettext.c b/gettext.c\nindex 8d08a61f84..5376a0de0f 100644\n--- a/gettext.c\n+++ b/gettext.c\n@@ -141,19 +141,23 @@ int gettext_width(const char *s)\n \n int is_utf8_locale(void)\n {\n-#ifdef NO_GETTEXT\n-\tif (!charset) {\n-\t\tconst char *env = getenv(\"LC_ALL\");\n-\t\tif (!env || !*env)\n-\t\t\tenv = getenv(\"LC_CTYPE\");\n-\t\tif (!env || !*env)\n-\t\t\tenv = getenv(\"LANG\");\n-\t\tif (!env)\n-\t\t\tenv = \"\";\n-\t\tif (strchr(env, '.'))\n-\t\t\tenv = strchr(env, '.') + 1;\n-\t\tcharset = xstrdup(env);\n+\tconst char *c = charset;\n+\n+\tif (!c) {\n+\t\tstatic char fallback_charset[64];\n+\t\tif (!*fallback_charset) {\n+\t\t\tconst char *env = getenv(\"LC_ALL\");\n+\t\t\tif (!env || !*env)\n+\t\t\t\tenv = getenv(\"LC_CTYPE\");\n+\t\t\tif (!env || !*env)\n+\t\t\t\tenv = getenv(\"LANG\");\n+\t\t\tif (!env)\n+\t\t\t\tenv = \"\";\n+\t\t\tif (strchr(env, '.'))\n+\t\t\t\tenv = strchr(env, '.') + 1;\n+\t\t\tstrlcpy(fallback_charset, env, sizeof(fallback_charset));\n+\t\t}\n+\t\tc = fallback_charset;\n \t}\n-#endif\n-\treturn is_encoding_utf8(charset);\n+\treturn is_encoding_utf8(c);\n }\n-- \n2.55.GIT\n\n"},{"id":"551021","messageId":"20260821135410.429698-3-jayatheerthkulkarni2005@gmail.com","threadId":"66204","inReplyTo":"20260821135410.429698-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch 2/2] repo: add Unicode support for `repo structure` output","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-21T13:53:45Z","receivedAt":"2026-08-21T13:55:46Z","isPatch":true,"body":"ASCII output ignores locale support for UTF-8. Use box-drawing\ncharacters and bullets when the locale supports UTF-8, since modern\nterminals render tables cleanly this way, and fall back to ASCII\notherwise.\n\nTests now discover an available UTF-8 locale to set a `UTF8_LOCALE`\nprerequisite, and existing ASCII table tests are explicitly pinned\nto `LC_ALL=C` so their behavior remains deterministic regardless of the\nrunner's environment.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n builtin/repo.c            | 110 ++++++++++++++++-------------\n t/t1901-repo-structure.sh | 141 +++++++++++++++++++++++++++++++++++++-\n 2 files changed, 203 insertions(+), 48 deletions(-)\n\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 84e012f83f..2c1cca6f2e 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -498,82 +498,86 @@ static void stats_table_setup_structure(struct stats_table *table,\n \tsize_t object_count_total;\n \tsize_t disk_object_total;\n \tsize_t ref_total;\n+\tconst int utf8 = is_utf8_locale();\n+\tconst char *bullet_l0 = utf8 ? \"•\" : \"*\";\n+\tconst char *bullet_l1 = utf8 ? \"  •\" : \"  *\";\n+\tconst char *bullet_l2 = utf8 ? \"    •\" : \"    *\";\n \n \tref_total = get_total_reference_count(refs);\n-\tstats_table_addf(table, \"* %s\", _(\"References\"));\n-\tstats_table_count_addf(table, ref_total, \"  * %s\", _(\"Count\"));\n-\tstats_table_count_addf(table, refs->branches, \"    * %s\", _(\"Branches\"));\n-\tstats_table_count_addf(table, refs->tags, \"    * %s\", _(\"Tags\"));\n-\tstats_table_count_addf(table, refs->remotes, \"    * %s\", _(\"Remotes\"));\n-\tstats_table_count_addf(table, refs->others, \"    * %s\", _(\"Others\"));\n+\tstats_table_addf(table, \"%s %s\", bullet_l0, _(\"References\"));\n+\tstats_table_count_addf(table, ref_total, \"%s %s\", bullet_l1, _(\"Count\"));\n+\tstats_table_count_addf(table, refs->branches, \"%s %s\", bullet_l2, _(\"Branches\"));\n+\tstats_table_count_addf(table, refs->tags, \"%s %s\", bullet_l2, _(\"Tags\"));\n+\tstats_table_count_addf(table, refs->remotes, \"%s %s\", bullet_l2, _(\"Remotes\"));\n+\tstats_table_count_addf(table, refs->others, \"%s %s\", bullet_l2, _(\"Others\"));\n \n \tobject_count_total = get_total_object_values(&objects->type_counts);\n \tstats_table_addf(table, \"\");\n-\tstats_table_addf(table, \"* %s\", _(\"Reachable objects\"));\n-\tstats_table_count_addf(table, object_count_total, \"  * %s\", _(\"Count\"));\n+\tstats_table_addf(table, \"%s %s\", bullet_l0, _(\"Reachable objects\"));\n+\tstats_table_count_addf(table, object_count_total, \"%s %s\", bullet_l1, _(\"Count\"));\n \tstats_table_count_addf(table, objects->type_counts.commits,\n-\t\t\t       \"    * %s\", _(\"Commits\"));\n+\t\t\t       \"%s %s\", bullet_l2, _(\"Commits\"));\n \tstats_table_count_addf(table, objects->type_counts.trees,\n-\t\t\t       \"    * %s\", _(\"Trees\"));\n+\t\t\t       \"%s %s\", bullet_l2, _(\"Trees\"));\n \tstats_table_count_addf(table, objects->type_counts.blobs,\n-\t\t\t       \"    * %s\", _(\"Blobs\"));\n+\t\t\t       \"%s %s\", bullet_l2, _(\"Blobs\"));\n \tstats_table_count_addf(table, objects->type_counts.tags,\n-\t\t\t       \"    * %s\", _(\"Tags\"));\n+\t\t\t       \"%s %s\", bullet_l2, _(\"Tags\"));\n \n \tinflated_object_total = get_total_object_values(&objects->inflated_sizes);\n \tstats_table_size_addf(table, inflated_object_total,\n-\t\t\t      \"  * %s\", _(\"Inflated size\"));\n+\t\t\t      \"%s %s\", bullet_l1, _(\"Inflated size\"));\n \tstats_table_size_addf(table, objects->inflated_sizes.commits,\n-\t\t\t      \"    * %s\", _(\"Commits\"));\n+\t\t\t      \"%s %s\", bullet_l2, _(\"Commits\"));\n \tstats_table_size_addf(table, objects->inflated_sizes.trees,\n-\t\t\t      \"    * %s\", _(\"Trees\"));\n+\t\t\t      \"%s %s\", bullet_l2, _(\"Trees\"));\n \tstats_table_size_addf(table, objects->inflated_sizes.blobs,\n-\t\t\t      \"    * %s\", _(\"Blobs\"));\n+\t\t\t      \"%s %s\", bullet_l2, _(\"Blobs\"));\n \tstats_table_size_addf(table, objects->inflated_sizes.tags,\n-\t\t\t      \"    * %s\", _(\"Tags\"));\n+\t\t\t      \"%s %s\", bullet_l2, _(\"Tags\"));\n \n \tdisk_object_total = get_total_object_values(&objects->disk_sizes);\n \tstats_table_size_addf(table, disk_object_total,\n-\t\t\t      \"  * %s\", _(\"Disk size\"));\n+\t\t\t      \"%s %s\", bullet_l1, _(\"Disk size\"));\n \tstats_table_size_addf(table, objects->disk_sizes.commits,\n-\t\t\t      \"    * %s\", _(\"Commits\"));\n+\t\t\t      \"%s %s\", bullet_l2, _(\"Commits\"));\n \tstats_table_size_addf(table, objects->disk_sizes.trees,\n-\t\t\t      \"    * %s\", _(\"Trees\"));\n+\t\t\t      \"%s %s\", bullet_l2, _(\"Trees\"));\n \tstats_table_size_addf(table, objects->disk_sizes.blobs,\n-\t\t\t      \"    * %s\", _(\"Blobs\"));\n+\t\t\t      \"%s %s\", bullet_l2, _(\"Blobs\"));\n \tstats_table_size_addf(table, objects->disk_sizes.tags,\n-\t\t\t      \"    * %s\", _(\"Tags\"));\n+\t\t\t      \"%s %s\", bullet_l2, _(\"Tags\"));\n \n \tstats_table_addf(table, \"\");\n-\tstats_table_addf(table, \"* %s\", _(\"Largest objects\"));\n-\tstats_table_addf(table, \"  * %s\", _(\"Commits\"));\n+\tstats_table_addf(table, \"%s %s\", bullet_l0, _(\"Largest objects\"));\n+\tstats_table_addf(table, \"%s %s\", bullet_l1, _(\"Commits\"));\n \tstats_table_object_size_addf(table,\n \t\t\t\t     &objects->largest.commit_size.oid,\n \t\t\t\t     objects->largest.commit_size.value,\n-\t\t\t\t     \"    * %s\", _(\"Maximum size\"));\n+\t\t\t\t     \"%s %s\", bullet_l2, _(\"Maximum size\"));\n \tstats_table_object_count_addf(table,\n \t\t\t\t      &objects->largest.parent_count.oid,\n \t\t\t\t      objects->largest.parent_count.value,\n-\t\t\t\t      \"    * %s\", _(\"Maximum parents\"));\n-\tstats_table_addf(table, \"  * %s\", _(\"Trees\"));\n+\t\t\t\t      \"%s %s\", bullet_l2, _(\"Maximum parents\"));\n+\tstats_table_addf(table, \"%s %s\", bullet_l1, _(\"Trees\"));\n \tstats_table_object_size_addf(table,\n \t\t\t\t     &objects->largest.tree_size.oid,\n \t\t\t\t     objects->largest.tree_size.value,\n-\t\t\t\t     \"    * %s\", _(\"Maximum size\"));\n+\t\t\t\t     \"%s %s\", bullet_l2, _(\"Maximum size\"));\n \tstats_table_object_count_addf(table,\n \t\t\t\t      &objects->largest.tree_entries.oid,\n \t\t\t\t      objects->largest.tree_entries.value,\n-\t\t\t\t      \"    * %s\", _(\"Maximum entries\"));\n-\tstats_table_addf(table, \"  * %s\", _(\"Blobs\"));\n+\t\t\t\t      \"%s %s\", bullet_l2, _(\"Maximum entries\"));\n+\tstats_table_addf(table, \"%s %s\", bullet_l1, _(\"Blobs\"));\n \tstats_table_object_size_addf(table,\n \t\t\t\t     &objects->largest.blob_size.oid,\n \t\t\t\t     objects->largest.blob_size.value,\n-\t\t\t\t     \"    * %s\", _(\"Maximum size\"));\n-\tstats_table_addf(table, \"  * %s\", _(\"Tags\"));\n+\t\t\t\t     \"%s %s\", bullet_l2, _(\"Maximum size\"));\n+\tstats_table_addf(table, \"%s %s\", bullet_l1, _(\"Tags\"));\n \tstats_table_object_size_addf(table,\n \t\t\t\t     &objects->largest.tag_size.oid,\n \t\t\t\t     objects->largest.tag_size.value,\n-\t\t\t\t     \"    * %s\", _(\"Maximum size\"));\n+\t\t\t\t     \"%s %s\", bullet_l2, _(\"Maximum size\"));\n }\n \n #define INDEX_WIDTH 4\n@@ -589,28 +593,42 @@ static void stats_table_print_structure(const struct stats_table *table)\n \tint unit_col_width = table->unit_col_width;\n \tstruct string_list_item *item;\n \tstruct strbuf buf = STRBUF_INIT;\n+\tconst int utf8 = is_utf8_locale();\n+\tconst char *border_left = utf8 ? \"│ \" : \"| \";\n+\tconst char *border_mid = utf8 ? \" │ \" : \" | \";\n+\tconst char *border_right = utf8 ? \" │\" : \" |\";\n \n \tif (title_name_width > name_col_width)\n \t\tname_col_width = title_name_width;\n \tif (title_value_width > value_col_width + unit_col_width + 1)\n \t\tvalue_col_width = title_value_width - unit_col_width;\n \n-\tstrbuf_addstr(&buf, \"| \");\n+\tstrbuf_addstr(&buf, border_left);\n \tstrbuf_utf8_align(&buf, ALIGN_LEFT, name_col_width + INDEX_WIDTH,\n \t\t\t  name_col_title);\n-\tstrbuf_addstr(&buf, \" | \");\n+\tstrbuf_addstr(&buf, border_mid);\n \tstrbuf_utf8_align(&buf, ALIGN_LEFT,\n \t\t\t  value_col_width + unit_col_width + 1, value_col_title);\n-\tstrbuf_addstr(&buf, \" |\");\n+\tstrbuf_addstr(&buf, border_right);\n \tprintf(\"%s\\n\", buf.buf);\n \n-\tprintf(\"| \");\n-\tfor (int i = 0; i < name_col_width + INDEX_WIDTH; i++)\n-\t\tputchar('-');\n-\tprintf(\" | \");\n-\tfor (int i = 0; i < value_col_width + unit_col_width + 1; i++)\n-\t\tputchar('-');\n-\tprintf(\" |\\n\");\n+\tif (utf8) {\n+\t\tprintf(\"├─\");\n+\t\tfor (int i = 0; i < name_col_width + INDEX_WIDTH; i++)\n+\t\t\tprintf(\"─\");\n+\t\tprintf(\"─┼─\");\n+\t\tfor (int i = 0; i < value_col_width + unit_col_width + 1; i++)\n+\t\t\tprintf(\"─\");\n+\t\tprintf(\"─┤\\n\");\n+\t} else {\n+\t\tprintf(\"| \");\n+\t\tfor (int i = 0; i < name_col_width + INDEX_WIDTH; i++)\n+\t\t\tputchar('-');\n+\t\tprintf(\" | \");\n+\t\tfor (int i = 0; i < value_col_width + unit_col_width + 1; i++)\n+\t\t\tputchar('-');\n+\t\tprintf(\" |\\n\");\n+\t}\n \n \tfor_each_string_list_item(item, &table->rows) {\n \t\tstruct stats_table_entry *entry = item->util;\n@@ -624,7 +642,7 @@ static void stats_table_print_structure(const struct stats_table *table)\n \t\t}\n \n \t\tstrbuf_reset(&buf);\n-\t\tstrbuf_addstr(&buf, \"| \");\n+\t\tstrbuf_addstr(&buf, border_left);\n \t\tstrbuf_utf8_align(&buf, ALIGN_LEFT, name_col_width, item->string);\n \n \t\tif (entry && entry->oid)\n@@ -633,11 +651,11 @@ static void stats_table_print_structure(const struct stats_table *table)\n \t\telse\n \t\t\tstrbuf_addchars(&buf, ' ', INDEX_WIDTH);\n \n-\t\tstrbuf_addstr(&buf, \" | \");\n+\t\tstrbuf_addstr(&buf, border_mid);\n \t\tstrbuf_utf8_align(&buf, ALIGN_RIGHT, value_col_width, value);\n \t\tstrbuf_addch(&buf, ' ');\n \t\tstrbuf_utf8_align(&buf, ALIGN_LEFT, unit_col_width, unit);\n-\t\tstrbuf_addstr(&buf, \" |\");\n+\t\tstrbuf_addstr(&buf, border_right);\n \t\tprintf(\"%s\\n\", buf.buf);\n \t}\n \ndiff --git a/t/t1901-repo-structure.sh b/t/t1901-repo-structure.sh\nindex 02cc2b594a..926db09175 100755\n--- a/t/t1901-repo-structure.sh\n+++ b/t/t1901-repo-structure.sh\n@@ -4,6 +4,29 @@ test_description='test git repo structure'\n \n . ./test-lib.sh\n \n+# Detect if a UTF-8 locale is available on the test system.\n+if test -z \"$GIT_TEST_UTF8_LOCALE\"\n+then\n+\tcase \"${LC_ALL:-$LANG}\" in\n+\t*.[Uu][Tt][Ff]8 | *.[Uu][Tt][Ff]-8)\n+\t\tGIT_TEST_UTF8_LOCALE=\"${LC_ALL:-$LANG}\"\n+\t\t;;\n+\t*)\n+\t\tif type locale >/dev/null 2>&1\n+\t\tthen\n+\t\t\tGIT_TEST_UTF8_LOCALE=$(locale -a 2>/dev/null | sed -n '/\\.[uU][tT][fF]-*8$/{\n+\t\t\t\tp\n+\t\t\t\tq\n+\t\t\t}')\n+\t\tfi\n+\t\t;;\n+\tesac\n+fi\n+if test -n \"$GIT_TEST_UTF8_LOCALE\"\n+then\n+\ttest_set_prereq UTF8_LOCALE\n+fi\n+\n object_type_disk_usage() {\n \tdisk_usage_opt=\"--disk-usage\"\n \n@@ -66,7 +89,10 @@ test_expect_success 'empty repository' '\n \t\t|     * Maximum size        |    0 B |\n \t\tEOF\n \n-\t\tgit repo structure >out 2>err &&\n+\t\t# Force a non-UTF8 locale so this test always exercises the\n+\t\t# ASCII fallback formatting, regardless of what locale the\n+\t\t# runner defaults to.\n+\t\tLC_ALL=C git repo structure >out 2>err &&\n \n \t\ttest_cmp expect out &&\n \t\ttest_line_count = 0 err\n@@ -137,7 +163,10 @@ test_expect_success SHA1 'repository with references and objects' '\n \t\t[6] 4dae4f5954f5e6feb3577cfb1b181daa3fd3afd2\n \t\tEOF\n \n-\t\tgit repo structure >out 2>err &&\n+\t\t# Force a non-UTF8 locale so this test always exercises the\n+\t\t# ASCII fallback formatting, regardless of what locale the\n+\t\t# runner defaults to.\n+\t\tLC_ALL=C git repo structure >out 2>err &&\n \n \t\ttest_cmp expect out &&\n \t\ttest_line_count = 0 err\n@@ -230,4 +259,112 @@ test_expect_success 'git repo structure -h shows only repo structure usage' '\n \ttest_grep ! \"git repo info\" actual\n '\n \n+test_expect_success UTF8_LOCALE 'unicode output under UTF-8 locale with missing locale dir' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\tcat >expect <<-\\EOF &&\n+\t\t│ Repository structure      │ Value  │\n+\t\t├───────────────────────────┼────────┤\n+\t\t│ • References              │        │\n+\t\t│   • Count                 │    0   │\n+\t\t│     • Branches            │    0   │\n+\t\t│     • Tags                │    0   │\n+\t\t│     • Remotes             │    0   │\n+\t\t│     • Others              │    0   │\n+\t\t│                           │        │\n+\t\t│ • Reachable objects       │        │\n+\t\t│   • Count                 │    0   │\n+\t\t│     • Commits             │    0   │\n+\t\t│     • Trees               │    0   │\n+\t\t│     • Blobs               │    0   │\n+\t\t│     • Tags                │    0   │\n+\t\t│   • Inflated size         │    0 B │\n+\t\t│     • Commits             │    0 B │\n+\t\t│     • Trees               │    0 B │\n+\t\t│     • Blobs               │    0 B │\n+\t\t│     • Tags                │    0 B │\n+\t\t│   • Disk size             │    0 B │\n+\t\t│     • Commits             │    0 B │\n+\t\t│     • Trees               │    0 B │\n+\t\t│     • Blobs               │    0 B │\n+\t\t│     • Tags                │    0 B │\n+\t\t│                           │        │\n+\t\t│ • Largest objects         │        │\n+\t\t│   • Commits               │        │\n+\t\t│     • Maximum size        │    0 B │\n+\t\t│     • Maximum parents     │    0   │\n+\t\t│   • Trees                 │        │\n+\t\t│     • Maximum size        │    0 B │\n+\t\t│     • Maximum entries     │    0   │\n+\t\t│   • Blobs                 │        │\n+\t\t│     • Maximum size        │    0 B │\n+\t\t│   • Tags                  │        │\n+\t\t│     • Maximum size        │    0 B │\n+\t\tEOF\n+\t\t# Point GIT_TEXTDOMAINDIR at a nonexistent path so\n+\t\t# git_setup_gettext() takes its early-return path (its\n+\t\t# locale-directory check fails) and never populates the\n+\t\t# gettext-internal charset. This exercises the\n+\t\t# is_utf8_locale() fallback that derives the charset\n+\t\t# directly from LC_ALL/LC_CTYPE/LANG instead, regardless of\n+\t\t# whether gettext itself was able to initialize.\n+\t\tGIT_TEXTDOMAINDIR=\"$TRASH_DIRECTORY/nonexistent-locale-dir\" \\\n+\t\tLC_ALL=\"$GIT_TEST_UTF8_LOCALE\" git repo structure >out 2>err &&\n+\t\ttest_cmp expect out &&\n+\t\ttest_line_count = 0 err\n+\t)\n+'\n+\n+test_expect_success UTF8_LOCALE 'unicode output under UTF-8 locale' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\tcat >expect <<-\\EOF &&\n+\t\t│ Repository structure      │ Value  │\n+\t\t├───────────────────────────┼────────┤\n+\t\t│ • References              │        │\n+\t\t│   • Count                 │    0   │\n+\t\t│     • Branches            │    0   │\n+\t\t│     • Tags                │    0   │\n+\t\t│     • Remotes             │    0   │\n+\t\t│     • Others              │    0   │\n+\t\t│                           │        │\n+\t\t│ • Reachable objects       │        │\n+\t\t│   • Count                 │    0   │\n+\t\t│     • Commits             │    0   │\n+\t\t│     • Trees               │    0   │\n+\t\t│     • Blobs               │    0   │\n+\t\t│     • Tags                │    0   │\n+\t\t│   • Inflated size         │    0 B │\n+\t\t│     • Commits             │    0 B │\n+\t\t│     • Trees               │    0 B │\n+\t\t│     • Blobs               │    0 B │\n+\t\t│     • Tags                │    0 B │\n+\t\t│   • Disk size             │    0 B │\n+\t\t│     • Commits             │    0 B │\n+\t\t│     • Trees               │    0 B │\n+\t\t│     • Blobs               │    0 B │\n+\t\t│     • Tags                │    0 B │\n+\t\t│                           │        │\n+\t\t│ • Largest objects         │        │\n+\t\t│   • Commits               │        │\n+\t\t│     • Maximum size        │    0 B │\n+\t\t│     • Maximum parents     │    0   │\n+\t\t│   • Trees                 │        │\n+\t\t│     • Maximum size        │    0 B │\n+\t\t│     • Maximum entries     │    0   │\n+\t\t│   • Blobs                 │        │\n+\t\t│     • Maximum size        │    0 B │\n+\t\t│   • Tags                  │        │\n+\t\t│     • Maximum size        │    0 B │\n+\t\tEOF\n+\t\tLC_ALL=\"$GIT_TEST_UTF8_LOCALE\" git repo structure >out 2>err &&\n+\t\ttest_cmp expect out &&\n+\t\ttest_line_count = 0 err\n+\t)\n+'\n+\n test_done\n-- \n2.55.GIT\n\n"},{"id":"551033","messageId":"xmqqse47pfyy.fsf@gitster.g","threadId":"66204","inReplyTo":"20260821135410.429698-1-jayatheerthkulkarni2005@gmail.com","subject":"Re: [GSoC Patch 0/2] add unicode support to git repo structure","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-21T16:59:17Z","receivedAt":"2026-08-21T16:59:20Z","isPatch":true,"body":"K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:\n\n> Currently, `git repo structure` always renders its table output using plain\n> ASCII characters (`|`, `-`, `*`), regardless of the user's locale. This\n> series introduces Unicode box-drawing characters (`│`, `├`, `─`, `┼`, `┤`)\n> and bullet points (`•`) when a UTF-8 locale is detected, providing a cleaner\n> and more visually distinct hierarchical output on modern terminals while\n> gracefully falling back to the existing ASCII formatting otherwise.\n\nGenerally speaking, Unicode box-drawing characters do not work as\nASCII art components as well as they should, because terminals often\ndo not agree on how wide they should be rendered.\n\nSo I am not very enthusiastic about this change.\n"},{"id":"551071","messageId":"aorzcZVdCK27pdjG@ugly.lan","threadId":"66204","inReplyTo":"xmqqse47pfyy.fsf@gitster.g","subject":"Re: [GSoC Patch 0/2] add unicode support to git repo structure","fromName":"Oswald Buddenhagen","fromEmail":"oswald.buddenhagen@gmx.de","sentAt":"2026-08-23T13:19:45Z","receivedAt":"2026-08-23T13:19:48Z","isPatch":true,"body":"On Fri, Aug 21, 2026 at 09:59:17AM -0700, Junio C Hamano wrote:\n>K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:\n>\n>> Currently, `git repo structure` always renders its table output using plain\n>> ASCII characters (`|`, `-`, `*`), regardless of the user's locale. This\n>> series introduces Unicode box-drawing characters (`│`, `├`, `─`, `┼`, `┤`)\n>> and bullet points (`•`) when a UTF-8 locale is detected, providing a cleaner\n>> and more visually distinct hierarchical output on modern terminals while\n>> gracefully falling back to the existing ASCII formatting otherwise.\n>\n>Generally speaking, Unicode box-drawing characters do not work as\n>ASCII art components as well as they should, because terminals often\n>do not agree on how wide they should be rendered.\n>\ni can instantly think of two popular applications i'm using every day \nwhich use box drawing chars: mutt (thread and attachment tree displays) \nand midnight commander (panel/dialog frames). if \"often\" above were \naccurate, these projects would be getting a lot of complaints about \nthat, which they don't.\n\nif compatibility with broken terminals/fonts is considered important, an \noption can be introduced to opt out from the modern experience.\n\n(any takers for giving `git log --graph` the same treatment?)\n"},{"id":"551075","messageId":"xmqqqzjovq1x.fsf@gitster.g","threadId":"66204","inReplyTo":"aorzcZVdCK27pdjG@ugly.lan","subject":"Re: [GSoC Patch 0/2] add unicode support to git repo structure","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-23T15:01:46Z","receivedAt":"2026-08-23T15:01:49Z","isPatch":true,"body":"Oswald Buddenhagen <oswald.buddenhagen@gmx.de> writes:\n\n> if compatibility with broken terminals/fonts is considered important, an \n> option can be introduced to opt out from the modern experience.\n\nAbsolutely.  My response was mostly a reaction to the implementation\nthat only paid attention to the locale settings, leaving no way for\npeople to opt out without discarding UTF-8 altogether.\n"},{"id":"551097","messageId":"20260823170645.rBeci%taahol@utu.fi","threadId":"66204","inReplyTo":"aorzcZVdCK27pdjG@ugly.lan","subject":"Re: [GSoC Patch 0/2] add unicode support to git repo structure","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-08-23T17:06:45Z","receivedAt":"2026-08-23T17:22:02Z","isPatch":true,"body":"Oswald Buddenhagen <oswald.buddenhagen@gmx.de> wrote:\n\n> i can instantly think of two popular applications i'm using every day \n> which use box drawing chars: mutt (thread and attachment tree displays) \n> and midnight commander (panel/dialog frames). if \"often\" above were \n> accurate, these projects would be getting a lot of complaints about \n> that, which they don't.\n> \n\nHmm, mutt seems to use ACS (alternative character set) as a fallback option:\n\n```\n            else if (Charset_is_utf8)\n              addstr(\"\\342\\224\\224\"); /* WACS_LLCORNER */\n            else\n              addch(ACS_LLCORNER);\n```\n\nHow relevant that is to us, I don't know, but perhaps something to think about.\n\n-- \nTuomas\n"}]}