branch -v: align even when branch names are in UTF-8

Branch names are usually in ASCII so they are not the problem. The problem most likely comes from "(no branch)" translation, which is in UTF-8 and makes display-width calculation just wrong. Clarify this by renaming the field "len" in struct ref_item to "width", as it stores the display-width and is used to compute the width of the screen needed to show the names of all the branches, and compute the display width using utf8_strwidth(), not byte-length with strlen(). Update document to mention the fact that we may want ref names in UTF-8. Encodings that produce invalid UTF-8 are safe as utf8_strwidth() falls back to strlen(). The ones that incidentally produce valid UTF-8 sequences will cause misalignment. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Nguyễn Thái Ngọc Duy committed Aug 26, 2012 at 01:17 UTC 1452bd64f14efd888fe9c732f603173ba7fa595e
2 files changed +17 -13
Documentation/revisions.txt
+2
@@ -55,6 +55,8 @@ when you run `git cherry-pick`.
55 +
56 Note that any of the 'refs/*' cases above may come either from
57 the '$GIT_DIR/refs' directory or from the '$GIT_DIR/packed-refs' file.
58 +While the ref name encoding is unspecified, UTF-8 is prefered as
59 +some output processing may assume ref names in UTF-8.
60
61 '<refname>@\{<date>\}', e.g. 'master@\{yesterday\}', 'HEAD@\{5 minutes ago\}'::
62 A ref followed by the suffix '@' with a date specification
builtin/branch.c
+15 -13
@@ -17,6 +17,7 @@
17 #include "revision.h"
18 #include "string-list.h"
19 #include "column.h"
20 +#include "utf8.h"
21
22 static const char * const builtin_branch_usage[] = {
23 "git branch [options] [-r | -a] [--merged | --no-merged]",
@@ -249,7 +250,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
250 struct ref_item {
251 char *name;
252 char *dest;
252 - unsigned int kind, len;
253 + unsigned int kind, width;
254 struct commit *commit;
255 };
256
@@ -354,14 +355,14 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
355 newitem->name = xstrdup(refname);
356 newitem->kind = kind;
357 newitem->commit = commit;
357 - newitem->len = strlen(refname);
358 + newitem->width = utf8_strwidth(refname);
359 newitem->dest = resolve_symref(orig_refname, prefix);
360 /* adjust for "remotes/" */
361 if (newitem->kind == REF_REMOTE_BRANCH &&
362 ref_list->kinds != REF_REMOTE_BRANCH)
362 - newitem->len += 8;
363 - if (newitem->len > ref_list->maxwidth)
364 - ref_list->maxwidth = newitem->len;
363 + newitem->width += 8;
364 + if (newitem->width > ref_list->maxwidth)
365 + ref_list->maxwidth = newitem->width;
366
367 return 0;
368 }
@@ -490,11 +491,12 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
491 }
492
493 strbuf_addf(&name, "%s%s", prefix, item->name);
493 - if (verbose)
494 + if (verbose) {
495 + int utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf);
496 strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color),
495 - maxwidth, name.buf,
497 + maxwidth + utf8_compensation, name.buf,
498 branch_get_color(BRANCH_COLOR_RESET));
497 - else
499 + } else
500 strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color),
501 name.buf, branch_get_color(BRANCH_COLOR_RESET));
502
@@ -519,8 +521,8 @@ static int calc_maxwidth(struct ref_list *refs)
521 for (i = 0; i < refs->index; i++) {
522 if (!matches_merge_filter(refs->list[i].commit))
523 continue;
522 - if (refs->list[i].len > w)
523 - w = refs->list[i].len;
524 + if (refs->list[i].width > w)
525 + w = refs->list[i].width;
526 }
527 return w;
528 }
@@ -533,12 +535,12 @@ static void show_detached(struct ref_list *ref_list)
535 if (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) {
536 struct ref_item item;
537 item.name = xstrdup(_("(no branch)"));
536 - item.len = strlen(item.name);
538 + item.width = utf8_strwidth(item.name);
539 item.kind = REF_LOCAL_BRANCH;
540 item.dest = NULL;
541 item.commit = head_commit;
540 - if (item.len > ref_list->maxwidth)
541 - ref_list->maxwidth = item.len;
542 + if (item.width > ref_list->maxwidth)
543 + ref_list->maxwidth = item.width;
544 print_ref_item(&item, ref_list->maxwidth, ref_list->verbose, ref_list->abbrev, 1, "");
545 free(item.name);
546 }