{"thread":{"id":"29734","subject":"[PATCH v6 00/11] Column display","startedAt":"2012-02-25T11:41:07Z","lastAt":"2012-03-14T18:29:54Z","messageCount":66,"participants":["Nguyễn Thái Ngọc Duy","Junio C Hamano","Nguyen Thai Ngoc Duy","Ramsay Jones","Thomas Rast"],"isPatch":true,"patchVersion":6,"patchTotal":11},"messages":[{"id":"185417","messageId":"1330170078-29353-1-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":null,"subject":"[PATCH v6 00/11] Column display","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-25T11:41:07Z","receivedAt":"2012-02-25T11:41:07Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Compared to v5 [1] this only has minor changes:\n\n - drop term_columns() patch because master already has that\n - style fixes\n - incorporate Ramsay's fix in option parsing code\n - make git_column_config() return either 0 or -1\n\n[1] http://thread.gmane.org/gmane.comp.version-control.git/189758/focus=189875\n\nNguyễn Thái Ngọc Duy (11):\n  column: add API to print items in columns\n  Add git-column and column mode parsing\n  Stop starting pager recursively\n  column: add columnar layout\n  column: support columns with different widths\n  column: add column.ui for default column output settings\n  help: reuse print_columns() for help -a\n  branch: add --column\n  status: add --column\n  column: support piping stdout to external git-column process\n  tag: add --column\n\n .gitignore                   |    1 +\n Documentation/config.txt     |   38 ++++\n Documentation/git-branch.txt |    9 +\n Documentation/git-column.txt |   53 +++++\n Documentation/git-status.txt |    7 +\n Documentation/git-tag.txt    |    9 +\n Makefile                     |    3 +\n builtin.h                    |    1 +\n builtin/branch.c             |   32 +++-\n builtin/column.c             |   62 ++++++\n builtin/commit.c             |    6 +\n builtin/tag.c                |   26 ++-\n column.c                     |  478 ++++++++++++++++++++++++++++++++++++++++++\n column.h                     |   40 ++++\n command-list.txt             |    1 +\n git.c                        |    1 +\n help.c                       |   48 ++---\n pager.c                      |    2 +-\n parse-options.h              |    2 +\n t/t3200-branch.sh            |   77 +++++++\n t/t7004-tag.sh               |   44 ++++\n t/t7508-status.sh            |   24 ++\n t/t9002-column.sh            |  161 ++++++++++++++\n wt-status.c                  |   28 +++-\n wt-status.h                  |    1 +\n 25 files changed, 1110 insertions(+), 44 deletions(-)\n create mode 100644 Documentation/git-column.txt\n create mode 100644 builtin/column.c\n create mode 100644 column.c\n create mode 100644 column.h\n create mode 100755 t/t9002-column.sh\n\n-- \n1.7.8.36.g69ee2\n"},{"id":"185418","messageId":"1330170078-29353-2-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330170078-29353-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v6 01/11] column: add API to print items in columns","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-25T11:41:08Z","receivedAt":"2012-02-25T11:41:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Simple code that print line-by-line and wants to columnize can do\nthis:\n\nstruct string_list list;\nstring_list_append(&list, ...);\nstring_list_append(&list, ...);\n...\nprint_columns(&list, ...);\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Makefile |    2 ++\n column.c |   39 +++++++++++++++++++++++++++++++++++++++\n column.h |   19 +++++++++++++++++++\n 3 files changed, 60 insertions(+), 0 deletions(-)\n create mode 100644 column.c\n create mode 100644 column.h\n\ndiff --git a/Makefile b/Makefile\nindex a0de4e9..4e9501b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -646,6 +646,7 @@ LIB_OBJS += bulk-checkin.o\n LIB_OBJS += bundle.o\n LIB_OBJS += cache-tree.o\n LIB_OBJS += color.o\n+LIB_OBJS += column.o\n LIB_OBJS += combine-diff.o\n LIB_OBJS += commit.o\n LIB_OBJS += compat/obstack.o\n@@ -2166,6 +2167,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o url.o http-backend.o: url.h\n+column.o help.o pager.o: column.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/column.c b/column.c\nnew file mode 100644\nindex 0000000..742ae18\n--- /dev/null\n+++ b/column.c\n@@ -0,0 +1,39 @@\n+#include \"cache.h\"\n+#include \"column.h\"\n+#include \"string-list.h\"\n+\n+#define MODE(mode) ((mode) & COL_MODE)\n+\n+/* Display without layout when COL_ENABLED is not set */\n+static void display_plain(const struct string_list *list,\n+\t\t\t  const char *indent, const char *nl)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < list->nr; i++)\n+\t\tprintf(\"%s%s%s\", indent, list->items[i].string, nl);\n+}\n+\n+void print_columns(const struct string_list *list, unsigned int mode,\n+\t\t   struct column_options *opts)\n+{\n+\tconst char *indent = \"\", *nl = \"\\n\";\n+\tint padding = 1, width = term_columns();\n+\n+\tif (!list->nr)\n+\t\treturn;\n+\tif (opts) {\n+\t\tif (opts->indent)\n+\t\t\tindent = opts->indent;\n+\t\tif (opts->nl)\n+\t\t\tnl = opts->nl;\n+\t\tif (opts->width)\n+\t\t\twidth = opts->width;\n+\t\tpadding = opts->padding;\n+\t}\n+\tif (width <= 1 || !(mode & COL_ENABLED)) {\n+\t\tdisplay_plain(list, indent, nl);\n+\t\treturn;\n+\t}\n+\tdie(\"BUG: invalid mode %d\", MODE(mode));\n+}\ndiff --git a/column.h b/column.h\nnew file mode 100644\nindex 0000000..8e4fdaa\n--- /dev/null\n+++ b/column.h\n@@ -0,0 +1,19 @@\n+#ifndef COLUMN_H\n+#define COLUMN_H\n+\n+#define COL_MODE          0x000F\n+#define COL_ENABLED      (1 << 4)\n+\n+struct column_options {\n+\tint width;\n+\tint padding;\n+\tconst char *indent;\n+\tconst char *nl;\n+};\n+\n+extern int term_columns(void);\n+extern void print_columns(const struct string_list *list,\n+\t\t\t  unsigned int mode,\n+\t\t\t  struct column_options *opts);\n+\n+#endif\n-- \n1.7.8.36.g69ee2\n"},{"id":"185420","messageId":"1330170078-29353-3-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330170078-29353-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v6 02/11] Add git-column and column mode parsing","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-25T11:41:09Z","receivedAt":"2012-02-25T11:41:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"A column option string consists of many token separated by either\nspace of commas. A token belongs to one of three groups:\n\n - enabling: always, never and auto\n - layout mode: to be implemented\n - other tuning, which could be negated be prefix 'no'\n\nA command line option without argument (e.g. --column) will enable\ncolumn output and reuse existing settings (layout mode and options..).\n--no-column disables columnar output.\n\nThanks-to: Ramsay Jones <ramsay@ramsay1.demon.co.uk>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore                   |    1 +\n Documentation/git-column.txt |   49 +++++++++++++++\n Makefile                     |    1 +\n builtin.h                    |    1 +\n builtin/column.c             |   41 +++++++++++++\n column.c                     |  133 ++++++++++++++++++++++++++++++++++++++++++\n column.h                     |   11 ++++\n command-list.txt             |    1 +\n git.c                        |    1 +\n parse-options.h              |    2 +\n t/t9002-column.sh            |   27 +++++++++\n 11 files changed, 268 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/git-column.txt\n create mode 100644 builtin/column.c\n create mode 100755 t/t9002-column.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex 87fcc5f..2540264 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -26,6 +26,7 @@\n /git-cherry-pick\n /git-clean\n /git-clone\n+/git-column\n /git-commit\n /git-commit-tree\n /git-config\ndiff --git a/Documentation/git-column.txt b/Documentation/git-column.txt\nnew file mode 100644\nindex 0000000..508b85f\n--- /dev/null\n+++ b/Documentation/git-column.txt\n@@ -0,0 +1,49 @@\n+git-column(1)\n+=============\n+\n+NAME\n+----\n+git-column - Display data in columns\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git column' [--mode=<mode> | --rawmode=<n>] [--width=<width>]\n+\t     [--indent=<string>] [--nl=<string>] [--pading=<n>]\n+\n+DESCRIPTION\n+-----------\n+This command formats its input into multiple columns.\n+\n+OPTIONS\n+-------\n+--mode=<mode>::\n+\tSpecify layout mode. See configuration variable column.ui for option\n+\tsyntax.\n+\n+--rawmode=<n>::\n+\tSame as --mode but take mode encoded as a number. This is mainly used\n+\tby other commands that have already parsed layout mode.\n+\n+--width=<width>::\n+\tSpecify the terminal width. By default 'git column' will detect the\n+\tterminal width, or fall back to 80 if it is unable to do so.\n+\n+--indent=<string>::\n+\tString to be printed at the beginning of each line.\n+\n+--nl=<N>::\n+\tString to be printed at the end of each line,\n+\tincluding newline character.\n+\n+--padding=<N>::\n+\tThe number of spaces between columns. One space by default.\n+\n+\n+Author\n+------\n+Written by Nguyen Thai Ngoc Duy <pclouds@gmail.com>\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Makefile b/Makefile\nindex 4e9501b..0998f0d 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -775,6 +775,7 @@ BUILTIN_OBJS += builtin/checkout-index.o\n BUILTIN_OBJS += builtin/checkout.o\n BUILTIN_OBJS += builtin/clean.o\n BUILTIN_OBJS += builtin/clone.o\n+BUILTIN_OBJS += builtin/column.o\n BUILTIN_OBJS += builtin/commit-tree.o\n BUILTIN_OBJS += builtin/commit.o\n BUILTIN_OBJS += builtin/config.o\ndiff --git a/builtin.h b/builtin.h\nindex 857b9c8..338f540 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -61,6 +61,7 @@ extern int cmd_cherry(int argc, const char **argv, const char *prefix);\n extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);\n extern int cmd_clone(int argc, const char **argv, const char *prefix);\n extern int cmd_clean(int argc, const char **argv, const char *prefix);\n+extern int cmd_column(int argc, const char **argv, const char *prefix);\n extern int cmd_commit(int argc, const char **argv, const char *prefix);\n extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_config(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/column.c b/builtin/column.c\nnew file mode 100644\nindex 0000000..3b0f74e\n--- /dev/null\n+++ b/builtin/column.c\n@@ -0,0 +1,41 @@\n+#include \"builtin.h\"\n+#include \"cache.h\"\n+#include \"strbuf.h\"\n+#include \"parse-options.h\"\n+#include \"string-list.h\"\n+#include \"column.h\"\n+\n+static const char * const builtin_column_usage[] = {\n+\t\"git column [options]\",\n+\tNULL\n+};\n+static unsigned int colopts;\n+\n+int cmd_column(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tstruct column_options copts;\n+\tstruct option options[] = {\n+\t\tOPT_COLUMN(0, \"mode\", &colopts, \"layout to use\"),\n+\t\tOPT_INTEGER(0, \"rawmode\", &colopts, \"layout to use\"),\n+\t\tOPT_INTEGER(0, \"width\", &copts.width, \"Maximum width\"),\n+\t\tOPT_STRING(0, \"indent\", &copts.indent, \"string\", \"Padding space on left border\"),\n+\t\tOPT_INTEGER(0, \"nl\", &copts.nl, \"Padding space on right border\"),\n+\t\tOPT_INTEGER(0, \"padding\", &copts.padding, \"Padding space between columns\"),\n+\t\tOPT_END()\n+\t};\n+\n+\tmemset(&copts, 0, sizeof(copts));\n+\tcopts.width = term_columns();\n+\tcopts.padding = 1;\n+\targc = parse_options(argc, argv, \"\", options, builtin_column_usage, 0);\n+\tif (argc)\n+\t\tusage_with_options(builtin_column_usage, options);\n+\n+\twhile (!strbuf_getline(&sb, stdin, '\\n'))\n+\t\tstring_list_append(&list, sb.buf);\n+\n+\tprint_columns(&list, colopts, &copts);\n+\treturn 0;\n+}\ndiff --git a/column.c b/column.c\nindex 742ae18..e62edf7 100644\n--- a/column.c\n+++ b/column.c\n@@ -1,6 +1,7 @@\n #include \"cache.h\"\n #include \"column.h\"\n #include \"string-list.h\"\n+#include \"parse-options.h\"\n \n #define MODE(mode) ((mode) & COL_MODE)\n \n@@ -37,3 +38,135 @@ void print_columns(const struct string_list *list, unsigned int mode,\n \t}\n \tdie(\"BUG: invalid mode %d\", MODE(mode));\n }\n+\n+struct colopt {\n+\tenum {\n+\t\tENABLE,\n+\t\tMODE,\n+\t\tOPTION\n+\t} type;\n+\tconst char *name;\n+\tint value;\n+};\n+\n+/*\n+ * Set COL_ENABLED and COL_ENABLED_SET. If 'set' is -1, check if\n+ * stdout is tty.\n+ */\n+static int set_enable_bit(unsigned int *mode, int set, int stdout_is_tty)\n+{\n+\tif (set < 0) {\t/* auto */\n+\t\tif (stdout_is_tty < 0)\n+\t\t\tstdout_is_tty = isatty(1);\n+\t\tset = stdout_is_tty || (pager_in_use() && pager_use_color);\n+\t}\n+\tif (set)\n+\t\t*mode = *mode | COL_ENABLED | COL_ENABLED_SET;\n+\telse\n+\t\t*mode = (*mode & ~COL_ENABLED) | COL_ENABLED_SET;\n+\treturn 0;\n+}\n+\n+/*\n+ * Set COL_MODE_*. mode is intially copied from column.ui. If\n+ * COL_ENABLED_SET is not set, then neither 'always', 'never' nor\n+ * 'auto' has been used. Default to 'always'.\n+ */\n+static int set_mode(unsigned int *mode, unsigned int value)\n+{\n+\t*mode = (*mode & ~COL_MODE) | value;\n+\tif (!(*mode & COL_ENABLED_SET))\n+\t\t*mode |= COL_ENABLED | COL_ENABLED_SET;\n+\n+\treturn 0;\n+}\n+\n+/* Set or unset other COL_* */\n+static int set_option(unsigned int *mode, unsigned int opt, int set)\n+{\n+\tif (set)\n+\t\t*mode |= opt;\n+\telse\n+\t\t*mode &= ~opt;\n+\treturn 0;\n+}\n+\n+static int parse_option(const char *arg, int len,\n+\t\t\tunsigned int *mode, int stdout_is_tty)\n+{\n+\tstruct colopt opts[] = {\n+\t\t{ ENABLE, \"always\",  1 },\n+\t\t{ ENABLE, \"never\",   0 },\n+\t\t{ ENABLE, \"auto\",   -1 },\n+\t};\n+\tint i;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(opts); i++) {\n+\t\tint set = 1, arg_len = len, name_len;\n+\t\tconst char *arg_str = arg;\n+\n+\t\tif (opts[i].type == OPTION) {\n+\t\t\tif (arg_len > 2 && !strncmp(arg_str, \"no\", 2)) {\n+\t\t\t\targ_str += 2;\n+\t\t\t\targ_len -= 2;\n+\t\t\t\tset = 0;\n+\t\t\t} else {\n+\t\t\t\tset = 1;\n+\t\t\t}\n+\t\t}\n+\n+\t\tname_len = strlen(opts[i].name);\n+\t\tif (arg_len != name_len ||\n+\t\t    strncmp(arg_str, opts[i].name, name_len))\n+\t\t\tcontinue;\n+\n+\t\tswitch (opts[i].type) {\n+\t\tcase ENABLE:\n+\t\t\treturn set_enable_bit(mode, opts[i].value,\n+\t\t\t\t\t      stdout_is_tty);\n+\t\tcase MODE:\n+\t\t\treturn set_mode(mode, opts[i].value);\n+\t\tcase OPTION:\n+\t\t\treturn set_option(mode, opts[i].value, set);\n+\t\tdefault:\n+\t\t\tdie(\"BUG: Unknown option type %d\", opts[i].type);\n+\t\t}\n+\t}\n+\n+\treturn error(\"unsupported style '%s'\", arg);\n+}\n+\n+int git_config_column(unsigned int *mode, const char *value,\n+\t\t      int stdout_is_tty)\n+{\n+\tconst char *sep = \" ,\";\n+\n+\twhile (*value) {\n+\t\tint len = strcspn(value, sep);\n+\t\tif (len) {\n+\t\t\tif (parse_option(value, len, mode, stdout_is_tty))\n+\t\t\t\treturn -1;\n+\n+\t\t\tvalue += len;\n+\t\t}\n+\t\tvalue += strspn(value, sep);\n+\t}\n+\treturn 0;\n+}\n+\n+int parseopt_column_callback(const struct option *opt,\n+\t\t\t     const char *arg, int unset)\n+{\n+\tunsigned int *mode = opt->value;\n+\t*mode |= COL_PARSEOPT;\n+\tif (unset) {\n+\t\t*mode = (*mode & ~COL_ENABLED) | COL_ENABLED_SET;\n+\t\treturn 0;\n+\t}\n+\tif (arg)\n+\t\treturn git_config_column(mode, arg, -1);\n+\n+\t/* no arg, turn it on */\n+\t*mode |= COL_ENABLED | COL_ENABLED_SET;\n+\treturn 0;\n+}\ndiff --git a/column.h b/column.h\nindex 8e4fdaa..67b1c4f 100644\n--- a/column.h\n+++ b/column.h\n@@ -3,6 +3,11 @@\n \n #define COL_MODE          0x000F\n #define COL_ENABLED      (1 << 4)\n+#define COL_ENABLED_SET  (1 << 5)  /* Has COL_ENABLED been set by config? */\n+#define COL_PARSEOPT     (1 << 8)  /* --column is given */\n+\n+#define explicitly_enable_column(c) \\\n+\t(((c) & (COL_PARSEOPT | COL_ENABLED)) == (COL_PARSEOPT | COL_ENABLED))\n \n struct column_options {\n \tint width;\n@@ -15,5 +20,11 @@ extern int term_columns(void);\n extern void print_columns(const struct string_list *list,\n \t\t\t  unsigned int mode,\n \t\t\t  struct column_options *opts);\n+extern int git_config_column(unsigned int *mode, const char *value,\n+\t\t\t     int stdout_is_tty);\n+\n+struct option;\n+extern int parseopt_column_callback(const struct option *opt,\n+\t\t\t\t    const char *arg, int unset);\n \n #endif\ndiff --git a/command-list.txt b/command-list.txt\nindex a36ee9b..fe06f15 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -20,6 +20,7 @@ git-cherry-pick                         mainporcelain\n git-citool                              mainporcelain\n git-clean                               mainporcelain\n git-clone                               mainporcelain common\n+git-column                              purehelpers\n git-commit                              mainporcelain common\n git-commit-tree                         plumbingmanipulators\n git-config                              ancillarymanipulators\ndiff --git a/git.c b/git.c\nindex 3805616..419e3cc 100644\n--- a/git.c\n+++ b/git.c\n@@ -348,6 +348,7 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"cherry-pick\", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"clean\", cmd_clean, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"clone\", cmd_clone },\n+\t\t{ \"column\", cmd_column },\n \t\t{ \"commit\", cmd_commit, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"commit-tree\", cmd_commit_tree, RUN_SETUP },\n \t\t{ \"config\", cmd_config, RUN_SETUP_GENTLY },\ndiff --git a/parse-options.h b/parse-options.h\nindex 2e811dc..56fcafd 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -238,5 +238,7 @@ extern int parse_opt_noop_cb(const struct option *, const char *, int);\n \t  PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }\n #define OPT__COLOR(var, h) \\\n \tOPT_COLOR_FLAG(0, \"color\", (var), (h))\n+#define OPT_COLUMN(s, l, v, h) \\\n+\t{ OPTION_CALLBACK, (s), (l), (v), \"style\", (h), PARSE_OPT_OPTARG, parseopt_column_callback }\n \n #endif\ndiff --git a/t/t9002-column.sh b/t/t9002-column.sh\nnew file mode 100755\nindex 0000000..b0b6d62\n--- /dev/null\n+++ b/t/t9002-column.sh\n@@ -0,0 +1,27 @@\n+#!/bin/sh\n+\n+test_description='git column'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\tcat >lista <<\\EOF\n+one\n+two\n+three\n+four\n+five\n+six\n+seven\n+eight\n+nine\n+ten\n+eleven\n+EOF\n+'\n+\n+test_expect_success 'never' '\n+\tgit column --mode=never <lista >actual &&\n+\ttest_cmp lista actual\n+'\n+\n+test_done\n-- \n1.7.8.36.g69ee2\n"},{"id":"185419","messageId":"1330170078-29353-4-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330170078-29353-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v6 03/11] Stop starting pager recursively","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-25T11:41:10Z","receivedAt":"2012-02-25T11:41:10Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"git-column can be used as a pager for other git commands, something\nlike this:\n\n    GIT_PAGER=\"git -p column --mode='dense color'\" git -p branch\n\nThe problem with this is that \"git -p column\" also has $GIT_PAGER\nset so the pager runs itself again as a pager, then again and again.\n\nStop this.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n pager.c |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/pager.c b/pager.c\nindex 05584de..4dcb08d 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -73,7 +73,7 @@ void setup_pager(void)\n {\n \tconst char *pager = git_pager(isatty(1));\n \n-\tif (!pager)\n+\tif (!pager || pager_in_use())\n \t\treturn;\n \n \t/*\n-- \n1.7.8.36.g69ee2\n"},{"id":"185421","messageId":"1330170078-29353-5-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330170078-29353-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v6 04/11] column: add columnar layout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-25T11:41:11Z","receivedAt":"2012-02-25T11:41:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"COL_MODE_COLUMN and COL_MODE_ROW fill column by column (or row by row\nrespectively), given the terminal width and how many space between\ncolumns.\n\nStrings are supposed to be in UTF-8. If strings contain ANSI escape\nstrings, COL_ANSI must be specified for correct length calculation.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n column.c          |  131 ++++++++++++++++++++++++++++++++++++++++++++++++++++-\n column.h          |    3 +\n t/t9002-column.sh |   86 +++++++++++++++++++++++++++++++++++\n 3 files changed, 219 insertions(+), 1 deletions(-)\n\ndiff --git a/column.c b/column.c\nindex e62edf7..6dca3b8 100644\n--- a/column.c\n+++ b/column.c\n@@ -2,8 +2,66 @@\n #include \"column.h\"\n #include \"string-list.h\"\n #include \"parse-options.h\"\n+#include \"utf8.h\"\n \n #define MODE(mode) ((mode) & COL_MODE)\n+#define XY2LINEAR(d, x, y) (MODE((d)->mode) == COL_MODE_COLUMN ? \\\n+\t\t\t    (x) * (d)->rows + (y) : \\\n+\t\t\t    (y) * (d)->cols + (x))\n+\n+struct column_data {\n+\tconst struct string_list *list; /* list of all cells */\n+\tint mode;\t\t\t/* COL_MODE */\n+\tint total_width;\t\t/* terminal width */\n+\tint padding;\t\t\t/* cell padding */\n+\tconst char *indent;\t\t/* left most column indentation */\n+\tconst char *nl;\n+\n+\tint rows, cols;\n+\tint *len;\t\t\t/* cell length */\n+};\n+\n+/* return length of 's' in letters, ANSI escapes stripped */\n+static int item_length(int mode, const char *s)\n+{\n+\tint len, i = 0;\n+\tstruct strbuf str = STRBUF_INIT;\n+\n+\tif (!(mode & COL_ANSI))\n+\t\treturn utf8_strwidth(s);\n+\n+\tstrbuf_addstr(&str, s);\n+\twhile ((s = strstr(str.buf + i, \"\\033[\")) != NULL) {\n+\t\tint len = strspn(s + 2, \"0123456789;\");\n+\t\ti = s - str.buf;\n+\t\tstrbuf_remove(&str, i, len + 3); /* \\033[<len><func char> */\n+\t}\n+\tlen = utf8_strwidth(str.buf);\n+\tstrbuf_release(&str);\n+\treturn len;\n+}\n+\n+/*\n+ * Calculate cell width, rows and cols for a table of equal cells, given\n+ * table width and how many spaces between cells.\n+ */\n+static void layout(struct column_data *data, int *width)\n+{\n+\tint i;\n+\n+\t*width = 0;\n+\tfor (i = 0; i < data->list->nr; i++)\n+\t\tif (*width < data->len[i])\n+\t\t\t*width = data->len[i];\n+\n+\t*width += data->padding;\n+\n+\tdata->cols = (data->total_width - strlen(data->indent)) / *width;\n+\tif (data->cols == 0)\n+\t\tdata->cols = 1;\n+\n+\tdata->rows = DIV_ROUND_UP(data->list->nr, data->cols);\n+}\n \n /* Display without layout when COL_ENABLED is not set */\n static void display_plain(const struct string_list *list,\n@@ -15,6 +73,65 @@ static void display_plain(const struct string_list *list,\n \t\tprintf(\"%s%s%s\", indent, list->items[i].string, nl);\n }\n \n+/* Print a cell to stdout with all necessary leading/traling space */\n+static int display_cell(struct column_data *data, int initial_width,\n+\t\t\tconst char *empty_cell, int x, int y)\n+{\n+\tint i, len, newline;\n+\n+\ti = XY2LINEAR(data, x, y);\n+\tif (i >= data->list->nr)\n+\t\treturn -1;\n+\tlen = data->len[i];\n+\tif (MODE(data->mode) == COL_MODE_COLUMN)\n+\t\tnewline = i + data->rows >= data->list->nr;\n+\telse\n+\t\tnewline = x == data->cols - 1 || i == data->list->nr - 1;\n+\n+\tprintf(\"%s%s%s\",\n+\t\t\tx == 0 ? data->indent : \"\",\n+\t\t\tdata->list->items[i].string,\n+\t\t\tnewline ? data->nl : empty_cell + len);\n+\treturn 0;\n+}\n+\n+/* Display COL_MODE_COLUMN or COL_MODE_ROW */\n+static void display_table(const struct string_list *list,\n+\t\t\t  int mode, int total_width,\n+\t\t\t  int padding, const char *indent,\n+\t\t\t  const char *nl)\n+{\n+\tstruct column_data data;\n+\tint x, y, i, initial_width;\n+\tchar *empty_cell;\n+\n+\tmemset(&data, 0, sizeof(data));\n+\tdata.list = list;\n+\tdata.mode = mode;\n+\tdata.total_width = total_width;\n+\tdata.padding = padding;\n+\tdata.indent = indent;\n+\tdata.nl = nl;\n+\n+\tdata.len = xmalloc(sizeof(*data.len) * list->nr);\n+\tfor (i = 0; i < list->nr; i++)\n+\t\tdata.len[i] = item_length(mode, list->items[i].string);\n+\n+\tlayout(&data, &initial_width);\n+\n+\tempty_cell = xmalloc(initial_width + 1);\n+\tmemset(empty_cell, ' ', initial_width);\n+\tempty_cell[initial_width] = '\\0';\n+\tfor (y = 0; y < data.rows; y++) {\n+\t\tfor (x = 0; x < data.cols; x++)\n+\t\t\tif (display_cell(&data, initial_width, empty_cell, x, y))\n+\t\t\t\tbreak;\n+\t}\n+\n+\tfree(data.len);\n+\tfree(empty_cell);\n+}\n+\n void print_columns(const struct string_list *list, unsigned int mode,\n \t\t   struct column_options *opts)\n {\n@@ -36,7 +153,16 @@ void print_columns(const struct string_list *list, unsigned int mode,\n \t\tdisplay_plain(list, indent, nl);\n \t\treturn;\n \t}\n-\tdie(\"BUG: invalid mode %d\", MODE(mode));\n+\n+\tswitch (MODE(mode)) {\n+\tcase COL_MODE_ROW:\n+\tcase COL_MODE_COLUMN:\n+\t\tdisplay_table(list, mode, width, padding, indent, nl);\n+\t\tbreak;\n+\n+\tdefault:\n+\t\tdie(\"BUG: invalid mode %d\", MODE(mode));\n+\t}\n }\n \n struct colopt {\n@@ -98,6 +224,9 @@ static int parse_option(const char *arg, int len,\n \t\t{ ENABLE, \"always\",  1 },\n \t\t{ ENABLE, \"never\",   0 },\n \t\t{ ENABLE, \"auto\",   -1 },\n+\t\t{ MODE,   \"column\", COL_MODE_COLUMN },\n+\t\t{ MODE,   \"row\",    COL_MODE_ROW },\n+\t\t{ OPTION, \"color\",  COL_ANSI },\n \t};\n \tint i;\n \ndiff --git a/column.h b/column.h\nindex 67b1c4f..d9d27c6 100644\n--- a/column.h\n+++ b/column.h\n@@ -2,8 +2,11 @@\n #define COLUMN_H\n \n #define COL_MODE          0x000F\n+#define COL_MODE_COLUMN        0   /* Fill columns before rows */\n+#define COL_MODE_ROW           1   /* Fill rows before columns */\n #define COL_ENABLED      (1 << 4)\n #define COL_ENABLED_SET  (1 << 5)  /* Has COL_ENABLED been set by config? */\n+#define COL_ANSI         (1 << 6)  /* Remove ANSI escapes from string length */\n #define COL_PARSEOPT     (1 << 8)  /* --column is given */\n \n #define explicitly_enable_column(c) \\\ndiff --git a/t/t9002-column.sh b/t/t9002-column.sh\nindex b0b6d62..cffb029 100755\n--- a/t/t9002-column.sh\n+++ b/t/t9002-column.sh\n@@ -24,4 +24,90 @@ test_expect_success 'never' '\n \ttest_cmp lista actual\n '\n \n+test_expect_success '80 columns' '\n+\tcat >expected <<\\EOF &&\n+one    two    three  four   five   six    seven  eight  nine   ten    eleven\n+EOF\n+\tCOLUMNS=80 git column --mode=column <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'COLUMNS = 1' '\n+\tcat >expected <<\\EOF &&\n+one\n+two\n+three\n+four\n+five\n+six\n+seven\n+eight\n+nine\n+ten\n+eleven\n+EOF\n+\tCOLUMNS=1 git column --mode=column <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'width = 1' '\n+\tgit column --mode=column --width=1 <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+COLUMNS=20\n+export COLUMNS\n+\n+test_expect_success '20 columns' '\n+\tcat >expected <<\\EOF &&\n+one    seven\n+two    eight\n+three  nine\n+four   ten\n+five   eleven\n+six\n+EOF\n+\tgit column --mode=column <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, padding 2' '\n+\tcat >expected <<\\EOF &&\n+one     seven\n+two     eight\n+three   nine\n+four    ten\n+five    eleven\n+six\n+EOF\n+\tgit column --mode=column --padding 2 <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, indented' '\n+\tcat >expected <<\\EOF &&\n+  one    seven\n+  two    eight\n+  three  nine\n+  four   ten\n+  five   eleven\n+  six\n+EOF\n+\tgit column --mode=column --indent=\"  \" <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, row first' '\n+\tcat >expected <<\\EOF &&\n+one    two\n+three  four\n+five   six\n+seven  eight\n+nine   ten\n+eleven\n+EOF\n+\tgit column --mode=row <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.7.8.36.g69ee2\n"},{"id":"185422","messageId":"1330170078-29353-6-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330170078-29353-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v6 05/11] column: support columns with different widths","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-25T11:41:12Z","receivedAt":"2012-02-25T11:41:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n column.c          |   80 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n column.h          |    2 +\n t/t9002-column.sh |   48 +++++++++++++++++++++++++++++++\n 3 files changed, 130 insertions(+), 0 deletions(-)\n\ndiff --git a/column.c b/column.c\nindex 6dca3b8..3c77997 100644\n--- a/column.c\n+++ b/column.c\n@@ -19,6 +19,7 @@ struct column_data {\n \n \tint rows, cols;\n \tint *len;\t\t\t/* cell length */\n+\tint *width;\t\t\t/* index to the longest row in column */\n };\n \n /* return length of 's' in letters, ANSI escapes stripped */\n@@ -63,6 +64,69 @@ static void layout(struct column_data *data, int *width)\n \tdata->rows = DIV_ROUND_UP(data->list->nr, data->cols);\n }\n \n+static void compute_column_width(struct column_data *data)\n+{\n+\tint i, x, y;\n+\tfor (x = 0; x < data->cols; x++) {\n+\t\tdata->width[x] = XY2LINEAR(data, x, 0);\n+\t\tfor (y = 0; y < data->rows; y++) {\n+\t\t\ti = XY2LINEAR(data, x, y);\n+\t\t\tif (i >= data->list->nr)\n+\t\t\t\tcontinue;\n+\t\t\tif (data->len[data->width[x]] < data->len[i])\n+\t\t\t\tdata->width[x] = i;\n+\t\t}\n+\t}\n+}\n+\n+/*\n+ * Shrink all columns by shortening them one row each time (and adding\n+ * more columns along the way). Hopefully the longest cell will be\n+ * moved to the next column, column is shrunk so we have more space\n+ * for new columns. The process ends when the whole thing no longer\n+ * fits in data->total_width.\n+ */\n+static void shrink_columns(struct column_data *data)\n+{\n+\tint x, y, total_width, cols, rows;\n+\n+\tdata->width = xrealloc(data->width,\n+\t\t\t       sizeof(*data->width) * data->cols);\n+\tfor (x = 0; x < data->cols; x++) {\n+\t\tdata->width[x] = 0;\n+\t\tfor (y = 0; y < data->rows; y++) {\n+\t\t\tint len1 = data->len[data->width[x]];\n+\t\t\tint len2 = data->len[XY2LINEAR(data, x, y)];\n+\t\t\tif (len1 < len2)\n+\t\t\t\tdata->width[x] = y;\n+\t\t}\n+\t}\n+\n+\twhile (data->rows > 1) {\n+\t\trows = data->rows;\n+\t\tcols = data->cols;\n+\n+\t\tdata->rows--;\n+\t\tdata->cols = DIV_ROUND_UP(data->list->nr, data->rows);\n+\t\tif (data->cols != cols)\n+\t\t\tdata->width = xrealloc(data->width, sizeof(*data->width) * data->cols);\n+\n+\t\tcompute_column_width(data);\n+\n+\t\ttotal_width = strlen(data->indent);\n+\t\tfor (x = 0; x < data->cols; x++) {\n+\t\t\ttotal_width += data->len[data->width[x]];\n+\t\t\ttotal_width += data->padding;\n+\t\t}\n+\t\tif (total_width > data->total_width) {\n+\t\t\tdata->rows = rows;\n+\t\t\tdata->cols = cols;\n+\t\t\tcompute_column_width(data);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+}\n+\n /* Display without layout when COL_ENABLED is not set */\n static void display_plain(const struct string_list *list,\n \t\t\t  const char *indent, const char *nl)\n@@ -82,7 +146,18 @@ static int display_cell(struct column_data *data, int initial_width,\n \ti = XY2LINEAR(data, x, y);\n \tif (i >= data->list->nr)\n \t\treturn -1;\n+\n \tlen = data->len[i];\n+\tif (data->width && data->len[data->width[x]] < initial_width) {\n+\t\t/*\n+\t\t * empty_cell has initial_width chars, if real column\n+\t\t * is narrower, increase len a bit so we fill less\n+\t\t * space.\n+\t\t */\n+\t\tlen += initial_width - data->len[data->width[x]];\n+\t\tlen -= data->padding;\n+\t}\n+\n \tif (MODE(data->mode) == COL_MODE_COLUMN)\n \t\tnewline = i + data->rows >= data->list->nr;\n \telse\n@@ -119,6 +194,9 @@ static void display_table(const struct string_list *list,\n \n \tlayout(&data, &initial_width);\n \n+\tif (mode & COL_DENSE)\n+\t\tshrink_columns(&data);\n+\n \tempty_cell = xmalloc(initial_width + 1);\n \tmemset(empty_cell, ' ', initial_width);\n \tempty_cell[initial_width] = '\\0';\n@@ -129,6 +207,7 @@ static void display_table(const struct string_list *list,\n \t}\n \n \tfree(data.len);\n+\tfree(data.width);\n \tfree(empty_cell);\n }\n \n@@ -227,6 +306,7 @@ static int parse_option(const char *arg, int len,\n \t\t{ MODE,   \"column\", COL_MODE_COLUMN },\n \t\t{ MODE,   \"row\",    COL_MODE_ROW },\n \t\t{ OPTION, \"color\",  COL_ANSI },\n+\t\t{ OPTION, \"dense\",  COL_DENSE },\n \t};\n \tint i;\n \ndiff --git a/column.h b/column.h\nindex d9d27c6..eb03c6c 100644\n--- a/column.h\n+++ b/column.h\n@@ -7,6 +7,8 @@\n #define COL_ENABLED      (1 << 4)\n #define COL_ENABLED_SET  (1 << 5)  /* Has COL_ENABLED been set by config? */\n #define COL_ANSI         (1 << 6)  /* Remove ANSI escapes from string length */\n+#define COL_DENSE        (1 << 7)  /* Shrink columns when possible,\n+\t\t\t\t      making space for more columns */\n #define COL_PARSEOPT     (1 << 8)  /* --column is given */\n \n #define explicitly_enable_column(c) \\\ndiff --git a/t/t9002-column.sh b/t/t9002-column.sh\nindex cffb029..fe7a30e 100755\n--- a/t/t9002-column.sh\n+++ b/t/t9002-column.sh\n@@ -71,6 +71,30 @@ EOF\n \ttest_cmp expected actual\n '\n \n+test_expect_success '20 columns, nodense' '\n+\tcat >expected <<\\EOF &&\n+one    seven\n+two    eight\n+three  nine\n+four   ten\n+five   eleven\n+six\n+EOF\n+\tgit column --mode=column,nodense < lista > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, dense' '\n+\tcat >expected <<\\EOF &&\n+one   five  nine\n+two   six   ten\n+three seven eleven\n+four  eight\n+EOF\n+\tgit column --mode=column,dense < lista > actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success '20 columns, padding 2' '\n \tcat >expected <<\\EOF &&\n one     seven\n@@ -110,4 +134,28 @@ EOF\n \ttest_cmp expected actual\n '\n \n+test_expect_success '20 columns, row first, nodense' '\n+\tcat >expected <<\\EOF &&\n+one    two\n+three  four\n+five   six\n+seven  eight\n+nine   ten\n+eleven\n+EOF\n+\tgit column --mode=row,nodense <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, row first, dense' '\n+\tcat >expected <<\\EOF &&\n+one   two    three\n+four  five   six\n+seven eight  nine\n+ten   eleven\n+EOF\n+\tgit column --mode=row,dense <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.7.8.36.g69ee2\n"},{"id":"185424","messageId":"1330170078-29353-7-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330170078-29353-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v6 06/11] column: add column.ui for default column output settings","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-25T11:41:13Z","receivedAt":"2012-02-25T11:41:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt     |   26 ++++++++++++++++++++++++++\n Documentation/git-column.txt |    6 +++++-\n builtin/column.c             |   21 +++++++++++++++++++++\n column.c                     |   28 ++++++++++++++++++++++++++++\n column.h                     |    2 ++\n 5 files changed, 82 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex abeb82b..5216598 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -821,6 +821,32 @@ color.ui::\n \t`never` if you prefer git commands not to use color unless enabled\n \texplicitly with some other configuration or the `--color` option.\n \n+column.ui::\n+\tSpecify whether supported commands should output in columns.\n+\tThis variable consists of a list of tokens separated by spaces\n+\tor commas:\n++\n+--\n+`always`;;\n+\talways show in columns\n+`never`;;\n+\tnever show in columns\n+`auto`;;\n+\tshow in columns if the output is to the terminal\n+`column`;;\n+\tfill columns before rows (default)\n+`row`;;\n+\tfill rows before columns\n+`dense`;;\n+\tmake unequal size columns to utilize more space\n+`nodense`;;\n+\tmake equal size columns\n+`color`;;\n+\tinput contains ANSI escape sequence for coloring\n+--\n++\n+\tThis option defaults to 'never'.\n+\n commit.status::\n \tA boolean to enable/disable inclusion of status information in the\n \tcommit message template when using an editor to prepare the commit\ndiff --git a/Documentation/git-column.txt b/Documentation/git-column.txt\nindex 508b85f..94fd7ac 100644\n--- a/Documentation/git-column.txt\n+++ b/Documentation/git-column.txt\n@@ -8,7 +8,7 @@ git-column - Display data in columns\n SYNOPSIS\n --------\n [verse]\n-'git column' [--mode=<mode> | --rawmode=<n>] [--width=<width>]\n+'git column' [--command=<name>] [--[raw]mode=<mode>] [--width=<width>]\n \t     [--indent=<string>] [--nl=<string>] [--pading=<n>]\n \n DESCRIPTION\n@@ -17,6 +17,10 @@ This command formats its input into multiple columns.\n \n OPTIONS\n -------\n+--command=<name>::\n+\tLook up layout mode using configuration variable column.<name> and\n+\tcolumn.ui.\n+\n --mode=<mode>::\n \tSpecify layout mode. See configuration variable column.ui for option\n \tsyntax.\ndiff --git a/builtin/column.c b/builtin/column.c\nindex 3b0f74e..102d71b 100644\n--- a/builtin/column.c\n+++ b/builtin/column.c\n@@ -11,12 +11,19 @@ static const char * const builtin_column_usage[] = {\n };\n static unsigned int colopts;\n \n+static int column_config(const char *var, const char *value, void *cb)\n+{\n+\treturn git_column_config(var, value, cb, &colopts);\n+}\n+\n int cmd_column(int argc, const char **argv, const char *prefix)\n {\n \tstruct string_list list = STRING_LIST_INIT_DUP;\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct column_options copts;\n+\tconst char *command = NULL, *real_command = NULL;\n \tstruct option options[] = {\n+\t\tOPT_STRING(0, \"command\", &real_command, \"name\", \"lookup config vars\"),\n \t\tOPT_COLUMN(0, \"mode\", &colopts, \"layout to use\"),\n \t\tOPT_INTEGER(0, \"rawmode\", &colopts, \"layout to use\"),\n \t\tOPT_INTEGER(0, \"width\", &copts.width, \"Maximum width\"),\n@@ -26,6 +33,15 @@ int cmd_column(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n+\t/* This one is special and must be the first one */\n+\tif (argc > 1 && !prefixcmp(argv[1], \"--command=\")) {\n+\t\tint nonitok = 0;\n+\t\tsetup_git_directory_gently(&nonitok);\n+\n+\t\tcommand = argv[1] + 10;\n+\t\tgit_config(column_config, (void *)command);\n+\t}\n+\n \tmemset(&copts, 0, sizeof(copts));\n \tcopts.width = term_columns();\n \tcopts.padding = 1;\n@@ -33,6 +49,11 @@ int cmd_column(int argc, const char **argv, const char *prefix)\n \tif (argc)\n \t\tusage_with_options(builtin_column_usage, options);\n \n+\tif (real_command || command) {\n+\t\tif (!real_command || !command || strcmp(real_command, command))\n+\t\t\tdie(_(\"--command must be the first argument\"));\n+\t}\n+\n \twhile (!strbuf_getline(&sb, stdin, '\\n'))\n \t\tstring_list_append(&list, sb.buf);\n \ndiff --git a/column.c b/column.c\nindex 3c77997..94fd1a1 100644\n--- a/column.c\n+++ b/column.c\n@@ -2,6 +2,7 @@\n #include \"column.h\"\n #include \"string-list.h\"\n #include \"parse-options.h\"\n+#include \"color.h\"\n #include \"utf8.h\"\n \n #define MODE(mode) ((mode) & COL_MODE)\n@@ -363,6 +364,33 @@ int git_config_column(unsigned int *mode, const char *value,\n \treturn 0;\n }\n \n+static int column_config(const char *var, const char *value,\n+\t\t\t const char *key, unsigned int *colopts)\n+{\n+\tif (git_config_column(colopts, value, -1))\n+\t\treturn error(\"invalid %s mode %s\", key, value);\n+\treturn 0;\n+}\n+\n+int git_column_config(const char *var, const char *value,\n+\t\t      const char *command, unsigned int *colopts)\n+{\n+\tif (!strcmp(var, \"column.ui\"))\n+\t\treturn column_config(var, value, \"column.ui\", colopts);\n+\n+\tif (command) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tint ret = 0;\n+\t\tstrbuf_addf(&sb, \"column.%s\", command);\n+\t\tif (!strcmp(var, sb.buf))\n+\t\t\tret = column_config(var, value, sb.buf, colopts);\n+\t\tstrbuf_release(&sb);\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n int parseopt_column_callback(const struct option *opt,\n \t\t\t     const char *arg, int unset)\n {\ndiff --git a/column.h b/column.h\nindex eb03c6c..43528da 100644\n--- a/column.h\n+++ b/column.h\n@@ -27,6 +27,8 @@ extern void print_columns(const struct string_list *list,\n \t\t\t  struct column_options *opts);\n extern int git_config_column(unsigned int *mode, const char *value,\n \t\t\t     int stdout_is_tty);\n+extern int git_column_config(const char *var, const char *value,\n+\t\t\t     const char *command, unsigned int *colopts);\n \n struct option;\n extern int parseopt_column_callback(const struct option *opt,\n-- \n1.7.8.36.g69ee2\n"},{"id":"185423","messageId":"1330170078-29353-8-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330170078-29353-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v6 07/11] help: reuse print_columns() for help -a","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-25T11:41:14Z","receivedAt":"2012-02-25T11:41:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n help.c |   48 ++++++++++++++----------------------------------\n 1 files changed, 14 insertions(+), 34 deletions(-)\n\ndiff --git a/help.c b/help.c\nindex 14eefc9..d6d2e19 100644\n--- a/help.c\n+++ b/help.c\n@@ -4,6 +4,8 @@\n #include \"levenshtein.h\"\n #include \"help.h\"\n #include \"common-cmds.h\"\n+#include \"string-list.h\"\n+#include \"column.h\"\n \n void add_cmdname(struct cmdnames *cmds, const char *name, int len)\n {\n@@ -70,31 +72,18 @@ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)\n \tcmds->cnt = cj;\n }\n \n-static void pretty_print_string_list(struct cmdnames *cmds, int longest)\n+static void pretty_print_string_list(struct cmdnames *cmds)\n {\n-\tint cols = 1, rows;\n-\tint space = longest + 1; /* min 1 SP between words */\n-\tint max_cols = term_columns() - 1; /* don't print *on* the edge */\n-\tint i, j;\n-\n-\tif (space < max_cols)\n-\t\tcols = max_cols / space;\n-\trows = DIV_ROUND_UP(cmds->cnt, cols);\n-\n-\tfor (i = 0; i < rows; i++) {\n-\t\tprintf(\"  \");\n+\tstruct string_list list = STRING_LIST_INIT_NODUP;\n+\tstruct column_options copts;\n+\tint i;\n \n-\t\tfor (j = 0; j < cols; j++) {\n-\t\t\tint n = j * rows + i;\n-\t\t\tint size = space;\n-\t\t\tif (n >= cmds->cnt)\n-\t\t\t\tbreak;\n-\t\t\tif (j == cols-1 || n + rows >= cmds->cnt)\n-\t\t\t\tsize = 1;\n-\t\t\tprintf(\"%-*s\", size, cmds->names[n]->name);\n-\t\t}\n-\t\tputchar('\\n');\n-\t}\n+\tfor (i = 0; i < cmds->cnt; i++)\n+\t\tstring_list_append(&list, cmds->names[i]->name);\n+\tmemset(&copts, 0, sizeof(copts));\n+\tcopts.indent = \"  \";\n+\tprint_columns(&list, COL_MODE_COLUMN | COL_ENABLED, &copts);\n+\tstring_list_clear(&list, 0);\n }\n \n static int is_executable(const char *name)\n@@ -206,22 +195,13 @@ void load_command_list(const char *prefix,\n void list_commands(const char *title, struct cmdnames *main_cmds,\n \t\t   struct cmdnames *other_cmds)\n {\n-\tint i, longest = 0;\n-\n-\tfor (i = 0; i < main_cmds->cnt; i++)\n-\t\tif (longest < main_cmds->names[i]->len)\n-\t\t\tlongest = main_cmds->names[i]->len;\n-\tfor (i = 0; i < other_cmds->cnt; i++)\n-\t\tif (longest < other_cmds->names[i]->len)\n-\t\t\tlongest = other_cmds->names[i]->len;\n-\n \tif (main_cmds->cnt) {\n \t\tconst char *exec_path = git_exec_path();\n \t\tprintf(\"available %s in '%s'\\n\", title, exec_path);\n \t\tprintf(\"----------------\");\n \t\tmput_char('-', strlen(title) + strlen(exec_path));\n \t\tputchar('\\n');\n-\t\tpretty_print_string_list(main_cmds, longest);\n+\t\tpretty_print_string_list(main_cmds);\n \t\tputchar('\\n');\n \t}\n \n@@ -230,7 +210,7 @@ void list_commands(const char *title, struct cmdnames *main_cmds,\n \t\tprintf(\"---------------------------------------\");\n \t\tmput_char('-', strlen(title));\n \t\tputchar('\\n');\n-\t\tpretty_print_string_list(other_cmds, longest);\n+\t\tpretty_print_string_list(other_cmds);\n \t\tputchar('\\n');\n \t}\n }\n-- \n1.7.8.36.g69ee2\n"},{"id":"185425","messageId":"1330170078-29353-9-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330170078-29353-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v6 08/11] branch: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-25T11:41:15Z","receivedAt":"2012-02-25T11:41:15Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt     |    4 ++\n Documentation/git-branch.txt |    9 +++++\n Makefile                     |    2 +-\n builtin/branch.c             |   32 +++++++++++++++--\n t/t3200-branch.sh            |   77 ++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 119 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 5216598..c14db27 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -847,6 +847,10 @@ column.ui::\n +\n \tThis option defaults to 'never'.\n \n+column.branch::\n+\tSpecify whether to output branch listing in `git branch` in columns.\n+\tSee `column.ui` for details.\n+\n commit.status::\n \tA boolean to enable/disable inclusion of status information in the\n \tcommit message template when using an editor to prepare the commit\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex 0427e80..ba5cccb 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -10,6 +10,7 @@ SYNOPSIS\n [verse]\n 'git branch' [--color[=<when>] | --no-color] [-r | -a]\n \t[--list] [-v [--abbrev=<length> | --no-abbrev]]\n+\t[--column[=<options>] | --no-column]\n \t[(--merged | --no-merged | --contains) [<commit>]] [<pattern>...]\n 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]\n 'git branch' (-m | -M) [<oldbranch>] <newbranch>\n@@ -107,6 +108,14 @@ OPTIONS\n \tdefault to color output.\n \tSame as `--color=never`.\n \n+--column[=<options>]::\n+--no-column::\n+\tDisplay branch listing in columns. See configuration variable\n+\tcolumn.branch for option syntax.`--column` and `--no-column`\n+\twithout options are equivalent to 'always' and 'never' respectively.\n++\n+This option is only applicable in non-verbose mode.\n+\n -r::\n --remotes::\n \tList or delete (if used with -d) the remote-tracking branches.\ndiff --git a/Makefile b/Makefile\nindex 0998f0d..320d3f8 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2168,7 +2168,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o url.o http-backend.o: url.h\n-column.o help.o pager.o: column.h\n+builtin/branch.o column.o help.o pager.o: column.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex cb17bc3..611cc0e 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -15,6 +15,8 @@\n #include \"branch.h\"\n #include \"diff.h\"\n #include \"revision.h\"\n+#include \"string-list.h\"\n+#include \"column.h\"\n \n static const char * const builtin_branch_usage[] = {\n \t\"git branch [options] [-r | -a] [--merged | --no-merged]\",\n@@ -53,6 +55,9 @@ static enum merge_filter {\n } merge_filter;\n static unsigned char merge_filter_ref[20];\n \n+static struct string_list output = STRING_LIST_INIT_DUP;\n+static unsigned int colopts;\n+\n static int parse_branch_color_slot(const char *var, int ofs)\n {\n \tif (!strcasecmp(var+ofs, \"plain\"))\n@@ -70,6 +75,8 @@ static int parse_branch_color_slot(const char *var, int ofs)\n \n static int git_branch_config(const char *var, const char *value, void *cb)\n {\n+\tif (!prefixcmp(var, \"column.\"))\n+\t\treturn git_column_config(var, value, \"branch\", &colopts);\n \tif (!strcmp(var, \"color.branch\")) {\n \t\tbranch_use_color = git_config_colorbool(var, value);\n \t\treturn 0;\n@@ -474,7 +481,12 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,\n \telse if (verbose)\n \t\t/* \" f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h\" */\n \t\tadd_verbose_info(&out, item, verbose, abbrev);\n-\tprintf(\"%s\\n\", out.buf);\n+\tif (colopts & COL_ENABLED) {\n+\t\tassert(!verbose && \"--column and --verbose are incompatible\");\n+\t\tstring_list_append(&output, out.buf);\n+\t} else {\n+\t\tprintf(\"%s\\n\", out.buf);\n+\t}\n \tstrbuf_release(&name);\n \tstrbuf_release(&out);\n }\n@@ -727,6 +739,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\t\tPARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,\n \t\t\topt_parse_merge_filter, (intptr_t) \"HEAD\",\n \t\t},\n+\t\tOPT_COLUMN(0, \"column\", &colopts, \"list branches in columns\"),\n \t\tOPT_END(),\n \t};\n \n@@ -749,6 +762,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t}\n \thashcpy(merge_filter_ref, head_sha1);\n \n+\n+\tcolopts |= COL_ANSI;\n \targc = parse_options(argc, argv, prefix, options, builtin_branch_usage,\n \t\t\t     0);\n \n@@ -760,12 +775,21 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \n \tif (abbrev == -1)\n \t\tabbrev = DEFAULT_ABBREV;\n+\tif (verbose) {\n+\t\tif (explicitly_enable_column(colopts))\n+\t\t\tdie(_(\"--column and --verbose are incompatible\"));\n+\t\tcolopts = 0;\n+\t}\n \n \tif (delete)\n \t\treturn delete_branches(argc, argv, delete > 1, kinds);\n-\telse if (list)\n-\t\treturn print_ref_list(kinds, detached, verbose, abbrev,\n-\t\t\t\t      with_commit, argv);\n+\telse if (list) {\n+\t\tint ret = print_ref_list(kinds, detached, verbose, abbrev,\n+\t\t\t\t\t with_commit, argv);\n+\t\tprint_columns(&output, colopts, NULL);\n+\t\tstring_list_clear(&output, 0);\n+\t\treturn ret;\n+\t}\n \telse if (edit_description) {\n \t\tconst char *branch_name;\n \t\tstruct strbuf branch_ref = STRBUF_INIT;\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex dd1aceb..c38592a 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -160,6 +160,83 @@ test_expect_success 'git branch --list -d t should fail' '\n \ttest_path_is_missing .git/refs/heads/t\n '\n \n+test_expect_success 'git branch --column' '\n+\tCOLUMNS=80 git branch --column=column >actual &&\n+\tcat >expected <<\\EOF &&\n+  a/b/c     bam       foo       l       * master    n         o/p       r\n+  abc       bar       j/k       m/m       master2   o/o       q\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'git branch --column with an extremely long branch name' '\n+\tlong=this/is/a/part/of/long/branch/name &&\n+\tlong=z$long/$long/$long/$long &&\n+\ttest_when_finished \"git branch -d $long\" &&\n+\tgit branch $long &&\n+\tCOLUMNS=80 git branch --column=column >actual &&\n+\tcat >expected <<EOF &&\n+  a/b/c\n+  abc\n+  bam\n+  bar\n+  foo\n+  j/k\n+  l\n+  m/m\n+* master\n+  master2\n+  n\n+  o/o\n+  o/p\n+  q\n+  r\n+  $long\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'git branch with column.*' '\n+\tgit config column.ui column &&\n+\tgit config column.branch \"dense\" &&\n+\tCOLUMNS=80 git branch >actual &&\n+\tgit config --unset column.branch &&\n+\tgit config --unset column.ui &&\n+\tcat >expected <<\\EOF &&\n+  a/b/c   bam   foo   l   * master    n     o/p   r\n+  abc     bar   j/k   m/m   master2   o/o   q\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'git branch --column -v should fail' '\n+\ttest_must_fail git branch --column -v\n+'\n+\n+test_expect_success 'git branch -v with column.ui ignored' '\n+\tgit config column.ui column &&\n+\tCOLUMNS=80 git branch -v | cut -c -10 | sed \"s/ *$//\" >actual &&\n+\tgit config --unset column.ui &&\n+\tcat >expected <<\\EOF &&\n+  a/b/c\n+  abc\n+  bam\n+  bar\n+  foo\n+  j/k\n+  l\n+  m/m\n+* master\n+  master2\n+  n\n+  o/o\n+  o/p\n+  q\n+  r\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n mv .git/config .git/config-saved\n \n test_expect_success 'git branch -m q q2 without config should succeed' '\n-- \n1.7.8.36.g69ee2\n"},{"id":"185426","messageId":"1330170078-29353-10-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330170078-29353-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v6 09/11] status: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-25T11:41:16Z","receivedAt":"2012-02-25T11:41:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt     |    4 ++++\n Documentation/git-status.txt |    7 +++++++\n Makefile                     |    2 +-\n builtin/commit.c             |    6 ++++++\n t/t7508-status.sh            |   24 ++++++++++++++++++++++++\n wt-status.c                  |   28 ++++++++++++++++++++++++++--\n wt-status.h                  |    1 +\n 7 files changed, 69 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex c14db27..ebb210c 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -851,6 +851,10 @@ column.branch::\n \tSpecify whether to output branch listing in `git branch` in columns.\n \tSee `column.ui` for details.\n \n+column.status::\n+\tSpecify whether to output untracked files in `git status` in columns.\n+\tSee `column.ui` for details.\n+\n commit.status::\n \tA boolean to enable/disable inclusion of status information in the\n \tcommit message template when using an editor to prepare the commit\ndiff --git a/Documentation/git-status.txt b/Documentation/git-status.txt\nindex 3d51717..2f87207 100644\n--- a/Documentation/git-status.txt\n+++ b/Documentation/git-status.txt\n@@ -77,6 +77,13 @@ configuration variable documented in linkgit:git-config[1].\n \tTerminate entries with NUL, instead of LF.  This implies\n \tthe `--porcelain` output format if no other format is given.\n \n+--column[=<options>]::\n+--no-column::\n+\tDisplay untracked files in columns. See configuration variable\n+\tcolumn.status for option syntax.`--column` and `--no-column`\n+\twithout options are equivalent to 'always' and 'never'\n+\trespectively.\n+\n \n OUTPUT\n ------\ndiff --git a/Makefile b/Makefile\nindex 320d3f8..d9c5f00 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2168,7 +2168,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o url.o http-backend.o: url.h\n-builtin/branch.o column.o help.o pager.o: column.h\n+builtin/branch.o builtin/commit.o column.o help.o pager.o: column.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 3714582..b42b83f 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -27,6 +27,7 @@\n #include \"quote.h\"\n #include \"submodule.h\"\n #include \"gpg-interface.h\"\n+#include \"column.h\"\n \n static const char * const builtin_commit_usage[] = {\n \t\"git commit [options] [--] <filepattern>...\",\n@@ -88,6 +89,7 @@ static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;\n static int no_post_rewrite, allow_empty_message;\n static char *untracked_files_arg, *force_date, *ignore_submodule_arg;\n static char *sign_commit;\n+static unsigned int colopts;\n \n /*\n  * The default commit message cleanup mode will remove the lines\n@@ -1145,6 +1147,8 @@ static int git_status_config(const char *k, const char *v, void *cb)\n {\n \tstruct wt_status *s = cb;\n \n+\tif (!prefixcmp(k, \"column.\"))\n+\t\treturn git_column_config(k, v, \"status\", &colopts);\n \tif (!strcmp(k, \"status.submodulesummary\")) {\n \t\tint is_bool;\n \t\ts->submodule_summary = git_config_bool_or_int(k, v, &is_bool);\n@@ -1210,6 +1214,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \t\t{ OPTION_STRING, 0, \"ignore-submodules\", &ignore_submodule_arg, \"when\",\n \t\t  \"ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)\",\n \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n+\t\tOPT_COLUMN(0, \"column\", &colopts, \"list untracked files in columns\"),\n \t\tOPT_END(),\n \t};\n \n@@ -1223,6 +1228,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix,\n \t\t\t     builtin_status_options,\n \t\t\t     builtin_status_usage, 0);\n+\ts.colopts = colopts;\n \n \tif (null_termination && status_format == STATUS_FORMAT_LONG)\n \t\tstatus_format = STATUS_FORMAT_PORCELAIN;\ndiff --git a/t/t7508-status.sh b/t/t7508-status.sh\nindex fc57b13..8f5cfac 100755\n--- a/t/t7508-status.sh\n+++ b/t/t7508-status.sh\n@@ -59,6 +59,30 @@ test_expect_success 'status (1)' '\n \ttest_i18ngrep \"use \\\"git rm --cached <file>\\.\\.\\.\\\" to unstage\" output\n '\n \n+test_expect_success 'status --column' '\n+\tCOLUMNS=50 git status --column=\"column dense\" >output &&\n+\tcat >expect <<\\EOF &&\n+# On branch master\n+# Changes to be committed:\n+#   (use \"git reset HEAD <file>...\" to unstage)\n+#\n+#\tnew file:   dir2/added\n+#\n+# Changes not staged for commit:\n+#   (use \"git add <file>...\" to update what will be committed)\n+#   (use \"git checkout -- <file>...\" to discard changes in working directory)\n+#\n+#\tmodified:   dir1/modified\n+#\n+# Untracked files:\n+#   (use \"git add <file>...\" to include in what will be committed)\n+#\n+#\tdir1/untracked dir2/untracked untracked\n+#\tdir2/modified  output\n+EOF\n+\ttest_cmp expect output\n+'\n+\n cat >expect <<\\EOF\n # On branch master\n # Changes to be committed:\ndiff --git a/wt-status.c b/wt-status.c\nindex 9ffc535..eee059e 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -11,6 +11,7 @@\n #include \"remote.h\"\n #include \"refs.h\"\n #include \"submodule.h\"\n+#include \"column.h\"\n \n static char default_wt_status_colors[][COLOR_MAXLEN] = {\n \tGIT_COLOR_NORMAL, /* WT_STATUS_HEADER */\n@@ -641,6 +642,8 @@ static void wt_status_print_other(struct wt_status *s,\n {\n \tint i;\n \tstruct strbuf buf = STRBUF_INIT;\n+\tstatic struct string_list output = STRING_LIST_INIT_DUP;\n+\tstruct column_options copts;\n \n \tif (!l->nr)\n \t\treturn;\n@@ -649,12 +652,33 @@ static void wt_status_print_other(struct wt_status *s,\n \n \tfor (i = 0; i < l->nr; i++) {\n \t\tstruct string_list_item *it;\n+\t\tconst char *path;\n \t\tit = &(l->items[i]);\n+\t\tpath = quote_path(it->string, strlen(it->string),\n+\t\t\t\t  &buf, s->prefix);\n+\t\tif (s->colopts & COL_ENABLED) {\n+\t\t\tstring_list_append(&output, path);\n+\t\t\tcontinue;\n+\t\t}\n \t\tstatus_printf(s, color(WT_STATUS_HEADER, s), \"\\t\");\n \t\tstatus_printf_more(s, color(WT_STATUS_UNTRACKED, s),\n-\t\t\t\"%s\\n\", quote_path(it->string, strlen(it->string),\n-\t\t\t\t\t    &buf, s->prefix));\n+\t\t\t\t   \"%s\\n\", path);\n \t}\n+\n+\tstrbuf_release(&buf);\n+\tif ((s->colopts & COL_ENABLED) == 0)\n+\t\treturn;\n+\n+\tstrbuf_addf(&buf, \"%s#\\t%s\",\n+\t\t    color(WT_STATUS_HEADER, s),\n+\t\t    color(WT_STATUS_UNTRACKED, s));\n+\tmemset(&copts, 0, sizeof(copts));\n+\tcopts.padding = 1;\n+\tcopts.indent = buf.buf;\n+\tif (want_color(s->use_color))\n+\t\tcopts.nl = GIT_COLOR_RESET \"\\n\";\n+\tprint_columns(&output, s->colopts, &copts);\n+\tstring_list_clear(&output, 0);\n \tstrbuf_release(&buf);\n }\n \ndiff --git a/wt-status.h b/wt-status.h\nindex 682b4c8..6dd7207 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -56,6 +56,7 @@ struct wt_status {\n \tenum untracked_status_type show_untracked_files;\n \tconst char *ignore_submodule_arg;\n \tchar color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];\n+\tint colopts;\n \n \t/* These are computed during processing of the individual sections */\n \tint commitable;\n-- \n1.7.8.36.g69ee2\n"},{"id":"185427","messageId":"1330170078-29353-11-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330170078-29353-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v6 10/11] column: support piping stdout to external git-column process","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-25T11:41:17Z","receivedAt":"2012-02-25T11:41:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"For too complicated output handling, it'd be easier to just spawn\ngit-column and redirect stdout to it. This patch provides helpers\nto do that.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n column.c |   69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n column.h |    3 ++\n 2 files changed, 72 insertions(+), 0 deletions(-)\n\ndiff --git a/column.c b/column.c\nindex 94fd1a1..ecf2d14 100644\n--- a/column.c\n+++ b/column.c\n@@ -2,6 +2,7 @@\n #include \"column.h\"\n #include \"string-list.h\"\n #include \"parse-options.h\"\n+#include \"run-command.h\"\n #include \"color.h\"\n #include \"utf8.h\"\n \n@@ -407,3 +408,71 @@ int parseopt_column_callback(const struct option *opt,\n \t*mode |= COL_ENABLED | COL_ENABLED_SET;\n \treturn 0;\n }\n+\n+static int fd_out = -1;\n+static struct child_process column_process;\n+\n+int run_column_filter(int colopts, const struct column_options *opts)\n+{\n+\tconst char *av[10];\n+\tint ret, ac = 0;\n+\tstruct strbuf sb_colopt  = STRBUF_INIT;\n+\tstruct strbuf sb_width   = STRBUF_INIT;\n+\tstruct strbuf sb_padding = STRBUF_INIT;\n+\n+\tif (fd_out != -1)\n+\t\treturn -1;\n+\n+\tav[ac++] = \"column\";\n+\tstrbuf_addf(&sb_colopt, \"--rawmode=%d\", colopts);\n+\tav[ac++] = sb_colopt.buf;\n+\tif (opts->width) {\n+\t\tstrbuf_addf(&sb_width, \"--width=%d\", opts->width);\n+\t\tav[ac++] = sb_width.buf;\n+\t}\n+\tif (opts->indent) {\n+\t\tav[ac++] = \"--indent\";\n+\t\tav[ac++] = opts->indent;\n+\t}\n+\tif (opts->padding) {\n+\t\tstrbuf_addf(&sb_padding, \"--padding=%d\", opts->padding);\n+\t\tav[ac++] = sb_padding.buf;\n+\t}\n+\tav[ac] = NULL;\n+\n+\tfflush(stdout);\n+\tmemset(&column_process, 0, sizeof(column_process));\n+\tcolumn_process.in = -1;\n+\tcolumn_process.out = dup(1);\n+\tcolumn_process.git_cmd = 1;\n+\tcolumn_process.argv = av;\n+\n+\tret = start_command(&column_process);\n+\n+\tstrbuf_release(&sb_colopt);\n+\tstrbuf_release(&sb_width);\n+\tstrbuf_release(&sb_padding);\n+\n+\tif (ret)\n+\t\treturn -2;\n+\n+\tfd_out = dup(1);\n+\tclose(1);\n+\tdup2(column_process.in, 1);\n+\tclose(column_process.in);\n+\treturn 0;\n+}\n+\n+int stop_column_filter(void)\n+{\n+\tif (fd_out == -1)\n+\t\treturn -1;\n+\n+\tfflush(stdout);\n+\tclose(1);\n+\tfinish_command(&column_process);\n+\tdup2(fd_out, 1);\n+\tclose(fd_out);\n+\tfd_out = -1;\n+\treturn 0;\n+}\ndiff --git a/column.h b/column.h\nindex 43528da..cb81c8a 100644\n--- a/column.h\n+++ b/column.h\n@@ -34,4 +34,7 @@ struct option;\n extern int parseopt_column_callback(const struct option *opt,\n \t\t\t\t    const char *arg, int unset);\n \n+extern int run_column_filter(int colopts, const struct column_options *);\n+extern int stop_column_filter();\n+\n #endif\n-- \n1.7.8.36.g69ee2\n"},{"id":"185428","messageId":"1330170078-29353-12-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330170078-29353-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v6 11/11] tag: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-25T11:41:18Z","receivedAt":"2012-02-25T11:41:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt  |    4 ++++\n Documentation/git-tag.txt |    9 +++++++++\n Makefile                  |    2 +-\n builtin/tag.c             |   26 +++++++++++++++++++++++---\n t/t7004-tag.sh            |   44 ++++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 81 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex ebb210c..145336a 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -855,6 +855,10 @@ column.status::\n \tSpecify whether to output untracked files in `git status` in columns.\n \tSee `column.ui` for details.\n \n+column.tag::\n+\tSpecify whether to output tag listing in `git tag` in columns.\n+\tSee `column.ui` for details.\n+\n commit.status::\n \tA boolean to enable/disable inclusion of status information in the\n \tcommit message template when using an editor to prepare the commit\ndiff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt\nindex 8d32b9a..e36a7c3 100644\n--- a/Documentation/git-tag.txt\n+++ b/Documentation/git-tag.txt\n@@ -13,6 +13,7 @@ SYNOPSIS\n \t<tagname> [<commit> | <object>]\n 'git tag' -d <tagname>...\n 'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]\n+\t[--column[=<options>] | --no-column] [<pattern>...]\n \t[<pattern>...]\n 'git tag' -v <tagname>...\n \n@@ -84,6 +85,14 @@ OPTIONS\n \tusing fnmatch(3)).  Multiple patterns may be given; if any of\n \tthem matches, the tag is shown.\n \n+--column[=<options>]::\n+--no-column::\n+\tDisplay tag listing in columns. See configuration variable\n+\tcolumn.tag for option syntax.`--column` and `--no-column`\n+\twithout options are equivalent to 'always' and 'never' respectively.\n++\n+This option is only applicable when listing tags without annotation lines.\n+\n --contains <commit>::\n \tOnly list tags which contain the specified commit.\n \ndiff --git a/Makefile b/Makefile\nindex d9c5f00..65fc6b9 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2168,7 +2168,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o url.o http-backend.o: url.h\n-builtin/branch.o builtin/commit.o column.o help.o pager.o: column.h\n+builtin/branch.o builtin/commit.o builtin/tag.o column.o help.o pager.o: column.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex fe7e5e5..e99cbff 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -16,6 +16,7 @@\n #include \"revision.h\"\n #include \"gpg-interface.h\"\n #include \"sha1-array.h\"\n+#include \"column.h\"\n \n static const char * const git_tag_usage[] = {\n \t\"git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]\",\n@@ -33,6 +34,7 @@ struct tag_filter {\n };\n \n static struct sha1_array points_at;\n+static unsigned int colopts;\n \n static int match_pattern(const char **patterns, const char *ref)\n {\n@@ -263,6 +265,8 @@ static int git_tag_config(const char *var, const char *value, void *cb)\n \tint status = git_gpg_config(var, value, cb);\n \tif (status)\n \t\treturn status;\n+\tif (!prefixcmp(var, \"column.\"))\n+\t\treturn git_column_config(var, value, \"tag\", &colopts);\n \treturn git_default_config(var, value, cb);\n }\n \n@@ -459,6 +463,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING('u', \"local-user\", &keyid, \"key-id\",\n \t\t\t\t\t\"use another key to sign the tag\"),\n \t\tOPT__FORCE(&force, \"replace the tag if exists\"),\n+\t\tOPT_COLUMN(0, \"column\", &colopts, \"show tag list in columns\"),\n \n \t\tOPT_GROUP(\"Tag listing options\"),\n \t\t{\n@@ -495,9 +500,24 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \n \tif (list + delete + verify > 1)\n \t\tusage_with_options(git_tag_usage, options);\n-\tif (list)\n-\t\treturn list_tags(argv, lines == -1 ? 0 : lines,\n-\t\t\t\t with_commit);\n+\tif (list && lines != -1) {\n+\t\tif (explicitly_enable_column(colopts))\n+\t\t\tdie(_(\"--column and -n are incompatible\"));\n+\t\tcolopts = 0;\n+\t}\n+\tif (list) {\n+\t\tint ret;\n+\t\tif (colopts & COL_ENABLED) {\n+\t\t\tstruct column_options copts;\n+\t\t\tmemset(&copts, 0, sizeof(copts));\n+\t\t\tcopts.padding = 2;\n+\t\t\trun_column_filter(colopts, &copts);\n+\t\t}\n+\t\tret = list_tags(argv, lines == -1 ? 0 : lines, with_commit);\n+\t\tif (colopts & COL_ENABLED)\n+\t\t\tstop_column_filter();\n+\t\treturn ret;\n+\t}\n \tif (lines != -1)\n \t\tdie(_(\"-n option is only allowed with -l.\"));\n \tif (with_commit)\ndiff --git a/t/t7004-tag.sh b/t/t7004-tag.sh\nindex f8c247a..5189446 100755\n--- a/t/t7004-tag.sh\n+++ b/t/t7004-tag.sh\n@@ -263,6 +263,50 @@ test_expect_success 'tag -l can accept multiple patterns' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'listing tags in column' '\n+\tCOLUMNS=40 git tag -l --column=row >actual &&\n+\tcat >expected <<\\EOF &&\n+a1      aa1     cba     t210    t211\n+v0.2.1  v1.0    v1.0.1  v1.1.3\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'listing tags in column with column.*' '\n+\tgit config column.tag row &&\n+\tgit config column.ui dense &&\n+\tCOLUMNS=40 git tag -l >actual &&\n+\tgit config --unset column.ui &&\n+\tgit config --unset column.tag &&\n+\tcat >expected <<\\EOF &&\n+a1      aa1   cba     t210    t211\n+v0.2.1  v1.0  v1.0.1  v1.1.3\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'listing tag with -n --column should fail' '\n+\ttest_must_fail git tag --column -n\n+'\n+\n+test_expect_success 'listing tags -n in column with column.ui ignored' '\n+\tgit config column.ui \"row dense\" &&\n+\tCOLUMNS=40 git tag -l -n >actual &&\n+\tgit config --unset column.ui &&\n+\tcat >expected <<\\EOF &&\n+a1              Foo\n+aa1             Foo\n+cba             Foo\n+t210            Foo\n+t211            Foo\n+v0.2.1          Foo\n+v1.0            Foo\n+v1.0.1          Foo\n+v1.1.3          Foo\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n # creating and verifying lightweight tags:\n \n test_expect_success \\\n-- \n1.7.8.36.g69ee2\n"},{"id":"185474","messageId":"7vaa45dwq7.fsf@alter.siamese.dyndns.org","threadId":"29734","inReplyTo":"1330170078-29353-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v6 00/11] Column display","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-26T23:02:40Z","receivedAt":"2012-02-26T23:02:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Nguyễn Thái Ngọc Duy (11):\n>   column: add API to print items in columns\n>   Add git-column and column mode parsing\n>   Stop starting pager recursively\n>   column: add columnar layout\n>   column: support columns with different widths\n>   column: add column.ui for default column output settings\n>   help: reuse print_columns() for help -a\n>   branch: add --column\n>   status: add --column\n>   column: support piping stdout to external git-column process\n>   tag: add --column\n\nThe first one adds something that nobody uses, hence it cannot be blamed\nby bisecting, but the \"API\" gets updated over time by other changes.\nPerhaps the first two may want to become a single commit.\n\nOther than that, the patch progression looks very logical.  The column\noutput subsystem gets enhanced using git-column as the scaffolding to help\ntesting and developing it, and then various commands start to make use of\nthe result when the subsystem has become usable in their context.\n\nBut I am very much reluctant to see us adding a \"git column\" subcommand; I\ncannot justify it myself because what it does is even less connected to\ngit than the \"--no-index\" mode of grep/diff commands, i.e. it does not\nhave much to do with \"Git, the version control system\".\n\nShouldn't it be \"test-column\" that is useful during the hacking, much like\n\"test-date\", \"test-chmtime\", and \"test_credential\"?\n"},{"id":"185475","messageId":"7v62etdwan.fsf@alter.siamese.dyndns.org","threadId":"29734","inReplyTo":"1330170078-29353-6-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v6 05/11] column: support columns with different widths","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-26T23:12:00Z","receivedAt":"2012-02-26T23:12:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n\nThe contrast between this and the previous one is striking.\n\nThe addition of MODE_COLUMN and MODE_ROW are very well explained, and it\nwill help people to later learn how to use the columns subsystem in their\nprograms.  But this does not even mention what \"different widths\" is, let\nalone explaining how to use it or if it is always on and the programs do\nnot have to worry about (and have no control over) its behaviour.\n\nI am guessing from the patch that you can tell the column subsystem to use\na different mode, just like you can use MODE_COLUMN or MODE_ROW to instruct\ncolumn/row major ordering, by using MODE_DENSE or something.\n\nI am also reading from the patch that the \"dense mode\" allocates minimum\nnecessary width dynamically to columns, which end up having different\nwidth.  So \"different\" is not the more important part; \"dense\" is.\n\nBut the reader should not have to guess these things.\n"},{"id":"185482","messageId":"CACsJy8AyZcNSPEY8S0WczVu0ychGKr61BoBm0iy39ZsVM=zHpA@mail.gmail.com","threadId":"29734","inReplyTo":"7vaa45dwq7.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v6 00/11] Column display","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-27T00:40:03Z","receivedAt":"2012-02-27T00:40:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2012/2/27 Junio C Hamano <gitster@pobox.com>:\n> But I am very much reluctant to see us adding a \"git column\" subcommand; I\n> cannot justify it myself because what it does is even less connected to\n> git than the \"--no-index\" mode of grep/diff commands, i.e. it does not\n> have much to do with \"Git, the version control system\".\n>\n> Shouldn't it be \"test-column\" that is useful during the hacking, much like\n> \"test-date\", \"test-chmtime\", and \"test_credential\"?\n\nIt used to be test-column. But we would need an external program to\npipe through to minimize changing display code, especially when\ndisplay code can get complicated (e.g. git-tag for example). The other\nreason is to get column display even when the command does not support\nit (e.g. GIT_PAGER=\"git column\" git diff --name-status HEAD~10)\n-- \nDuy\n"},{"id":"185486","messageId":"7v8vjpcaz3.fsf@alter.siamese.dyndns.org","threadId":"29734","inReplyTo":"CACsJy8AyZcNSPEY8S0WczVu0ychGKr61BoBm0iy39ZsVM=zHpA@mail.gmail.com","subject":"Re: [PATCH v6 00/11] Column display","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-27T01:37:52Z","receivedAt":"2012-02-27T01:37:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n> It used to be test-column. But we would need an external program to\n> pipe through to minimize changing display code, especially when\n> display code can get complicated (e.g. git-tag for example).\n\nHrm, I was hoping that we don't have to resort to a hacky implementation\nlike that; upload-pack/receive-pack that are not run directly by the end\nusers but having to be somewhere in path were bad enough, but they were\nvery much about git.\n\nBut at least with your series, \"git column\" is as invisible as git-sh-setup\nand git-stripspace by being marked as purehelpers, so your patch may be\nthe best we could do.  I dunno.\n"},{"id":"185493","messageId":"7vvcmsbxvz.fsf@alter.siamese.dyndns.org","threadId":"29734","inReplyTo":"1330170078-29353-7-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v6 06/11] column: add column.ui for default column output settings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-27T06:20:32Z","receivedAt":"2012-02-27T06:20:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  Documentation/config.txt     |   26 ++++++++++++++++++++++++++\n>  Documentation/git-column.txt |    6 +++++-\n>  builtin/column.c             |   21 +++++++++++++++++++++\n>  column.c                     |   28 ++++++++++++++++++++++++++++\n>  column.h                     |    2 ++\n>  5 files changed, 82 insertions(+), 1 deletions(-)\n>\n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> index abeb82b..5216598 100644\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -821,6 +821,32 @@ color.ui::\n>  \t`never` if you prefer git commands not to use color unless enabled\n>  \texplicitly with some other configuration or the `--color` option.\n>  \n> +column.ui::\n> +\tSpecify whether supported commands should output in columns.\n> +\tThis variable consists of a list of tokens separated by spaces\n> +\tor commas:\n> ++\n> +--\n> +`always`;;\n> +\talways show in columns\n> +`never`;;\n> +\tnever show in columns\n> +`auto`;;\n> +\tshow in columns if the output is to the terminal\n> +`column`;;\n> +\tfill columns before rows (default)\n> +`row`;;\n> +\tfill rows before columns\n> +`dense`;;\n> +\tmake unequal size columns to utilize more space\n> +`nodense`;;\n> +\tmake equal size columns\n> +`color`;;\n> +\tinput contains ANSI escape sequence for coloring\n> +--\n> ++\n> +\tThis option defaults to 'never'.\n\nI tried comparing output from \n\n    $ git help -a\n    $ git -c column.ui=row help -a\n\nbut did not spot differences.  What am I doing wrong?\n"},{"id":"185495","messageId":"CACsJy8DNiCfk44rQ-VWs6zzS7EYkO66baqwTUg5pL3WH6MK8Zw@mail.gmail.com","threadId":"29734","inReplyTo":"7vvcmsbxvz.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v6 06/11] column: add column.ui for default column output settings","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-27T07:04:51Z","receivedAt":"2012-02-27T07:04:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2012/2/27 Junio C Hamano <gitster@pobox.com>:\n> I tried comparing output from\n>\n>    $ git help -a\n>    $ git -c column.ui=row help -a\n>\n> but did not spot differences.  What am I doing wrong?\n\nConfig does not affect \"help -a\". It has always been sorted column\nfirst. But yeah if you prefer row-first, then it should probably\nfollow that too.\n-- \nDuy\n"},{"id":"185496","messageId":"7vipisbtw0.fsf@alter.siamese.dyndns.org","threadId":"29734","inReplyTo":"7v8vjpcaz3.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v6 00/11] Column display","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-27T07:46:55Z","receivedAt":"2012-02-27T07:46:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> But at least with your series, \"git column\" is as invisible as git-sh-setup\n> and git-stripspace by being marked as purehelpers, so your patch may be\n> the best we could do.  I dunno.\n\nI finally realized the true reason why I was so reluctant to take this\nseries.  If the series were presented like the following, I wouldn't have\nhad any problem:\n\n    There is a widely used program, \"column\", that takes a data stream\n    one-item-per-line as its input and produces columnar output.  Some\n    lists we produce as our output, e.g. \"git tag --list\", are often too\n    tall and sufficiently narrow, that \"git tag --list | column\" is a\n    useful thing to do.  The first patch in this series teaches \"git tag\n    --list\" to do just that but internally, so the user does not have to\n    pipe manually from the command line.\n\n    Luckily, \"column\" is a free software, and more importantly, it is\n    written modularly enough so that we can reuse its primary logic to\n    produce lines for columnar output without having to feed its input\n    from the standard input or drain its output directly to the standard\n    output.  Build an API around its core, and plug that logic to \"branch\n    --list\", \"help -a\" and other commands.\n\nBut that is not the case.\n\nYes, there is \"column\" that comes with BSD, and I am not suggesting to gut\nit and reuse its code, but by adding our own \"git column\" and implementing\nit as a built-in internal, we are making it harder for the outside world\nto benefit from our implementation, even when our \"git column\" some day\nbecomes polished enough to the point that people who are used to the BSD\n\"column\" start to wish their \"column\" had extra features offered by \"git\ncolumn\", without swallowing \"git\" as a whole.\n\nCome to think of it, that is really the same distaste I had against both\n\"diff --no-index\" and \"grep --no-index\".  We know adding our enhancements\nover non-git versions of these programs is a better gift to the outside\nworld, but we instead added the --no-index mode to Git to only keep the\nbenefit to ourselves, because it is far easier for us to do so.\n\nBut I do not deeply care either way.\n"},{"id":"185510","messageId":"CACsJy8BbryVXCXbPRmOv0mPcwMNOjgEe2W1bW-Aisn3YiK5=Aw@mail.gmail.com","threadId":"29734","inReplyTo":"7vipisbtw0.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v6 00/11] Column display","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-27T08:14:06Z","receivedAt":"2012-02-27T08:14:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Feb 27, 2012 at 2:46 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Come to think of it, that is really the same distaste I had against both\n> \"diff --no-index\" and \"grep --no-index\".  We know adding our enhancements\n> over non-git versions of these programs is a better gift to the outside\n> world, but we instead added the --no-index mode to Git to only keep the\n> benefit to ourselves, because it is far easier for us to do so.\n\nExactly. Some people like me also benefit from the popularity of Git.\nIf it's (likely) installed, I get many extra features for free. There\nare machines that I'm not allowed to install new stuff on. If it comes\nwith BSD \"column\", not too bad. If not arghhh.\n-- \nDuy\n"},{"id":"185572","messageId":"4F4BE300.2060302@ramsay1.demon.co.uk","threadId":"29734","inReplyTo":"1330170078-29353-3-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v6 02/11] Add git-column and column mode parsing","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsay1.demon.co.uk","sentAt":"2012-02-27T20:09:36Z","receivedAt":"2012-02-27T20:09:36Z","isPatch":true,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"Nguyễn Thái Ngọc Duy wrote:\n[...]\n> +static int parse_option(const char *arg, int len,\n> +\t\t\tunsigned int *mode, int stdout_is_tty)\n> +{\n> +\tstruct colopt opts[] = {\n> +\t\t{ ENABLE, \"always\",  1 },\n> +\t\t{ ENABLE, \"never\",   0 },\n> +\t\t{ ENABLE, \"auto\",   -1 },\n> +\t};\n\nHmm, I don't recognise this table from last time ...\n\n> +\tint i;\n> +\n> +\tfor (i = 0; i < ARRAY_SIZE(opts); i++) {\n> +\t\tint set = 1, arg_len = len, name_len;\n\nset is initialised here (mainly to silence the compiler) in each\nloop, but then ...\n\n> +\t\tconst char *arg_str = arg;\n> +\n> +\t\tif (opts[i].type == OPTION) {\n> +\t\t\tif (arg_len > 2 && !strncmp(arg_str, \"no\", 2)) {\n> +\t\t\t\targ_str += 2;\n> +\t\t\t\targ_len -= 2;\n> +\t\t\t\tset = 0;\n> +\t\t\t} else {\n> +\t\t\t\tset = 1;\n\n... this else clause is no longer required.\n\n> +\t\t\t}\n> +\t\t}\n> +\n> +\t\tname_len = strlen(opts[i].name);\n> +\t\tif (arg_len != name_len ||\n> +\t\t    strncmp(arg_str, opts[i].name, name_len))\n> +\t\t\tcontinue;\n> +\n> +\t\tswitch (opts[i].type) {\n> +\t\tcase ENABLE:\n> +\t\t\treturn set_enable_bit(mode, opts[i].value,\n> +\t\t\t\t\t      stdout_is_tty);\n\ngiven the above table, can the following case limbs ever be reached?\n(the \"no\" prefix is only applied to the OPTION type, so most of the\nabove code seems to be useless now ...)\n\n> +\t\tcase MODE:\n> +\t\t\treturn set_mode(mode, opts[i].value);\n> +\t\tcase OPTION:\n> +\t\t\treturn set_option(mode, opts[i].value, set);\n> +\t\tdefault:\n> +\t\t\tdie(\"BUG: Unknown option type %d\", opts[i].type);\n> +\t\t}\n> +\t}\n> +\n> +\treturn error(\"unsupported style '%s'\", arg);\n> +}\n> +\n[...]\n\nNote, I only skimmed the patch text, I haven't applied it or tested it ...\n\nATB,\nRamsay Jones\n"},{"id":"185601","messageId":"CACsJy8AAi7N39a1zrKpTGoFJbySxUZvh2JZRyhyE-0tXk3-sGA@mail.gmail.com","threadId":"29734","inReplyTo":"4F4BE300.2060302@ramsay1.demon.co.uk","subject":"Re: [PATCH v6 02/11] Add git-column and column mode parsing","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-28T11:00:08Z","receivedAt":"2012-02-28T11:00:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2012/2/28 Ramsay Jones <ramsay@ramsay1.demon.co.uk>:\n> Nguyễn Thái Ngọc Duy wrote:\n> [...]\n>> +static int parse_option(const char *arg, int len,\n>> +                     unsigned int *mode, int stdout_is_tty)\n>> +{\n>> +     struct colopt opts[] = {\n>> +             { ENABLE, \"always\",  1 },\n>> +             { ENABLE, \"never\",   0 },\n>> +             { ENABLE, \"auto\",   -1 },\n>> +     };\n>\n> Hmm, I don't recognise this table from last time ...\n>\n>> +                     }\n>> +             }\n>> +\n>> +             name_len = strlen(opts[i].name);\n>> +             if (arg_len != name_len ||\n>> +                 strncmp(arg_str, opts[i].name, name_len))\n>> +                     continue;\n>> +\n>> +             switch (opts[i].type) {\n>> +             case ENABLE:\n>> +                     return set_enable_bit(mode, opts[i].value,\n>> +                                           stdout_is_tty);\n>\n> given the above table, can the following case limbs ever be reached?\n> (the \"no\" prefix is only applied to the OPTION type, so most of the\n> above code seems to be useless now ...)\n\nNot in this patch, no. The table is extended later on with more modes\nand options.\n\n>\n>> +             case MODE:\n>> +                     return set_mode(mode, opts[i].value);\n>> +             case OPTION:\n>> +                     return set_option(mode, opts[i].value, set);\n>> +             default:\n>> +                     die(\"BUG: Unknown option type %d\", opts[i].type);\n>> +             }\n>> +     }\n>> +\n>> +     return error(\"unsupported style '%s'\", arg);\n>> +}\n>> +\n\n-- \nDuy\n"},{"id":"185606","messageId":"1330430331-19945-1-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330170078-29353-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v7 00/10] Column display","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-28T11:58:41Z","receivedAt":"2012-02-28T11:58:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Changes from v6 [1]:\n\n - Merge the first two patches into one\n - Make \"help -a\" support column.ui\n - Remove redundant code Ramsay pointed out in 01/10\n - Reword COL_DENSE commit message, 04/10\n\n[1] http://article.gmane.org/gmane.comp.version-control.git/191522\n\nNguyễn Thái Ngọc Duy (10):\n  Add git-column for columnar display\n  Stop starting pager recursively\n  column: add columnar layout\n  column: add dense layout support\n  column: add column.ui for default column output settings\n  help: reuse print_columns() for help -a\n  branch: add --column\n  status: add --column\n  column: support piping stdout to external git-column process\n  tag: add --column\n\n .gitignore                   |    1 +\n Documentation/config.txt     |   38 ++++\n Documentation/git-branch.txt |    9 +\n Documentation/git-column.txt |   53 +++++\n Documentation/git-status.txt |    7 +\n Documentation/git-tag.txt    |    9 +\n Makefile                     |    3 +\n builtin.h                    |    1 +\n builtin/branch.c             |   32 +++-\n builtin/column.c             |   62 ++++++\n builtin/commit.c             |    6 +\n builtin/help.c               |    7 +-\n builtin/tag.c                |   26 ++-\n column.c                     |  476 ++++++++++++++++++++++++++++++++++++++++++\n column.h                     |   40 ++++\n command-list.txt             |    1 +\n git.c                        |    1 +\n help.c                       |   54 ++----\n help.h                       |    2 +-\n pager.c                      |    2 +-\n parse-options.h              |    2 +\n t/t3200-branch.sh            |   77 +++++++\n t/t7004-tag.sh               |   44 ++++\n t/t7508-status.sh            |   24 ++\n t/t9002-column.sh            |  161 ++++++++++++++\n wt-status.c                  |   28 +++-\n wt-status.h                  |    1 +\n 27 files changed, 1119 insertions(+), 48 deletions(-)\n create mode 100644 Documentation/git-column.txt\n create mode 100644 builtin/column.c\n create mode 100644 column.c\n create mode 100644 column.h\n create mode 100755 t/t9002-column.sh\n\n-- \n1.7.8.36.g69ee2\n"},{"id":"185605","messageId":"1330430331-19945-2-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330430331-19945-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v7 01/10] Add git-column for columnar display","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-28T11:58:42Z","receivedAt":"2012-02-28T11:58:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This command is not particularly tied to anything specific to git. The\ncommand is added for supporting column display in various commands,\neither by reusing code or piping output to this command for layour.\nThe command at this stage is just a skeleton, no layout code yet.\n\nA column option string consists of many token separated by either\nspace of commas. A token belongs to one of three groups:\n\n - enabling: always, never and auto\n - layout mode: to be implemented\n - other tuning, which could be negated be prefix 'no'\n\nA command line option without argument (e.g. --column) will enable\ncolumn output and reuse existing settings (layout mode and options..).\n--no-column disables columnar output.\n\nThanks-to: Ramsay Jones <ramsay@ramsay1.demon.co.uk>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore                   |    1 +\n Documentation/git-column.txt |   49 ++++++++++++\n Makefile                     |    3 +\n builtin.h                    |    1 +\n builtin/column.c             |   41 ++++++++++\n column.c                     |  170 ++++++++++++++++++++++++++++++++++++++++++\n column.h                     |   30 ++++++++\n command-list.txt             |    1 +\n git.c                        |    1 +\n parse-options.h              |    2 +\n t/t9002-column.sh            |   27 +++++++\n 11 files changed, 326 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/git-column.txt\n create mode 100644 builtin/column.c\n create mode 100644 column.c\n create mode 100644 column.h\n create mode 100755 t/t9002-column.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex 87fcc5f..2540264 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -26,6 +26,7 @@\n /git-cherry-pick\n /git-clean\n /git-clone\n+/git-column\n /git-commit\n /git-commit-tree\n /git-config\ndiff --git a/Documentation/git-column.txt b/Documentation/git-column.txt\nnew file mode 100644\nindex 0000000..508b85f\n--- /dev/null\n+++ b/Documentation/git-column.txt\n@@ -0,0 +1,49 @@\n+git-column(1)\n+=============\n+\n+NAME\n+----\n+git-column - Display data in columns\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git column' [--mode=<mode> | --rawmode=<n>] [--width=<width>]\n+\t     [--indent=<string>] [--nl=<string>] [--pading=<n>]\n+\n+DESCRIPTION\n+-----------\n+This command formats its input into multiple columns.\n+\n+OPTIONS\n+-------\n+--mode=<mode>::\n+\tSpecify layout mode. See configuration variable column.ui for option\n+\tsyntax.\n+\n+--rawmode=<n>::\n+\tSame as --mode but take mode encoded as a number. This is mainly used\n+\tby other commands that have already parsed layout mode.\n+\n+--width=<width>::\n+\tSpecify the terminal width. By default 'git column' will detect the\n+\tterminal width, or fall back to 80 if it is unable to do so.\n+\n+--indent=<string>::\n+\tString to be printed at the beginning of each line.\n+\n+--nl=<N>::\n+\tString to be printed at the end of each line,\n+\tincluding newline character.\n+\n+--padding=<N>::\n+\tThe number of spaces between columns. One space by default.\n+\n+\n+Author\n+------\n+Written by Nguyen Thai Ngoc Duy <pclouds@gmail.com>\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Makefile b/Makefile\nindex a0de4e9..0998f0d 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -646,6 +646,7 @@ LIB_OBJS += bulk-checkin.o\n LIB_OBJS += bundle.o\n LIB_OBJS += cache-tree.o\n LIB_OBJS += color.o\n+LIB_OBJS += column.o\n LIB_OBJS += combine-diff.o\n LIB_OBJS += commit.o\n LIB_OBJS += compat/obstack.o\n@@ -774,6 +775,7 @@ BUILTIN_OBJS += builtin/checkout-index.o\n BUILTIN_OBJS += builtin/checkout.o\n BUILTIN_OBJS += builtin/clean.o\n BUILTIN_OBJS += builtin/clone.o\n+BUILTIN_OBJS += builtin/column.o\n BUILTIN_OBJS += builtin/commit-tree.o\n BUILTIN_OBJS += builtin/commit.o\n BUILTIN_OBJS += builtin/config.o\n@@ -2166,6 +2168,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o url.o http-backend.o: url.h\n+column.o help.o pager.o: column.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/builtin.h b/builtin.h\nindex 857b9c8..338f540 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -61,6 +61,7 @@ extern int cmd_cherry(int argc, const char **argv, const char *prefix);\n extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);\n extern int cmd_clone(int argc, const char **argv, const char *prefix);\n extern int cmd_clean(int argc, const char **argv, const char *prefix);\n+extern int cmd_column(int argc, const char **argv, const char *prefix);\n extern int cmd_commit(int argc, const char **argv, const char *prefix);\n extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_config(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/column.c b/builtin/column.c\nnew file mode 100644\nindex 0000000..3b0f74e\n--- /dev/null\n+++ b/builtin/column.c\n@@ -0,0 +1,41 @@\n+#include \"builtin.h\"\n+#include \"cache.h\"\n+#include \"strbuf.h\"\n+#include \"parse-options.h\"\n+#include \"string-list.h\"\n+#include \"column.h\"\n+\n+static const char * const builtin_column_usage[] = {\n+\t\"git column [options]\",\n+\tNULL\n+};\n+static unsigned int colopts;\n+\n+int cmd_column(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tstruct column_options copts;\n+\tstruct option options[] = {\n+\t\tOPT_COLUMN(0, \"mode\", &colopts, \"layout to use\"),\n+\t\tOPT_INTEGER(0, \"rawmode\", &colopts, \"layout to use\"),\n+\t\tOPT_INTEGER(0, \"width\", &copts.width, \"Maximum width\"),\n+\t\tOPT_STRING(0, \"indent\", &copts.indent, \"string\", \"Padding space on left border\"),\n+\t\tOPT_INTEGER(0, \"nl\", &copts.nl, \"Padding space on right border\"),\n+\t\tOPT_INTEGER(0, \"padding\", &copts.padding, \"Padding space between columns\"),\n+\t\tOPT_END()\n+\t};\n+\n+\tmemset(&copts, 0, sizeof(copts));\n+\tcopts.width = term_columns();\n+\tcopts.padding = 1;\n+\targc = parse_options(argc, argv, \"\", options, builtin_column_usage, 0);\n+\tif (argc)\n+\t\tusage_with_options(builtin_column_usage, options);\n+\n+\twhile (!strbuf_getline(&sb, stdin, '\\n'))\n+\t\tstring_list_append(&list, sb.buf);\n+\n+\tprint_columns(&list, colopts, &copts);\n+\treturn 0;\n+}\ndiff --git a/column.c b/column.c\nnew file mode 100644\nindex 0000000..d61da81\n--- /dev/null\n+++ b/column.c\n@@ -0,0 +1,170 @@\n+#include \"cache.h\"\n+#include \"column.h\"\n+#include \"string-list.h\"\n+#include \"parse-options.h\"\n+\n+#define MODE(mode) ((mode) & COL_MODE)\n+\n+/* Display without layout when COL_ENABLED is not set */\n+static void display_plain(const struct string_list *list,\n+\t\t\t  const char *indent, const char *nl)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < list->nr; i++)\n+\t\tprintf(\"%s%s%s\", indent, list->items[i].string, nl);\n+}\n+\n+void print_columns(const struct string_list *list, unsigned int mode,\n+\t\t   struct column_options *opts)\n+{\n+\tconst char *indent = \"\", *nl = \"\\n\";\n+\tint padding = 1, width = term_columns();\n+\n+\tif (!list->nr)\n+\t\treturn;\n+\tif (opts) {\n+\t\tif (opts->indent)\n+\t\t\tindent = opts->indent;\n+\t\tif (opts->nl)\n+\t\t\tnl = opts->nl;\n+\t\tif (opts->width)\n+\t\t\twidth = opts->width;\n+\t\tpadding = opts->padding;\n+\t}\n+\tif (width <= 1 || !(mode & COL_ENABLED)) {\n+\t\tdisplay_plain(list, indent, nl);\n+\t\treturn;\n+\t}\n+\tdie(\"BUG: invalid mode %d\", MODE(mode));\n+}\n+\n+struct colopt {\n+\tenum {\n+\t\tENABLE,\n+\t\tMODE,\n+\t\tOPTION\n+\t} type;\n+\tconst char *name;\n+\tint value;\n+};\n+\n+/*\n+ * Set COL_ENABLED and COL_ENABLED_SET. If 'set' is -1, check if\n+ * stdout is tty.\n+ */\n+static int set_enable_bit(unsigned int *mode, int set, int stdout_is_tty)\n+{\n+\tif (set < 0) {\t/* auto */\n+\t\tif (stdout_is_tty < 0)\n+\t\t\tstdout_is_tty = isatty(1);\n+\t\tset = stdout_is_tty || (pager_in_use() && pager_use_color);\n+\t}\n+\tif (set)\n+\t\t*mode = *mode | COL_ENABLED | COL_ENABLED_SET;\n+\telse\n+\t\t*mode = (*mode & ~COL_ENABLED) | COL_ENABLED_SET;\n+\treturn 0;\n+}\n+\n+/*\n+ * Set COL_MODE_*. mode is intially copied from column.ui. If\n+ * COL_ENABLED_SET is not set, then neither 'always', 'never' nor\n+ * 'auto' has been used. Default to 'always'.\n+ */\n+static int set_mode(unsigned int *mode, unsigned int value)\n+{\n+\t*mode = (*mode & ~COL_MODE) | value;\n+\tif (!(*mode & COL_ENABLED_SET))\n+\t\t*mode |= COL_ENABLED | COL_ENABLED_SET;\n+\n+\treturn 0;\n+}\n+\n+/* Set or unset other COL_* */\n+static int set_option(unsigned int *mode, unsigned int opt, int set)\n+{\n+\tif (set)\n+\t\t*mode |= opt;\n+\telse\n+\t\t*mode &= ~opt;\n+\treturn 0;\n+}\n+\n+static int parse_option(const char *arg, int len,\n+\t\t\tunsigned int *mode, int stdout_is_tty)\n+{\n+\tstruct colopt opts[] = {\n+\t\t{ ENABLE, \"always\",  1 },\n+\t\t{ ENABLE, \"never\",   0 },\n+\t\t{ ENABLE, \"auto\",   -1 },\n+\t};\n+\tint i;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(opts); i++) {\n+\t\tint set = 1, arg_len = len, name_len;\n+\t\tconst char *arg_str = arg;\n+\n+\t\tif (opts[i].type == OPTION) {\n+\t\t\tif (arg_len > 2 && !strncmp(arg_str, \"no\", 2)) {\n+\t\t\t\targ_str += 2;\n+\t\t\t\targ_len -= 2;\n+\t\t\t\tset = 0;\n+\t\t\t}\n+\t\t}\n+\n+\t\tname_len = strlen(opts[i].name);\n+\t\tif (arg_len != name_len ||\n+\t\t    strncmp(arg_str, opts[i].name, name_len))\n+\t\t\tcontinue;\n+\n+\t\tswitch (opts[i].type) {\n+\t\tcase ENABLE:\n+\t\t\treturn set_enable_bit(mode, opts[i].value,\n+\t\t\t\t\t      stdout_is_tty);\n+\t\tcase MODE:\n+\t\t\treturn set_mode(mode, opts[i].value);\n+\t\tcase OPTION:\n+\t\t\treturn set_option(mode, opts[i].value, set);\n+\t\tdefault:\n+\t\t\tdie(\"BUG: Unknown option type %d\", opts[i].type);\n+\t\t}\n+\t}\n+\n+\treturn error(\"unsupported style '%s'\", arg);\n+}\n+\n+int git_config_column(unsigned int *mode, const char *value,\n+\t\t      int stdout_is_tty)\n+{\n+\tconst char *sep = \" ,\";\n+\n+\twhile (*value) {\n+\t\tint len = strcspn(value, sep);\n+\t\tif (len) {\n+\t\t\tif (parse_option(value, len, mode, stdout_is_tty))\n+\t\t\t\treturn -1;\n+\n+\t\t\tvalue += len;\n+\t\t}\n+\t\tvalue += strspn(value, sep);\n+\t}\n+\treturn 0;\n+}\n+\n+int parseopt_column_callback(const struct option *opt,\n+\t\t\t     const char *arg, int unset)\n+{\n+\tunsigned int *mode = opt->value;\n+\t*mode |= COL_PARSEOPT;\n+\tif (unset) {\n+\t\t*mode = (*mode & ~COL_ENABLED) | COL_ENABLED_SET;\n+\t\treturn 0;\n+\t}\n+\tif (arg)\n+\t\treturn git_config_column(mode, arg, -1);\n+\n+\t/* no arg, turn it on */\n+\t*mode |= COL_ENABLED | COL_ENABLED_SET;\n+\treturn 0;\n+}\ndiff --git a/column.h b/column.h\nnew file mode 100644\nindex 0000000..67b1c4f\n--- /dev/null\n+++ b/column.h\n@@ -0,0 +1,30 @@\n+#ifndef COLUMN_H\n+#define COLUMN_H\n+\n+#define COL_MODE          0x000F\n+#define COL_ENABLED      (1 << 4)\n+#define COL_ENABLED_SET  (1 << 5)  /* Has COL_ENABLED been set by config? */\n+#define COL_PARSEOPT     (1 << 8)  /* --column is given */\n+\n+#define explicitly_enable_column(c) \\\n+\t(((c) & (COL_PARSEOPT | COL_ENABLED)) == (COL_PARSEOPT | COL_ENABLED))\n+\n+struct column_options {\n+\tint width;\n+\tint padding;\n+\tconst char *indent;\n+\tconst char *nl;\n+};\n+\n+extern int term_columns(void);\n+extern void print_columns(const struct string_list *list,\n+\t\t\t  unsigned int mode,\n+\t\t\t  struct column_options *opts);\n+extern int git_config_column(unsigned int *mode, const char *value,\n+\t\t\t     int stdout_is_tty);\n+\n+struct option;\n+extern int parseopt_column_callback(const struct option *opt,\n+\t\t\t\t    const char *arg, int unset);\n+\n+#endif\ndiff --git a/command-list.txt b/command-list.txt\nindex a36ee9b..fe06f15 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -20,6 +20,7 @@ git-cherry-pick                         mainporcelain\n git-citool                              mainporcelain\n git-clean                               mainporcelain\n git-clone                               mainporcelain common\n+git-column                              purehelpers\n git-commit                              mainporcelain common\n git-commit-tree                         plumbingmanipulators\n git-config                              ancillarymanipulators\ndiff --git a/git.c b/git.c\nindex 3805616..419e3cc 100644\n--- a/git.c\n+++ b/git.c\n@@ -348,6 +348,7 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"cherry-pick\", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"clean\", cmd_clean, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"clone\", cmd_clone },\n+\t\t{ \"column\", cmd_column },\n \t\t{ \"commit\", cmd_commit, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"commit-tree\", cmd_commit_tree, RUN_SETUP },\n \t\t{ \"config\", cmd_config, RUN_SETUP_GENTLY },\ndiff --git a/parse-options.h b/parse-options.h\nindex 2e811dc..56fcafd 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -238,5 +238,7 @@ extern int parse_opt_noop_cb(const struct option *, const char *, int);\n \t  PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }\n #define OPT__COLOR(var, h) \\\n \tOPT_COLOR_FLAG(0, \"color\", (var), (h))\n+#define OPT_COLUMN(s, l, v, h) \\\n+\t{ OPTION_CALLBACK, (s), (l), (v), \"style\", (h), PARSE_OPT_OPTARG, parseopt_column_callback }\n \n #endif\ndiff --git a/t/t9002-column.sh b/t/t9002-column.sh\nnew file mode 100755\nindex 0000000..b0b6d62\n--- /dev/null\n+++ b/t/t9002-column.sh\n@@ -0,0 +1,27 @@\n+#!/bin/sh\n+\n+test_description='git column'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\tcat >lista <<\\EOF\n+one\n+two\n+three\n+four\n+five\n+six\n+seven\n+eight\n+nine\n+ten\n+eleven\n+EOF\n+'\n+\n+test_expect_success 'never' '\n+\tgit column --mode=never <lista >actual &&\n+\ttest_cmp lista actual\n+'\n+\n+test_done\n-- \n1.7.8.36.g69ee2\n"},{"id":"185607","messageId":"1330430331-19945-3-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330430331-19945-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v7 02/10] Stop starting pager recursively","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-28T11:58:43Z","receivedAt":"2012-02-28T11:58:43Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"git-column can be used as a pager for other git commands, something\nlike this:\n\n    GIT_PAGER=\"git -p column --mode='dense color'\" git -p branch\n\nThe problem with this is that \"git -p column\" also has $GIT_PAGER\nset so the pager runs itself again as a pager, then again and again.\n\nStop this.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n pager.c |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/pager.c b/pager.c\nindex 05584de..4dcb08d 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -73,7 +73,7 @@ void setup_pager(void)\n {\n \tconst char *pager = git_pager(isatty(1));\n \n-\tif (!pager)\n+\tif (!pager || pager_in_use())\n \t\treturn;\n \n \t/*\n-- \n1.7.8.36.g69ee2\n"},{"id":"185608","messageId":"1330430331-19945-4-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330430331-19945-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v7 03/10] column: add columnar layout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-28T11:58:44Z","receivedAt":"2012-02-28T11:58:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"COL_MODE_COLUMN and COL_MODE_ROW fill column by column (or row by row\nrespectively), given the terminal width and how many space between\ncolumns.\n\nStrings are supposed to be in UTF-8. If strings contain ANSI escape\nstrings, COL_ANSI must be specified for correct length calculation.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n column.c          |  131 ++++++++++++++++++++++++++++++++++++++++++++++++++++-\n column.h          |    3 +\n t/t9002-column.sh |   86 +++++++++++++++++++++++++++++++++++\n 3 files changed, 219 insertions(+), 1 deletions(-)\n\ndiff --git a/column.c b/column.c\nindex d61da81..6aaa829 100644\n--- a/column.c\n+++ b/column.c\n@@ -2,8 +2,66 @@\n #include \"column.h\"\n #include \"string-list.h\"\n #include \"parse-options.h\"\n+#include \"utf8.h\"\n \n #define MODE(mode) ((mode) & COL_MODE)\n+#define XY2LINEAR(d, x, y) (MODE((d)->mode) == COL_MODE_COLUMN ? \\\n+\t\t\t    (x) * (d)->rows + (y) : \\\n+\t\t\t    (y) * (d)->cols + (x))\n+\n+struct column_data {\n+\tconst struct string_list *list; /* list of all cells */\n+\tint mode;\t\t\t/* COL_MODE */\n+\tint total_width;\t\t/* terminal width */\n+\tint padding;\t\t\t/* cell padding */\n+\tconst char *indent;\t\t/* left most column indentation */\n+\tconst char *nl;\n+\n+\tint rows, cols;\n+\tint *len;\t\t\t/* cell length */\n+};\n+\n+/* return length of 's' in letters, ANSI escapes stripped */\n+static int item_length(int mode, const char *s)\n+{\n+\tint len, i = 0;\n+\tstruct strbuf str = STRBUF_INIT;\n+\n+\tif (!(mode & COL_ANSI))\n+\t\treturn utf8_strwidth(s);\n+\n+\tstrbuf_addstr(&str, s);\n+\twhile ((s = strstr(str.buf + i, \"\\033[\")) != NULL) {\n+\t\tint len = strspn(s + 2, \"0123456789;\");\n+\t\ti = s - str.buf;\n+\t\tstrbuf_remove(&str, i, len + 3); /* \\033[<len><func char> */\n+\t}\n+\tlen = utf8_strwidth(str.buf);\n+\tstrbuf_release(&str);\n+\treturn len;\n+}\n+\n+/*\n+ * Calculate cell width, rows and cols for a table of equal cells, given\n+ * table width and how many spaces between cells.\n+ */\n+static void layout(struct column_data *data, int *width)\n+{\n+\tint i;\n+\n+\t*width = 0;\n+\tfor (i = 0; i < data->list->nr; i++)\n+\t\tif (*width < data->len[i])\n+\t\t\t*width = data->len[i];\n+\n+\t*width += data->padding;\n+\n+\tdata->cols = (data->total_width - strlen(data->indent)) / *width;\n+\tif (data->cols == 0)\n+\t\tdata->cols = 1;\n+\n+\tdata->rows = DIV_ROUND_UP(data->list->nr, data->cols);\n+}\n \n /* Display without layout when COL_ENABLED is not set */\n static void display_plain(const struct string_list *list,\n@@ -15,6 +73,65 @@ static void display_plain(const struct string_list *list,\n \t\tprintf(\"%s%s%s\", indent, list->items[i].string, nl);\n }\n \n+/* Print a cell to stdout with all necessary leading/traling space */\n+static int display_cell(struct column_data *data, int initial_width,\n+\t\t\tconst char *empty_cell, int x, int y)\n+{\n+\tint i, len, newline;\n+\n+\ti = XY2LINEAR(data, x, y);\n+\tif (i >= data->list->nr)\n+\t\treturn -1;\n+\tlen = data->len[i];\n+\tif (MODE(data->mode) == COL_MODE_COLUMN)\n+\t\tnewline = i + data->rows >= data->list->nr;\n+\telse\n+\t\tnewline = x == data->cols - 1 || i == data->list->nr - 1;\n+\n+\tprintf(\"%s%s%s\",\n+\t\t\tx == 0 ? data->indent : \"\",\n+\t\t\tdata->list->items[i].string,\n+\t\t\tnewline ? data->nl : empty_cell + len);\n+\treturn 0;\n+}\n+\n+/* Display COL_MODE_COLUMN or COL_MODE_ROW */\n+static void display_table(const struct string_list *list,\n+\t\t\t  int mode, int total_width,\n+\t\t\t  int padding, const char *indent,\n+\t\t\t  const char *nl)\n+{\n+\tstruct column_data data;\n+\tint x, y, i, initial_width;\n+\tchar *empty_cell;\n+\n+\tmemset(&data, 0, sizeof(data));\n+\tdata.list = list;\n+\tdata.mode = mode;\n+\tdata.total_width = total_width;\n+\tdata.padding = padding;\n+\tdata.indent = indent;\n+\tdata.nl = nl;\n+\n+\tdata.len = xmalloc(sizeof(*data.len) * list->nr);\n+\tfor (i = 0; i < list->nr; i++)\n+\t\tdata.len[i] = item_length(mode, list->items[i].string);\n+\n+\tlayout(&data, &initial_width);\n+\n+\tempty_cell = xmalloc(initial_width + 1);\n+\tmemset(empty_cell, ' ', initial_width);\n+\tempty_cell[initial_width] = '\\0';\n+\tfor (y = 0; y < data.rows; y++) {\n+\t\tfor (x = 0; x < data.cols; x++)\n+\t\t\tif (display_cell(&data, initial_width, empty_cell, x, y))\n+\t\t\t\tbreak;\n+\t}\n+\n+\tfree(data.len);\n+\tfree(empty_cell);\n+}\n+\n void print_columns(const struct string_list *list, unsigned int mode,\n \t\t   struct column_options *opts)\n {\n@@ -36,7 +153,16 @@ void print_columns(const struct string_list *list, unsigned int mode,\n \t\tdisplay_plain(list, indent, nl);\n \t\treturn;\n \t}\n-\tdie(\"BUG: invalid mode %d\", MODE(mode));\n+\n+\tswitch (MODE(mode)) {\n+\tcase COL_MODE_ROW:\n+\tcase COL_MODE_COLUMN:\n+\t\tdisplay_table(list, mode, width, padding, indent, nl);\n+\t\tbreak;\n+\n+\tdefault:\n+\t\tdie(\"BUG: invalid mode %d\", MODE(mode));\n+\t}\n }\n \n struct colopt {\n@@ -98,6 +224,9 @@ static int parse_option(const char *arg, int len,\n \t\t{ ENABLE, \"always\",  1 },\n \t\t{ ENABLE, \"never\",   0 },\n \t\t{ ENABLE, \"auto\",   -1 },\n+\t\t{ MODE,   \"column\", COL_MODE_COLUMN },\n+\t\t{ MODE,   \"row\",    COL_MODE_ROW },\n+\t\t{ OPTION, \"color\",  COL_ANSI },\n \t};\n \tint i;\n \ndiff --git a/column.h b/column.h\nindex 67b1c4f..d9d27c6 100644\n--- a/column.h\n+++ b/column.h\n@@ -2,8 +2,11 @@\n #define COLUMN_H\n \n #define COL_MODE          0x000F\n+#define COL_MODE_COLUMN        0   /* Fill columns before rows */\n+#define COL_MODE_ROW           1   /* Fill rows before columns */\n #define COL_ENABLED      (1 << 4)\n #define COL_ENABLED_SET  (1 << 5)  /* Has COL_ENABLED been set by config? */\n+#define COL_ANSI         (1 << 6)  /* Remove ANSI escapes from string length */\n #define COL_PARSEOPT     (1 << 8)  /* --column is given */\n \n #define explicitly_enable_column(c) \\\ndiff --git a/t/t9002-column.sh b/t/t9002-column.sh\nindex b0b6d62..cffb029 100755\n--- a/t/t9002-column.sh\n+++ b/t/t9002-column.sh\n@@ -24,4 +24,90 @@ test_expect_success 'never' '\n \ttest_cmp lista actual\n '\n \n+test_expect_success '80 columns' '\n+\tcat >expected <<\\EOF &&\n+one    two    three  four   five   six    seven  eight  nine   ten    eleven\n+EOF\n+\tCOLUMNS=80 git column --mode=column <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'COLUMNS = 1' '\n+\tcat >expected <<\\EOF &&\n+one\n+two\n+three\n+four\n+five\n+six\n+seven\n+eight\n+nine\n+ten\n+eleven\n+EOF\n+\tCOLUMNS=1 git column --mode=column <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'width = 1' '\n+\tgit column --mode=column --width=1 <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+COLUMNS=20\n+export COLUMNS\n+\n+test_expect_success '20 columns' '\n+\tcat >expected <<\\EOF &&\n+one    seven\n+two    eight\n+three  nine\n+four   ten\n+five   eleven\n+six\n+EOF\n+\tgit column --mode=column <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, padding 2' '\n+\tcat >expected <<\\EOF &&\n+one     seven\n+two     eight\n+three   nine\n+four    ten\n+five    eleven\n+six\n+EOF\n+\tgit column --mode=column --padding 2 <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, indented' '\n+\tcat >expected <<\\EOF &&\n+  one    seven\n+  two    eight\n+  three  nine\n+  four   ten\n+  five   eleven\n+  six\n+EOF\n+\tgit column --mode=column --indent=\"  \" <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, row first' '\n+\tcat >expected <<\\EOF &&\n+one    two\n+three  four\n+five   six\n+seven  eight\n+nine   ten\n+eleven\n+EOF\n+\tgit column --mode=row <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.7.8.36.g69ee2\n"},{"id":"185609","messageId":"1330430331-19945-5-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330430331-19945-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v7 04/10] column: add dense layout support","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-28T11:58:45Z","receivedAt":"2012-02-28T11:58:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Normally, all items will be gone through once. The largest cell will set\ncolumn width for all columns. With this strategy, one long item will\nstretch out all columns, wasting space in between them.\n\nWith COL_DENSE enabled, it shrinks all columns to minimum, then attempts\nto push the last row's cells over to the next column with hope that\neverything still fits even there's one row less. The process is repeated\nuntil the new layout cannot fit in given width anymore, or there's only\none row left (perfect!).\n\nApparently, this mode consumes more cpu than the former, but makes\nbetter use of terminal space. For layouting one or two screens, cpu\nusage should not be a problem.\n\nThanks-to: Ramsay Jones <ramsay@ramsay1.demon.co.uk>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n column.c          |   80 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n column.h          |    2 +\n t/t9002-column.sh |   48 +++++++++++++++++++++++++++++++\n 3 files changed, 130 insertions(+), 0 deletions(-)\n\ndiff --git a/column.c b/column.c\nindex 6aaa829..95faec5 100644\n--- a/column.c\n+++ b/column.c\n@@ -19,6 +19,7 @@ struct column_data {\n \n \tint rows, cols;\n \tint *len;\t\t\t/* cell length */\n+\tint *width;\t\t\t/* index to the longest row in column */\n };\n \n /* return length of 's' in letters, ANSI escapes stripped */\n@@ -63,6 +64,69 @@ static void layout(struct column_data *data, int *width)\n \tdata->rows = DIV_ROUND_UP(data->list->nr, data->cols);\n }\n \n+static void compute_column_width(struct column_data *data)\n+{\n+\tint i, x, y;\n+\tfor (x = 0; x < data->cols; x++) {\n+\t\tdata->width[x] = XY2LINEAR(data, x, 0);\n+\t\tfor (y = 0; y < data->rows; y++) {\n+\t\t\ti = XY2LINEAR(data, x, y);\n+\t\t\tif (i >= data->list->nr)\n+\t\t\t\tcontinue;\n+\t\t\tif (data->len[data->width[x]] < data->len[i])\n+\t\t\t\tdata->width[x] = i;\n+\t\t}\n+\t}\n+}\n+\n+/*\n+ * Shrink all columns by shortening them one row each time (and adding\n+ * more columns along the way). Hopefully the longest cell will be\n+ * moved to the next column, column is shrunk so we have more space\n+ * for new columns. The process ends when the whole thing no longer\n+ * fits in data->total_width.\n+ */\n+static void shrink_columns(struct column_data *data)\n+{\n+\tint x, y, total_width, cols, rows;\n+\n+\tdata->width = xrealloc(data->width,\n+\t\t\t       sizeof(*data->width) * data->cols);\n+\tfor (x = 0; x < data->cols; x++) {\n+\t\tdata->width[x] = 0;\n+\t\tfor (y = 0; y < data->rows; y++) {\n+\t\t\tint len1 = data->len[data->width[x]];\n+\t\t\tint len2 = data->len[XY2LINEAR(data, x, y)];\n+\t\t\tif (len1 < len2)\n+\t\t\t\tdata->width[x] = y;\n+\t\t}\n+\t}\n+\n+\twhile (data->rows > 1) {\n+\t\trows = data->rows;\n+\t\tcols = data->cols;\n+\n+\t\tdata->rows--;\n+\t\tdata->cols = DIV_ROUND_UP(data->list->nr, data->rows);\n+\t\tif (data->cols != cols)\n+\t\t\tdata->width = xrealloc(data->width, sizeof(*data->width) * data->cols);\n+\n+\t\tcompute_column_width(data);\n+\n+\t\ttotal_width = strlen(data->indent);\n+\t\tfor (x = 0; x < data->cols; x++) {\n+\t\t\ttotal_width += data->len[data->width[x]];\n+\t\t\ttotal_width += data->padding;\n+\t\t}\n+\t\tif (total_width > data->total_width) {\n+\t\t\tdata->rows = rows;\n+\t\t\tdata->cols = cols;\n+\t\t\tcompute_column_width(data);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+}\n+\n /* Display without layout when COL_ENABLED is not set */\n static void display_plain(const struct string_list *list,\n \t\t\t  const char *indent, const char *nl)\n@@ -82,7 +146,18 @@ static int display_cell(struct column_data *data, int initial_width,\n \ti = XY2LINEAR(data, x, y);\n \tif (i >= data->list->nr)\n \t\treturn -1;\n+\n \tlen = data->len[i];\n+\tif (data->width && data->len[data->width[x]] < initial_width) {\n+\t\t/*\n+\t\t * empty_cell has initial_width chars, if real column\n+\t\t * is narrower, increase len a bit so we fill less\n+\t\t * space.\n+\t\t */\n+\t\tlen += initial_width - data->len[data->width[x]];\n+\t\tlen -= data->padding;\n+\t}\n+\n \tif (MODE(data->mode) == COL_MODE_COLUMN)\n \t\tnewline = i + data->rows >= data->list->nr;\n \telse\n@@ -119,6 +194,9 @@ static void display_table(const struct string_list *list,\n \n \tlayout(&data, &initial_width);\n \n+\tif (mode & COL_DENSE)\n+\t\tshrink_columns(&data);\n+\n \tempty_cell = xmalloc(initial_width + 1);\n \tmemset(empty_cell, ' ', initial_width);\n \tempty_cell[initial_width] = '\\0';\n@@ -129,6 +207,7 @@ static void display_table(const struct string_list *list,\n \t}\n \n \tfree(data.len);\n+\tfree(data.width);\n \tfree(empty_cell);\n }\n \n@@ -227,6 +306,7 @@ static int parse_option(const char *arg, int len,\n \t\t{ MODE,   \"column\", COL_MODE_COLUMN },\n \t\t{ MODE,   \"row\",    COL_MODE_ROW },\n \t\t{ OPTION, \"color\",  COL_ANSI },\n+\t\t{ OPTION, \"dense\",  COL_DENSE },\n \t};\n \tint i;\n \ndiff --git a/column.h b/column.h\nindex d9d27c6..eb03c6c 100644\n--- a/column.h\n+++ b/column.h\n@@ -7,6 +7,8 @@\n #define COL_ENABLED      (1 << 4)\n #define COL_ENABLED_SET  (1 << 5)  /* Has COL_ENABLED been set by config? */\n #define COL_ANSI         (1 << 6)  /* Remove ANSI escapes from string length */\n+#define COL_DENSE        (1 << 7)  /* Shrink columns when possible,\n+\t\t\t\t      making space for more columns */\n #define COL_PARSEOPT     (1 << 8)  /* --column is given */\n \n #define explicitly_enable_column(c) \\\ndiff --git a/t/t9002-column.sh b/t/t9002-column.sh\nindex cffb029..fe7a30e 100755\n--- a/t/t9002-column.sh\n+++ b/t/t9002-column.sh\n@@ -71,6 +71,30 @@ EOF\n \ttest_cmp expected actual\n '\n \n+test_expect_success '20 columns, nodense' '\n+\tcat >expected <<\\EOF &&\n+one    seven\n+two    eight\n+three  nine\n+four   ten\n+five   eleven\n+six\n+EOF\n+\tgit column --mode=column,nodense < lista > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, dense' '\n+\tcat >expected <<\\EOF &&\n+one   five  nine\n+two   six   ten\n+three seven eleven\n+four  eight\n+EOF\n+\tgit column --mode=column,dense < lista > actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success '20 columns, padding 2' '\n \tcat >expected <<\\EOF &&\n one     seven\n@@ -110,4 +134,28 @@ EOF\n \ttest_cmp expected actual\n '\n \n+test_expect_success '20 columns, row first, nodense' '\n+\tcat >expected <<\\EOF &&\n+one    two\n+three  four\n+five   six\n+seven  eight\n+nine   ten\n+eleven\n+EOF\n+\tgit column --mode=row,nodense <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, row first, dense' '\n+\tcat >expected <<\\EOF &&\n+one   two    three\n+four  five   six\n+seven eight  nine\n+ten   eleven\n+EOF\n+\tgit column --mode=row,dense <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.7.8.36.g69ee2\n"},{"id":"185610","messageId":"1330430331-19945-6-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330430331-19945-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v7 05/10] column: add column.ui for default column output settings","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-28T11:58:46Z","receivedAt":"2012-02-28T11:58:46Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config.txt     |   26 ++++++++++++++++++++++++++\n Documentation/git-column.txt |    6 +++++-\n builtin/column.c             |   21 +++++++++++++++++++++\n column.c                     |   28 ++++++++++++++++++++++++++++\n column.h                     |    2 ++\n 5 files changed, 82 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex abeb82b..5216598 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -821,6 +821,32 @@ color.ui::\n \t`never` if you prefer git commands not to use color unless enabled\n \texplicitly with some other configuration or the `--color` option.\n \n+column.ui::\n+\tSpecify whether supported commands should output in columns.\n+\tThis variable consists of a list of tokens separated by spaces\n+\tor commas:\n++\n+--\n+`always`;;\n+\talways show in columns\n+`never`;;\n+\tnever show in columns\n+`auto`;;\n+\tshow in columns if the output is to the terminal\n+`column`;;\n+\tfill columns before rows (default)\n+`row`;;\n+\tfill rows before columns\n+`dense`;;\n+\tmake unequal size columns to utilize more space\n+`nodense`;;\n+\tmake equal size columns\n+`color`;;\n+\tinput contains ANSI escape sequence for coloring\n+--\n++\n+\tThis option defaults to 'never'.\n+\n commit.status::\n \tA boolean to enable/disable inclusion of status information in the\n \tcommit message template when using an editor to prepare the commit\ndiff --git a/Documentation/git-column.txt b/Documentation/git-column.txt\nindex 508b85f..94fd7ac 100644\n--- a/Documentation/git-column.txt\n+++ b/Documentation/git-column.txt\n@@ -8,7 +8,7 @@ git-column - Display data in columns\n SYNOPSIS\n --------\n [verse]\n-'git column' [--mode=<mode> | --rawmode=<n>] [--width=<width>]\n+'git column' [--command=<name>] [--[raw]mode=<mode>] [--width=<width>]\n \t     [--indent=<string>] [--nl=<string>] [--pading=<n>]\n \n DESCRIPTION\n@@ -17,6 +17,10 @@ This command formats its input into multiple columns.\n \n OPTIONS\n -------\n+--command=<name>::\n+\tLook up layout mode using configuration variable column.<name> and\n+\tcolumn.ui.\n+\n --mode=<mode>::\n \tSpecify layout mode. See configuration variable column.ui for option\n \tsyntax.\ndiff --git a/builtin/column.c b/builtin/column.c\nindex 3b0f74e..102d71b 100644\n--- a/builtin/column.c\n+++ b/builtin/column.c\n@@ -11,12 +11,19 @@ static const char * const builtin_column_usage[] = {\n };\n static unsigned int colopts;\n \n+static int column_config(const char *var, const char *value, void *cb)\n+{\n+\treturn git_column_config(var, value, cb, &colopts);\n+}\n+\n int cmd_column(int argc, const char **argv, const char *prefix)\n {\n \tstruct string_list list = STRING_LIST_INIT_DUP;\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct column_options copts;\n+\tconst char *command = NULL, *real_command = NULL;\n \tstruct option options[] = {\n+\t\tOPT_STRING(0, \"command\", &real_command, \"name\", \"lookup config vars\"),\n \t\tOPT_COLUMN(0, \"mode\", &colopts, \"layout to use\"),\n \t\tOPT_INTEGER(0, \"rawmode\", &colopts, \"layout to use\"),\n \t\tOPT_INTEGER(0, \"width\", &copts.width, \"Maximum width\"),\n@@ -26,6 +33,15 @@ int cmd_column(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n+\t/* This one is special and must be the first one */\n+\tif (argc > 1 && !prefixcmp(argv[1], \"--command=\")) {\n+\t\tint nonitok = 0;\n+\t\tsetup_git_directory_gently(&nonitok);\n+\n+\t\tcommand = argv[1] + 10;\n+\t\tgit_config(column_config, (void *)command);\n+\t}\n+\n \tmemset(&copts, 0, sizeof(copts));\n \tcopts.width = term_columns();\n \tcopts.padding = 1;\n@@ -33,6 +49,11 @@ int cmd_column(int argc, const char **argv, const char *prefix)\n \tif (argc)\n \t\tusage_with_options(builtin_column_usage, options);\n \n+\tif (real_command || command) {\n+\t\tif (!real_command || !command || strcmp(real_command, command))\n+\t\t\tdie(_(\"--command must be the first argument\"));\n+\t}\n+\n \twhile (!strbuf_getline(&sb, stdin, '\\n'))\n \t\tstring_list_append(&list, sb.buf);\n \ndiff --git a/column.c b/column.c\nindex 95faec5..67bfd06 100644\n--- a/column.c\n+++ b/column.c\n@@ -2,6 +2,7 @@\n #include \"column.h\"\n #include \"string-list.h\"\n #include \"parse-options.h\"\n+#include \"color.h\"\n #include \"utf8.h\"\n \n #define MODE(mode) ((mode) & COL_MODE)\n@@ -361,6 +362,33 @@ int git_config_column(unsigned int *mode, const char *value,\n \treturn 0;\n }\n \n+static int column_config(const char *var, const char *value,\n+\t\t\t const char *key, unsigned int *colopts)\n+{\n+\tif (git_config_column(colopts, value, -1))\n+\t\treturn error(\"invalid %s mode %s\", key, value);\n+\treturn 0;\n+}\n+\n+int git_column_config(const char *var, const char *value,\n+\t\t      const char *command, unsigned int *colopts)\n+{\n+\tif (!strcmp(var, \"column.ui\"))\n+\t\treturn column_config(var, value, \"column.ui\", colopts);\n+\n+\tif (command) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tint ret = 0;\n+\t\tstrbuf_addf(&sb, \"column.%s\", command);\n+\t\tif (!strcmp(var, sb.buf))\n+\t\t\tret = column_config(var, value, sb.buf, colopts);\n+\t\tstrbuf_release(&sb);\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n int parseopt_column_callback(const struct option *opt,\n \t\t\t     const char *arg, int unset)\n {\ndiff --git a/column.h b/column.h\nindex eb03c6c..43528da 100644\n--- a/column.h\n+++ b/column.h\n@@ -27,6 +27,8 @@ extern void print_columns(const struct string_list *list,\n \t\t\t  struct column_options *opts);\n extern int git_config_column(unsigned int *mode, const char *value,\n \t\t\t     int stdout_is_tty);\n+extern int git_column_config(const char *var, const char *value,\n+\t\t\t     const char *command, unsigned int *colopts);\n \n struct option;\n extern int parseopt_column_callback(const struct option *opt,\n-- \n1.7.8.36.g69ee2\n"},{"id":"185611","messageId":"1330430331-19945-7-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330430331-19945-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v7 06/10] help: reuse print_columns() for help -a","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-28T11:58:47Z","receivedAt":"2012-02-28T11:58:47Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/help.c |    7 ++++++-\n help.c         |   54 ++++++++++++++++++------------------------------------\n help.h         |    2 +-\n 3 files changed, 25 insertions(+), 38 deletions(-)\n\ndiff --git a/builtin/help.c b/builtin/help.c\nindex 61ff798..c64f152 100644\n--- a/builtin/help.c\n+++ b/builtin/help.c\n@@ -9,6 +9,7 @@\n #include \"common-cmds.h\"\n #include \"parse-options.h\"\n #include \"run-command.h\"\n+#include \"column.h\"\n #include \"help.h\"\n \n static struct man_viewer_list {\n@@ -30,6 +31,7 @@ enum help_format {\n };\n \n static int show_all = 0;\n+static unsigned int colopts;\n static enum help_format help_format = HELP_FORMAT_NONE;\n static struct option builtin_help_options[] = {\n \tOPT_BOOLEAN('a', \"all\", &show_all, \"print all available commands\"),\n@@ -251,6 +253,8 @@ static int add_man_viewer_info(const char *var, const char *value)\n \n static int git_help_config(const char *var, const char *value, void *cb)\n {\n+\tif (!prefixcmp(var, \"column.\"))\n+\t\treturn git_column_config(var, value, \"help\", &colopts);\n \tif (!strcmp(var, \"help.format\")) {\n \t\tif (!value)\n \t\t\treturn config_error_nonbool(var);\n@@ -424,8 +428,9 @@ int cmd_help(int argc, const char **argv, const char *prefix)\n \tparsed_help_format = help_format;\n \n \tif (show_all) {\n+\t\tgit_config(git_help_config, NULL);\n \t\tprintf(\"usage: %s\\n\\n\", git_usage_string);\n-\t\tlist_commands(\"git commands\", &main_cmds, &other_cmds);\n+\t\tlist_commands(\"git commands\", colopts, &main_cmds, &other_cmds);\n \t\tprintf(\"%s\\n\", git_more_info_string);\n \t\treturn 0;\n \t}\ndiff --git a/help.c b/help.c\nindex 14eefc9..3155c68 100644\n--- a/help.c\n+++ b/help.c\n@@ -4,6 +4,8 @@\n #include \"levenshtein.h\"\n #include \"help.h\"\n #include \"common-cmds.h\"\n+#include \"string-list.h\"\n+#include \"column.h\"\n \n void add_cmdname(struct cmdnames *cmds, const char *name, int len)\n {\n@@ -70,31 +72,20 @@ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)\n \tcmds->cnt = cj;\n }\n \n-static void pretty_print_string_list(struct cmdnames *cmds, int longest)\n+static void pretty_print_string_list(struct cmdnames *cmds, unsigned int colopts)\n {\n-\tint cols = 1, rows;\n-\tint space = longest + 1; /* min 1 SP between words */\n-\tint max_cols = term_columns() - 1; /* don't print *on* the edge */\n-\tint i, j;\n-\n-\tif (space < max_cols)\n-\t\tcols = max_cols / space;\n-\trows = DIV_ROUND_UP(cmds->cnt, cols);\n-\n-\tfor (i = 0; i < rows; i++) {\n-\t\tprintf(\"  \");\n+\tstruct string_list list = STRING_LIST_INIT_NODUP;\n+\tstruct column_options copts;\n+\tint i;\n \n-\t\tfor (j = 0; j < cols; j++) {\n-\t\t\tint n = j * rows + i;\n-\t\t\tint size = space;\n-\t\t\tif (n >= cmds->cnt)\n-\t\t\t\tbreak;\n-\t\t\tif (j == cols-1 || n + rows >= cmds->cnt)\n-\t\t\t\tsize = 1;\n-\t\t\tprintf(\"%-*s\", size, cmds->names[n]->name);\n-\t\t}\n-\t\tputchar('\\n');\n-\t}\n+\tfor (i = 0; i < cmds->cnt; i++)\n+\t\tstring_list_append(&list, cmds->names[i]->name);\n+\tmemset(&copts, 0, sizeof(copts));\n+\tcopts.indent = \"  \";\n+\tcopts.padding = 2;\n+\t/* always enable column display here */\n+\tprint_columns(&list, colopts | COL_ENABLED, &copts);\n+\tstring_list_clear(&list, 0);\n }\n \n static int is_executable(const char *name)\n@@ -203,25 +194,16 @@ void load_command_list(const char *prefix,\n \texclude_cmds(other_cmds, main_cmds);\n }\n \n-void list_commands(const char *title, struct cmdnames *main_cmds,\n-\t\t   struct cmdnames *other_cmds)\n+void list_commands(const char *title, unsigned int colopts,\n+\t\t   struct cmdnames *main_cmds, struct cmdnames *other_cmds)\n {\n-\tint i, longest = 0;\n-\n-\tfor (i = 0; i < main_cmds->cnt; i++)\n-\t\tif (longest < main_cmds->names[i]->len)\n-\t\t\tlongest = main_cmds->names[i]->len;\n-\tfor (i = 0; i < other_cmds->cnt; i++)\n-\t\tif (longest < other_cmds->names[i]->len)\n-\t\t\tlongest = other_cmds->names[i]->len;\n-\n \tif (main_cmds->cnt) {\n \t\tconst char *exec_path = git_exec_path();\n \t\tprintf(\"available %s in '%s'\\n\", title, exec_path);\n \t\tprintf(\"----------------\");\n \t\tmput_char('-', strlen(title) + strlen(exec_path));\n \t\tputchar('\\n');\n-\t\tpretty_print_string_list(main_cmds, longest);\n+\t\tpretty_print_string_list(main_cmds, colopts);\n \t\tputchar('\\n');\n \t}\n \n@@ -230,7 +212,7 @@ void list_commands(const char *title, struct cmdnames *main_cmds,\n \t\tprintf(\"---------------------------------------\");\n \t\tmput_char('-', strlen(title));\n \t\tputchar('\\n');\n-\t\tpretty_print_string_list(other_cmds, longest);\n+\t\tpretty_print_string_list(other_cmds, colopts);\n \t\tputchar('\\n');\n \t}\n }\ndiff --git a/help.h b/help.h\nindex b6b12d5..854d2d4 100644\n--- a/help.h\n+++ b/help.h\n@@ -25,7 +25,7 @@ extern void add_cmdname(struct cmdnames *cmds, const char *name, int len);\n /* Here we require that excludes is a sorted list. */\n extern void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);\n extern int is_in_cmdlist(struct cmdnames *cmds, const char *name);\n-extern void list_commands(const char *title,\n+extern void list_commands(const char *title, unsigned int colopts,\n \t\t\t  struct cmdnames *main_cmds,\n \t\t\t  struct cmdnames *other_cmds);\n \n-- \n1.7.8.36.g69ee2\n"},{"id":"185612","messageId":"1330430331-19945-8-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330430331-19945-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v7 07/10] branch: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-28T11:58:48Z","receivedAt":"2012-02-28T11:58:48Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config.txt     |    4 ++\n Documentation/git-branch.txt |    9 +++++\n Makefile                     |    2 +-\n builtin/branch.c             |   32 +++++++++++++++--\n t/t3200-branch.sh            |   77 ++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 119 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 5216598..c14db27 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -847,6 +847,10 @@ column.ui::\n +\n \tThis option defaults to 'never'.\n \n+column.branch::\n+\tSpecify whether to output branch listing in `git branch` in columns.\n+\tSee `column.ui` for details.\n+\n commit.status::\n \tA boolean to enable/disable inclusion of status information in the\n \tcommit message template when using an editor to prepare the commit\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex 0427e80..ba5cccb 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -10,6 +10,7 @@ SYNOPSIS\n [verse]\n 'git branch' [--color[=<when>] | --no-color] [-r | -a]\n \t[--list] [-v [--abbrev=<length> | --no-abbrev]]\n+\t[--column[=<options>] | --no-column]\n \t[(--merged | --no-merged | --contains) [<commit>]] [<pattern>...]\n 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]\n 'git branch' (-m | -M) [<oldbranch>] <newbranch>\n@@ -107,6 +108,14 @@ OPTIONS\n \tdefault to color output.\n \tSame as `--color=never`.\n \n+--column[=<options>]::\n+--no-column::\n+\tDisplay branch listing in columns. See configuration variable\n+\tcolumn.branch for option syntax.`--column` and `--no-column`\n+\twithout options are equivalent to 'always' and 'never' respectively.\n++\n+This option is only applicable in non-verbose mode.\n+\n -r::\n --remotes::\n \tList or delete (if used with -d) the remote-tracking branches.\ndiff --git a/Makefile b/Makefile\nindex 0998f0d..320d3f8 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2168,7 +2168,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o url.o http-backend.o: url.h\n-column.o help.o pager.o: column.h\n+builtin/branch.o column.o help.o pager.o: column.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex cb17bc3..611cc0e 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -15,6 +15,8 @@\n #include \"branch.h\"\n #include \"diff.h\"\n #include \"revision.h\"\n+#include \"string-list.h\"\n+#include \"column.h\"\n \n static const char * const builtin_branch_usage[] = {\n \t\"git branch [options] [-r | -a] [--merged | --no-merged]\",\n@@ -53,6 +55,9 @@ static enum merge_filter {\n } merge_filter;\n static unsigned char merge_filter_ref[20];\n \n+static struct string_list output = STRING_LIST_INIT_DUP;\n+static unsigned int colopts;\n+\n static int parse_branch_color_slot(const char *var, int ofs)\n {\n \tif (!strcasecmp(var+ofs, \"plain\"))\n@@ -70,6 +75,8 @@ static int parse_branch_color_slot(const char *var, int ofs)\n \n static int git_branch_config(const char *var, const char *value, void *cb)\n {\n+\tif (!prefixcmp(var, \"column.\"))\n+\t\treturn git_column_config(var, value, \"branch\", &colopts);\n \tif (!strcmp(var, \"color.branch\")) {\n \t\tbranch_use_color = git_config_colorbool(var, value);\n \t\treturn 0;\n@@ -474,7 +481,12 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,\n \telse if (verbose)\n \t\t/* \" f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h\" */\n \t\tadd_verbose_info(&out, item, verbose, abbrev);\n-\tprintf(\"%s\\n\", out.buf);\n+\tif (colopts & COL_ENABLED) {\n+\t\tassert(!verbose && \"--column and --verbose are incompatible\");\n+\t\tstring_list_append(&output, out.buf);\n+\t} else {\n+\t\tprintf(\"%s\\n\", out.buf);\n+\t}\n \tstrbuf_release(&name);\n \tstrbuf_release(&out);\n }\n@@ -727,6 +739,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\t\tPARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,\n \t\t\topt_parse_merge_filter, (intptr_t) \"HEAD\",\n \t\t},\n+\t\tOPT_COLUMN(0, \"column\", &colopts, \"list branches in columns\"),\n \t\tOPT_END(),\n \t};\n \n@@ -749,6 +762,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t}\n \thashcpy(merge_filter_ref, head_sha1);\n \n+\n+\tcolopts |= COL_ANSI;\n \targc = parse_options(argc, argv, prefix, options, builtin_branch_usage,\n \t\t\t     0);\n \n@@ -760,12 +775,21 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \n \tif (abbrev == -1)\n \t\tabbrev = DEFAULT_ABBREV;\n+\tif (verbose) {\n+\t\tif (explicitly_enable_column(colopts))\n+\t\t\tdie(_(\"--column and --verbose are incompatible\"));\n+\t\tcolopts = 0;\n+\t}\n \n \tif (delete)\n \t\treturn delete_branches(argc, argv, delete > 1, kinds);\n-\telse if (list)\n-\t\treturn print_ref_list(kinds, detached, verbose, abbrev,\n-\t\t\t\t      with_commit, argv);\n+\telse if (list) {\n+\t\tint ret = print_ref_list(kinds, detached, verbose, abbrev,\n+\t\t\t\t\t with_commit, argv);\n+\t\tprint_columns(&output, colopts, NULL);\n+\t\tstring_list_clear(&output, 0);\n+\t\treturn ret;\n+\t}\n \telse if (edit_description) {\n \t\tconst char *branch_name;\n \t\tstruct strbuf branch_ref = STRBUF_INIT;\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex dd1aceb..c38592a 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -160,6 +160,83 @@ test_expect_success 'git branch --list -d t should fail' '\n \ttest_path_is_missing .git/refs/heads/t\n '\n \n+test_expect_success 'git branch --column' '\n+\tCOLUMNS=80 git branch --column=column >actual &&\n+\tcat >expected <<\\EOF &&\n+  a/b/c     bam       foo       l       * master    n         o/p       r\n+  abc       bar       j/k       m/m       master2   o/o       q\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'git branch --column with an extremely long branch name' '\n+\tlong=this/is/a/part/of/long/branch/name &&\n+\tlong=z$long/$long/$long/$long &&\n+\ttest_when_finished \"git branch -d $long\" &&\n+\tgit branch $long &&\n+\tCOLUMNS=80 git branch --column=column >actual &&\n+\tcat >expected <<EOF &&\n+  a/b/c\n+  abc\n+  bam\n+  bar\n+  foo\n+  j/k\n+  l\n+  m/m\n+* master\n+  master2\n+  n\n+  o/o\n+  o/p\n+  q\n+  r\n+  $long\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'git branch with column.*' '\n+\tgit config column.ui column &&\n+\tgit config column.branch \"dense\" &&\n+\tCOLUMNS=80 git branch >actual &&\n+\tgit config --unset column.branch &&\n+\tgit config --unset column.ui &&\n+\tcat >expected <<\\EOF &&\n+  a/b/c   bam   foo   l   * master    n     o/p   r\n+  abc     bar   j/k   m/m   master2   o/o   q\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'git branch --column -v should fail' '\n+\ttest_must_fail git branch --column -v\n+'\n+\n+test_expect_success 'git branch -v with column.ui ignored' '\n+\tgit config column.ui column &&\n+\tCOLUMNS=80 git branch -v | cut -c -10 | sed \"s/ *$//\" >actual &&\n+\tgit config --unset column.ui &&\n+\tcat >expected <<\\EOF &&\n+  a/b/c\n+  abc\n+  bam\n+  bar\n+  foo\n+  j/k\n+  l\n+  m/m\n+* master\n+  master2\n+  n\n+  o/o\n+  o/p\n+  q\n+  r\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n mv .git/config .git/config-saved\n \n test_expect_success 'git branch -m q q2 without config should succeed' '\n-- \n1.7.8.36.g69ee2\n"},{"id":"185613","messageId":"1330430331-19945-9-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330430331-19945-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v7 08/10] status: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-28T11:58:49Z","receivedAt":"2012-02-28T11:58:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config.txt     |    4 ++++\n Documentation/git-status.txt |    7 +++++++\n Makefile                     |    2 +-\n builtin/commit.c             |    6 ++++++\n t/t7508-status.sh            |   24 ++++++++++++++++++++++++\n wt-status.c                  |   28 ++++++++++++++++++++++++++--\n wt-status.h                  |    1 +\n 7 files changed, 69 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex c14db27..ebb210c 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -851,6 +851,10 @@ column.branch::\n \tSpecify whether to output branch listing in `git branch` in columns.\n \tSee `column.ui` for details.\n \n+column.status::\n+\tSpecify whether to output untracked files in `git status` in columns.\n+\tSee `column.ui` for details.\n+\n commit.status::\n \tA boolean to enable/disable inclusion of status information in the\n \tcommit message template when using an editor to prepare the commit\ndiff --git a/Documentation/git-status.txt b/Documentation/git-status.txt\nindex 3d51717..2f87207 100644\n--- a/Documentation/git-status.txt\n+++ b/Documentation/git-status.txt\n@@ -77,6 +77,13 @@ configuration variable documented in linkgit:git-config[1].\n \tTerminate entries with NUL, instead of LF.  This implies\n \tthe `--porcelain` output format if no other format is given.\n \n+--column[=<options>]::\n+--no-column::\n+\tDisplay untracked files in columns. See configuration variable\n+\tcolumn.status for option syntax.`--column` and `--no-column`\n+\twithout options are equivalent to 'always' and 'never'\n+\trespectively.\n+\n \n OUTPUT\n ------\ndiff --git a/Makefile b/Makefile\nindex 320d3f8..d9c5f00 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2168,7 +2168,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o url.o http-backend.o: url.h\n-builtin/branch.o column.o help.o pager.o: column.h\n+builtin/branch.o builtin/commit.o column.o help.o pager.o: column.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 3714582..b42b83f 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -27,6 +27,7 @@\n #include \"quote.h\"\n #include \"submodule.h\"\n #include \"gpg-interface.h\"\n+#include \"column.h\"\n \n static const char * const builtin_commit_usage[] = {\n \t\"git commit [options] [--] <filepattern>...\",\n@@ -88,6 +89,7 @@ static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;\n static int no_post_rewrite, allow_empty_message;\n static char *untracked_files_arg, *force_date, *ignore_submodule_arg;\n static char *sign_commit;\n+static unsigned int colopts;\n \n /*\n  * The default commit message cleanup mode will remove the lines\n@@ -1145,6 +1147,8 @@ static int git_status_config(const char *k, const char *v, void *cb)\n {\n \tstruct wt_status *s = cb;\n \n+\tif (!prefixcmp(k, \"column.\"))\n+\t\treturn git_column_config(k, v, \"status\", &colopts);\n \tif (!strcmp(k, \"status.submodulesummary\")) {\n \t\tint is_bool;\n \t\ts->submodule_summary = git_config_bool_or_int(k, v, &is_bool);\n@@ -1210,6 +1214,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \t\t{ OPTION_STRING, 0, \"ignore-submodules\", &ignore_submodule_arg, \"when\",\n \t\t  \"ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)\",\n \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n+\t\tOPT_COLUMN(0, \"column\", &colopts, \"list untracked files in columns\"),\n \t\tOPT_END(),\n \t};\n \n@@ -1223,6 +1228,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix,\n \t\t\t     builtin_status_options,\n \t\t\t     builtin_status_usage, 0);\n+\ts.colopts = colopts;\n \n \tif (null_termination && status_format == STATUS_FORMAT_LONG)\n \t\tstatus_format = STATUS_FORMAT_PORCELAIN;\ndiff --git a/t/t7508-status.sh b/t/t7508-status.sh\nindex fc57b13..8f5cfac 100755\n--- a/t/t7508-status.sh\n+++ b/t/t7508-status.sh\n@@ -59,6 +59,30 @@ test_expect_success 'status (1)' '\n \ttest_i18ngrep \"use \\\"git rm --cached <file>\\.\\.\\.\\\" to unstage\" output\n '\n \n+test_expect_success 'status --column' '\n+\tCOLUMNS=50 git status --column=\"column dense\" >output &&\n+\tcat >expect <<\\EOF &&\n+# On branch master\n+# Changes to be committed:\n+#   (use \"git reset HEAD <file>...\" to unstage)\n+#\n+#\tnew file:   dir2/added\n+#\n+# Changes not staged for commit:\n+#   (use \"git add <file>...\" to update what will be committed)\n+#   (use \"git checkout -- <file>...\" to discard changes in working directory)\n+#\n+#\tmodified:   dir1/modified\n+#\n+# Untracked files:\n+#   (use \"git add <file>...\" to include in what will be committed)\n+#\n+#\tdir1/untracked dir2/untracked untracked\n+#\tdir2/modified  output\n+EOF\n+\ttest_cmp expect output\n+'\n+\n cat >expect <<\\EOF\n # On branch master\n # Changes to be committed:\ndiff --git a/wt-status.c b/wt-status.c\nindex 9ffc535..eee059e 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -11,6 +11,7 @@\n #include \"remote.h\"\n #include \"refs.h\"\n #include \"submodule.h\"\n+#include \"column.h\"\n \n static char default_wt_status_colors[][COLOR_MAXLEN] = {\n \tGIT_COLOR_NORMAL, /* WT_STATUS_HEADER */\n@@ -641,6 +642,8 @@ static void wt_status_print_other(struct wt_status *s,\n {\n \tint i;\n \tstruct strbuf buf = STRBUF_INIT;\n+\tstatic struct string_list output = STRING_LIST_INIT_DUP;\n+\tstruct column_options copts;\n \n \tif (!l->nr)\n \t\treturn;\n@@ -649,12 +652,33 @@ static void wt_status_print_other(struct wt_status *s,\n \n \tfor (i = 0; i < l->nr; i++) {\n \t\tstruct string_list_item *it;\n+\t\tconst char *path;\n \t\tit = &(l->items[i]);\n+\t\tpath = quote_path(it->string, strlen(it->string),\n+\t\t\t\t  &buf, s->prefix);\n+\t\tif (s->colopts & COL_ENABLED) {\n+\t\t\tstring_list_append(&output, path);\n+\t\t\tcontinue;\n+\t\t}\n \t\tstatus_printf(s, color(WT_STATUS_HEADER, s), \"\\t\");\n \t\tstatus_printf_more(s, color(WT_STATUS_UNTRACKED, s),\n-\t\t\t\"%s\\n\", quote_path(it->string, strlen(it->string),\n-\t\t\t\t\t    &buf, s->prefix));\n+\t\t\t\t   \"%s\\n\", path);\n \t}\n+\n+\tstrbuf_release(&buf);\n+\tif ((s->colopts & COL_ENABLED) == 0)\n+\t\treturn;\n+\n+\tstrbuf_addf(&buf, \"%s#\\t%s\",\n+\t\t    color(WT_STATUS_HEADER, s),\n+\t\t    color(WT_STATUS_UNTRACKED, s));\n+\tmemset(&copts, 0, sizeof(copts));\n+\tcopts.padding = 1;\n+\tcopts.indent = buf.buf;\n+\tif (want_color(s->use_color))\n+\t\tcopts.nl = GIT_COLOR_RESET \"\\n\";\n+\tprint_columns(&output, s->colopts, &copts);\n+\tstring_list_clear(&output, 0);\n \tstrbuf_release(&buf);\n }\n \ndiff --git a/wt-status.h b/wt-status.h\nindex 682b4c8..6dd7207 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -56,6 +56,7 @@ struct wt_status {\n \tenum untracked_status_type show_untracked_files;\n \tconst char *ignore_submodule_arg;\n \tchar color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];\n+\tint colopts;\n \n \t/* These are computed during processing of the individual sections */\n \tint commitable;\n-- \n1.7.8.36.g69ee2\n"},{"id":"185614","messageId":"1330430331-19945-10-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330430331-19945-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v7 09/10] column: support piping stdout to external git-column process","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-28T11:58:50Z","receivedAt":"2012-02-28T11:58:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"For too complicated output handling, it'd be easier to just spawn\ngit-column and redirect stdout to it. This patch provides helpers\nto do that.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n column.c |   69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n column.h |    3 ++\n 2 files changed, 72 insertions(+), 0 deletions(-)\n\ndiff --git a/column.c b/column.c\nindex 67bfd06..103d98c 100644\n--- a/column.c\n+++ b/column.c\n@@ -2,6 +2,7 @@\n #include \"column.h\"\n #include \"string-list.h\"\n #include \"parse-options.h\"\n+#include \"run-command.h\"\n #include \"color.h\"\n #include \"utf8.h\"\n \n@@ -405,3 +406,71 @@ int parseopt_column_callback(const struct option *opt,\n \t*mode |= COL_ENABLED | COL_ENABLED_SET;\n \treturn 0;\n }\n+\n+static int fd_out = -1;\n+static struct child_process column_process;\n+\n+int run_column_filter(int colopts, const struct column_options *opts)\n+{\n+\tconst char *av[10];\n+\tint ret, ac = 0;\n+\tstruct strbuf sb_colopt  = STRBUF_INIT;\n+\tstruct strbuf sb_width   = STRBUF_INIT;\n+\tstruct strbuf sb_padding = STRBUF_INIT;\n+\n+\tif (fd_out != -1)\n+\t\treturn -1;\n+\n+\tav[ac++] = \"column\";\n+\tstrbuf_addf(&sb_colopt, \"--rawmode=%d\", colopts);\n+\tav[ac++] = sb_colopt.buf;\n+\tif (opts->width) {\n+\t\tstrbuf_addf(&sb_width, \"--width=%d\", opts->width);\n+\t\tav[ac++] = sb_width.buf;\n+\t}\n+\tif (opts->indent) {\n+\t\tav[ac++] = \"--indent\";\n+\t\tav[ac++] = opts->indent;\n+\t}\n+\tif (opts->padding) {\n+\t\tstrbuf_addf(&sb_padding, \"--padding=%d\", opts->padding);\n+\t\tav[ac++] = sb_padding.buf;\n+\t}\n+\tav[ac] = NULL;\n+\n+\tfflush(stdout);\n+\tmemset(&column_process, 0, sizeof(column_process));\n+\tcolumn_process.in = -1;\n+\tcolumn_process.out = dup(1);\n+\tcolumn_process.git_cmd = 1;\n+\tcolumn_process.argv = av;\n+\n+\tret = start_command(&column_process);\n+\n+\tstrbuf_release(&sb_colopt);\n+\tstrbuf_release(&sb_width);\n+\tstrbuf_release(&sb_padding);\n+\n+\tif (ret)\n+\t\treturn -2;\n+\n+\tfd_out = dup(1);\n+\tclose(1);\n+\tdup2(column_process.in, 1);\n+\tclose(column_process.in);\n+\treturn 0;\n+}\n+\n+int stop_column_filter(void)\n+{\n+\tif (fd_out == -1)\n+\t\treturn -1;\n+\n+\tfflush(stdout);\n+\tclose(1);\n+\tfinish_command(&column_process);\n+\tdup2(fd_out, 1);\n+\tclose(fd_out);\n+\tfd_out = -1;\n+\treturn 0;\n+}\ndiff --git a/column.h b/column.h\nindex 43528da..cb81c8a 100644\n--- a/column.h\n+++ b/column.h\n@@ -34,4 +34,7 @@ struct option;\n extern int parseopt_column_callback(const struct option *opt,\n \t\t\t\t    const char *arg, int unset);\n \n+extern int run_column_filter(int colopts, const struct column_options *);\n+extern int stop_column_filter();\n+\n #endif\n-- \n1.7.8.36.g69ee2\n"},{"id":"185615","messageId":"1330430331-19945-11-git-send-email-pclouds@gmail.com","threadId":"29734","inReplyTo":"1330430331-19945-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v7 10/10] tag: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-28T11:58:51Z","receivedAt":"2012-02-28T11:58:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config.txt  |    4 ++++\n Documentation/git-tag.txt |    9 +++++++++\n Makefile                  |    2 +-\n builtin/tag.c             |   26 +++++++++++++++++++++++---\n t/t7004-tag.sh            |   44 ++++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 81 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex ebb210c..145336a 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -855,6 +855,10 @@ column.status::\n \tSpecify whether to output untracked files in `git status` in columns.\n \tSee `column.ui` for details.\n \n+column.tag::\n+\tSpecify whether to output tag listing in `git tag` in columns.\n+\tSee `column.ui` for details.\n+\n commit.status::\n \tA boolean to enable/disable inclusion of status information in the\n \tcommit message template when using an editor to prepare the commit\ndiff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt\nindex 8d32b9a..e36a7c3 100644\n--- a/Documentation/git-tag.txt\n+++ b/Documentation/git-tag.txt\n@@ -13,6 +13,7 @@ SYNOPSIS\n \t<tagname> [<commit> | <object>]\n 'git tag' -d <tagname>...\n 'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]\n+\t[--column[=<options>] | --no-column] [<pattern>...]\n \t[<pattern>...]\n 'git tag' -v <tagname>...\n \n@@ -84,6 +85,14 @@ OPTIONS\n \tusing fnmatch(3)).  Multiple patterns may be given; if any of\n \tthem matches, the tag is shown.\n \n+--column[=<options>]::\n+--no-column::\n+\tDisplay tag listing in columns. See configuration variable\n+\tcolumn.tag for option syntax.`--column` and `--no-column`\n+\twithout options are equivalent to 'always' and 'never' respectively.\n++\n+This option is only applicable when listing tags without annotation lines.\n+\n --contains <commit>::\n \tOnly list tags which contain the specified commit.\n \ndiff --git a/Makefile b/Makefile\nindex d9c5f00..65fc6b9 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2168,7 +2168,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o url.o http-backend.o: url.h\n-builtin/branch.o builtin/commit.o column.o help.o pager.o: column.h\n+builtin/branch.o builtin/commit.o builtin/tag.o column.o help.o pager.o: column.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex fe7e5e5..e99cbff 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -16,6 +16,7 @@\n #include \"revision.h\"\n #include \"gpg-interface.h\"\n #include \"sha1-array.h\"\n+#include \"column.h\"\n \n static const char * const git_tag_usage[] = {\n \t\"git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]\",\n@@ -33,6 +34,7 @@ struct tag_filter {\n };\n \n static struct sha1_array points_at;\n+static unsigned int colopts;\n \n static int match_pattern(const char **patterns, const char *ref)\n {\n@@ -263,6 +265,8 @@ static int git_tag_config(const char *var, const char *value, void *cb)\n \tint status = git_gpg_config(var, value, cb);\n \tif (status)\n \t\treturn status;\n+\tif (!prefixcmp(var, \"column.\"))\n+\t\treturn git_column_config(var, value, \"tag\", &colopts);\n \treturn git_default_config(var, value, cb);\n }\n \n@@ -459,6 +463,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING('u', \"local-user\", &keyid, \"key-id\",\n \t\t\t\t\t\"use another key to sign the tag\"),\n \t\tOPT__FORCE(&force, \"replace the tag if exists\"),\n+\t\tOPT_COLUMN(0, \"column\", &colopts, \"show tag list in columns\"),\n \n \t\tOPT_GROUP(\"Tag listing options\"),\n \t\t{\n@@ -495,9 +500,24 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \n \tif (list + delete + verify > 1)\n \t\tusage_with_options(git_tag_usage, options);\n-\tif (list)\n-\t\treturn list_tags(argv, lines == -1 ? 0 : lines,\n-\t\t\t\t with_commit);\n+\tif (list && lines != -1) {\n+\t\tif (explicitly_enable_column(colopts))\n+\t\t\tdie(_(\"--column and -n are incompatible\"));\n+\t\tcolopts = 0;\n+\t}\n+\tif (list) {\n+\t\tint ret;\n+\t\tif (colopts & COL_ENABLED) {\n+\t\t\tstruct column_options copts;\n+\t\t\tmemset(&copts, 0, sizeof(copts));\n+\t\t\tcopts.padding = 2;\n+\t\t\trun_column_filter(colopts, &copts);\n+\t\t}\n+\t\tret = list_tags(argv, lines == -1 ? 0 : lines, with_commit);\n+\t\tif (colopts & COL_ENABLED)\n+\t\t\tstop_column_filter();\n+\t\treturn ret;\n+\t}\n \tif (lines != -1)\n \t\tdie(_(\"-n option is only allowed with -l.\"));\n \tif (with_commit)\ndiff --git a/t/t7004-tag.sh b/t/t7004-tag.sh\nindex f8c247a..5189446 100755\n--- a/t/t7004-tag.sh\n+++ b/t/t7004-tag.sh\n@@ -263,6 +263,50 @@ test_expect_success 'tag -l can accept multiple patterns' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'listing tags in column' '\n+\tCOLUMNS=40 git tag -l --column=row >actual &&\n+\tcat >expected <<\\EOF &&\n+a1      aa1     cba     t210    t211\n+v0.2.1  v1.0    v1.0.1  v1.1.3\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'listing tags in column with column.*' '\n+\tgit config column.tag row &&\n+\tgit config column.ui dense &&\n+\tCOLUMNS=40 git tag -l >actual &&\n+\tgit config --unset column.ui &&\n+\tgit config --unset column.tag &&\n+\tcat >expected <<\\EOF &&\n+a1      aa1   cba     t210    t211\n+v0.2.1  v1.0  v1.0.1  v1.1.3\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'listing tag with -n --column should fail' '\n+\ttest_must_fail git tag --column -n\n+'\n+\n+test_expect_success 'listing tags -n in column with column.ui ignored' '\n+\tgit config column.ui \"row dense\" &&\n+\tCOLUMNS=40 git tag -l -n >actual &&\n+\tgit config --unset column.ui &&\n+\tcat >expected <<\\EOF &&\n+a1              Foo\n+aa1             Foo\n+cba             Foo\n+t210            Foo\n+t211            Foo\n+v0.2.1          Foo\n+v1.0            Foo\n+v1.0.1          Foo\n+v1.1.3          Foo\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n # creating and verifying lightweight tags:\n \n test_expect_success \\\n-- \n1.7.8.36.g69ee2\n"},{"id":"185642","messageId":"7v8vjm25iy.fsf@alter.siamese.dyndns.org","threadId":"29734","inReplyTo":"1330430331-19945-2-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v7 01/10] Add git-column for columnar display","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-28T18:10:13Z","receivedAt":"2012-02-28T18:10:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n\n> diff --git a/Documentation/git-column.txt b/Documentation/git-column.txt\n> new file mode 100644\n> index 0000000..508b85f\n> --- /dev/null\n> +++ b/Documentation/git-column.txt\n> @@ -0,0 +1,49 @@\n> +git-column(1)\n> +=============\n> +\n> +NAME\n> +----\n> +git-column - Display data in columns\n> +\n> +SYNOPSIS\n> +--------\n> +[verse]\n> +'git column' [--mode=<mode> | --rawmode=<n>] [--width=<width>]\n\nPlease spell this \"--raw-mode\".\n\n> diff --git a/builtin/column.c b/builtin/column.c\n> new file mode 100644\n> index 0000000..3b0f74e\n> --- /dev/null\n> +++ b/builtin/column.c\n> @@ -0,0 +1,41 @@\n> +#include \"builtin.h\"\n> +#include \"cache.h\"\n> +#include \"strbuf.h\"\n> +#include \"parse-options.h\"\n> +#include \"string-list.h\"\n> +#include \"column.h\"\n> +\n> +static const char * const builtin_column_usage[] = {\n> +\t\"git column [options]\",\n> +\tNULL\n> +};\n> +static unsigned int colopts;\n> +\n> +int cmd_column(int argc, const char **argv, const char *prefix)\n> +{\n> +\tstruct string_list list = STRING_LIST_INIT_DUP;\n> +\tstruct strbuf sb = STRBUF_INIT;\n> +\tstruct column_options copts;\n> +\tstruct option options[] = {\n> +\t\tOPT_COLUMN(0, \"mode\", &colopts, \"layout to use\"),\n> +\t\tOPT_INTEGER(0, \"rawmode\", &colopts, \"layout to use\"),\n> +\t\tOPT_INTEGER(0, \"width\", &copts.width, \"Maximum width\"),\n> +\t\tOPT_STRING(0, \"indent\", &copts.indent, \"string\", \"Padding space on left border\"),\n> +\t\tOPT_INTEGER(0, \"nl\", &copts.nl, \"Padding space on right border\"),\n> +\t\tOPT_INTEGER(0, \"padding\", &copts.padding, \"Padding space between columns\"),\n> +\t\tOPT_END()\n> +\t};\n> +\n> +\tmemset(&copts, 0, sizeof(copts));\n> +\tcopts.width = term_columns();\n> +\tcopts.padding = 1;\n> +\targc = parse_options(argc, argv, \"\", options, builtin_column_usage, 0);\n\nCurious. The usual pattern is to set up the built-in default, call\ngit_config() to override it with the configured values, and then call\nparse_options() to override at the runtime.  I see configuration callback\ndefined in column.c but no call to git_config() here?\n\n> +\tif (argc)\n> +\t\tusage_with_options(builtin_column_usage, options);\n> +\n> +\twhile (!strbuf_getline(&sb, stdin, '\\n'))\n> +\t\tstring_list_append(&list, sb.buf);\n> +\n> +\tprint_columns(&list, colopts, &copts);\n> +\treturn 0;\n> +}\n> diff --git a/column.c b/column.c\n> new file mode 100644\n> index 0000000..d61da81\n> --- /dev/null\n> +++ b/column.c\n> @@ -0,0 +1,170 @@\n> +#include \"cache.h\"\n> +#include \"column.h\"\n> +#include \"string-list.h\"\n> +#include \"parse-options.h\"\n> +\n> +#define MODE(mode) ((mode) & COL_MODE)\n> +\n> +/* Display without layout when COL_ENABLED is not set */\n> +static void display_plain(const struct string_list *list,\n> +\t\t\t  const char *indent, const char *nl)\n> +{\n> +\tint i;\n> +\n> +\tfor (i = 0; i < list->nr; i++)\n> +\t\tprintf(\"%s%s%s\", indent, list->items[i].string, nl);\n> +}\n> +\n> +void print_columns(const struct string_list *list, unsigned int mode,\n> +\t\t   struct column_options *opts)\n> +{\n> +\tconst char *indent = \"\", *nl = \"\\n\";\n> +\tint padding = 1, width = term_columns();\n> +\n> +\tif (!list->nr)\n> +\t\treturn;\n> +\tif (opts) {\n> +\t\tif (opts->indent)\n> +\t\t\tindent = opts->indent;\n> +\t\tif (opts->nl)\n> +\t\t\tnl = opts->nl;\n> +\t\tif (opts->width)\n> +\t\t\twidth = opts->width;\n> +\t\tpadding = opts->padding;\n> +\t}\n> +\tif (width <= 1 || !(mode & COL_ENABLED)) {\n\nCurious why this is \"1\".  If your terminal is only 2 columns wide, you\nwouldn't be able to show your list items in two columns as you would want\nto have an inter-column gap, no?\n\n> +\t\tdisplay_plain(list, indent, nl);\n> +\t\treturn;\n> +\t}\n> +\tdie(\"BUG: invalid mode %d\", MODE(mode));\n> +}\n> +\n> +struct colopt {\n> +\tenum {\n> +\t\tENABLE,\n> +\t\tMODE,\n> +\t\tOPTION\n> +\t} type;\n> +\tconst char *name;\n> +\tint value;\n> +};\n> +\n> +/*\n> + * Set COL_ENABLED and COL_ENABLED_SET. If 'set' is -1, check if\n> + * stdout is tty.\n> + */\n> +static int set_enable_bit(unsigned int *mode, int set, int stdout_is_tty)\n> +{\n\nSomehow it looks to me that this is setting the ENABLED bit, not enable\nbit.\n\n> +\tif (set < 0) {\t/* auto */\n> +\t\tif (stdout_is_tty < 0)\n> +\t\t\tstdout_is_tty = isatty(1);\n> +\t\tset = stdout_is_tty || (pager_in_use() && pager_use_color);\n\nWhy does this have anything to do with the use of color?\n\n> +\t}\n> +\tif (set)\n> +\t\t*mode = *mode | COL_ENABLED | COL_ENABLED_SET;\n> +\telse\n> +\t\t*mode = (*mode & ~COL_ENABLED) | COL_ENABLED_SET;\n> +\treturn 0;\n> +}\n\nOK, so we record the desired value (either COL_ENABLED or not) and the\nfact that a call to set_enable_bit() function set it.  Which implies that\nthis function must be called from only one codepath (either setting from\nthe configuration mechanism, or by parsing the command line option) but\nnot both.  I guess this is only called from configuration codepath?\n\n> +/*\n> + * Set COL_MODE_*. mode is intially copied from column.ui. If\n> + * COL_ENABLED_SET is not set, then neither 'always', 'never' nor\n> + * 'auto' has been used. Default to 'always'.\n> + */\n> +static int set_mode(unsigned int *mode, unsigned int value)\n> +{\n> +\t*mode = (*mode & ~COL_MODE) | value;\n> +\tif (!(*mode & COL_ENABLED_SET))\n> +\t\t*mode |= COL_ENABLED | COL_ENABLED_SET;\n> +\n> +\treturn 0;\n> +}\n\nI am *imagining* that \"*mode\" begins with some compiled-in default, then\ngit_config() will read from column.* variables and set \"*mode\" but not in\nthis codepath, and by the time command line option triggers this codepath,\nwe should have seen everything the config wants to tell us, so if the\nconfig did not say anything (i.e. COL_ENABLED_SET is not set yet), we\nforce enable the feature (the user wanting it to operate in some mode is\nan indication enough that the user wants to enable the machinery as a\nwhole). So this function is designed to be called from command line option\nparsing, but never from the configuration parsing.\n\nBut the parse_option() function below calls this, which would mean it is\nalso for command line option parsing and not configuration parsing.  The\nsame function however calls set_enable_bit() we saw earlier that can only\nbe called from configuration codepath.  What is going on?\n\nI am afraid that we do not have enough to judge if this is sane in this\npatch, as there is no support for column.ui at this stage.  Perhaps the\nseries is not structured well and has things that are not yet relevant in\nearly part of it.  Sigh.\n\nThe remainder of the patch unreviewed.\n"},{"id":"185643","messageId":"7v4nua25cz.fsf@alter.siamese.dyndns.org","threadId":"29734","inReplyTo":"1330430331-19945-3-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v7 02/10] Stop starting pager recursively","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-28T18:13:48Z","receivedAt":"2012-02-28T18:13:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n\n> git-column can be used as a pager for other git commands, something\n> like this:\n>\n>     GIT_PAGER=\"git -p column --mode='dense color'\" git -p branch\n>\n> The problem with this is that \"git -p column\" also has $GIT_PAGER\n> set so the pager runs itself again as a pager, then again and again.\n>\n> Stop this.\n\nA natural question that may come after reading only the above is if \"git\ncolumn\" is the only one that has this problem.  In other words, is the\nundesirable behaviour you observed caused by a bug in setup_pager() that\nneeds to be fixed, or should it be fixed in \"git column\"?\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>  pager.c |    2 +-\n>  1 files changed, 1 insertions(+), 1 deletions(-)\n>\n> diff --git a/pager.c b/pager.c\n> index 05584de..4dcb08d 100644\n> --- a/pager.c\n> +++ b/pager.c\n> @@ -73,7 +73,7 @@ void setup_pager(void)\n>  {\n>  \tconst char *pager = git_pager(isatty(1));\n>  \n> -\tif (!pager)\n> +\tif (!pager || pager_in_use())\n>  \t\treturn;\n>  \n>  \t/*\n"},{"id":"185644","messageId":"7vy5rmzukh.fsf@alter.siamese.dyndns.org","threadId":"29734","inReplyTo":"1330430331-19945-4-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v7 03/10] column: add columnar layout","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-28T18:22:54Z","receivedAt":"2012-02-28T18:22:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n\n> COL_MODE_COLUMN and COL_MODE_ROW fill column by column (or row by row\n> respectively), given the terminal width and how many space between\n> columns.\n>\n> Strings are supposed to be in UTF-8. If strings contain ANSI escape\n> strings, COL_ANSI must be specified for correct length calculation.\n\nHrm, is it too heavyweight to autodetect and relieve the caller from the\nburden of passing COL_ANSI?\n\nPerhaps if you update utf8_strwidth() so that it takes <ptr, len> input\nnot NUL terminated string, you can do item_length like this, no?\n\n\tcp = s;\n\twidth = 0;\n\twhile (1) {\n\t\tep = strstr(cp, \"\\033[\");\n\t\tif (!ep) {\n\t\t\twidth += utf8_strwidth(cp);\n                        break;\n\t\t}\n                utf8_strwidth_counted(cp, ep - cp);\n\t\t... scan ep to skip the \"\\033[...;X\"\n\t\tcp = ep;\n\t}\n"},{"id":"185646","messageId":"7vty2azudf.fsf@alter.siamese.dyndns.org","threadId":"29734","inReplyTo":"1330430331-19945-5-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v7 04/10] column: add dense layout support","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-28T18:27:08Z","receivedAt":"2012-02-28T18:27:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n\n> Normally, all items will be gone through once. The largest cell will set\n> column width for all columns. With this strategy, one long item will\n> stretch out all columns, wasting space in between them.\n>\n> With COL_DENSE enabled, it shrinks all columns to minimum, then attempts\n> to push the last row's cells over to the next column with hope that\n> everything still fits even there's one row less. The process is repeated\n> until the new layout cannot fit in given width anymore, or there's only\n> one row left (perfect!).\n\nAs you have given 4 bits for COL_MODE in the previous patch, I expected\nthat this will be one of the mode that you can use, e.g. column-first dense,\nor row-first no-dense, with two more bits for operating modes.  Not calling\nit COL_MODE_DENSE and assigning a \"are we dense of not?\" bit out of COL_MODE\nbits feels wrong.\n"},{"id":"185648","messageId":"7vpqcyztk0.fsf@alter.siamese.dyndns.org","threadId":"29734","inReplyTo":"1330430331-19945-6-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v7 05/10] column: add column.ui for default column output settings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-28T18:44:47Z","receivedAt":"2012-02-28T18:44:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n\n> +static int column_config(const char *var, const char *value,\n> +\t\t\t const char *key, unsigned int *colopts)\n> +{\n> +\tif (git_config_column(colopts, value, -1))\n\nAre we sure nobody has put us on pager at this point, so that you can tell\ngit_config_column() that it is OK to use isatty(1) to figure it out, or we\ncould already be on pager (i.e. pager_in_use() is true) in which case we\nknow we are interactive and should behave the same way as writing to a\nterminal?\n\n> +\t\treturn error(\"invalid %s mode %s\", key, value);\n> +\treturn 0;\n> +}\n> +\n> +int git_column_config(const char *var, const char *value,\n> +\t\t      const char *command, unsigned int *colopts)\n> +{\n> +\tif (!strcmp(var, \"column.ui\"))\n> +\t\treturn column_config(var, value, \"column.ui\", colopts);\n> +\n> +\tif (command) {\n> +\t\tstruct strbuf sb = STRBUF_INIT;\n> +\t\tint ret = 0;\n> +\t\tstrbuf_addf(&sb, \"column.%s\", command);\n> +\t\tif (!strcmp(var, sb.buf))\n> +\t\t\tret = column_config(var, value, sb.buf, colopts);\n> +\t\tstrbuf_release(&sb);\n> +\t\treturn ret;\n> +\t}\n\nThis feels wrong. Depending on the order column.ui and column.frotz appear\nin the configuration file, asking for \"git column --command=frotz\" would\nyield random results, no?\n\nShouldn't the flow of logic be more like:\n\n\tgit_config(git_column_config);\n        -> git_column_config() is called for column.ui and column.frotz\n           in no specified order; keep two *char variables to store the\n           string value given from configuration\n\tif (kept value from column.frotz is missing)\n\t\tgit_config_column(..., kept value from column.ui, ...);\n\telse\n\t\tgit_config_column(..., kept value from column.frotz, ...); \n\n> diff --git a/column.h b/column.h\n> index eb03c6c..43528da 100644\n> --- a/column.h\n> +++ b/column.h\n> @@ -27,6 +27,8 @@ extern void print_columns(const struct string_list *list,\n>  \t\t\t  struct column_options *opts);\n>  extern int git_config_column(unsigned int *mode, const char *value,\n>  \t\t\t     int stdout_is_tty);\n> +extern int git_column_config(const char *var, const char *value,\n> +\t\t\t     const char *command, unsigned int *colopts);\n\nAlso please rename git_config_column() in the earlier patch, perhaps like\n\"parse_column_config_string()\" or something more sensible, to avoid\nconfusion.\n"},{"id":"185654","messageId":"7vipiqzsd8.fsf@alter.siamese.dyndns.org","threadId":"29734","inReplyTo":"7v4nua25cz.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v7 02/10] Stop starting pager recursively","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-28T19:10:27Z","receivedAt":"2012-02-28T19:10:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n>\n>> git-column can be used as a pager for other git commands, something\n>> like this:\n>>\n>>     GIT_PAGER=\"git -p column --mode='dense color'\" git -p branch\n>>\n>> The problem with this is that \"git -p column\" also has $GIT_PAGER\n>> set so the pager runs itself again as a pager, then again and again.\n>>\n>> Stop this.\n>\n> A natural question that may come after reading only the above is if \"git\n> column\" is the only one that has this problem.  In other words, is the\n> undesirable behaviour you observed caused by a bug in setup_pager() that\n> needs to be fixed, or should it be fixed in \"git column\"?\n\nPut another way, if there is another git command X that can be used as a\nfilter to the output of a git command Y, do you suffer from the same issue\nto when you abuse the GIT_PAGER mechanism to pipe the output from Y to X?\nThat is a sure sign that the pager mechanism needs improvement (obviously,\nan alternative answer could be \"don't do that then\", though).\n\nFor example, shortlog is designed to be X for Y=log, i.e.\n\n\t$ git log v1.0.0.. | git shortlog\n\nis a perfectly valid way to use the command.  I could imagine that this\npatch may improve the situation if you abuse GIT_PAGER mechanism to\nimplement the above pipeline, i.e.\n\n\t$ GIT_PAGER=\"git -p shortlog\" git log v1.0.0..\n\nAlthough I never tried it.\n"},{"id":"185690","messageId":"CACsJy8DJM5hwWnTs2rkhCa7a=vKRRsLMjZGBotmDDpAaGG+Sxw@mail.gmail.com","threadId":"29734","inReplyTo":"7vipiqzsd8.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v7 02/10] Stop starting pager recursively","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-29T01:54:13Z","receivedAt":"2012-02-29T01:54:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2012/2/29 Junio C Hamano <gitster@pobox.com>:\n>> A natural question that may come after reading only the above is if \"git\n>> column\" is the only one that has this problem.  In other words, is the\n>> undesirable behaviour you observed caused by a bug in setup_pager() that\n>> needs to be fixed, or should it be fixed in \"git column\"?\n>\n> Put another way, if there is another git command X that can be used as a\n> filter to the output of a git command Y, do you suffer from the same issue\n> to when you abuse the GIT_PAGER mechanism to pipe the output from Y to X?\n> That is a sure sign that the pager mechanism needs improvement (obviously,\n> an alternative answer could be \"don't do that then\", though).\n>\n> For example, shortlog is designed to be X for Y=log, i.e.\n>\n>        $ git log v1.0.0.. | git shortlog\n>\n> is a perfectly valid way to use the command.  I could imagine that this\n> patch may improve the situation if you abuse GIT_PAGER mechanism to\n> implement the above pipeline, i.e.\n>\n>        $ GIT_PAGER=\"git -p shortlog\" git log v1.0.0..\n>\n> Although I never tried it.\n\nIt does improve that situation, or at least it stops git from forking\nfrantically.\n-- \nDuy\n"},{"id":"185694","messageId":"7vr4xewbrz.fsf@alter.siamese.dyndns.org","threadId":"29734","inReplyTo":"CACsJy8DJM5hwWnTs2rkhCa7a=vKRRsLMjZGBotmDDpAaGG+Sxw@mail.gmail.com","subject":"Re: [PATCH v7 02/10] Stop starting pager recursively","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-29T03:37:04Z","receivedAt":"2012-02-29T03:37:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n> It does improve that situation, or at least it stops git from forking\n> frantically.\n\nThat \"at least\" sounds suspicious ;-) Do you mean that there is other\nbreakages in that mode of operation?\n"},{"id":"185696","messageId":"CACsJy8BfBP9m8rdEY3mk6g4G7n5LyFPvvNgNK2bkwC6L7qTDCg@mail.gmail.com","threadId":"29734","inReplyTo":"7vr4xewbrz.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v7 02/10] Stop starting pager recursively","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-29T03:40:40Z","receivedAt":"2012-02-29T03:40:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Feb 29, 2012 at 10:37 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>\n>> It does improve that situation, or at least it stops git from forking\n>> frantically.\n>\n> That \"at least\" sounds suspicious ;-) Do you mean that there is other\n> breakages in that mode of operation?\n\nWell, \"shortlog -p\" did not run $PAGER (i.e. less). I haven't had time\nto dig further.\n-- \nDuy\n"},{"id":"185699","messageId":"7vaa42w8by.fsf@alter.siamese.dyndns.org","threadId":"29734","inReplyTo":"CACsJy8BfBP9m8rdEY3mk6g4G7n5LyFPvvNgNK2bkwC6L7qTDCg@mail.gmail.com","subject":"Re: [PATCH v7 02/10] Stop starting pager recursively","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-29T04:51:29Z","receivedAt":"2012-02-29T04:51:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n> On Wed, Feb 29, 2012 at 10:37 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>>\n>>> It does improve that situation, or at least it stops git from forking\n>>> frantically.\n>>\n>> That \"at least\" sounds suspicious ;-) Do you mean that there is other\n>> breakages in that mode of operation?\n>\n> Well, \"shortlog -p\" did not run $PAGER (i.e. less). I haven't had time\n> to dig further.\n\nOk. for that matter,\n\n    $ GIT_PAGER=\"git -p column\" git tag --list\n\ndoes not seem to use pager, either, so that may not be limited to\nshortlog, but in any case, I can see how the current code without your\npatch can fall into a fork loop, and your patch is an improvement, even if\nit may not yet be the whole solution.\n"},{"id":"185909","messageId":"87ehtbb5x7.fsf@thomas.inf.ethz.ch","threadId":"29734","inReplyTo":"1330430331-19945-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v7 00/10] Column display","fromName":"Thomas Rast","fromEmail":"trast@inf.ethz.ch","sentAt":"2012-03-02T11:25:56Z","receivedAt":"2012-03-02T11:25:56Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n\n> Nguyễn Thái Ngọc Duy (10):\n>   Add git-column for columnar display\n>   Stop starting pager recursively\n>   column: add columnar layout\n>   column: add dense layout support\n>   column: add column.ui for default column output settings\n>   help: reuse print_columns() for help -a\n>   branch: add --column\n>   status: add --column\n>   column: support piping stdout to external git-column process\n>   tag: add --column\n\nI ran valgrind over the tests in pu, and I'm seeing these errors\npresumably coming from this series:\n\nt3200-branch.out:==340== Invalid read of size 4\nt3200-branch.out:==340==    at 0x48E676: shrink_columns (column.c:101)\nt3200-branch.out:==340==    by 0x48EB76: display_table (column.c:200)\nt3200-branch.out:==340==    by 0x48ED1B: print_columns (column.c:241)\nt3200-branch.out:==340==    by 0x41967F: cmd_branch (branch.c:793)\nt3200-branch.out:==340==    by 0x40550B: run_builtin (git.c:308)\nt3200-branch.out:==340==    by 0x4056A6: handle_internal_command (git.c:468)\nt3200-branch.out:==340==    by 0x4057C0: run_argv (git.c:514)\nt3200-branch.out:==340==    by 0x40594D: main (git.c:589)\nt3200-branch.out:==340==  Address 0x57023ac is 0 bytes after a block of size 60 alloc'd\nt3200-branch.out:==340==    at 0x4C226FA: malloc (vg_replace_malloc.c:263)\nt3200-branch.out:==340==    by 0x521A11: xmalloc (wrapper.c:35)\nt3200-branch.out:==340==    by 0x48EAE8: display_table (column.c:193)\nt3200-branch.out:==340==    by 0x48ED1B: print_columns (column.c:241)\nt3200-branch.out:==340==    by 0x41967F: cmd_branch (branch.c:793)\nt3200-branch.out:==340==    by 0x40550B: run_builtin (git.c:308)\nt3200-branch.out:==340==    by 0x4056A6: handle_internal_command (git.c:468)\nt3200-branch.out:==340==    by 0x4057C0: run_argv (git.c:514)\nt3200-branch.out:==340==    by 0x40594D: main (git.c:589)\n\nand very similar errors in t7004-tag.out, t9002-column.out and\nt7508-status.out.  Can you look into it?\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"185912","messageId":"CACsJy8BQy_j08x5k_bzcngTy4qKi9DPkKCDA1hEUR6-osRTf2g@mail.gmail.com","threadId":"29734","inReplyTo":"7v8vjm25iy.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v7 01/10] Add git-column for columnar display","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-03-02T12:36:30Z","receivedAt":"2012-03-02T12:36:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2012/2/29 Junio C Hamano <gitster@pobox.com>:\n>> +     memset(&copts, 0, sizeof(copts));\n>> +     copts.width = term_columns();\n>> +     copts.padding = 1;\n>> +     argc = parse_options(argc, argv, \"\", options, builtin_column_usage, 0);\n>\n> Curious. The usual pattern is to set up the built-in default, call\n> git_config() to override it with the configured values, and then call\n> parse_options() to override at the runtime.  I see configuration callback\n> defined in column.c but no call to git_config() here?\n\nHistory reason. It was test-column and I did not care much whether it\nrespected config. Will fix.\n\n>> +void print_columns(const struct string_list *list, unsigned int mode,\n>> +                struct column_options *opts)\n>> +{\n>> +     const char *indent = \"\", *nl = \"\\n\";\n>> +     int padding = 1, width = term_columns();\n>> +\n>> +     if (!list->nr)\n>> +             return;\n>> +     if (opts) {\n>> +             if (opts->indent)\n>> +                     indent = opts->indent;\n>> +             if (opts->nl)\n>> +                     nl = opts->nl;\n>> +             if (opts->width)\n>> +                     width = opts->width;\n>> +             padding = opts->padding;\n>> +     }\n>> +     if (width <= 1 || !(mode & COL_ENABLED)) {\n>\n> Curious why this is \"1\".  If your terminal is only 2 columns wide, you\n> wouldn't be able to show your list items in two columns as you would want\n> to have an inter-column gap, no?\n\nYeah, should drop that \"width <= 1\". Other layout functions should be\nable to deal with too narrow width anyway.\n\n\n>> +     if (set < 0) {  /* auto */\n>> +             if (stdout_is_tty < 0)\n>> +                     stdout_is_tty = isatty(1);\n>> +             set = stdout_is_tty || (pager_in_use() && pager_use_color);\n>\n> Why does this have anything to do with the use of color?\n\nHm.. no it does not have to.\n\n>> +     }\n>> +     if (set)\n>> +             *mode = *mode | COL_ENABLED | COL_ENABLED_SET;\n>> +     else\n>> +             *mode = (*mode & ~COL_ENABLED) | COL_ENABLED_SET;\n>> +     return 0;\n>> +}\n>\n> OK, so we record the desired value (either COL_ENABLED or not) and the\n> fact that a call to set_enable_bit() function set it.  Which implies that\n> this function must be called from only one codepath (either setting from\n> the configuration mechanism, or by parsing the command line option) but\n> not both.  I guess this is only called from configuration codepath?\n\nBoth share the same code path. COL_ENABLED_SET is probably badly\nnamed, perhaps COL_ENABLED_VALID is better. It's there to deal\ndistinguish \"never\" and not explicitly stating any of\nnever/auto/always. In both cases, COL_ENABLED is 0. COL_ENABLED_SET is\nset in the former, but not in the latter. I depend on that to make\ndefault behavior \"always\". Now thinking again, it would make more\nsense to set default \"never\", and drop COL_ENABLED_SET.\n\n> I am afraid that we do not have enough to judge if this is sane in this\n> patch, as there is no support for column.ui at this stage.  Perhaps the\n> series is not structured well and has things that are not yet relevant in\n> early part of it.  Sigh.\n\nIf it's not clear whether it's sane, it's more likely not. I'll\nre-read the series again and redo. One good thing about git\ndevelopment is only tidy things can become part of upstream, which\nmakes archaelogy a pleasant experience.\n-- \nDuy\n"},{"id":"185915","messageId":"CACsJy8CdmK0827qjiW6-tCkhpGK5Prkhb=bt7wPgAuSsf1qpDw@mail.gmail.com","threadId":"29734","inReplyTo":"7vty2azudf.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v7 04/10] column: add dense layout support","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-03-02T12:47:37Z","receivedAt":"2012-03-02T12:47:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2012/2/29 Junio C Hamano <gitster@pobox.com>:\n> Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n>\n>> Normally, all items will be gone through once. The largest cell will set\n>> column width for all columns. With this strategy, one long item will\n>> stretch out all columns, wasting space in between them.\n>>\n>> With COL_DENSE enabled, it shrinks all columns to minimum, then attempts\n>> to push the last row's cells over to the next column with hope that\n>> everything still fits even there's one row less. The process is repeated\n>> until the new layout cannot fit in given width anymore, or there's only\n>> one row left (perfect!).\n>\n> As you have given 4 bits for COL_MODE in the previous patch, I expected\n> that this will be one of the mode that you can use, e.g. column-first dense,\n> or row-first no-dense, with two more bits for operating modes.  Not calling\n> it COL_MODE_DENSE and assigning a \"are we dense of not?\" bit out of COL_MODE\n> bits feels wrong.\n\nI reserve the 4 bits for strategies to fill the layout. One day I'm\ncrazy enough maybe I'll add spiral mode that put the first item in the\ncenter, then next items in a spiral outward. COL_DENSE is about cell\nsize and should be applicable to any layout strategies. Maybe\nCOL_LAYOUT_COLUMN, COL_LAYOUT_ROW, COL_WIDTH_DENSE (and\nCOL_WIDTH_EQUAL) would be better names\n-- \nDuy\n"},{"id":"185933","messageId":"7vwr72ewfm.fsf@alter.siamese.dyndns.org","threadId":"29734","inReplyTo":"CACsJy8CdmK0827qjiW6-tCkhpGK5Prkhb=bt7wPgAuSsf1qpDw@mail.gmail.com","subject":"Re: [PATCH v7 04/10] column: add dense layout support","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-03-02T17:37:17Z","receivedAt":"2012-03-02T17:37:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n> I reserve the 4 bits for strategies to fill the layout.\n> ... Maybe\n> COL_LAYOUT_COLUMN, COL_LAYOUT_ROW, COL_WIDTH_DENSE (and\n> COL_WIDTH_EQUAL) would be better names\n\nOK, so these 4-bits are more like layout-bits?  That makes sense.\n"},{"id":"186640","messageId":"CACsJy8BYDtAqJwAPK4nF+KvednNS8kX3TZGa+o-_=UsapA19oA@mail.gmail.com","threadId":"29734","inReplyTo":"87ehtbb5x7.fsf@thomas.inf.ethz.ch","subject":"Re: [PATCH v7 00/10] Column display","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-03-11T07:02:24Z","receivedAt":"2012-03-11T07:02:24Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2012/3/2 Thomas Rast <trast@inf.ethz.ch>:\n> I ran valgrind over the tests in pu, and I'm seeing these errors\n> presumably coming from this series:\n>\n> t3200-branch.out:==340== Invalid read of size 4\n> t3200-branch.out:==340==    at 0x48E676: shrink_columns (column.c:101)\n> t3200-branch.out:==340==    by 0x48EB76: display_table (column.c:200)\n> t3200-branch.out:==340==    by 0x48ED1B: print_columns (column.c:241)\n> t3200-branch.out:==340==    by 0x41967F: cmd_branch (branch.c:793)\n> t3200-branch.out:==340==    by 0x40550B: run_builtin (git.c:308)\n> t3200-branch.out:==340==    by 0x4056A6: handle_internal_command (git.c:468)\n> t3200-branch.out:==340==    by 0x4057C0: run_argv (git.c:514)\n> t3200-branch.out:==340==    by 0x40594D: main (git.c:589)\n> t3200-branch.out:==340==  Address 0x57023ac is 0 bytes after a block of size 60 alloc'd\n> t3200-branch.out:==340==    at 0x4C226FA: malloc (vg_replace_malloc.c:263)\n> t3200-branch.out:==340==    by 0x521A11: xmalloc (wrapper.c:35)\n> t3200-branch.out:==340==    by 0x48EAE8: display_table (column.c:193)\n> t3200-branch.out:==340==    by 0x48ED1B: print_columns (column.c:241)\n> t3200-branch.out:==340==    by 0x41967F: cmd_branch (branch.c:793)\n> t3200-branch.out:==340==    by 0x40550B: run_builtin (git.c:308)\n> t3200-branch.out:==340==    by 0x4056A6: handle_internal_command (git.c:468)\n> t3200-branch.out:==340==    by 0x4057C0: run_argv (git.c:514)\n> t3200-branch.out:==340==    by 0x40594D: main (git.c:589)\n>\n> and very similar errors in t7004-tag.out, t9002-column.out and\n> t7508-status.out.  Can you look into it?\n\nThanks. There is a bug in shrink_columns() that causes invalid access.\nIt wil be fixed in the next reroll.\n-- \nDuy\n"},{"id":"186680","messageId":"7vd38iuzks.fsf@alter.siamese.dyndns.org","threadId":"29734","inReplyTo":"CACsJy8BYDtAqJwAPK4nF+KvednNS8kX3TZGa+o-_=UsapA19oA@mail.gmail.com","subject":"Re: [PATCH v7 00/10] Column display","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-03-12T06:02:11Z","receivedAt":"2012-03-12T06:02:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n> 2012/3/2 Thomas Rast <trast@inf.ethz.ch>:\n>> I ran valgrind over the tests in pu, and I'm seeing these errors\n>> ...\n>> Can you look into it?\n>\n> Thanks. There is a bug in shrink_columns() that causes invalid access.\n> It wil be fixed in the next reroll.\n\nThanks.  I thought this topic was the closest to completion and was\nthe only candidate for 1.7.10 among various topics with loose ends\nyou have in flight (even though it is way too late for 1.7.10 now).\n"},{"id":"186842","messageId":"CACsJy8BZ9yFQ7bWsLf5O56zwrSzcj487syVRiQ2s5XoPuNkahQ@mail.gmail.com","threadId":"29734","inReplyTo":"cover.1331638663.git.pclouds@gmail.com","subject":"Re: [PATCH v7 00/9] Column display","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-03-13T12:08:20Z","receivedAt":"2012-03-13T12:08:20Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2012/3/13 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>:\n> This series fixes problems found in v6:\n\nThis series is actually v8, changes compared to the replied-to v7.\nSorry I messed it up.\n-- \nDuy\n"},{"id":"186832","messageId":"cover.1331638663.git.pclouds@gmail.com","threadId":"29734","inReplyTo":"1330430331-19945-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v7 00/9] Column display","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-03-13T12:09:12Z","receivedAt":"2012-03-13T12:09:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This series fixes problems found in v6:\n\n - rework config code. I think it looks nicer now.\n - fix valgrind issue in shrink_columns()\n - make \"help -a\" respect column.*\n - only use up to term_columns() - 1. If we try to print to the last\n   column, the line may be wrapped around.\n\nThis series misses the 1.7.10 train already and I wasn't going to post\nthis until the 1.7.10 comes out, but I'd like to take feedback on 3\nextra WIP patches at the end. They deal with cases where column\nlayout is not as efficient as it should be. More details in the patch\ndescription.\n\nNguyễn Thái Ngọc Duy (9):\n  Add column layout skeleton and git-column\n  Stop starting pager recursively\n  column: add columnar layout\n  column: add dense layout support\n  help: reuse print_columns() for help -a\n  branch: add --column\n  status: add --column\n  column: support piping stdout to external git-column process\n  tag: add --column\n\nNguyễn Thái Ngọc Duy (3):\n  ls-files: support --column\n  column: support \"denser\" mode\n  column: support grouping entries\n\n .gitignore                   |    1 +\n Documentation/config.txt     |   38 +++\n Documentation/git-branch.txt |    9 +\n Documentation/git-column.txt |   53 +++\n Documentation/git-status.txt |    7 +\n Documentation/git-tag.txt    |    9 +\n Makefile                     |    3 +\n builtin.h                    |    1 +\n builtin/branch.c             |   32 ++-\n builtin/column.c             |   59 ++++\n builtin/commit.c             |    7 +\n builtin/help.c               |    7 +-\n builtin/ls-files.c           |    8 +\n builtin/tag.c                |   27 ++-\n column.c                     |  724 ++++++++++++++++++++++++++++++++++++++++++\n column.h                     |   44 +++\n command-list.txt             |    1 +\n git.c                        |    1 +\n help.c                       |   59 ++--\n help.h                       |    2 +-\n pager.c                      |    2 +-\n parse-options.h              |    2 +\n t/t3200-branch.sh            |   77 +++++\n t/t7004-tag.sh               |   44 +++\n t/t7508-status.sh            |   24 ++\n t/t9002-column.sh            |  179 +++++++++++\n wt-status.c                  |   28 ++-\n wt-status.h                  |    1 +\n 28 files changed, 1401 insertions(+), 48 deletions(-)\n create mode 100644 Documentation/git-column.txt\n create mode 100644 builtin/column.c\n create mode 100644 column.c\n create mode 100644 column.h\n create mode 100755 t/t9002-column.sh\n\n-- \n1.7.8.36.g69ee2\n"},{"id":"186833","messageId":"1294ed94f981a7029b47a44143c3eb9fb6b820f1.1331638663.git.pclouds@gmail.com","threadId":"29734","inReplyTo":"cover.1331638663.git.pclouds@gmail.com","subject":"[PATCH v7 01/9] Add column layout skeleton and git-column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-03-13T12:09:13Z","receivedAt":"2012-03-13T12:09:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"A column option string consists of many token separated by either\na space or a  comma. A token belongs to one of three groups:\n\n - enabling: always, never and auto\n - layout mode: currently plain (which does not layout at all)\n - other future tuning flags\n\ngit-column can be used to pipe output to from a command that wants\ncolumn layout, but not to mess with its own output code. Simpler output\ncode can be changed to use column layout code directly.\n\nThanks-to: Ramsay Jones <ramsay@ramsay1.demon.co.uk>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore                   |    1 +\n Documentation/config.txt     |   18 +++++\n Documentation/git-column.txt |   53 +++++++++++++\n Makefile                     |    3 +\n builtin.h                    |    1 +\n builtin/column.c             |   59 +++++++++++++++\n column.c                     |  170 ++++++++++++++++++++++++++++++++++++++++++\n column.h                     |   35 +++++++++\n command-list.txt             |    1 +\n git.c                        |    1 +\n parse-options.h              |    2 +\n t/t9002-column.sh            |   45 +++++++++++\n 12 files changed, 389 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/git-column.txt\n create mode 100644 builtin/column.c\n create mode 100644 column.c\n create mode 100644 column.h\n create mode 100755 t/t9002-column.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex 87fcc5f..2540264 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -26,6 +26,7 @@\n /git-cherry-pick\n /git-clean\n /git-clone\n+/git-column\n /git-commit\n /git-commit-tree\n /git-config\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex abeb82b..147c41c 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -821,6 +821,24 @@ color.ui::\n \t`never` if you prefer git commands not to use color unless enabled\n \texplicitly with some other configuration or the `--color` option.\n \n+column.ui::\n+\tSpecify whether supported commands should output in columns.\n+\tThis variable consists of a list of tokens separated by spaces\n+\tor commas:\n++\n+--\n+`always`;;\n+\talways show in columns\n+`never`;;\n+\tnever show in columns\n+`auto`;;\n+\tshow in columns if the output is to the terminal\n+`plain`;;\n+\tshow in one column\n+--\n++\n+\tThis option defaults to 'never'.\n+\n commit.status::\n \tA boolean to enable/disable inclusion of status information in the\n \tcommit message template when using an editor to prepare the commit\ndiff --git a/Documentation/git-column.txt b/Documentation/git-column.txt\nnew file mode 100644\nindex 0000000..9be16ee\n--- /dev/null\n+++ b/Documentation/git-column.txt\n@@ -0,0 +1,53 @@\n+git-column(1)\n+=============\n+\n+NAME\n+----\n+git-column - Display data in columns\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git column' [--command=<name>] [--[raw-]mode=<mode>] [--width=<width>]\n+\t     [--indent=<string>] [--nl=<string>] [--pading=<n>]\n+\n+DESCRIPTION\n+-----------\n+This command formats its input into multiple columns.\n+\n+OPTIONS\n+-------\n+--command=<name>::\n+\tLook up layout mode using configuration variable column.<name> and\n+\tcolumn.ui.\n+\n+--mode=<mode>::\n+\tSpecify layout mode. See configuration variable column.ui for option\n+\tsyntax.\n+\n+--raw-mode=<n>::\n+\tSame as --mode but take mode encoded as a number. This is mainly used\n+\tby other commands that have already parsed layout mode.\n+\n+--width=<width>::\n+\tSpecify the terminal width. By default 'git column' will detect the\n+\tterminal width, or fall back to 80 if it is unable to do so.\n+\n+--indent=<string>::\n+\tString to be printed at the beginning of each line.\n+\n+--nl=<N>::\n+\tString to be printed at the end of each line,\n+\tincluding newline character.\n+\n+--padding=<N>::\n+\tThe number of spaces between columns. One space by default.\n+\n+\n+Author\n+------\n+Written by Nguyen Thai Ngoc Duy <pclouds@gmail.com>\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Makefile b/Makefile\nindex a0de4e9..0998f0d 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -646,6 +646,7 @@ LIB_OBJS += bulk-checkin.o\n LIB_OBJS += bundle.o\n LIB_OBJS += cache-tree.o\n LIB_OBJS += color.o\n+LIB_OBJS += column.o\n LIB_OBJS += combine-diff.o\n LIB_OBJS += commit.o\n LIB_OBJS += compat/obstack.o\n@@ -774,6 +775,7 @@ BUILTIN_OBJS += builtin/checkout-index.o\n BUILTIN_OBJS += builtin/checkout.o\n BUILTIN_OBJS += builtin/clean.o\n BUILTIN_OBJS += builtin/clone.o\n+BUILTIN_OBJS += builtin/column.o\n BUILTIN_OBJS += builtin/commit-tree.o\n BUILTIN_OBJS += builtin/commit.o\n BUILTIN_OBJS += builtin/config.o\n@@ -2166,6 +2168,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o url.o http-backend.o: url.h\n+column.o help.o pager.o: column.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/builtin.h b/builtin.h\nindex 857b9c8..338f540 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -61,6 +61,7 @@ extern int cmd_cherry(int argc, const char **argv, const char *prefix);\n extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);\n extern int cmd_clone(int argc, const char **argv, const char *prefix);\n extern int cmd_clean(int argc, const char **argv, const char *prefix);\n+extern int cmd_column(int argc, const char **argv, const char *prefix);\n extern int cmd_commit(int argc, const char **argv, const char *prefix);\n extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_config(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/column.c b/builtin/column.c\nnew file mode 100644\nindex 0000000..5ea798a\n--- /dev/null\n+++ b/builtin/column.c\n@@ -0,0 +1,59 @@\n+#include \"builtin.h\"\n+#include \"cache.h\"\n+#include \"strbuf.h\"\n+#include \"parse-options.h\"\n+#include \"string-list.h\"\n+#include \"column.h\"\n+\n+static const char * const builtin_column_usage[] = {\n+\t\"git column [options]\",\n+\tNULL\n+};\n+static unsigned int colopts;\n+\n+static int column_config(const char *var, const char *value, void *cb)\n+{\n+\treturn git_column_config(var, value, cb, &colopts);\n+}\n+\n+int cmd_column(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tstruct column_options copts;\n+\tconst char *command = NULL, *real_command = NULL;\n+\tstruct option options[] = {\n+\t\tOPT_STRING(0, \"command\", &real_command, \"name\", \"lookup config vars\"),\n+\t\tOPT_COLUMN(0, \"mode\", &colopts, \"layout to use\"),\n+\t\tOPT_INTEGER(0, \"raw-mode\", &colopts, \"layout to use\"),\n+\t\tOPT_INTEGER(0, \"width\", &copts.width, \"Maximum width\"),\n+\t\tOPT_STRING(0, \"indent\", &copts.indent, \"string\", \"Padding space on left border\"),\n+\t\tOPT_INTEGER(0, \"nl\", &copts.nl, \"Padding space on right border\"),\n+\t\tOPT_INTEGER(0, \"padding\", &copts.padding, \"Padding space between columns\"),\n+\t\tOPT_END()\n+\t};\n+\n+\t/* This one is special and must be the first one */\n+\tif (argc > 1 && !prefixcmp(argv[1], \"--command=\")) {\n+\t\tcommand = argv[1] + 10;\n+\t\tgit_config(column_config, (void *)command);\n+\t} else\n+\t\tgit_config(column_config, NULL);\n+\n+\tmemset(&copts, 0, sizeof(copts));\n+\tcopts.width = term_columns();\n+\tcopts.padding = 1;\n+\targc = parse_options(argc, argv, \"\", options, builtin_column_usage, 0);\n+\tif (argc)\n+\t\tusage_with_options(builtin_column_usage, options);\n+\tif (real_command || command) {\n+\t\tif (!real_command || !command || strcmp(real_command, command))\n+\t\t\tdie(_(\"--command must be the first argument\"));\n+\t}\n+\tfinalize_colopts(&colopts, -1);\n+\twhile (!strbuf_getline(&sb, stdin, '\\n'))\n+\t\tstring_list_append(&list, sb.buf);\n+\n+\tprint_columns(&list, colopts, &copts);\n+\treturn 0;\n+}\ndiff --git a/column.c b/column.c\nnew file mode 100644\nindex 0000000..f0877c0\n--- /dev/null\n+++ b/column.c\n@@ -0,0 +1,170 @@\n+#include \"cache.h\"\n+#include \"column.h\"\n+#include \"string-list.h\"\n+#include \"parse-options.h\"\n+\n+/* Display without layout when not enabled */\n+static void display_plain(const struct string_list *list,\n+\t\t\t  const char *indent, const char *nl)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < list->nr; i++)\n+\t\tprintf(\"%s%s%s\", indent, list->items[i].string, nl);\n+}\n+\n+void print_columns(const struct string_list *list, unsigned int colopts,\n+\t\t   const struct column_options *opts)\n+{\n+\tstruct column_options nopts;\n+\n+\tif (!list->nr)\n+\t\treturn;\n+\tassert(COL_ENABLE(colopts) != COL_AUTO);\n+\n+\tmemset(&nopts, 0, sizeof(nopts));\n+\tnopts.indent = opts && opts->indent ? opts->indent : \"\";\n+\tnopts.nl = opts && opts->nl ? opts->nl : \"\\n\";\n+\tnopts.padding = opts ? opts->padding : 1;\n+\tnopts.width = opts && opts->width ? opts->width : term_columns() - 1;\n+\tif (!COL_ENABLE(colopts)) {\n+\t\tdisplay_plain(list, \"\", \"\\n\");\n+\t\treturn;\n+\t}\n+\tswitch (COL_LAYOUT(colopts)) {\n+\tcase COL_PLAIN:\n+\t\tdisplay_plain(list, nopts.indent, nopts.nl);\n+\t\tbreak;\n+\tdefault:\n+\t\tdie(\"BUG: invalid layout mode %d\", COL_LAYOUT(colopts));\n+\t}\n+}\n+\n+int finalize_colopts(unsigned int *colopts, int stdout_is_tty)\n+{\n+\tif (COL_ENABLE(*colopts) == COL_AUTO) {\n+\t\tif (stdout_is_tty < 0)\n+\t\t\tstdout_is_tty = isatty(1);\n+\t\t*colopts &= ~COL_ENABLE_MASK;\n+\t\tif (stdout_is_tty)\n+\t\t\t*colopts |= COL_ENABLED;\n+\t}\n+\treturn 0;\n+}\n+\n+struct colopt {\n+\tconst char *name;\n+\tunsigned int value;\n+\tunsigned int mask;\n+};\n+\n+#define LAYOUT_SET 1\n+#define ENABLE_SET 2\n+\n+static int parse_option(const char *arg, int len, unsigned int *colopts,\n+\t\t\tint *group_set)\n+{\n+\tstruct colopt opts[] = {\n+\t\t{ \"always\", COL_ENABLED,  COL_ENABLE_MASK },\n+\t\t{ \"never\",  COL_DISABLED, COL_ENABLE_MASK },\n+\t\t{ \"auto\",   COL_AUTO,     COL_ENABLE_MASK },\n+\t\t{ \"plain\",  COL_PLAIN,    COL_LAYOUT_MASK },\n+\t};\n+\tint i;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(opts); i++) {\n+\t\tint arg_len = len, name_len;\n+\t\tconst char *arg_str = arg;\n+\n+\t\tname_len = strlen(opts[i].name);\n+\t\tif (arg_len != name_len ||\n+\t\t    strncmp(arg_str, opts[i].name, name_len))\n+\t\t\tcontinue;\n+\n+\t\tswitch (opts[i].mask) {\n+\t\tcase COL_ENABLE_MASK:\n+\t\t\t*group_set |= ENABLE_SET;\n+\t\t\tbreak;\n+\t\tcase COL_LAYOUT_MASK:\n+\t\t\t*group_set |= LAYOUT_SET;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (opts[i].mask)\n+\t\t\t*colopts = (*colopts & ~opts[i].mask) | opts[i].value;\n+\t\treturn 0;\n+\t}\n+\n+\treturn error(\"unsupported option '%s'\", arg);\n+}\n+\n+static int parse_config(unsigned int *colopts, const char *value)\n+{\n+\tconst char *sep = \" ,\";\n+\tint group_set = 0;\n+\n+\twhile (*value) {\n+\t\tint len = strcspn(value, sep);\n+\t\tif (len) {\n+\t\t\tif (parse_option(value, len, colopts, &group_set))\n+\t\t\t\treturn -1;\n+\n+\t\t\tvalue += len;\n+\t\t}\n+\t\tvalue += strspn(value, sep);\n+\t}\n+\t/*\n+\t * Setting layout implies \"always\" if neither always, never\n+\t * nor auto is specified.\n+\t *\n+\t * Current COL_ENABLE() value is disregarded. This means if\n+\t * you set column.ui = auto and pass --column=row, then \"auto\"\n+\t * will become \"always\".\n+\t */\n+\tif ((group_set & LAYOUT_SET) && !(group_set & ENABLE_SET))\n+\t\t*colopts = (*colopts & ~COL_ENABLE_MASK) | COL_ENABLED;\n+\treturn 0;\n+}\n+\n+static int column_config(const char *var, const char *value,\n+\t\t\t const char *key, unsigned int *colopts)\n+{\n+\tif (parse_config(colopts, value))\n+\t\treturn error(\"invalid %s mode %s\", key, value);\n+\treturn 0;\n+}\n+\n+int git_column_config(const char *var, const char *value,\n+\t\t      const char *command, unsigned int *colopts)\n+{\n+\tif (!strcmp(var, \"column.ui\"))\n+\t\treturn column_config(var, value, \"column.ui\", colopts);\n+\n+\tif (command) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tint ret = 0;\n+\t\tstrbuf_addf(&sb, \"column.%s\", command);\n+\t\tif (!strcmp(var, sb.buf))\n+\t\t\tret = column_config(var, value, sb.buf, colopts);\n+\t\tstrbuf_release(&sb);\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int parseopt_column_callback(const struct option *opt,\n+\t\t\t     const char *arg, int unset)\n+{\n+\tunsigned int *colopts = opt->value;\n+\t*colopts |= COL_PARSEOPT;\n+\t*colopts &= ~COL_ENABLE_MASK;\n+\tif (unset)\t\t/* --no-column == never */\n+\t\treturn 0;\n+\t/* --column == always unless \"arg\" states otherwise */\n+\t*colopts |= COL_ENABLED;\n+\tif (arg)\n+\t\treturn parse_config(colopts, arg);\n+\n+\treturn 0;\n+}\ndiff --git a/column.h b/column.h\nnew file mode 100644\nindex 0000000..778a71c\n--- /dev/null\n+++ b/column.h\n@@ -0,0 +1,35 @@\n+#ifndef COLUMN_H\n+#define COLUMN_H\n+\n+#define COL_LAYOUT_MASK   0x000F\n+#define COL_ENABLE_MASK   0x0030   /* always, never or auto */\n+#define COL_PARSEOPT      0x0040   /* --column is given from cmdline */\n+\n+#define COL_ENABLE(c) ((c) & COL_ENABLE_MASK)\n+#define COL_DISABLED      0x0000   /* must be zero */\n+#define COL_ENABLED       0x0010\n+#define COL_AUTO          0x0020\n+\n+#define COL_LAYOUT(c) ((c) & COL_LAYOUT_MASK)\n+#define COL_PLAIN             15   /* one column */\n+\n+#define explicitly_enable_column(c) \\\n+\t(((c) & COL_PARSEOPT) && COL_ENABLE(c) == COL_ENABLED)\n+\n+struct column_options {\n+\tint width;\n+\tint padding;\n+\tconst char *indent;\n+\tconst char *nl;\n+};\n+\n+struct option;\n+extern int parseopt_column_callback(const struct option *, const char *, int);\n+extern int git_column_config(const char *var, const char *value,\n+\t\t\t     const char *command, unsigned int *colopts);\n+extern int finalize_colopts(unsigned int *colopts, int stdout_is_tty);\n+\n+extern void print_columns(const struct string_list *list, unsigned int colopts,\n+\t\t\t  const struct column_options *opts);\n+\n+#endif\ndiff --git a/command-list.txt b/command-list.txt\nindex a36ee9b..fe06f15 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -20,6 +20,7 @@ git-cherry-pick                         mainporcelain\n git-citool                              mainporcelain\n git-clean                               mainporcelain\n git-clone                               mainporcelain common\n+git-column                              purehelpers\n git-commit                              mainporcelain common\n git-commit-tree                         plumbingmanipulators\n git-config                              ancillarymanipulators\ndiff --git a/git.c b/git.c\nindex 3805616..ee727cb 100644\n--- a/git.c\n+++ b/git.c\n@@ -348,6 +348,7 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"cherry-pick\", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"clean\", cmd_clean, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"clone\", cmd_clone },\n+\t\t{ \"column\", cmd_column, RUN_SETUP_GENTLY },\n \t\t{ \"commit\", cmd_commit, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"commit-tree\", cmd_commit_tree, RUN_SETUP },\n \t\t{ \"config\", cmd_config, RUN_SETUP_GENTLY },\ndiff --git a/parse-options.h b/parse-options.h\nindex 2e811dc..56fcafd 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -238,5 +238,7 @@ extern int parse_opt_noop_cb(const struct option *, const char *, int);\n \t  PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }\n #define OPT__COLOR(var, h) \\\n \tOPT_COLOR_FLAG(0, \"color\", (var), (h))\n+#define OPT_COLUMN(s, l, v, h) \\\n+\t{ OPTION_CALLBACK, (s), (l), (v), \"style\", (h), PARSE_OPT_OPTARG, parseopt_column_callback }\n \n #endif\ndiff --git a/t/t9002-column.sh b/t/t9002-column.sh\nnew file mode 100755\nindex 0000000..a7f3cd9\n--- /dev/null\n+++ b/t/t9002-column.sh\n@@ -0,0 +1,45 @@\n+#!/bin/sh\n+\n+test_description='git column'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\tcat >lista <<\\EOF\n+one\n+two\n+three\n+four\n+five\n+six\n+seven\n+eight\n+nine\n+ten\n+eleven\n+EOF\n+'\n+\n+test_expect_success 'never' '\n+\tgit column --indent=Z --mode=never <lista >actual &&\n+\ttest_cmp lista actual\n+'\n+\n+test_expect_success 'always' '\n+\tcat >expected <<\\EOF &&\n+Zone\n+Ztwo\n+Zthree\n+Zfour\n+Zfive\n+Zsix\n+Zseven\n+Zeight\n+Znine\n+Zten\n+Zeleven\n+EOF\n+\tgit column --indent=Z --mode=plain <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_done\n-- \n1.7.8.36.g69ee2\n"},{"id":"186834","messageId":"dd6e9c2189fb4502aaf9f7368cb7f13216961cfa.1331638663.git.pclouds@gmail.com","threadId":"29734","inReplyTo":"1294ed94f981a7029b47a44143c3eb9fb6b820f1.1331638663.git.pclouds@gmail.com","subject":"[PATCH v7 02/9] Stop starting pager recursively","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-03-13T12:09:14Z","receivedAt":"2012-03-13T12:09:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"git-column can be used as a pager for other git commands, something\nlike this:\n\n    GIT_PAGER=\"git -p column --mode='dense color'\" git -p branch\n\nThe problem with this is that \"git -p column\" also has $GIT_PAGER set so\nthe pager runs itself again as another pager. The end result is an\ninfinite loop of forking. Other git commands have the same problem if\nbeing abused this way.\n\nCheck if $GIT_PAGER is already set and stop launching another pager.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n pager.c |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/pager.c b/pager.c\nindex 05584de..4dcb08d 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -73,7 +73,7 @@ void setup_pager(void)\n {\n \tconst char *pager = git_pager(isatty(1));\n \n-\tif (!pager)\n+\tif (!pager || pager_in_use())\n \t\treturn;\n \n \t/*\n-- \n1.7.8.36.g69ee2\n"},{"id":"186835","messageId":"c439d91ff126e2a9d7fe627de7346e19801dd6d4.1331638663.git.pclouds@gmail.com","threadId":"29734","inReplyTo":"dd6e9c2189fb4502aaf9f7368cb7f13216961cfa.1331638663.git.pclouds@gmail.com","subject":"[PATCH v7 03/9] column: add columnar layout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-03-13T12:09:15Z","receivedAt":"2012-03-13T12:09:15Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"COL_COLUMN and COL_ROW fill column by column (or row by row\nrespectively), given the terminal width and how many space between\ncolumns. All cells have equal width.\n\nStrings are supposed to be in UTF-8. Valid ANSI escape strings are OK.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt |    4 ++\n column.c                 |  114 ++++++++++++++++++++++++++++++++++++++++++++++\n column.h                 |    2 +\n t/t9002-column.sh        |   86 ++++++++++++++++++++++++++++++++++\n 4 files changed, 206 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 147c41c..d1d74a1 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -833,6 +833,10 @@ column.ui::\n \tnever show in columns\n `auto`;;\n \tshow in columns if the output is to the terminal\n+`column`;;\n+\tfill columns before rows (default)\n+`row`;;\n+\tfill rows before columns\n `plain`;;\n \tshow in one column\n --\ndiff --git a/column.c b/column.c\nindex f0877c0..17c2479 100644\n--- a/column.c\n+++ b/column.c\n@@ -2,6 +2,59 @@\n #include \"column.h\"\n #include \"string-list.h\"\n #include \"parse-options.h\"\n+#include \"utf8.h\"\n+\n+#define XY2LINEAR(d, x, y) (COL_LAYOUT((d)->colopts) == COL_COLUMN ? \\\n+\t\t\t    (x) * (d)->rows + (y) : \\\n+\t\t\t    (y) * (d)->cols + (x))\n+\n+struct column_data {\n+\tconst struct string_list *list;\n+\tunsigned int colopts;\n+\tstruct column_options opts;\n+\n+\tint rows, cols;\n+\tint *len;\t\t/* cell length */\n+};\n+\n+/* return length of 's' in letters, ANSI escapes stripped */\n+static int item_length(unsigned int colopts, const char *s)\n+{\n+\tint len, i = 0;\n+\tstruct strbuf str = STRBUF_INIT;\n+\n+\tstrbuf_addstr(&str, s);\n+\twhile ((s = strstr(str.buf + i, \"\\033[\")) != NULL) {\n+\t\tint len = strspn(s + 2, \"0123456789;\");\n+\t\ti = s - str.buf;\n+\t\tstrbuf_remove(&str, i, len + 3); /* \\033[<len><func char> */\n+\t}\n+\tlen = utf8_strwidth(str.buf);\n+\tstrbuf_release(&str);\n+\treturn len;\n+}\n+\n+/*\n+ * Calculate cell width, rows and cols for a table of equal cells, given\n+ * table width and how many spaces between cells.\n+ */\n+static void layout(struct column_data *data, int *width)\n+{\n+\tint i;\n+\n+\t*width = 0;\n+\tfor (i = 0; i < data->list->nr; i++)\n+\t\tif (*width < data->len[i])\n+\t\t\t*width = data->len[i];\n+\n+\t*width += data->opts.padding;\n+\n+\tdata->cols = (data->opts.width - strlen(data->opts.indent)) / *width;\n+\tif (data->cols == 0)\n+\t\tdata->cols = 1;\n+\n+\tdata->rows = DIV_ROUND_UP(data->list->nr, data->cols);\n+}\n \n /* Display without layout when not enabled */\n static void display_plain(const struct string_list *list,\n@@ -13,6 +66,61 @@ static void display_plain(const struct string_list *list,\n \t\tprintf(\"%s%s%s\", indent, list->items[i].string, nl);\n }\n \n+/* Print a cell to stdout with all necessary leading/traling space */\n+static int display_cell(struct column_data *data, int initial_width,\n+\t\t\tconst char *empty_cell, int x, int y)\n+{\n+\tint i, len, newline;\n+\n+\ti = XY2LINEAR(data, x, y);\n+\tif (i >= data->list->nr)\n+\t\treturn -1;\n+\tlen = data->len[i];\n+\tif (COL_LAYOUT(data->colopts) == COL_COLUMN)\n+\t\tnewline = i + data->rows >= data->list->nr;\n+\telse\n+\t\tnewline = x == data->cols - 1 || i == data->list->nr - 1;\n+\n+\tprintf(\"%s%s%s\",\n+\t       x == 0 ? data->opts.indent : \"\",\n+\t       data->list->items[i].string,\n+\t       newline ? data->opts.nl : empty_cell + len);\n+\treturn 0;\n+}\n+\n+/* Display COL_COLUMN or COL_ROW */\n+static void display_table(const struct string_list *list,\n+\t\t\t  unsigned int colopts,\n+\t\t\t  const struct column_options *opts)\n+{\n+\tstruct column_data data;\n+\tint x, y, i, initial_width;\n+\tchar *empty_cell;\n+\n+\tmemset(&data, 0, sizeof(data));\n+\tdata.list = list;\n+\tdata.colopts = colopts;\n+\tdata.opts = *opts;\n+\n+\tdata.len = xmalloc(sizeof(*data.len) * list->nr);\n+\tfor (i = 0; i < list->nr; i++)\n+\t\tdata.len[i] = item_length(colopts, list->items[i].string);\n+\n+\tlayout(&data, &initial_width);\n+\n+\tempty_cell = xmalloc(initial_width + 1);\n+\tmemset(empty_cell, ' ', initial_width);\n+\tempty_cell[initial_width] = '\\0';\n+\tfor (y = 0; y < data.rows; y++) {\n+\t\tfor (x = 0; x < data.cols; x++)\n+\t\t\tif (display_cell(&data, initial_width, empty_cell, x, y))\n+\t\t\t\tbreak;\n+\t}\n+\n+\tfree(data.len);\n+\tfree(empty_cell);\n+}\n+\n void print_columns(const struct string_list *list, unsigned int colopts,\n \t\t   const struct column_options *opts)\n {\n@@ -35,6 +143,10 @@ void print_columns(const struct string_list *list, unsigned int colopts,\n \tcase COL_PLAIN:\n \t\tdisplay_plain(list, nopts.indent, nopts.nl);\n \t\tbreak;\n+\tcase COL_ROW:\n+\tcase COL_COLUMN:\n+\t\tdisplay_table(list, colopts, &nopts);\n+\t\tbreak;\n \tdefault:\n \t\tdie(\"BUG: invalid layout mode %d\", COL_LAYOUT(colopts));\n \t}\n@@ -69,6 +181,8 @@ static int parse_option(const char *arg, int len, unsigned int *colopts,\n \t\t{ \"never\",  COL_DISABLED, COL_ENABLE_MASK },\n \t\t{ \"auto\",   COL_AUTO,     COL_ENABLE_MASK },\n \t\t{ \"plain\",  COL_PLAIN,    COL_LAYOUT_MASK },\n+\t\t{ \"column\", COL_COLUMN,   COL_LAYOUT_MASK },\n+\t\t{ \"row\",    COL_ROW,      COL_LAYOUT_MASK },\n \t};\n \tint i;\n \ndiff --git a/column.h b/column.h\nindex 778a71c..79ac50d 100644\n--- a/column.h\n+++ b/column.h\n@@ -11,6 +11,8 @@\n #define COL_AUTO          0x0020\n \n #define COL_LAYOUT(c) ((c) & COL_LAYOUT_MASK)\n+#define COL_COLUMN             0   /* Fill columns before rows */\n+#define COL_ROW                1   /* Fill rows before columns */\n #define COL_PLAIN             15   /* one column */\n \n #define explicitly_enable_column(c) \\\ndiff --git a/t/t9002-column.sh b/t/t9002-column.sh\nindex a7f3cd9..ec288ae 100755\n--- a/t/t9002-column.sh\n+++ b/t/t9002-column.sh\n@@ -42,4 +42,90 @@ EOF\n \ttest_cmp expected actual\n '\n \n+test_expect_success '80 columns' '\n+\tcat >expected <<\\EOF &&\n+one    two    three  four   five   six    seven  eight  nine   ten    eleven\n+EOF\n+\tCOLUMNS=80 git column --mode=column <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'COLUMNS = 1' '\n+\tcat >expected <<\\EOF &&\n+one\n+two\n+three\n+four\n+five\n+six\n+seven\n+eight\n+nine\n+ten\n+eleven\n+EOF\n+\tCOLUMNS=1 git column --mode=column <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'width = 1' '\n+\tgit column --mode=column --width=1 <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+COLUMNS=20\n+export COLUMNS\n+\n+test_expect_success '20 columns' '\n+\tcat >expected <<\\EOF &&\n+one    seven\n+two    eight\n+three  nine\n+four   ten\n+five   eleven\n+six\n+EOF\n+\tgit column --mode=column <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, padding 2' '\n+\tcat >expected <<\\EOF &&\n+one     seven\n+two     eight\n+three   nine\n+four    ten\n+five    eleven\n+six\n+EOF\n+\tgit column --mode=column --padding 2 <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, indented' '\n+\tcat >expected <<\\EOF &&\n+  one    seven\n+  two    eight\n+  three  nine\n+  four   ten\n+  five   eleven\n+  six\n+EOF\n+\tgit column --mode=column --indent=\"  \" <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, row first' '\n+\tcat >expected <<\\EOF &&\n+one    two\n+three  four\n+five   six\n+seven  eight\n+nine   ten\n+eleven\n+EOF\n+\tgit column --mode=row <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.7.8.36.g69ee2\n"},{"id":"186836","messageId":"28f1df2d0cc13d69fa571600c4723094b6fe514b.1331638663.git.pclouds@gmail.com","threadId":"29734","inReplyTo":"c439d91ff126e2a9d7fe627de7346e19801dd6d4.1331638663.git.pclouds@gmail.com","subject":"[PATCH v7 04/9] column: add dense layout support","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-03-13T12:09:16Z","receivedAt":"2012-03-13T12:09:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Normally all cells (and in turn columns) share the same width. This\nlayout mode can waste space because one long item can stretch our all\ncolumns.\n\nWith COL_DENSE enabled, column width is calculated indepdendently. All\ncolumns are shrunk to minimum, then it attempts to push cells of the\nlast row over to the next column with hope that everything still fits\neven there's one row less. The process is repeated until the new layout\ncannot fit in given width any more, or there's only one row left\n(perfect!).\n\nApparently, this mode consumes more cpu than the old one, but it makes\nbetter use of terminal space. For layouting one or two screens, cpu\nusage should not be detectable.\n\nThis patch introduces option handling code besides layout modes and\nenable/disable to expose this feature as \"dense\". The feature can be\nturned off by specifying \"nodense\".\n\nThanks-to: Ramsay Jones <ramsay@ramsay1.demon.co.uk>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt |    4 ++\n column.c                 |   84 +++++++++++++++++++++++++++++++++++++++++++++-\n column.h                 |    2 +\n t/t9002-column.sh        |   48 ++++++++++++++++++++++++++\n 4 files changed, 137 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex d1d74a1..aacf8ab 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -839,6 +839,10 @@ column.ui::\n \tfill rows before columns\n `plain`;;\n \tshow in one column\n+`dense`;;\n+\tmake unequal size columns to utilize more space\n+`nodense`;;\n+\tmake equal size columns\n --\n +\n \tThis option defaults to 'never'.\ndiff --git a/column.c b/column.c\nindex 17c2479..3aac28b 100644\n--- a/column.c\n+++ b/column.c\n@@ -15,6 +15,7 @@ struct column_data {\n \n \tint rows, cols;\n \tint *len;\t\t/* cell length */\n+\tint *width;\t      /* index to the longest row in column */\n };\n \n /* return length of 's' in letters, ANSI escapes stripped */\n@@ -56,6 +57,57 @@ static void layout(struct column_data *data, int *width)\n \tdata->rows = DIV_ROUND_UP(data->list->nr, data->cols);\n }\n \n+static void compute_column_width(struct column_data *data)\n+{\n+\tint i, x, y;\n+\tfor (x = 0; x < data->cols; x++) {\n+\t\tdata->width[x] = XY2LINEAR(data, x, 0);\n+\t\tfor (y = 0; y < data->rows; y++) {\n+\t\t\ti = XY2LINEAR(data, x, y);\n+\t\t\tif (i < data->list->nr &&\n+\t\t\t    data->len[data->width[x]] < data->len[i])\n+\t\t\t\tdata->width[x] = i;\n+\t\t}\n+\t}\n+}\n+\n+/*\n+ * Shrink all columns by shortening them one row each time (and adding\n+ * more columns along the way). Hopefully the longest cell will be\n+ * moved to the next column, column is shrunk so we have more space\n+ * for new columns. The process ends when the whole thing no longer\n+ * fits in data->total_width.\n+ */\n+static void shrink_columns(struct column_data *data)\n+{\n+\tdata->width = xrealloc(data->width,\n+\t\t\t       sizeof(*data->width) * data->cols);\n+\twhile (data->rows > 1) {\n+\t\tint x, total_width, cols, rows;\n+\t\trows = data->rows;\n+\t\tcols = data->cols;\n+\n+\t\tdata->rows--;\n+\t\tdata->cols = DIV_ROUND_UP(data->list->nr, data->rows);\n+\t\tif (data->cols != cols)\n+\t\t\tdata->width = xrealloc(data->width,\n+\t\t\t\t\t       sizeof(*data->width) * data->cols);\n+\t\tcompute_column_width(data);\n+\n+\t\ttotal_width = strlen(data->opts.indent);\n+\t\tfor (x = 0; x < data->cols; x++) {\n+\t\t\ttotal_width += data->len[data->width[x]];\n+\t\t\ttotal_width += data->opts.padding;\n+\t\t}\n+\t\tif (total_width > data->opts.width) {\n+\t\t\tdata->rows = rows;\n+\t\t\tdata->cols = cols;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tcompute_column_width(data);\n+}\n+\n /* Display without layout when not enabled */\n static void display_plain(const struct string_list *list,\n \t\t\t  const char *indent, const char *nl)\n@@ -75,7 +127,18 @@ static int display_cell(struct column_data *data, int initial_width,\n \ti = XY2LINEAR(data, x, y);\n \tif (i >= data->list->nr)\n \t\treturn -1;\n+\n \tlen = data->len[i];\n+\tif (data->width && data->len[data->width[x]] < initial_width) {\n+\t\t/*\n+\t\t * empty_cell has initial_width chars, if real column\n+\t\t * is narrower, increase len a bit so we fill less\n+\t\t * space.\n+\t\t */\n+\t\tlen += initial_width - data->len[data->width[x]];\n+\t\tlen -= data->opts.padding;\n+\t}\n+\n \tif (COL_LAYOUT(data->colopts) == COL_COLUMN)\n \t\tnewline = i + data->rows >= data->list->nr;\n \telse\n@@ -108,6 +171,9 @@ static void display_table(const struct string_list *list,\n \n \tlayout(&data, &initial_width);\n \n+\tif (colopts & COL_DENSE)\n+\t\tshrink_columns(&data);\n+\n \tempty_cell = xmalloc(initial_width + 1);\n \tmemset(empty_cell, ' ', initial_width);\n \tempty_cell[initial_width] = '\\0';\n@@ -118,6 +184,7 @@ static void display_table(const struct string_list *list,\n \t}\n \n \tfree(data.len);\n+\tfree(data.width);\n \tfree(empty_cell);\n }\n \n@@ -183,13 +250,22 @@ static int parse_option(const char *arg, int len, unsigned int *colopts,\n \t\t{ \"plain\",  COL_PLAIN,    COL_LAYOUT_MASK },\n \t\t{ \"column\", COL_COLUMN,   COL_LAYOUT_MASK },\n \t\t{ \"row\",    COL_ROW,      COL_LAYOUT_MASK },\n+\t\t{ \"dense\",  COL_DENSE,    0 },\n \t};\n \tint i;\n \n \tfor (i = 0; i < ARRAY_SIZE(opts); i++) {\n-\t\tint arg_len = len, name_len;\n+\t\tint set = 1, arg_len = len, name_len;\n \t\tconst char *arg_str = arg;\n \n+\t\tif (!opts[i].mask) {\n+\t\t\tif (arg_len > 2 && !strncmp(arg_str, \"no\", 2)) {\n+\t\t\t\targ_str += 2;\n+\t\t\t\targ_len -= 2;\n+\t\t\t\tset = 0;\n+\t\t\t}\n+\t\t}\n+\n \t\tname_len = strlen(opts[i].name);\n \t\tif (arg_len != name_len ||\n \t\t    strncmp(arg_str, opts[i].name, name_len))\n@@ -206,6 +282,12 @@ static int parse_option(const char *arg, int len, unsigned int *colopts,\n \n \t\tif (opts[i].mask)\n \t\t\t*colopts = (*colopts & ~opts[i].mask) | opts[i].value;\n+\t\telse {\n+\t\t\tif (set)\n+\t\t\t\t*colopts |= opts[i].value;\n+\t\t\telse\n+\t\t\t\t*colopts &= ~opts[i].value;\n+\t\t}\n \t\treturn 0;\n \t}\n \ndiff --git a/column.h b/column.h\nindex 79ac50d..b3e979f 100644\n--- a/column.h\n+++ b/column.h\n@@ -4,6 +4,8 @@\n #define COL_LAYOUT_MASK   0x000F\n #define COL_ENABLE_MASK   0x0030   /* always, never or auto */\n #define COL_PARSEOPT      0x0040   /* --column is given from cmdline */\n+#define COL_DENSE         0x0080   /* Shrink columns when possible,\n+\t\t\t\t      making space for more columns */\n \n #define COL_ENABLE(c) ((c) & COL_ENABLE_MASK)\n #define COL_DISABLED      0x0000   /* must be zero */\ndiff --git a/t/t9002-column.sh b/t/t9002-column.sh\nindex ec288ae..c7d7a65 100755\n--- a/t/t9002-column.sh\n+++ b/t/t9002-column.sh\n@@ -89,6 +89,30 @@ EOF\n \ttest_cmp expected actual\n '\n \n+test_expect_success '20 columns, nodense' '\n+\tcat >expected <<\\EOF &&\n+one    seven\n+two    eight\n+three  nine\n+four   ten\n+five   eleven\n+six\n+EOF\n+\tgit column --mode=column,nodense < lista > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, dense' '\n+\tcat >expected <<\\EOF &&\n+one   five  nine\n+two   six   ten\n+three seven eleven\n+four  eight\n+EOF\n+\tgit column --mode=column,dense < lista > actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success '20 columns, padding 2' '\n \tcat >expected <<\\EOF &&\n one     seven\n@@ -128,4 +152,28 @@ EOF\n \ttest_cmp expected actual\n '\n \n+test_expect_success '20 columns, row first, nodense' '\n+\tcat >expected <<\\EOF &&\n+one    two\n+three  four\n+five   six\n+seven  eight\n+nine   ten\n+eleven\n+EOF\n+\tgit column --mode=row,nodense <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, row first, dense' '\n+\tcat >expected <<\\EOF &&\n+one   two    three\n+four  five   six\n+seven eight  nine\n+ten   eleven\n+EOF\n+\tgit column --mode=row,dense <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.7.8.36.g69ee2\n"},{"id":"186838","messageId":"dfa792c463da82097de69b1fba5dc54147dd1951.1331638663.git.pclouds@gmail.com","threadId":"29734","inReplyTo":"28f1df2d0cc13d69fa571600c4723094b6fe514b.1331638663.git.pclouds@gmail.com","subject":"[PATCH v7 05/9] help: reuse print_columns() for help -a","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-03-13T12:09:17Z","receivedAt":"2012-03-13T12:09:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"help -a\" also respects column.ui (and column.help if presents)\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/help.c |    7 +++++-\n help.c         |   59 +++++++++++++++++++++----------------------------------\n help.h         |    2 +-\n 3 files changed, 30 insertions(+), 38 deletions(-)\n\ndiff --git a/builtin/help.c b/builtin/help.c\nindex 61ff798..c64f152 100644\n--- a/builtin/help.c\n+++ b/builtin/help.c\n@@ -9,6 +9,7 @@\n #include \"common-cmds.h\"\n #include \"parse-options.h\"\n #include \"run-command.h\"\n+#include \"column.h\"\n #include \"help.h\"\n \n static struct man_viewer_list {\n@@ -30,6 +31,7 @@ enum help_format {\n };\n \n static int show_all = 0;\n+static unsigned int colopts;\n static enum help_format help_format = HELP_FORMAT_NONE;\n static struct option builtin_help_options[] = {\n \tOPT_BOOLEAN('a', \"all\", &show_all, \"print all available commands\"),\n@@ -251,6 +253,8 @@ static int add_man_viewer_info(const char *var, const char *value)\n \n static int git_help_config(const char *var, const char *value, void *cb)\n {\n+\tif (!prefixcmp(var, \"column.\"))\n+\t\treturn git_column_config(var, value, \"help\", &colopts);\n \tif (!strcmp(var, \"help.format\")) {\n \t\tif (!value)\n \t\t\treturn config_error_nonbool(var);\n@@ -424,8 +428,9 @@ int cmd_help(int argc, const char **argv, const char *prefix)\n \tparsed_help_format = help_format;\n \n \tif (show_all) {\n+\t\tgit_config(git_help_config, NULL);\n \t\tprintf(\"usage: %s\\n\\n\", git_usage_string);\n-\t\tlist_commands(\"git commands\", &main_cmds, &other_cmds);\n+\t\tlist_commands(\"git commands\", colopts, &main_cmds, &other_cmds);\n \t\tprintf(\"%s\\n\", git_more_info_string);\n \t\treturn 0;\n \t}\ndiff --git a/help.c b/help.c\nindex 14eefc9..a815ae6 100644\n--- a/help.c\n+++ b/help.c\n@@ -4,6 +4,8 @@\n #include \"levenshtein.h\"\n #include \"help.h\"\n #include \"common-cmds.h\"\n+#include \"string-list.h\"\n+#include \"column.h\"\n \n void add_cmdname(struct cmdnames *cmds, const char *name, int len)\n {\n@@ -70,31 +72,25 @@ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)\n \tcmds->cnt = cj;\n }\n \n-static void pretty_print_string_list(struct cmdnames *cmds, int longest)\n+static void pretty_print_string_list(struct cmdnames *cmds,\n+\t\t\t\t     unsigned int colopts)\n {\n-\tint cols = 1, rows;\n-\tint space = longest + 1; /* min 1 SP between words */\n-\tint max_cols = term_columns() - 1; /* don't print *on* the edge */\n-\tint i, j;\n-\n-\tif (space < max_cols)\n-\t\tcols = max_cols / space;\n-\trows = DIV_ROUND_UP(cmds->cnt, cols);\n-\n-\tfor (i = 0; i < rows; i++) {\n-\t\tprintf(\"  \");\n+\tstruct string_list list = STRING_LIST_INIT_NODUP;\n+\tstruct column_options copts;\n+\tint i;\n \n-\t\tfor (j = 0; j < cols; j++) {\n-\t\t\tint n = j * rows + i;\n-\t\t\tint size = space;\n-\t\t\tif (n >= cmds->cnt)\n-\t\t\t\tbreak;\n-\t\t\tif (j == cols-1 || n + rows >= cmds->cnt)\n-\t\t\t\tsize = 1;\n-\t\t\tprintf(\"%-*s\", size, cmds->names[n]->name);\n-\t\t}\n-\t\tputchar('\\n');\n-\t}\n+\tfor (i = 0; i < cmds->cnt; i++)\n+\t\tstring_list_append(&list, cmds->names[i]->name);\n+\t/*\n+\t * always enable column display, we only consult column.*\n+\t * about layout strategy and stuff\n+\t */\n+\tcolopts = (colopts & ~COL_ENABLE_MASK) | COL_ENABLED;\n+\tmemset(&copts, 0, sizeof(copts));\n+\tcopts.indent = \"  \";\n+\tcopts.padding = 2;\n+\tprint_columns(&list, colopts, &copts);\n+\tstring_list_clear(&list, 0);\n }\n \n static int is_executable(const char *name)\n@@ -203,25 +199,16 @@ void load_command_list(const char *prefix,\n \texclude_cmds(other_cmds, main_cmds);\n }\n \n-void list_commands(const char *title, struct cmdnames *main_cmds,\n-\t\t   struct cmdnames *other_cmds)\n+void list_commands(const char *title, unsigned int colopts,\n+\t\t   struct cmdnames *main_cmds, struct cmdnames *other_cmds)\n {\n-\tint i, longest = 0;\n-\n-\tfor (i = 0; i < main_cmds->cnt; i++)\n-\t\tif (longest < main_cmds->names[i]->len)\n-\t\t\tlongest = main_cmds->names[i]->len;\n-\tfor (i = 0; i < other_cmds->cnt; i++)\n-\t\tif (longest < other_cmds->names[i]->len)\n-\t\t\tlongest = other_cmds->names[i]->len;\n-\n \tif (main_cmds->cnt) {\n \t\tconst char *exec_path = git_exec_path();\n \t\tprintf(\"available %s in '%s'\\n\", title, exec_path);\n \t\tprintf(\"----------------\");\n \t\tmput_char('-', strlen(title) + strlen(exec_path));\n \t\tputchar('\\n');\n-\t\tpretty_print_string_list(main_cmds, longest);\n+\t\tpretty_print_string_list(main_cmds, colopts);\n \t\tputchar('\\n');\n \t}\n \n@@ -230,7 +217,7 @@ void list_commands(const char *title, struct cmdnames *main_cmds,\n \t\tprintf(\"---------------------------------------\");\n \t\tmput_char('-', strlen(title));\n \t\tputchar('\\n');\n-\t\tpretty_print_string_list(other_cmds, longest);\n+\t\tpretty_print_string_list(other_cmds, colopts);\n \t\tputchar('\\n');\n \t}\n }\ndiff --git a/help.h b/help.h\nindex b6b12d5..854d2d4 100644\n--- a/help.h\n+++ b/help.h\n@@ -25,7 +25,7 @@ extern void add_cmdname(struct cmdnames *cmds, const char *name, int len);\n /* Here we require that excludes is a sorted list. */\n extern void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);\n extern int is_in_cmdlist(struct cmdnames *cmds, const char *name);\n-extern void list_commands(const char *title,\n+extern void list_commands(const char *title, unsigned int colopts,\n \t\t\t  struct cmdnames *main_cmds,\n \t\t\t  struct cmdnames *other_cmds);\n \n-- \n1.7.8.36.g69ee2\n"},{"id":"186841","messageId":"dc618ca926bdbdbb96ae59615cbbe57cde9f763f.1331638663.git.pclouds@gmail.com","threadId":"29734","inReplyTo":"dfa792c463da82097de69b1fba5dc54147dd1951.1331638663.git.pclouds@gmail.com","subject":"[PATCH v7 06/9] branch: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-03-13T12:09:18Z","receivedAt":"2012-03-13T12:09:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt     |    4 ++\n Documentation/git-branch.txt |    9 +++++\n Makefile                     |    2 +-\n builtin/branch.c             |   32 +++++++++++++++--\n t/t3200-branch.sh            |   77 ++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 119 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex aacf8ab..31878b7 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -847,6 +847,10 @@ column.ui::\n +\n \tThis option defaults to 'never'.\n \n+column.branch::\n+\tSpecify whether to output branch listing in `git branch` in columns.\n+\tSee `column.ui` for details.\n+\n commit.status::\n \tA boolean to enable/disable inclusion of status information in the\n \tcommit message template when using an editor to prepare the commit\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex 0427e80..ba5cccb 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -10,6 +10,7 @@ SYNOPSIS\n [verse]\n 'git branch' [--color[=<when>] | --no-color] [-r | -a]\n \t[--list] [-v [--abbrev=<length> | --no-abbrev]]\n+\t[--column[=<options>] | --no-column]\n \t[(--merged | --no-merged | --contains) [<commit>]] [<pattern>...]\n 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]\n 'git branch' (-m | -M) [<oldbranch>] <newbranch>\n@@ -107,6 +108,14 @@ OPTIONS\n \tdefault to color output.\n \tSame as `--color=never`.\n \n+--column[=<options>]::\n+--no-column::\n+\tDisplay branch listing in columns. See configuration variable\n+\tcolumn.branch for option syntax.`--column` and `--no-column`\n+\twithout options are equivalent to 'always' and 'never' respectively.\n++\n+This option is only applicable in non-verbose mode.\n+\n -r::\n --remotes::\n \tList or delete (if used with -d) the remote-tracking branches.\ndiff --git a/Makefile b/Makefile\nindex 0998f0d..320d3f8 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2168,7 +2168,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o url.o http-backend.o: url.h\n-column.o help.o pager.o: column.h\n+builtin/branch.o column.o help.o pager.o: column.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex cb17bc3..2da0168 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -15,6 +15,8 @@\n #include \"branch.h\"\n #include \"diff.h\"\n #include \"revision.h\"\n+#include \"string-list.h\"\n+#include \"column.h\"\n \n static const char * const builtin_branch_usage[] = {\n \t\"git branch [options] [-r | -a] [--merged | --no-merged]\",\n@@ -53,6 +55,9 @@ static enum merge_filter {\n } merge_filter;\n static unsigned char merge_filter_ref[20];\n \n+static struct string_list output = STRING_LIST_INIT_DUP;\n+static unsigned int colopts;\n+\n static int parse_branch_color_slot(const char *var, int ofs)\n {\n \tif (!strcasecmp(var+ofs, \"plain\"))\n@@ -70,6 +75,8 @@ static int parse_branch_color_slot(const char *var, int ofs)\n \n static int git_branch_config(const char *var, const char *value, void *cb)\n {\n+\tif (!prefixcmp(var, \"column.\"))\n+\t\treturn git_column_config(var, value, \"branch\", &colopts);\n \tif (!strcmp(var, \"color.branch\")) {\n \t\tbranch_use_color = git_config_colorbool(var, value);\n \t\treturn 0;\n@@ -474,7 +481,12 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,\n \telse if (verbose)\n \t\t/* \" f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h\" */\n \t\tadd_verbose_info(&out, item, verbose, abbrev);\n-\tprintf(\"%s\\n\", out.buf);\n+\tif (COL_ENABLE(colopts)) {\n+\t\tassert(!verbose && \"--column and --verbose are incompatible\");\n+\t\tstring_list_append(&output, out.buf);\n+\t} else {\n+\t\tprintf(\"%s\\n\", out.buf);\n+\t}\n \tstrbuf_release(&name);\n \tstrbuf_release(&out);\n }\n@@ -727,6 +739,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\t\tPARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,\n \t\t\topt_parse_merge_filter, (intptr_t) \"HEAD\",\n \t\t},\n+\t\tOPT_COLUMN(0, \"column\", &colopts, \"list branches in columns\"),\n \t\tOPT_END(),\n \t};\n \n@@ -749,6 +762,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t}\n \thashcpy(merge_filter_ref, head_sha1);\n \n+\n \targc = parse_options(argc, argv, prefix, options, builtin_branch_usage,\n \t\t\t     0);\n \n@@ -760,12 +774,22 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \n \tif (abbrev == -1)\n \t\tabbrev = DEFAULT_ABBREV;\n+\tfinalize_colopts(&colopts, -1);\n+\tif (verbose) {\n+\t\tif (explicitly_enable_column(colopts))\n+\t\t\tdie(_(\"--column and --verbose are incompatible\"));\n+\t\tcolopts = 0;\n+\t}\n \n \tif (delete)\n \t\treturn delete_branches(argc, argv, delete > 1, kinds);\n-\telse if (list)\n-\t\treturn print_ref_list(kinds, detached, verbose, abbrev,\n-\t\t\t\t      with_commit, argv);\n+\telse if (list) {\n+\t\tint ret = print_ref_list(kinds, detached, verbose, abbrev,\n+\t\t\t\t\t with_commit, argv);\n+\t\tprint_columns(&output, colopts, NULL);\n+\t\tstring_list_clear(&output, 0);\n+\t\treturn ret;\n+\t}\n \telse if (edit_description) {\n \t\tconst char *branch_name;\n \t\tstruct strbuf branch_ref = STRBUF_INIT;\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex dd1aceb..9f82d5e 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -160,6 +160,83 @@ test_expect_success 'git branch --list -d t should fail' '\n \ttest_path_is_missing .git/refs/heads/t\n '\n \n+test_expect_success 'git branch --column' '\n+\tCOLUMNS=81 git branch --column=column >actual &&\n+\tcat >expected <<\\EOF &&\n+  a/b/c     bam       foo       l       * master    n         o/p       r\n+  abc       bar       j/k       m/m       master2   o/o       q\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'git branch --column with an extremely long branch name' '\n+\tlong=this/is/a/part/of/long/branch/name &&\n+\tlong=z$long/$long/$long/$long &&\n+\ttest_when_finished \"git branch -d $long\" &&\n+\tgit branch $long &&\n+\tCOLUMNS=80 git branch --column=column >actual &&\n+\tcat >expected <<EOF &&\n+  a/b/c\n+  abc\n+  bam\n+  bar\n+  foo\n+  j/k\n+  l\n+  m/m\n+* master\n+  master2\n+  n\n+  o/o\n+  o/p\n+  q\n+  r\n+  $long\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'git branch with column.*' '\n+\tgit config column.ui column &&\n+\tgit config column.branch \"dense\" &&\n+\tCOLUMNS=80 git branch >actual &&\n+\tgit config --unset column.branch &&\n+\tgit config --unset column.ui &&\n+\tcat >expected <<\\EOF &&\n+  a/b/c   bam   foo   l   * master    n     o/p   r\n+  abc     bar   j/k   m/m   master2   o/o   q\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'git branch --column -v should fail' '\n+\ttest_must_fail git branch --column -v\n+'\n+\n+test_expect_success 'git branch -v with column.ui ignored' '\n+\tgit config column.ui column &&\n+\tCOLUMNS=80 git branch -v | cut -c -10 | sed \"s/ *$//\" >actual &&\n+\tgit config --unset column.ui &&\n+\tcat >expected <<\\EOF &&\n+  a/b/c\n+  abc\n+  bam\n+  bar\n+  foo\n+  j/k\n+  l\n+  m/m\n+* master\n+  master2\n+  n\n+  o/o\n+  o/p\n+  q\n+  r\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n mv .git/config .git/config-saved\n \n test_expect_success 'git branch -m q q2 without config should succeed' '\n-- \n1.7.8.36.g69ee2\n"},{"id":"186840","messageId":"d7319b7a0445fc476ae1e928bdc0750287f7fb42.1331638663.git.pclouds@gmail.com","threadId":"29734","inReplyTo":"dc618ca926bdbdbb96ae59615cbbe57cde9f763f.1331638663.git.pclouds@gmail.com","subject":"[PATCH v7 07/9] status: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-03-13T12:09:19Z","receivedAt":"2012-03-13T12:09:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt     |    4 ++++\n Documentation/git-status.txt |    7 +++++++\n Makefile                     |    2 +-\n builtin/commit.c             |    7 +++++++\n t/t7508-status.sh            |   24 ++++++++++++++++++++++++\n wt-status.c                  |   28 ++++++++++++++++++++++++++--\n wt-status.h                  |    1 +\n 7 files changed, 70 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 31878b7..acdd49f 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -851,6 +851,10 @@ column.branch::\n \tSpecify whether to output branch listing in `git branch` in columns.\n \tSee `column.ui` for details.\n \n+column.status::\n+\tSpecify whether to output untracked files in `git status` in columns.\n+\tSee `column.ui` for details.\n+\n commit.status::\n \tA boolean to enable/disable inclusion of status information in the\n \tcommit message template when using an editor to prepare the commit\ndiff --git a/Documentation/git-status.txt b/Documentation/git-status.txt\nindex 3d51717..2f87207 100644\n--- a/Documentation/git-status.txt\n+++ b/Documentation/git-status.txt\n@@ -77,6 +77,13 @@ configuration variable documented in linkgit:git-config[1].\n \tTerminate entries with NUL, instead of LF.  This implies\n \tthe `--porcelain` output format if no other format is given.\n \n+--column[=<options>]::\n+--no-column::\n+\tDisplay untracked files in columns. See configuration variable\n+\tcolumn.status for option syntax.`--column` and `--no-column`\n+\twithout options are equivalent to 'always' and 'never'\n+\trespectively.\n+\n \n OUTPUT\n ------\ndiff --git a/Makefile b/Makefile\nindex 320d3f8..d9c5f00 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2168,7 +2168,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o url.o http-backend.o: url.h\n-builtin/branch.o column.o help.o pager.o: column.h\n+builtin/branch.o builtin/commit.o column.o help.o pager.o: column.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 3714582..cc1a709 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -27,6 +27,7 @@\n #include \"quote.h\"\n #include \"submodule.h\"\n #include \"gpg-interface.h\"\n+#include \"column.h\"\n \n static const char * const builtin_commit_usage[] = {\n \t\"git commit [options] [--] <filepattern>...\",\n@@ -88,6 +89,7 @@ static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;\n static int no_post_rewrite, allow_empty_message;\n static char *untracked_files_arg, *force_date, *ignore_submodule_arg;\n static char *sign_commit;\n+static unsigned int colopts;\n \n /*\n  * The default commit message cleanup mode will remove the lines\n@@ -1145,6 +1147,8 @@ static int git_status_config(const char *k, const char *v, void *cb)\n {\n \tstruct wt_status *s = cb;\n \n+\tif (!prefixcmp(k, \"column.\"))\n+\t\treturn git_column_config(k, v, \"status\", &colopts);\n \tif (!strcmp(k, \"status.submodulesummary\")) {\n \t\tint is_bool;\n \t\ts->submodule_summary = git_config_bool_or_int(k, v, &is_bool);\n@@ -1210,6 +1214,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \t\t{ OPTION_STRING, 0, \"ignore-submodules\", &ignore_submodule_arg, \"when\",\n \t\t  \"ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)\",\n \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n+\t\tOPT_COLUMN(0, \"column\", &colopts, \"list untracked files in columns\"),\n \t\tOPT_END(),\n \t};\n \n@@ -1223,6 +1228,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix,\n \t\t\t     builtin_status_options,\n \t\t\t     builtin_status_usage, 0);\n+\tfinalize_colopts(&colopts, -1);\n+\ts.colopts = colopts;\n \n \tif (null_termination && status_format == STATUS_FORMAT_LONG)\n \t\tstatus_format = STATUS_FORMAT_PORCELAIN;\ndiff --git a/t/t7508-status.sh b/t/t7508-status.sh\nindex fc57b13..8f5cfac 100755\n--- a/t/t7508-status.sh\n+++ b/t/t7508-status.sh\n@@ -59,6 +59,30 @@ test_expect_success 'status (1)' '\n \ttest_i18ngrep \"use \\\"git rm --cached <file>\\.\\.\\.\\\" to unstage\" output\n '\n \n+test_expect_success 'status --column' '\n+\tCOLUMNS=50 git status --column=\"column dense\" >output &&\n+\tcat >expect <<\\EOF &&\n+# On branch master\n+# Changes to be committed:\n+#   (use \"git reset HEAD <file>...\" to unstage)\n+#\n+#\tnew file:   dir2/added\n+#\n+# Changes not staged for commit:\n+#   (use \"git add <file>...\" to update what will be committed)\n+#   (use \"git checkout -- <file>...\" to discard changes in working directory)\n+#\n+#\tmodified:   dir1/modified\n+#\n+# Untracked files:\n+#   (use \"git add <file>...\" to include in what will be committed)\n+#\n+#\tdir1/untracked dir2/untracked untracked\n+#\tdir2/modified  output\n+EOF\n+\ttest_cmp expect output\n+'\n+\n cat >expect <<\\EOF\n # On branch master\n # Changes to be committed:\ndiff --git a/wt-status.c b/wt-status.c\nindex 9ffc535..c1dcf75 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -11,6 +11,7 @@\n #include \"remote.h\"\n #include \"refs.h\"\n #include \"submodule.h\"\n+#include \"column.h\"\n \n static char default_wt_status_colors[][COLOR_MAXLEN] = {\n \tGIT_COLOR_NORMAL, /* WT_STATUS_HEADER */\n@@ -641,6 +642,8 @@ static void wt_status_print_other(struct wt_status *s,\n {\n \tint i;\n \tstruct strbuf buf = STRBUF_INIT;\n+\tstatic struct string_list output = STRING_LIST_INIT_DUP;\n+\tstruct column_options copts;\n \n \tif (!l->nr)\n \t\treturn;\n@@ -649,12 +652,33 @@ static void wt_status_print_other(struct wt_status *s,\n \n \tfor (i = 0; i < l->nr; i++) {\n \t\tstruct string_list_item *it;\n+\t\tconst char *path;\n \t\tit = &(l->items[i]);\n+\t\tpath = quote_path(it->string, strlen(it->string),\n+\t\t\t\t  &buf, s->prefix);\n+\t\tif (COL_ENABLE(s->colopts)) {\n+\t\t\tstring_list_append(&output, path);\n+\t\t\tcontinue;\n+\t\t}\n \t\tstatus_printf(s, color(WT_STATUS_HEADER, s), \"\\t\");\n \t\tstatus_printf_more(s, color(WT_STATUS_UNTRACKED, s),\n-\t\t\t\"%s\\n\", quote_path(it->string, strlen(it->string),\n-\t\t\t\t\t    &buf, s->prefix));\n+\t\t\t\t   \"%s\\n\", path);\n \t}\n+\n+\tstrbuf_release(&buf);\n+\tif (!COL_ENABLE(s->colopts))\n+\t\treturn;\n+\n+\tstrbuf_addf(&buf, \"%s#\\t%s\",\n+\t\t    color(WT_STATUS_HEADER, s),\n+\t\t    color(WT_STATUS_UNTRACKED, s));\n+\tmemset(&copts, 0, sizeof(copts));\n+\tcopts.padding = 1;\n+\tcopts.indent = buf.buf;\n+\tif (want_color(s->use_color))\n+\t\tcopts.nl = GIT_COLOR_RESET \"\\n\";\n+\tprint_columns(&output, s->colopts, &copts);\n+\tstring_list_clear(&output, 0);\n \tstrbuf_release(&buf);\n }\n \ndiff --git a/wt-status.h b/wt-status.h\nindex 682b4c8..6dd7207 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -56,6 +56,7 @@ struct wt_status {\n \tenum untracked_status_type show_untracked_files;\n \tconst char *ignore_submodule_arg;\n \tchar color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];\n+\tint colopts;\n \n \t/* These are computed during processing of the individual sections */\n \tint commitable;\n-- \n1.7.8.36.g69ee2\n"},{"id":"186839","messageId":"45b5fc83af9ef53f79eb66742eedcbfe14a3d532.1331638664.git.pclouds@gmail.com","threadId":"29734","inReplyTo":"d7319b7a0445fc476ae1e928bdc0750287f7fb42.1331638663.git.pclouds@gmail.com","subject":"[PATCH v7 08/9] column: support piping stdout to external git-column process","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-03-13T12:09:20Z","receivedAt":"2012-03-13T12:09:20Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"For too complicated output handling, it'd be easier to just spawn\ngit-column and redirect stdout to it. This patch provides helpers\nto do that.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n column.c |   69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n column.h |    3 ++\n 2 files changed, 72 insertions(+), 0 deletions(-)\n\ndiff --git a/column.c b/column.c\nindex 3aac28b..1e545a0 100644\n--- a/column.c\n+++ b/column.c\n@@ -2,6 +2,7 @@\n #include \"column.h\"\n #include \"string-list.h\"\n #include \"parse-options.h\"\n+#include \"run-command.h\"\n #include \"utf8.h\"\n \n #define XY2LINEAR(d, x, y) (COL_LAYOUT((d)->colopts) == COL_COLUMN ? \\\n@@ -364,3 +365,71 @@ int parseopt_column_callback(const struct option *opt,\n \n \treturn 0;\n }\n+\n+static int fd_out = -1;\n+static struct child_process column_process;\n+\n+int run_column_filter(int colopts, const struct column_options *opts)\n+{\n+\tconst char *av[10];\n+\tint ret, ac = 0;\n+\tstruct strbuf sb_colopt  = STRBUF_INIT;\n+\tstruct strbuf sb_width   = STRBUF_INIT;\n+\tstruct strbuf sb_padding = STRBUF_INIT;\n+\n+\tif (fd_out != -1)\n+\t\treturn -1;\n+\n+\tav[ac++] = \"column\";\n+\tstrbuf_addf(&sb_colopt, \"--raw-mode=%d\", colopts);\n+\tav[ac++] = sb_colopt.buf;\n+\tif (opts && opts->width) {\n+\t\tstrbuf_addf(&sb_width, \"--width=%d\", opts->width);\n+\t\tav[ac++] = sb_width.buf;\n+\t}\n+\tif (opts && opts->indent) {\n+\t\tav[ac++] = \"--indent\";\n+\t\tav[ac++] = opts->indent;\n+\t}\n+\tif (opts && opts->padding) {\n+\t\tstrbuf_addf(&sb_padding, \"--padding=%d\", opts->padding);\n+\t\tav[ac++] = sb_padding.buf;\n+\t}\n+\tav[ac] = NULL;\n+\n+\tfflush(stdout);\n+\tmemset(&column_process, 0, sizeof(column_process));\n+\tcolumn_process.in = -1;\n+\tcolumn_process.out = dup(1);\n+\tcolumn_process.git_cmd = 1;\n+\tcolumn_process.argv = av;\n+\n+\tret = start_command(&column_process);\n+\n+\tstrbuf_release(&sb_colopt);\n+\tstrbuf_release(&sb_width);\n+\tstrbuf_release(&sb_padding);\n+\n+\tif (ret)\n+\t\treturn -2;\n+\n+\tfd_out = dup(1);\n+\tclose(1);\n+\tdup2(column_process.in, 1);\n+\tclose(column_process.in);\n+\treturn 0;\n+}\n+\n+int stop_column_filter(void)\n+{\n+\tif (fd_out == -1)\n+\t\treturn -1;\n+\n+\tfflush(stdout);\n+\tclose(1);\n+\tfinish_command(&column_process);\n+\tdup2(fd_out, 1);\n+\tclose(fd_out);\n+\tfd_out = -1;\n+\treturn 0;\n+}\ndiff --git a/column.h b/column.h\nindex b3e979f..b6872fe 100644\n--- a/column.h\n+++ b/column.h\n@@ -36,4 +36,7 @@ extern int finalize_colopts(unsigned int *colopts, int stdout_is_tty);\n extern void print_columns(const struct string_list *list, unsigned int colopts,\n \t\t\t  const struct column_options *opts);\n \n+extern int run_column_filter(int colopts, const struct column_options *);\n+extern int stop_column_filter(void);\n+\n #endif\n-- \n1.7.8.36.g69ee2\n"},{"id":"186837","messageId":"b1d1aead1851563dbab0cb98db27db07a1addf19.1331638664.git.pclouds@gmail.com","threadId":"29734","inReplyTo":"45b5fc83af9ef53f79eb66742eedcbfe14a3d532.1331638664.git.pclouds@gmail.com","subject":"[PATCH v7 09/9] tag: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-03-13T12:09:21Z","receivedAt":"2012-03-13T12:09:21Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt  |    4 ++++\n Documentation/git-tag.txt |    9 +++++++++\n Makefile                  |    2 +-\n builtin/tag.c             |   27 ++++++++++++++++++++++++---\n t/t7004-tag.sh            |   44 ++++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 82 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex acdd49f..62c128c 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -855,6 +855,10 @@ column.status::\n \tSpecify whether to output untracked files in `git status` in columns.\n \tSee `column.ui` for details.\n \n+column.tag::\n+\tSpecify whether to output tag listing in `git tag` in columns.\n+\tSee `column.ui` for details.\n+\n commit.status::\n \tA boolean to enable/disable inclusion of status information in the\n \tcommit message template when using an editor to prepare the commit\ndiff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt\nindex 8d32b9a..e36a7c3 100644\n--- a/Documentation/git-tag.txt\n+++ b/Documentation/git-tag.txt\n@@ -13,6 +13,7 @@ SYNOPSIS\n \t<tagname> [<commit> | <object>]\n 'git tag' -d <tagname>...\n 'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]\n+\t[--column[=<options>] | --no-column] [<pattern>...]\n \t[<pattern>...]\n 'git tag' -v <tagname>...\n \n@@ -84,6 +85,14 @@ OPTIONS\n \tusing fnmatch(3)).  Multiple patterns may be given; if any of\n \tthem matches, the tag is shown.\n \n+--column[=<options>]::\n+--no-column::\n+\tDisplay tag listing in columns. See configuration variable\n+\tcolumn.tag for option syntax.`--column` and `--no-column`\n+\twithout options are equivalent to 'always' and 'never' respectively.\n++\n+This option is only applicable when listing tags without annotation lines.\n+\n --contains <commit>::\n \tOnly list tags which contain the specified commit.\n \ndiff --git a/Makefile b/Makefile\nindex d9c5f00..65fc6b9 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2168,7 +2168,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o url.o http-backend.o: url.h\n-builtin/branch.o builtin/commit.o column.o help.o pager.o: column.h\n+builtin/branch.o builtin/commit.o builtin/tag.o column.o help.o pager.o: column.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex fe7e5e5..7104adf 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -16,6 +16,7 @@\n #include \"revision.h\"\n #include \"gpg-interface.h\"\n #include \"sha1-array.h\"\n+#include \"column.h\"\n \n static const char * const git_tag_usage[] = {\n \t\"git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]\",\n@@ -33,6 +34,7 @@ struct tag_filter {\n };\n \n static struct sha1_array points_at;\n+static unsigned int colopts;\n \n static int match_pattern(const char **patterns, const char *ref)\n {\n@@ -263,6 +265,8 @@ static int git_tag_config(const char *var, const char *value, void *cb)\n \tint status = git_gpg_config(var, value, cb);\n \tif (status)\n \t\treturn status;\n+\tif (!prefixcmp(var, \"column.\"))\n+\t\treturn git_column_config(var, value, \"tag\", &colopts);\n \treturn git_default_config(var, value, cb);\n }\n \n@@ -459,6 +463,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING('u', \"local-user\", &keyid, \"key-id\",\n \t\t\t\t\t\"use another key to sign the tag\"),\n \t\tOPT__FORCE(&force, \"replace the tag if exists\"),\n+\t\tOPT_COLUMN(0, \"column\", &colopts, \"show tag list in columns\"),\n \n \t\tOPT_GROUP(\"Tag listing options\"),\n \t\t{\n@@ -495,9 +500,25 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \n \tif (list + delete + verify > 1)\n \t\tusage_with_options(git_tag_usage, options);\n-\tif (list)\n-\t\treturn list_tags(argv, lines == -1 ? 0 : lines,\n-\t\t\t\t with_commit);\n+\tfinalize_colopts(&colopts, -1);\n+\tif (list && lines != -1) {\n+\t\tif (explicitly_enable_column(colopts))\n+\t\t\tdie(_(\"--column and -n are incompatible\"));\n+\t\tcolopts = 0;\n+\t}\n+\tif (list) {\n+\t\tint ret;\n+\t\tif (COL_ENABLE(colopts)) {\n+\t\t\tstruct column_options copts;\n+\t\t\tmemset(&copts, 0, sizeof(copts));\n+\t\t\tcopts.padding = 2;\n+\t\t\trun_column_filter(colopts, &copts);\n+\t\t}\n+\t\tret = list_tags(argv, lines == -1 ? 0 : lines, with_commit);\n+\t\tif (COL_ENABLE(colopts))\n+\t\t\tstop_column_filter();\n+\t\treturn ret;\n+\t}\n \tif (lines != -1)\n \t\tdie(_(\"-n option is only allowed with -l.\"));\n \tif (with_commit)\ndiff --git a/t/t7004-tag.sh b/t/t7004-tag.sh\nindex f8c247a..5189446 100755\n--- a/t/t7004-tag.sh\n+++ b/t/t7004-tag.sh\n@@ -263,6 +263,50 @@ test_expect_success 'tag -l can accept multiple patterns' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'listing tags in column' '\n+\tCOLUMNS=40 git tag -l --column=row >actual &&\n+\tcat >expected <<\\EOF &&\n+a1      aa1     cba     t210    t211\n+v0.2.1  v1.0    v1.0.1  v1.1.3\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'listing tags in column with column.*' '\n+\tgit config column.tag row &&\n+\tgit config column.ui dense &&\n+\tCOLUMNS=40 git tag -l >actual &&\n+\tgit config --unset column.ui &&\n+\tgit config --unset column.tag &&\n+\tcat >expected <<\\EOF &&\n+a1      aa1   cba     t210    t211\n+v0.2.1  v1.0  v1.0.1  v1.1.3\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'listing tag with -n --column should fail' '\n+\ttest_must_fail git tag --column -n\n+'\n+\n+test_expect_success 'listing tags -n in column with column.ui ignored' '\n+\tgit config column.ui \"row dense\" &&\n+\tCOLUMNS=40 git tag -l -n >actual &&\n+\tgit config --unset column.ui &&\n+\tcat >expected <<\\EOF &&\n+a1              Foo\n+aa1             Foo\n+cba             Foo\n+t210            Foo\n+t211            Foo\n+v0.2.1          Foo\n+v1.0            Foo\n+v1.0.1          Foo\n+v1.1.3          Foo\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n # creating and verifying lightweight tags:\n \n test_expect_success \\\n-- \n1.7.8.36.g69ee2\n"},{"id":"186843","messageId":"79a9a0dd76d72d9cda14378acb16a2fc6456dd0e.1331638664.git.pclouds@gmail.com","threadId":"29734","inReplyTo":"b1d1aead1851563dbab0cb98db27db07a1addf19.1331638664.git.pclouds@gmail.com","subject":"[PATCH v7 10/9] ls-files: support --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-03-13T12:11:34Z","receivedAt":"2012-03-13T12:11:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":".. because ls-files is a good show case to the next patches..\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/ls-files.c |    8 ++++++++\n 1 files changed, 8 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 7cff175..e191f4f 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -13,6 +13,7 @@\n #include \"parse-options.h\"\n #include \"resolve-undo.h\"\n #include \"string-list.h\"\n+#include \"column.h\"\n \n static int abbrev;\n static int show_deleted;\n@@ -35,6 +36,7 @@ static int error_unmatch;\n static char *ps_matched;\n static const char *with_tree;\n static int exc_given;\n+static unsigned int colopts;\n \n static const char *tag_cached = \"\";\n static const char *tag_unmerged = \"\";\n@@ -496,6 +498,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\tOPT_STRING(0, \"with-tree\", &with_tree, \"tree-ish\",\n \t\t\t\"pretend that paths removed since <tree-ish> are still present\"),\n \t\tOPT__ABBREV(&abbrev),\n+\t\tOPT_COLUMN(0, \"column\", &colopts, \"list branches in columns\"),\n \t\tOPT_BOOLEAN(0, \"debug\", &debug_mode, \"show debugging data\"),\n \t\tOPT_END()\n \t};\n@@ -514,6 +517,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \n \targc = parse_options(argc, argv, prefix, builtin_ls_files_options,\n \t\t\tls_files_usage, 0);\n+\tfinalize_colopts(&colopts, -1);\n \tif (show_tag || show_valid_bit) {\n \t\ttag_cached = \"H \";\n \t\ttag_unmerged = \"M \";\n@@ -575,7 +579,11 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tdie(\"ls-files --with-tree is incompatible with -s or -u\");\n \t\toverlay_tree_on_cache(with_tree, max_prefix);\n \t}\n+\tif (COL_ENABLE(colopts))\n+\t\trun_column_filter(colopts, NULL);\n \tshow_files(&dir);\n+\tif (COL_ENABLE(colopts))\n+\t\tstop_column_filter();\n \tif (show_resolve_undo)\n \t\tshow_ru_info();\n \n-- \n1.7.8.36.g69ee2\n"},{"id":"186844","messageId":"25ba0cdc7d4898e65af12b584c0e9ebf4e412557.1331638664.git.pclouds@gmail.com","threadId":"29734","inReplyTo":"79a9a0dd76d72d9cda14378acb16a2fc6456dd0e.1331638664.git.pclouds@gmail.com","subject":"[PATCH v7 11/9] column: support \"denser\" mode","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-03-13T12:11:35Z","receivedAt":"2012-03-13T12:11:35Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Sometimes a few long entries in the listing would stretch out columns,\nwasting space. If these entries are cut out, the columns could become\nsmaller, hence more columns.\n\nThis new mode does that by looking for the longest entry, if cutting the\nlist before that entry results in much denser layout, then the entry\nwill be cut out and printed in a separate line. The remaining will put\nin a new layout.\n\nMultiple tables with different column width might be unpleasant to\nlook at, especially if the tables are really short. But on the other\nhand it could be quite handy. For example,\n\"COLUMNS=80 ./git ls-files --column=dense -- '*.[ch]'\" takes 415\nlines, while \"denser\" only takes 223 because it break the layout at\ncontrib/credential/osxkeychain/git-credential-osxkeychain.c\nand relayout again:\n\nabspath.c                  builtin/rm.c\nadvice.c                   builtin/send-pack.c\n<snip>\nbuiltin/rev-parse.c        connected.h\nbuiltin/revert.c           contrib/convert-objects/convert-objects.c\n\ncontrib/credential/osxkeychain/git-credential-osxkeychain.c\n\ncontrib/examples/builtin-fetch--tool.c refs.c                   xdiff/xutils.c\ncontrib/svn-fe/svn-fe.c                refs.h                   xdiff/xutils.h\nconvert.c                              remote-curl.c            zlib.c\nconvert.h                              remote.c\n<snip>\nreflog-walk.c                          xdiff/xprepare.h\nreflog-walk.h                          xdiff/xtypes.h\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n column.c |   74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---\n column.h |    1 +\n 2 files changed, 71 insertions(+), 4 deletions(-)\n\ndiff --git a/column.c b/column.c\nindex 1e545a0..80eefa0 100644\n--- a/column.c\n+++ b/column.c\n@@ -152,10 +152,55 @@ static int display_cell(struct column_data *data, int initial_width,\n \treturn 0;\n }\n \n+/*\n+ * Attempt to put the longest cell into a separate line, see if it\n+ * improves the layout\n+ */\n+static int break_long_line(const struct column_data *old_data)\n+{\n+\tstruct column_data data;\n+\tstruct string_list faked_list;\n+\tint initial_width, x, y, i, item = 0, row1, row2;\n+\tchar *empty_cell;\n+\n+\tmemcpy(&data, old_data, sizeof(data));\n+\tfor (i = 0; i < data.list->nr; i++)\n+\t\tif (data.len[i] > data.len[item])\n+\t\t\titem = i;\n+\tdata.list = &faked_list;\n+\tdata.width = NULL;\n+\tfaked_list = *old_data->list;\n+\n+\tfaked_list.nr = item + 1;\n+\tlayout(&data, &initial_width);\n+\tshrink_columns(&data);\n+\trow1 = data.rows;\n+\n+\tfaked_list.nr = item;\n+\tlayout(&data, &initial_width);\n+\tshrink_columns(&data);\n+\trow2 = data.rows;\n+\n+\tif (row1 - row2 < 3)\n+\t\treturn -1;\n+\n+\tempty_cell = xmalloc(initial_width + 1);\n+\tmemset(empty_cell, ' ', initial_width);\n+\tempty_cell[initial_width] = '\\0';\n+\tfor (y = 0; y < data.rows; y++) {\n+\t\tfor (x = 0; x < data.cols; x++)\n+\t\t\tif (display_cell(&data, initial_width, empty_cell, x, y))\n+\t\t\t\tbreak;\n+\t}\n+\tfree(data.width);\n+\tfree(empty_cell);\n+\treturn item;\n+}\n+\n /* Display COL_COLUMN or COL_ROW */\n-static void display_table(const struct string_list *list,\n-\t\t\t  unsigned int colopts,\n-\t\t\t  const struct column_options *opts)\n+static int display_table(const struct string_list *list,\n+\t\t\t unsigned int colopts,\n+\t\t\t const struct column_options *opts)\n {\n \tstruct column_data data;\n \tint x, y, i, initial_width;\n@@ -174,6 +219,19 @@ static void display_table(const struct string_list *list,\n \n \tif (colopts & COL_DENSE)\n \t\tshrink_columns(&data);\n+\tif (colopts & COL_DENSER) {\n+\t\ti = break_long_line(&data);\n+\t\tif (i != -1) {\n+\t\t\tprintf(\"%s%s\" \"%s%s%s\" \"%s%s\",\n+\t\t\t       indent, nl,\n+\t\t\t       indent, list->items[i].string, nl,\n+\t\t\t       indent, nl);\n+\t\t\tfree(data.len);\n+\t\t\tfree(data.width);\n+\t\t\treturn i + 1;\n+\t\t}\n+\t\tshrink_columns(&data);\n+\t}\n \n \tempty_cell = xmalloc(initial_width + 1);\n \tmemset(empty_cell, ' ', initial_width);\n@@ -187,12 +245,15 @@ static void display_table(const struct string_list *list,\n \tfree(data.len);\n \tfree(data.width);\n \tfree(empty_cell);\n+\treturn list->nr;\n }\n \n void print_columns(const struct string_list *list, unsigned int colopts,\n \t\t   const struct column_options *opts)\n {\n \tstruct column_options nopts;\n+\tint processed;\n+\tstruct string_list l = *list;\n \n \tif (!list->nr)\n \t\treturn;\n@@ -213,7 +274,11 @@ void print_columns(const struct string_list *list, unsigned int colopts,\n \t\tbreak;\n \tcase COL_ROW:\n \tcase COL_COLUMN:\n-\t\tdisplay_table(list, colopts, &nopts);\n+\t\twhile (l.nr &&\n+\t\t       (processed = display_table(&l, colopts, &nopts)) < l.nr) {\n+\t\t\tl.items += processed;\n+\t\t\tl.nr -= processed;\n+\t\t}\n \t\tbreak;\n \tdefault:\n \t\tdie(\"BUG: invalid layout mode %d\", COL_LAYOUT(colopts));\n@@ -252,6 +317,7 @@ static int parse_option(const char *arg, int len, unsigned int *colopts,\n \t\t{ \"column\", COL_COLUMN,   COL_LAYOUT_MASK },\n \t\t{ \"row\",    COL_ROW,      COL_LAYOUT_MASK },\n \t\t{ \"dense\",  COL_DENSE,    0 },\n+\t\t{ \"denser\", COL_DENSER,   0 },\n \t};\n \tint i;\n \ndiff --git a/column.h b/column.h\nindex b6872fe..dbc5da2 100644\n--- a/column.h\n+++ b/column.h\n@@ -6,6 +6,7 @@\n #define COL_PARSEOPT      0x0040   /* --column is given from cmdline */\n #define COL_DENSE         0x0080   /* Shrink columns when possible,\n \t\t\t\t      making space for more columns */\n+#define COL_DENSER        0x0100\n \n #define COL_ENABLE(c) ((c) & COL_ENABLE_MASK)\n #define COL_DISABLED      0x0000   /* must be zero */\n-- \n1.7.8.36.g69ee2\n"},{"id":"186845","messageId":"c3c9422bcfccad34e7b97c39a267207cfb41c0b7.1331638664.git.pclouds@gmail.com","threadId":"29734","inReplyTo":"25ba0cdc7d4898e65af12b584c0e9ebf4e412557.1331638664.git.pclouds@gmail.com","subject":"[PATCH v7 12/9] column: support grouping entries","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-03-13T12:11:36Z","receivedAt":"2012-03-13T12:11:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"If many entries share the same prefix (e.g. \"git ls-files Documentation/\"),\ncutting out the prefix helps put more information on the same space.\n\nIf \"group\" is specified, the list of entries will be searched for\nlargest non-overlapping rectangles of text. Estimation is done on each\nrectangle to see if there are any savings in rows if we group them.\nGroups are printed first, then the remaining as the last group.\n\nHandling the remaining part this way may not be ideal but I don't want\nto split all directories like \"ls -R\". That takes too many lines.\nMaybe I should prepend \".../\" to all grouped items to make it clear\nthey are grouped.  There's also problem with ansi escape codes that\nI'll need to handle if this sounds like a good way to go.\n\nThis code may be used for diffstat too (e.g. when most of the diff is\nin Documentation/).\n\nFor demonstration, this is what\n\"COLUMNS=80 git ls-files --column=group -- '*.[ch]'\" looks like\n\nbuiltin/:\nadd.c                gc.c                 read-tree.c\nannotate.c           grep.c               receive-pack.c\n<snip>\nfor-each-ref.c       prune.c              verify-tag.c\nfsck.c               push.c               write-tree.c\n\ncompat/:\nbasename.c                  regex/regexec.c\nbswap.h                     setenv.c\n<snip>\nregex/regex_internal.c      win32mmap.c\nregex/regex_internal.h      winansi.c\n\ncontrib/:\nconvert-objects/convert-objects.c\ncredential/osxkeychain/git-credential-osxkeychain.c\nexamples/builtin-fetch--tool.c\nsvn-fe/svn-fe.c\n\nvcs-svn/:\nfast_export.c    line_buffer.h    sliding_window.c svndiff.h\nfast_export.h    repo_tree.c      sliding_window.h svndump.c\nline_buffer.c    repo_tree.h      svndiff.c        svndump.h\n\nxdiff/:\nxdiff.h      xemit.c      xinclude.h   xpatience.c  xtypes.h\nxdiffi.c     xemit.h      xmacros.h    xprepare.c   xutils.c\nxdiffi.h     xhistogram.c xmerge.c     xprepare.h   xutils.h\n\n...:\nabspath.c                  pack-check.c\nadvice.c                   pack-refs.c\n<snip>\nobject.c                   xdiff-interface.h\nobject.h                   zlib.c\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n column.c |  229 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n column.h |    1 +\n 2 files changed, 227 insertions(+), 3 deletions(-)\n\ndiff --git a/column.c b/column.c\nindex 80eefa0..8214a9b 100644\n--- a/column.c\n+++ b/column.c\n@@ -19,6 +19,11 @@ struct column_data {\n \tint *width;\t      /* index to the longest row in column */\n };\n \n+struct area {\n+\tint start, end;\t\t/* in string_list */\n+\tint size;\n+};\n+\n /* return length of 's' in letters, ANSI escapes stripped */\n static int item_length(unsigned int colopts, const char *s)\n {\n@@ -223,9 +228,9 @@ static int display_table(const struct string_list *list,\n \t\ti = break_long_line(&data);\n \t\tif (i != -1) {\n \t\t\tprintf(\"%s%s\" \"%s%s%s\" \"%s%s\",\n-\t\t\t       indent, nl,\n-\t\t\t       indent, list->items[i].string, nl,\n-\t\t\t       indent, nl);\n+\t\t\t       opts->indent, opts->nl,\n+\t\t\t       opts->indent, list->items[i].string, opts->nl,\n+\t\t\t       opts->indent, opts->nl);\n \t\t\tfree(data.len);\n \t\t\tfree(data.width);\n \t\t\treturn i + 1;\n@@ -248,6 +253,218 @@ static int display_table(const struct string_list *list,\n \treturn list->nr;\n }\n \n+/*\n+ * Find out the contiguous list of entries sharing the same directory\n+ * prefix that nr * (prefix_len - skip) is largest, where nr is the\n+ * number of entries and prefix_len is the shared directory prefix's\n+ * length.\n+ */\n+static int largest_block(const struct string_list *list, int start, int skip, int *len)\n+{\n+\tconst char *str = list->items[start].string;\n+\tconst char *slash;\n+\tint largest_area = 0;\n+\n+\tfor (slash = str + strlen(str) - 1; slash > str + skip; slash--) {\n+\t\tint i, area;\n+\t\tif (*slash != '/')\n+\t\t\tcontinue;\n+\t\tfor (i = start; i < list->nr; i++) {\n+\t\t\tconst char *s = list->items[i].string;\n+\t\t\tif (strlen(s) < slash + 1 - str ||\n+\t\t\t    memcmp(str + skip, s + skip, slash + 1 - (str + skip)))\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tarea = (i - start) * (slash + 1 - str - skip);\n+\t\tif (area > largest_area) {\n+\t\t\tlargest_area = area;\n+\t\t\t*len = i - start;\n+\t\t}\n+\t}\n+\treturn largest_area;\n+}\n+\n+static int area_size_cmp(const void *a, const void *b)\n+{\n+\tconst struct area *area1 = a;\n+\tconst struct area *area2 = b;\n+\treturn area2->size - area1->size;\n+}\n+\n+/*\n+ * Make a sorted list of non-overlapping blocks, largest ones first\n+ */\n+static struct area *find_large_blocks(const struct string_list *list, int *nr_p)\n+{\n+\tint i, nr = 0, alloc = 16;\n+\tstruct area *areas = xmalloc(sizeof(*areas) * alloc);\n+\tstruct area last;\n+\tmemset(&last, 0, sizeof(last));\n+\n+\tfor (i = 0; i < list->nr; i++) {\n+\t\tint len, size = largest_block(list, i, 0, &len);\n+\t\tif (!size || len == 1)\n+\t\t\tcontinue;\n+\t\t/* the new found area is overlapped with the old one,\n+\t\t   but smaller, skip it */\n+\t\tif (i < last.end) {\n+\t\t\tif (size < last.size)\n+\t\t\t\tcontinue;\n+\t\t\tlast.start = i;\n+\t\t\tlast.end = i + len;\n+\t\t\tlast.size = size;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (last.size) {\n+\t\t\tif (nr + 1 < alloc)\n+\t\t\t\tALLOC_GROW(areas, nr + 1, alloc);\n+\t\t\tareas[nr++] = last;\n+\t\t}\n+\t\tlast.start = i;\n+\t\tlast.end = i + len;\n+\t\tlast.size = size;\n+\t}\n+\tif (last.size) {\n+\t\tif (nr + 1 >= alloc)\n+\t\t\tALLOC_GROW(areas, nr + 1, alloc);\n+\t\tareas[nr++] = last;\n+\t}\n+\tqsort(areas, nr, sizeof(*areas), area_size_cmp);\n+\t*nr_p = nr;\n+\treturn areas;\n+}\n+\n+static int area_start_cmp(const void *a, const void *b)\n+{\n+\tconst struct area *area1 = a;\n+\tconst struct area *area2 = b;\n+\treturn area1->start - area2->start;\n+}\n+\n+/*\n+ * Assume list is split into two tables: one from \"start\" to \"stop\",\n+ * where all strings are truncated \"skip\" bytes, the other the rest of\n+ * the strings. Calculate how many rows required if all cells of each\n+ * table are of the same width.\n+ */\n+static int split_layout_gain(const struct string_list *list, int *lengths,\n+\t\t\t     const struct column_options *opts,\n+\t\t\t     int start, int stop, int skip)\n+{\n+\tint i, width0, width1, width2, cols, rows0, rows1;\n+\tint indent = strlen(opts->indent);\n+\n+\twidth0 = width1 = width2 = 0;\n+\tfor (i = 0; i < list->nr; i++) {\n+\t\tint len = lengths[i];\n+\t\tif (width0 < len)\n+\t\t\twidth0 = len;\n+\t\tif (i >= start && i < stop) {\n+\t\t\tlen -= skip;\n+\t\t\tif (width2 < len)\n+\t\t\t\twidth2 = len;\n+\t\t} else {\n+\t\t\tif (width1 < len)\n+\t\t\t\twidth1 = len;\n+\t\t}\n+\t}\n+\n+\twidth0 += opts->padding;\n+\tcols = (opts->width - indent) / width0;\n+\tif (cols == 0)\n+\t\tcols = 1;\n+\trows0 = DIV_ROUND_UP(list->nr, cols);\n+\n+\twidth1 += opts->padding;\n+\tcols = (opts->width - indent) / width1;\n+\tif (cols == 0)\n+\t\tcols = 1;\n+\trows1 = DIV_ROUND_UP(list->nr - (stop - start), cols);\n+\n+\twidth2 += opts->padding;\n+\tcols = (opts->width - indent) / width2;\n+\tif (cols == 0)\n+\t\tcols = 1;\n+\trows1 += DIV_ROUND_UP(stop - start, cols);\n+\treturn rows0 - rows1;\n+}\n+\n+static void group_by_prefix(const struct string_list *list, unsigned int colopts,\n+\t\t\t    const struct column_options *opts)\n+{\n+\tint i, nr;\n+\tstruct area *areas = find_large_blocks(list, &nr);\n+\tstruct string_list new_list = STRING_LIST_INIT_NODUP;\n+\tstruct area *dst;\n+\tint *len;\n+\n+\tassert(colopts & COL_GROUP);\n+\t/* avoid inifinite loop when calling print_columns again */\n+\tcolopts &= ~COL_GROUP;\n+\n+\tlen = xmalloc(sizeof(*len) * list->nr);\n+\tfor (i = 0; i < list->nr; i++)\n+\t\tlen[i] = item_length(colopts, list->items[i].string);\n+\n+\t/*\n+\t * Calculate and see if there is any saving when print this as\n+\t * a group. Base our calculation on non-dense mode even if\n+\t * users want dense output because the calculation would be\n+\t * less expensive.\n+\t */\n+\tdst = areas;\n+\tfor (i = 0; i < nr; i++) {\n+\t\tstruct area *area = areas + i;\n+\t\tint rows, skip = area->size / (area->end - area->start);\n+\t\trows = split_layout_gain(list, len, opts,\n+\t\t\t\t\t area->start, area->end, skip);\n+\n+\t\tif (rows > 3) {\n+\t\t\tif (areas + i != dst)\n+\t\t\t\t*dst = *area;\n+\t\t\tdst++;\n+\t\t}\n+\t}\n+\tfree(len);\n+\n+\tnr = dst - areas;\n+\tif (!nr) {\n+\t\tprint_columns(list, colopts, opts);\n+\t\treturn;\n+\t}\n+\tqsort(areas, nr, sizeof(*areas), area_start_cmp);\n+\n+\t/*\n+\t * We now have list of worthy groups, sorted by offset. Print\n+\t * group by group, then the rest.\n+\t */\n+\tfor (i = 0; i < nr; i++) {\n+\t\tstruct area *area = areas + i;\n+\t\tint j, skip = area->size / (area->end - area->start);\n+\n+\t\tfor (j = area->start; j < area->end; j++)\n+\t\t\tstring_list_append(&new_list,\n+\t\t\t\t\t   list->items[j].string + skip);\n+\t\tprintf(\"\\n%.*s:\\n\", skip, list->items[area->start].string);\n+\t\tprint_columns(&new_list, colopts, opts);\n+\t\tstring_list_clear(&new_list, 0);\n+\t}\n+\n+\tprintf(\"\\n%s:\\n\", \"...\");\n+\tfor (i = 0; i < nr; i++) {\n+\t\tstruct area *area = areas + i;\n+\t\tint j;\n+\t\tfor (j = i ? area[-1].end : 0; j < area->start; j++)\n+\t\t\tstring_list_append(&new_list, list->items[j].string);\n+\t}\n+\tfor (i = areas[nr-1].end; i < list->nr; i++)\n+\t\tstring_list_append(&new_list, list->items[i].string);\n+\tprint_columns(&new_list, colopts, opts);\n+\tstring_list_clear(&new_list, 0);\n+\n+\tfree(areas);\n+}\n+\n void print_columns(const struct string_list *list, unsigned int colopts,\n \t\t   const struct column_options *opts)\n {\n@@ -264,6 +481,11 @@ void print_columns(const struct string_list *list, unsigned int colopts,\n \tnopts.nl = opts && opts->nl ? opts->nl : \"\\n\";\n \tnopts.padding = opts ? opts->padding : 1;\n \tnopts.width = opts && opts->width ? opts->width : term_columns() - 1;\n+\n+\tif (colopts & COL_GROUP) {\n+\t\tgroup_by_prefix(list, colopts, &nopts);\n+\t\treturn;\n+\t}\n \tif (!COL_ENABLE(colopts)) {\n \t\tdisplay_plain(list, \"\", \"\\n\");\n \t\treturn;\n@@ -318,6 +540,7 @@ static int parse_option(const char *arg, int len, unsigned int *colopts,\n \t\t{ \"row\",    COL_ROW,      COL_LAYOUT_MASK },\n \t\t{ \"dense\",  COL_DENSE,    0 },\n \t\t{ \"denser\", COL_DENSER,   0 },\n+\t\t{ \"group\",  COL_GROUP,    0 },\n \t};\n \tint i;\n \ndiff --git a/column.h b/column.h\nindex dbc5da2..f3934cf 100644\n--- a/column.h\n+++ b/column.h\n@@ -7,6 +7,7 @@\n #define COL_DENSE         0x0080   /* Shrink columns when possible,\n \t\t\t\t      making space for more columns */\n #define COL_DENSER        0x0100\n+#define COL_GROUP         0x0200\n \n #define COL_ENABLE(c) ((c) & COL_ENABLE_MASK)\n #define COL_DISABLED      0x0000   /* must be zero */\n-- \n1.7.8.36.g69ee2\n"},{"id":"186910","messageId":"7vaa3kt9zf.fsf@alter.siamese.dyndns.org","threadId":"29734","inReplyTo":"1294ed94f981a7029b47a44143c3eb9fb6b820f1.1331638663.git.pclouds@gmail.com","subject":"Re: [PATCH v7 01/9] Add column layout skeleton and git-column","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-03-13T22:24:52Z","receivedAt":"2012-03-13T22:24:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> +#define COL_ENABLE(c) ((c) & COL_ENABLE_MASK)\n\nThat is a misleading name for a boolean macro.  It looked as if this\n\n> +\tassert(COL_ENABLE(colopts) != COL_AUTO);\n\nwas asking the helper to *enable* the column machinery with the given set\nof option in colopts, and expecting the helper to answer how it enabled\n(\"I took the 'automatic' decision path\").  But that is not what is\nhappening.\n\nUnfortunately, COL_ENABLED?(c) is not an option, but this seriously needs\na better name to avoid reader confusion.\n\nRegarding the \"denser\" mode, I very much like the simplicity of the idea.\nI was wondering if a solution that aims for the maximum density that does\nnot shuffle the original order of items would end up taking the output\nfrom \"fmt\" and distributing the words on each line evenly to the width,\nwhich would be totally unusable. Your \"punt at an item that does not fit\nand restart from there\" is simple and seems to work well.\n\nI haven't formed an opinion on your \"grouping\" mode yet.  The hardcoded\nslash hierarchy delimiter somewhat bothers me, but I haven't thought it\ndeeply enough to judge if it is worth making it more generic. My gut\nfeeling is that '/' probably is OK.\n\nThanks.\n"},{"id":"186929","messageId":"CACsJy8D8U7L4P+z9DT6hq0Rze=p2M6UZKMCqia1kN-QGJFE_zg@mail.gmail.com","threadId":"29734","inReplyTo":"7vaa3kt9zf.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v7 01/9] Add column layout skeleton and git-column","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-03-14T11:17:59Z","receivedAt":"2012-03-14T11:17:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2012/3/14 Junio C Hamano <gitster@pobox.com>:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> +#define COL_ENABLE(c) ((c) & COL_ENABLE_MASK)\n>\n> That is a misleading name for a boolean macro.  It looked as if this\n>\n>> +     assert(COL_ENABLE(colopts) != COL_AUTO);\n>\n> was asking the helper to *enable* the column machinery with the given set\n> of option in colopts, and expecting the helper to answer how it enabled\n> (\"I took the 'automatic' decision path\").  But that is not what is\n> happening.\n>\n> Unfortunately, COL_ENABLED?(c) is not an option, but this seriously needs\n> a better name to avoid reader confusion.\n\nI'm running out of names. Suggestions are welcome.\n\n> I haven't formed an opinion on your \"grouping\" mode yet.  The hardcoded\n> slash hierarchy delimiter somewhat bothers me, but I haven't thought it\n> deeply enough to judge if it is worth making it more generic. My gut\n> feeling is that '/' probably is OK.\n\nThe column user should be able to decide how to display the group\nline. I rely a lot on coloring, and I was already thinking about\ngiving group line a highlight color, which must be configurable\nbecause different column user uses different base colors.\n-- \nDuy\n"},{"id":"186977","messageId":"7vbonzqbml.fsf@alter.siamese.dyndns.org","threadId":"29734","inReplyTo":"CACsJy8D8U7L4P+z9DT6hq0Rze=p2M6UZKMCqia1kN-QGJFE_zg@mail.gmail.com","subject":"Re: [PATCH v7 01/9] Add column layout skeleton and git-column","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-03-14T18:29:54Z","receivedAt":"2012-03-14T18:29:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n> 2012/3/14 Junio C Hamano <gitster@pobox.com>:\n>> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>>\n>>> +#define COL_ENABLE(c) ((c) & COL_ENABLE_MASK)\n>>\n>> That is a misleading name for a boolean macro.  It looked as if this\n>>\n>>> +     assert(COL_ENABLE(colopts) != COL_AUTO);\n>>\n>> was asking the helper to *enable* the column machinery with the given set\n>> of option in colopts, and expecting the helper to answer how it enabled\n>> (\"I took the 'automatic' decision path\").  But that is not what is\n>> happening.\n> ...\n> I'm running out of names. Suggestions are welcome.\n\nAn obvious interface to expose to API users (as opposed to internal\ndetails to the implementation) would be \"extern bool COL_ENABLED(opts);\",\nno?\n"}]}