{"thread":{"id":"29535","subject":"[PATCH v4 00/13] Column display again","startedAt":"2012-02-03T13:34:25Z","lastAt":"2012-02-06T17:58:26Z","messageCount":43,"participants":["Nguyễn Thái Ngọc Duy","Junio C Hamano","Nguyen Thai Ngoc Duy"],"isPatch":true,"patchVersion":4,"patchTotal":13},"messages":[{"id":"183716","messageId":"1328276078-27955-1-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":null,"subject":"[PATCH v4 00/13] Column display again","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-03T13:34:25Z","receivedAt":"2012-02-03T13:34:25Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Reroll of v3 [1]. This series adds support for column display like\n\"ls\". \"git branch\", \"git tag -l\" and \"git status\"'s untracked files\ncan now be displayed in columns. There's also \"git-column\" to help\ndo the layout for commands that are not aware of column display. In\nfact git-tag uses it this way.\n\nI've been thinking of supporting the layout where a cell's content can\noccupy more than one cell. It'll make better use of space when there\nare a few long entries. Something like this:\n\nabc    def    ghk\nlonglonglong  short\nagain  here   now\n\n\"git status\" may benefit when you have untracked files in some deep\ndirectories.\n\n\"ls -R\" layout is also an option (\"branch -r\" and ls-files will\nbenefit due to dense tree output). But these ideas will have to wait\nuntil later.\n\nCompared to v3, it has \"git status\" support, the return of git-column,\nand column.* conf vars.\n\n[1] http://mid.gmane.org/1300625873-18435-1-git-send-email-pclouds@gmail.com\n\nNguyễn Thái Ngọc Duy (13):\n  Save terminal width before setting up pager\n  column: add API to print items in columns\n  parseopt: make OPT_INTEGER support hexadecimal as well\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    |   11 +-\n Makefile                     |    3 +\n builtin.h                    |    1 +\n builtin/branch.c             |   26 ++-\n builtin/column.c             |   64 ++++++\n builtin/commit.c             |   13 +-\n builtin/tag.c                |   25 ++-\n column.c                     |  493 ++++++++++++++++++++++++++++++++++++++++++\n column.h                     |   41 ++++\n command-list.txt             |    1 +\n git.c                        |    1 +\n help.c                       |   70 ++-----\n pager.c                      |   37 +++-\n parse-options.c              |    5 +-\n parse-options.h              |    2 +\n t/t9002-column.sh            |  135 ++++++++++++\n wt-status.c                  |   38 +++-\n wt-status.h                  |    2 +-\n 23 files changed, 1002 insertions(+), 74 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":"183717","messageId":"1328276078-27955-2-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328276078-27955-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 01/13] Save terminal width before setting up pager","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-03T13:34:26Z","receivedAt":"2012-02-03T13:34:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"term_columns() checks for terminal width via ioctl(2). After\nredirecting, stdin is no longer terminal to get terminal width.\n\nCheck terminal width and save it before redirect stdin in\nsetup_pager() and let term_columns() reuse the value.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Makefile |    1 +\n column.h |    6 ++++++\n help.c   |   23 +----------------------\n pager.c  |   35 +++++++++++++++++++++++++++++++++++\n 4 files changed, 43 insertions(+), 22 deletions(-)\n create mode 100644 column.h\n\ndiff --git a/Makefile b/Makefile\nindex c457c34..cbbc699 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2114,6 +2114,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+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.h b/column.h\nnew file mode 100644\nindex 0000000..55d8067\n--- /dev/null\n+++ b/column.h\n@@ -0,0 +1,6 @@\n+#ifndef COLUMN_H\n+#define COLUMN_H\n+\n+extern int term_columns(void);\n+\n+#endif\ndiff --git a/help.c b/help.c\nindex cbbe966..672561b 100644\n--- a/help.c\n+++ b/help.c\n@@ -4,28 +4,7 @@\n #include \"levenshtein.h\"\n #include \"help.h\"\n #include \"common-cmds.h\"\n-\n-/* most GUI terminals set COLUMNS (although some don't export it) */\n-static int term_columns(void)\n-{\n-\tchar *col_string = getenv(\"COLUMNS\");\n-\tint n_cols;\n-\n-\tif (col_string && (n_cols = atoi(col_string)) > 0)\n-\t\treturn n_cols;\n-\n-#ifdef TIOCGWINSZ\n-\t{\n-\t\tstruct winsize ws;\n-\t\tif (!ioctl(1, TIOCGWINSZ, &ws)) {\n-\t\t\tif (ws.ws_col)\n-\t\t\t\treturn ws.ws_col;\n-\t\t}\n-\t}\n-#endif\n-\n-\treturn 80;\n-}\n+#include \"column.h\"\n \n void add_cmdname(struct cmdnames *cmds, const char *name, int len)\n {\ndiff --git a/pager.c b/pager.c\nindex 975955b..772a5a6 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -6,6 +6,21 @@\n #define DEFAULT_PAGER \"less\"\n #endif\n \n+static int spawned_pager;\n+static int max_columns;\n+\n+static int retrieve_terminal_width(void)\n+{\n+#ifdef TIOCGWINSZ\n+\tstruct winsize ws;\n+\tif (ioctl(1, TIOCGWINSZ, &ws))  /* e.g., ENOSYS */\n+\t\treturn 0;\n+\treturn ws.ws_col;\n+#else\n+\treturn 0;\n+#endif\n+}\n+\n /*\n  * This is split up from the rest of git so that we can do\n  * something different on Windows.\n@@ -72,12 +87,17 @@ const char *git_pager(int stdout_is_tty)\n void setup_pager(void)\n {\n \tconst char *pager = git_pager(isatty(1));\n+\tint width;\n \n \tif (!pager)\n \t\treturn;\n \n \tsetenv(\"GIT_PAGER_IN_USE\", \"true\", 1);\n \n+\twidth = retrieve_terminal_width();\n+\tif (width)\n+\t\tmax_columns = width;\n+\n \t/* spawn the pager */\n \tpager_argv[0] = pager;\n \tpager_process.use_shell = 1;\n@@ -110,3 +130,18 @@ int pager_in_use(void)\n \tenv = getenv(\"GIT_PAGER_IN_USE\");\n \treturn env ? git_config_bool(\"GIT_PAGER_IN_USE\", env) : 0;\n }\n+\n+int term_columns()\n+{\n+\tchar *col_string = getenv(\"COLUMNS\");\n+\tint n_cols;\n+\n+\tif (col_string && (n_cols = atoi(col_string)) > 0)\n+\t\treturn n_cols;\n+\n+\tif (spawned_pager && max_columns)\n+\t\treturn max_columns;\n+\n+\tn_cols = retrieve_terminal_width();\n+\treturn n_cols ? n_cols : 80;\n+}\n-- \n1.7.8.36.g69ee2\n"},{"id":"183718","messageId":"1328276078-27955-3-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328276078-27955-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 02/13] column: add API to print items in columns","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-03T13:34:27Z","receivedAt":"2012-02-03T13:34:27Z","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\nprint_cell(...);\nprint_cell(...);\n...\nprint_columns(...);\n\nif column layout is disabled, print_cell() prints directly and\nprint_columns() becomes no-op. Otherwise print_cell() saves all items\nin a string list for print_columns() to process later.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Makefile |    3 ++-\n column.c |   54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n column.h |   17 +++++++++++++++++\n 3 files changed, 73 insertions(+), 1 deletions(-)\n create mode 100644 column.c\n\ndiff --git a/Makefile b/Makefile\nindex cbbc699..5f0531b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -637,6 +637,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@@ -2114,7 +2115,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-help.o pager.o: column.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..4ae98f3\n--- /dev/null\n+++ b/column.c\n@@ -0,0 +1,54 @@\n+#include \"cache.h\"\n+#include \"column.h\"\n+#include \"string-list.h\"\n+\n+#define MODE(mode) ((mode) & COL_MODE)\n+\n+struct string_list_item *add_cell_to_list(struct string_list *list,\n+\t\t\t\t\t  int mode,\n+\t\t\t\t\t  const char *string)\n+{\n+\tif (mode & COL_ENABLED)\n+\t\treturn string_list_append(list, string);\n+\treturn NULL;\n+}\n+\n+void print_cell(struct string_list *list, int mode, const char *string)\n+{\n+\tif (!add_cell_to_list(list, mode, string))\n+\t\tprintf(\"%s\\n\", string);\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+{\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, 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\nindex 55d8067..e0f8c26 100644\n--- a/column.h\n+++ b/column.h\n@@ -1,6 +1,23 @@\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 struct string_list_item *add_cell_to_list(struct string_list *list,\n+\t\t\t\t\t\t int mode,\n+\t\t\t\t\t\t const char *string);\n+/* add cell to list, or print it out if column mode is disabled */\n+extern void print_cell(struct string_list *list, int mode, const char *string);\n+extern void print_columns(const struct string_list *list,\n+\t\t\t  int mode, struct column_options *opts);\n \n #endif\n-- \n1.7.8.36.g69ee2\n"},{"id":"183719","messageId":"1328276078-27955-4-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328276078-27955-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 03/13] parseopt: make OPT_INTEGER support hexadecimal as well","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-03T13:34:28Z","receivedAt":"2012-02-03T13:34:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n parse-options.c |    5 ++++-\n 1 files changed, 4 insertions(+), 1 deletions(-)\n\ndiff --git a/parse-options.c b/parse-options.c\nindex f0098eb..7c9109d 100644\n--- a/parse-options.c\n+++ b/parse-options.c\n@@ -139,7 +139,10 @@ static int get_value(struct parse_opt_ctx_t *p,\n \t\t}\n \t\tif (get_arg(p, opt, flags, &arg))\n \t\t\treturn -1;\n-\t\t*(int *)opt->value = strtol(arg, (char **)&s, 10);\n+\t\tif (!prefixcmp(arg, \"0x\") || !prefixcmp(arg, \"0X\"))\n+\t\t\t*(int *)opt->value = strtol(arg + 2, (char **)&s, 16);\n+\t\telse\n+\t\t\t*(int *)opt->value = strtol(arg, (char **)&s, 10);\n \t\tif (*s)\n \t\t\treturn opterror(opt, \"expects a numerical value\", flags);\n \t\treturn 0;\n-- \n1.7.8.36.g69ee2\n"},{"id":"183721","messageId":"1328276078-27955-5-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328276078-27955-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 04/13] Add git-column and column mode parsing","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-03T13:34:29Z","receivedAt":"2012-02-03T13:34:29Z","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\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                     |  125 ++++++++++++++++++++++++++++++++++++++++++\n column.h                     |    6 ++\n command-list.txt             |    1 +\n git.c                        |    1 +\n parse-options.h              |    2 +\n t/t9002-column.sh            |   27 +++++++++\n 11 files changed, 255 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 3b7680e..039e5ef 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 5f0531b..92700ca 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -766,6 +766,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..c4a0431\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 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 4ae98f3..bd15bea 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@@ -52,3 +53,127 @@ void print_columns(const struct string_list *list, 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(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(int *mode, 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(int *mode, 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\tint *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, set, name_len;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(opts); i++) {\n+\t\tif (opts[i].type == OPTION) {\n+\t\t\tif (len > 2 && !strncmp(arg, \"no\", 2)) {\n+\t\t\t\targ += 2;\n+\t\t\t\tlen -= 2;\n+\t\t\t\tset = 0;\n+\t\t\t}\n+\t\t\telse\n+\t\t\t\tset = 1;\n+\t\t}\n+\n+\t\tname_len = strlen(opts[i].name);\n+\t\tif (len != name_len ||\n+\t\t    strncmp(arg, opts[i].name, name_len))\n+\t\t\tcontinue;\n+\n+\t\tswitch (opts[i].type) {\n+\t\tcase ENABLE: return set_enable_bit(mode, opts[i].value,\n+\t\t\t\t\t\t   stdout_is_tty);\n+\t\tcase MODE: return set_mode(mode, opts[i].value);\n+\t\tcase OPTION: return set_option(mode, opts[i].value, set);\n+\t\tdefault: die(\"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(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+\tint *mode = opt->value;\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 e0f8c26..59f26dc 100644\n--- a/column.h\n+++ b/column.h\n@@ -3,6 +3,7 @@\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 \n struct column_options {\n \tint width;\n@@ -19,5 +20,10 @@ extern struct string_list_item *add_cell_to_list(struct string_list *list,\n extern void print_cell(struct string_list *list, int mode, const char *string);\n extern void print_columns(const struct string_list *list,\n \t\t\t  int mode, struct column_options *opts);\n+extern int git_config_column(int *mode, const char *value, 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":"183720","messageId":"1328276078-27955-6-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328276078-27955-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 05/13] Stop starting pager recursively","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-03T13:34:30Z","receivedAt":"2012-02-03T13:34:30Z","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 772a5a6..e4353dc 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -89,7 +89,7 @@ void setup_pager(void)\n \tconst char *pager = git_pager(isatty(1));\n \tint width;\n \n-\tif (!pager)\n+\tif (!pager || pager_in_use())\n \t\treturn;\n \n \tsetenv(\"GIT_PAGER_IN_USE\", \"true\", 1);\n-- \n1.7.8.36.g69ee2\n"},{"id":"183723","messageId":"1328276078-27955-7-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328276078-27955-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 06/13] column: add columnar layout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-03T13:34:31Z","receivedAt":"2012-02-03T13:34:31Z","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 bd15bea..4681106 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 struct string_list_item *add_cell_to_list(struct string_list *list,\n \t\t\t\t\t  int mode,\n@@ -30,6 +88,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, int mode,\n \t\t   struct column_options *opts)\n {\n@@ -51,7 +168,16 @@ void print_columns(const struct string_list *list, 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@@ -113,6 +239,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, set, name_len;\n \ndiff --git a/column.h b/column.h\nindex 59f26dc..38976ea 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 \n struct column_options {\n \tint width;\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":"183722","messageId":"1328276078-27955-8-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328276078-27955-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 07/13] column: support columns with different widths","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-03T13:34:32Z","receivedAt":"2012-02-03T13:34:32Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\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 |   22 ++++++++++++++\n 3 files changed, 104 insertions(+), 0 deletions(-)\n\ndiff --git a/column.c b/column.c\nindex 4681106..0f658d9 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 struct string_list_item *add_cell_to_list(struct string_list *list,\n \t\t\t\t\t  int mode,\n \t\t\t\t\t  const char *string)\n@@ -97,7 +161,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@@ -134,6 +209,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@@ -144,6 +222,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@@ -242,6 +321,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, set, name_len;\n \ndiff --git a/column.h b/column.h\nindex 38976ea..1912cb0 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 \n struct column_options {\n \tint width;\ndiff --git a/t/t9002-column.sh b/t/t9002-column.sh\nindex cffb029..23d340e 100755\n--- a/t/t9002-column.sh\n+++ b/t/t9002-column.sh\n@@ -71,6 +71,17 @@ EOF\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 +121,15 @@ EOF\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":"183725","messageId":"1328276078-27955-9-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328276078-27955-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 08/13] column: add column.ui for default column output settings","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-03T13:34:33Z","receivedAt":"2012-02-03T13:34:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-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             |   23 +++++++++++++++++++++++\n column.c                     |   36 ++++++++++++++++++++++++++++++++++++\n column.h                     |    4 ++++\n 5 files changed, 94 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 c4a0431..c4e1fe4 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 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,17 @@ 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\tif (!colopts)\n+\t\t\tcolopts = git_colopts;\n+\t}\n+\n \tmemset(&copts, 0, sizeof(copts));\n \tcopts.width = term_columns();\n \tcopts.padding = 1;\n@@ -33,6 +51,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 0f658d9..671ee5d 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@@ -22,6 +23,8 @@ struct column_data {\n \tint *width;\t\t\t/* index to the longest row in column */\n };\n \n+int git_colopts;\n+\n /* return length of 's' in letters, ANSI escapes stripped */\n static int item_length(int mode, const char *s)\n {\n@@ -371,6 +374,39 @@ int git_config_column(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, int *colopts)\n+{\n+\tif (!strcmp(var, key)) {\n+\t\tint ret = git_config_column(colopts, value, -1);\n+\t\tif (ret)\n+\t\t\tdie(\"invalid %s mode %s\", key, value);\n+\t\treturn 0;\n+\t}\n+\treturn 1;\t\t/* go on */\n+}\n+\n+int git_column_config(const char *var, const char *value,\n+\t\t      const char *command, int *colopts)\n+{\n+\tint ret;\n+\n+\tret = column_config(var, value, \"column.ui\", &git_colopts);\n+\tif (ret <= 0)\n+\t\treturn ret;\n+\n+\tif (command) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tstrbuf_addf(&sb, \"column.%s\", command);\n+\t\tret = column_config(var, value, sb.buf, colopts);\n+\t\tstrbuf_release(&sb);\n+\t\tif (ret <= 0)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 1;\t\t/* go on */\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 1912cb0..afdafc4 100644\n--- a/column.h\n+++ b/column.h\n@@ -17,6 +17,8 @@ struct column_options {\n \tconst char *nl;\n };\n \n+extern int git_colopts;\n+\n extern int term_columns(void);\n extern struct string_list_item *add_cell_to_list(struct string_list *list,\n \t\t\t\t\t\t int mode,\n@@ -26,6 +28,8 @@ extern void print_cell(struct string_list *list, int mode, const char *string);\n extern void print_columns(const struct string_list *list,\n \t\t\t  int mode, struct column_options *opts);\n extern int git_config_column(int *mode, const char *value, int stdout_is_tty);\n+extern int git_column_config(const char *var, const char *value,\n+\t\t\t     const char *command, int *colopts);\n \n struct option;\n extern int parseopt_column_callback(const struct option *opt,\n-- \n1.7.8.36.g69ee2\n"},{"id":"183724","messageId":"1328276078-27955-10-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328276078-27955-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 09/13] help: reuse print_columns() for help -a","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-03T13:34:34Z","receivedAt":"2012-02-03T13:34:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n help.c |   47 +++++++++++++----------------------------------\n 1 files changed, 13 insertions(+), 34 deletions(-)\n\ndiff --git a/help.c b/help.c\nindex 672561b..d6d2e19 100644\n--- a/help.c\n+++ b/help.c\n@@ -4,6 +4,7 @@\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@@ -71,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@@ -207,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@@ -231,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":"183726","messageId":"1328276078-27955-11-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328276078-27955-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 10/13] branch: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-03T13:34:35Z","receivedAt":"2012-02-03T13:34:35Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-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             |   26 +++++++++++++++++++++++---\n 4 files changed, 37 insertions(+), 4 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 92700ca..061f6e5 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2116,7 +2116,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 7095718..d78690a 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 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,9 @@ 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+\tint status = git_column_config(var, value, \"branch\", &colopts);\n+\tif (status <= 0)\n+\t\treturn status;\n \tif (!strcmp(var, \"color.branch\")) {\n \t\tbranch_use_color = git_config_colorbool(var, value);\n \t\treturn 0;\n@@ -474,7 +482,7 @@ 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+\tprint_cell(&output, colopts, out.buf);\n \tstrbuf_release(&name);\n \tstrbuf_release(&out);\n }\n@@ -727,6 +735,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 +758,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t}\n \thashcpy(merge_filter_ref, head_sha1);\n \n+\tif (!colopts)\n+\t\tcolopts = git_colopts | COL_ANSI;\n+\n \targc = parse_options(argc, argv, prefix, options, builtin_branch_usage,\n \t\t\t     0);\n \n@@ -763,9 +775,17 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\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+\telse if (list) {\n+\t\tint ret;\n+\t\tif (verbose)\n+\t\t\tcolopts = 0;\n+\n+\t\tret = print_ref_list(kinds, detached, verbose, abbrev,\n \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\tif (detached)\n-- \n1.7.8.36.g69ee2\n"},{"id":"183727","messageId":"1328276078-27955-12-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328276078-27955-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 11/13] status: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-03T13:34:36Z","receivedAt":"2012-02-03T13:34:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-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             |   13 +++++++++++--\n wt-status.c                  |   38 ++++++++++++++++++++++++++++++++------\n wt-status.h                  |    2 +-\n 6 files changed, 56 insertions(+), 10 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 061f6e5..b2644bc 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2116,7 +2116,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 eba1377..8ce6a18 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 int colopts;\n \n /*\n  * The default commit message cleanup mode will remove the lines\n@@ -519,7 +521,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int\n \t\twt_porcelain_print(s, null_termination);\n \t\tbreak;\n \tcase STATUS_FORMAT_LONG:\n-\t\twt_status_print(s);\n+\t\twt_status_print(s, 0);\n \t\tbreak;\n \t}\n \n@@ -1138,7 +1140,11 @@ static int parse_status_slot(const char *var, int offset)\n static int git_status_config(const char *k, const char *v, void *cb)\n {\n \tstruct wt_status *s = cb;\n+\tint status;\n \n+\tstatus = git_column_config(k, v, \"status\", &colopts);\n+\tif (status <= 0)\n+\t\treturn status;\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@@ -1204,6 +1210,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@@ -1213,6 +1220,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \twt_status_prepare(&s);\n \tgitmodules_config();\n \tgit_config(git_status_config, &s);\n+\tif (!colopts)\n+\t\tcolopts = git_colopts | COL_ANSI;\n \tdetermine_whence(&s);\n \targc = parse_options(argc, argv, prefix,\n \t\t\t     builtin_status_options,\n@@ -1251,7 +1260,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \tcase STATUS_FORMAT_LONG:\n \t\ts.verbose = verbose;\n \t\ts.ignore_submodule_arg = ignore_submodule_arg;\n-\t\twt_status_print(&s);\n+\t\twt_status_print(&s, colopts);\n \t\tbreak;\n \t}\n \treturn 0;\ndiff --git a/wt-status.c b/wt-status.c\nindex 9ffc535..86291e9 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@@ -637,10 +638,13 @@ static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitt\n static void wt_status_print_other(struct wt_status *s,\n \t\t\t\t  struct string_list *l,\n \t\t\t\t  const char *what,\n-\t\t\t\t  const char *how)\n+\t\t\t\t  const char *how,\n+\t\t\t\t  int colopts)\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 +653,32 @@ 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 (colopts & COL_ENABLED) {\n+\t\t\tadd_cell_to_list(&output, colopts, 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 ((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+\tcopts.nl = GIT_COLOR_RESET \"\\n\";\n+\tprint_columns(&output, colopts, &copts);\n+\tstring_list_clear(&output, 0);\n \tstrbuf_release(&buf);\n }\n \n@@ -704,7 +728,7 @@ static void wt_status_print_tracking(struct wt_status *s)\n \tcolor_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), \"#\");\n }\n \n-void wt_status_print(struct wt_status *s)\n+void wt_status_print(struct wt_status *s, int colopts)\n {\n \tconst char *branch_color = color(WT_STATUS_ONBRANCH, s);\n \tconst char *branch_status_color = color(WT_STATUS_HEADER, s);\n@@ -742,9 +766,11 @@ void wt_status_print(struct wt_status *s)\n \t\twt_status_print_submodule_summary(s, 1);  /* unstaged */\n \t}\n \tif (s->show_untracked_files) {\n-\t\twt_status_print_other(s, &s->untracked, _(\"Untracked\"), \"add\");\n+\t\twt_status_print_other(s, &s->untracked,\n+\t\t\t\t      _(\"Untracked\"), \"add\", colopts);\n \t\tif (s->show_ignored_files)\n-\t\t\twt_status_print_other(s, &s->ignored, _(\"Ignored\"), \"add -f\");\n+\t\t\twt_status_print_other(s, &s->ignored,\n+\t\t\t\t\t      _(\"Ignored\"), \"add -f\", colopts);\n \t} else if (s->commitable)\n \t\tstatus_printf_ln(s, GIT_COLOR_NORMAL, _(\"Untracked files not listed%s\"),\n \t\t\tadvice_status_hints\ndiff --git a/wt-status.h b/wt-status.h\nindex 682b4c8..4ab2799 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -69,7 +69,7 @@ struct wt_status {\n };\n \n void wt_status_prepare(struct wt_status *s);\n-void wt_status_print(struct wt_status *s);\n+void wt_status_print(struct wt_status *s, int colopts);\n void wt_status_collect(struct wt_status *s);\n \n void wt_shortstatus_print(struct wt_status *s, int null_termination, int show_branch);\n-- \n1.7.8.36.g69ee2\n"},{"id":"183729","messageId":"1328276078-27955-13-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328276078-27955-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 12/13] column: support piping stdout to external git-column process","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-03T13:34:37Z","receivedAt":"2012-02-03T13:34:37Z","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 671ee5d..ff8e4ac 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@@ -422,3 +423,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=0x%x\", 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()\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 afdafc4..58d73b8 100644\n--- a/column.h\n+++ b/column.h\n@@ -35,4 +35,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":"183728","messageId":"1328276078-27955-14-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328276078-27955-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 13/13] tag: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-03T13:34:38Z","receivedAt":"2012-02-03T13:34:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt  |    4 ++++\n Documentation/git-tag.txt |   11 ++++++++++-\n Makefile                  |    2 +-\n builtin/tag.c             |   25 ++++++++++++++++++++++---\n 4 files changed, 37 insertions(+), 5 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 53ff5f6..5ead91e 100644\n--- a/Documentation/git-tag.txt\n+++ b/Documentation/git-tag.txt\n@@ -12,7 +12,8 @@ SYNOPSIS\n 'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n \t<tagname> [<commit> | <object>]\n 'git tag' -d <tagname>...\n-'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>...]\n+'git tag' [-n[<num>]] -l [--contains <commit>]\n+\t[--column[=<options>] | --no-column] [<pattern>...]\n 'git tag' -v <tagname>...\n \n DESCRIPTION\n@@ -83,6 +84,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 b2644bc..eb7b6fc 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2116,7 +2116,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 31f02e8..cecbe5c 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -15,6 +15,7 @@\n #include \"diff.h\"\n #include \"revision.h\"\n #include \"gpg-interface.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@@ -30,6 +31,8 @@ struct tag_filter {\n \tstruct commit_list *with_commit;\n };\n \n+static int colopts;\n+\n static int match_pattern(const char **patterns, const char *ref)\n {\n \t/* no pattern means match everything */\n@@ -230,6 +233,9 @@ 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+\tstatus = git_column_config(var, value, \"tag\", &colopts);\n+\tif (status <= 0)\n+\t\treturn status;\n \treturn git_default_config(var, value, cb);\n }\n \n@@ -409,6 +415,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@@ -421,6 +428,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t};\n \n \tgit_config(git_tag_config, NULL);\n+\tif (!colopts)\n+\t\tcolopts = git_colopts;\n \n \tmemset(&opt, 0, sizeof(opt));\n \n@@ -441,9 +450,19 @@ 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) {\n+\t\tint ret;\n+\t\tif (lines == -1) {\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 (lines == -1)\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)\n-- \n1.7.8.36.g69ee2\n"},{"id":"183777","messageId":"7vehubmsrf.fsf@alter.siamese.dyndns.org","threadId":"29535","inReplyTo":"1328276078-27955-3-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v4 02/13] column: add API to print items in columns","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-03T22:55:32Z","receivedAt":"2012-02-03T22:55: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> +struct string_list_item *add_cell_to_list(struct string_list *list,\n> +\t\t\t\t\t  int mode,\n> +\t\t\t\t\t  const char *string)\n> +{\n> +\tif (mode & COL_ENABLED)\n> +\t\treturn string_list_append(list, string);\n> +\treturn NULL;\n> +}\n> +\n> +void print_cell(struct string_list *list, int mode, const char *string)\n> +{\n> +\tif (!add_cell_to_list(list, mode, string))\n> +\t\tprintf(\"%s\\n\", string);\n> +}\n\nI find these two functions showing an extremely bad taste in the code\ndesign. \"If we for some reason fail to add the string to the list, we\nfallback to print it straight away\" is the natural reading of this code,\nbut that is absolutly not the impression the author of this piece of\ncrap^Wcode wanted to give to its readers.\n\nAm I being too ascestically perfectionist?\n"},{"id":"183779","messageId":"7vaa4zmsku.fsf@alter.siamese.dyndns.org","threadId":"29535","inReplyTo":"1328276078-27955-4-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v4 03/13] parseopt: make OPT_INTEGER support hexadecimal as well","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-03T22:59:29Z","receivedAt":"2012-02-03T22:59:29Z","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> -\t\t*(int *)opt->value = strtol(arg, (char **)&s, 10);\n> +\t\tif (!prefixcmp(arg, \"0x\") || !prefixcmp(arg, \"0X\"))\n> +\t\t\t*(int *)opt->value = strtol(arg + 2, (char **)&s, 16);\n> +\t\telse\n> +\t\t\t*(int *)opt->value = strtol(arg, (char **)&s, 10);\n\nCan't you just do \"strtol(arg, (char **)&s, 0)\" instead?\n"},{"id":"183780","messageId":"7v62fnmsc5.fsf@alter.siamese.dyndns.org","threadId":"29535","inReplyTo":"1328276078-27955-9-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v4 08/13] column: add column.ui for default column output settings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-03T23:04:42Z","receivedAt":"2012-02-03T23:04:42Z","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/column.h b/column.h\n> index 1912cb0..afdafc4 100644\n> --- a/column.h\n> +++ b/column.h\n> @@ -17,6 +17,8 @@ struct column_options {\n>  \tconst char *nl;\n>  };\n>  \n> +extern int git_colopts;\n\nFor a global state variable, I'd prefer to see it spelled out, e.g.\ngit_column_opts or even git_column_options.  It's not like you would be\nreferring to this variable from everywhere---you would use it only from\nfallback codepaths after parse_options() returns, or something, no?\n"},{"id":"183781","messageId":"7v1uqbmsb9.fsf@alter.siamese.dyndns.org","threadId":"29535","inReplyTo":"1328276078-27955-10-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v4 09/13] help: reuse print_columns() for help -a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-03T23:05:14Z","receivedAt":"2012-02-03T23:05:14Z","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>  help.c |   47 +++++++++++++----------------------------------\n>  1 files changed, 13 insertions(+), 34 deletions(-)\n\nNice.\n"},{"id":"183784","messageId":"7vwr83ldfz.fsf@alter.siamese.dyndns.org","threadId":"29535","inReplyTo":"1328276078-27955-11-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v4 10/13] branch: add --column","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-03T23:11:44Z","receivedAt":"2012-02-03T23:11:44Z","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> @@ -474,7 +482,7 @@ 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> +\tprint_cell(&output, colopts, out.buf);\n>  \tstrbuf_release(&name);\n>  \tstrbuf_release(&out);\n>  }\n\nHmm, disabling column output when verbose is in effect without telling the\nuser what we are doing needs to be fixed, but because of that, at least\nthis codepath won't try to stuff potentially long strings in a columnar\nform.\n\nI am not sure about the utility of columnar output for \"git branch\" in the\nshort form.  You no longer can just scan the leftmost column to scan for\n'*' to see the current branch.\n"},{"id":"183785","messageId":"7vsjirld82.fsf@alter.siamese.dyndns.org","threadId":"29535","inReplyTo":"1328276078-27955-3-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v4 02/13] column: add API to print items in columns","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-03T23:16:29Z","receivedAt":"2012-02-03T23:16:29Z","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 MODE(mode) ((mode) & COL_MODE)\n> + ...\n> +void print_columns(const struct string_list *list, 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\nUnless there is a compelling reason not to, make a flag word used as\ncollection of bitfields an unsigned, i.e. not \"int mode\".\n"},{"id":"183786","messageId":"7vobtfld2d.fsf@alter.siamese.dyndns.org","threadId":"29535","inReplyTo":"1328276078-27955-12-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v4 11/13] status: add --column","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-03T23:19:54Z","receivedAt":"2012-02-03T23:19: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> @@ -1251,7 +1260,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n>  \tcase STATUS_FORMAT_LONG:\n>  \t\ts.verbose = verbose;\n>  \t\ts.ignore_submodule_arg = ignore_submodule_arg;\n> -\t\twt_status_print(&s);\n> +\t\twt_status_print(&s, colopts);\n>  \t\tbreak;\n>  \t}\n\nDo you really need to pass colopts around as a separate parameter all the\nway through the callchain?\n\nWhy isn't it a new member of wt_status that sits next to existing\nuse_color, verbose, etc. that define _how_ the status is shown?\n"},{"id":"183788","messageId":"7vhaz7lcku.fsf@alter.siamese.dyndns.org","threadId":"29535","inReplyTo":"1328276078-27955-14-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v4 13/13] tag: add --column","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-03T23:30:25Z","receivedAt":"2012-02-03T23:30:25Z","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> @@ -421,6 +428,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n>  \t};\n>  \n>  \tgit_config(git_tag_config, NULL);\n> +\tif (!colopts)\n> +\t\tcolopts = git_colopts;\n>  \n>  \tmemset(&opt, 0, sizeof(opt));\n>  \n> @@ -441,9 +450,19 @@ 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) {\n> +\t\tint ret;\n> +\t\tif (lines == -1) {\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 (lines == -1)\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)\n\nThe patch is surprisingly small, which is a good sign.  The same comment\nto the silent suppression \"branch -v --column\" applies here to \"tag -n\".\n"},{"id":"183796","messageId":"CACsJy8Ba2qxyT4XqeRmUv63Z3rT1-FmBkZ3tB6YMh6qrXjLP1Q@mail.gmail.com","threadId":"29535","inReplyTo":"7vaa4zmsku.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v4 03/13] parseopt: make OPT_INTEGER support hexadecimal as well","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-04T04:55:30Z","receivedAt":"2012-02-04T04:55:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2012/2/4 Junio C Hamano <gitster@pobox.com>:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> -             *(int *)opt->value = strtol(arg, (char **)&s, 10);\n>> +             if (!prefixcmp(arg, \"0x\") || !prefixcmp(arg, \"0X\"))\n>> +                     *(int *)opt->value = strtol(arg + 2, (char **)&s, 16);\n>> +             else\n>> +                     *(int *)opt->value = strtol(arg, (char **)&s, 10);\n>\n> Can't you just do \"strtol(arg, (char **)&s, 0)\" instead?\n\nI could but that means \"01234\" is now in base 8 and that's currently\naccepted as base 10. 0x1234 does not have this problem because current\ngit rejects it.\n-- \nDuy\n"},{"id":"183797","messageId":"CACsJy8AdSh_XH4o29yfEG8AxUK-=+zdeLanZA7C81qExiJQceQ@mail.gmail.com","threadId":"29535","inReplyTo":"7vwr83ldfz.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v4 10/13] branch: add --column","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-04T05:01:18Z","receivedAt":"2012-02-04T05:01:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2012/2/4 Junio C Hamano <gitster@pobox.com>:\n> I am not sure about the utility of columnar output for \"git branch\" in the\n> short form.  You no longer can just scan the leftmost column to scan for\n> '*' to see the current branch.\n\nI rely on color for that. Without color you may need to scan more\ncolumns for '*'. Though I would really like to see \"git branch\n--current\" added to show just current branch if I'm on colorless\nterminal.\n-- \nDuy\n"},{"id":"183802","messageId":"7vobtfjh9d.fsf@alter.siamese.dyndns.org","threadId":"29535","inReplyTo":"CACsJy8Ba2qxyT4XqeRmUv63Z3rT1-FmBkZ3tB6YMh6qrXjLP1Q@mail.gmail.com","subject":"Re: [PATCH v4 03/13] parseopt: make OPT_INTEGER support hexadecimal as well","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-04T05:32:14Z","receivedAt":"2012-02-04T05:32:14Z","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/2/4 Junio C Hamano <gitster@pobox.com>:\n>> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>>\n>>> -             *(int *)opt->value = strtol(arg, (char **)&s, 10);\n>>> +             if (!prefixcmp(arg, \"0x\") || !prefixcmp(arg, \"0X\"))\n>>> +                     *(int *)opt->value = strtol(arg + 2, (char **)&s, 16);\n>>> +             else\n>>> +                     *(int *)opt->value = strtol(arg, (char **)&s, 10);\n>>\n>> Can't you just do \"strtol(arg, (char **)&s, 0)\" instead?\n>\n> I could but that means \"01234\" is now in base 8 and that's currently\n> accepted as base 10.\n\nYes, but I wonder if that is a problem in practice. Who in the right mind\nwould give 00001000 to tell git that they want one thousand?\n"},{"id":"183804","messageId":"CACsJy8Db44OVC7+BEbp7hdbO8+3ZysCKPkwoTJyhLFvcLaF90g@mail.gmail.com","threadId":"29535","inReplyTo":"7vobtfjh9d.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v4 03/13] parseopt: make OPT_INTEGER support hexadecimal as well","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-04T06:15:56Z","receivedAt":"2012-02-04T06:15:56Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sat, Feb 4, 2012 at 12:32 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>\n>> 2012/2/4 Junio C Hamano <gitster@pobox.com>:\n>>> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>>>\n>>>> -             *(int *)opt->value = strtol(arg, (char **)&s, 10);\n>>>> +             if (!prefixcmp(arg, \"0x\") || !prefixcmp(arg, \"0X\"))\n>>>> +                     *(int *)opt->value = strtol(arg + 2, (char **)&s, 16);\n>>>> +             else\n>>>> +                     *(int *)opt->value = strtol(arg, (char **)&s, 10);\n>>>\n>>> Can't you just do \"strtol(arg, (char **)&s, 0)\" instead?\n>>\n>> I could but that means \"01234\" is now in base 8 and that's currently\n>> accepted as base 10.\n>\n> Yes, but I wonder if that is a problem in practice. Who in the right mind\n> would give 00001000 to tell git that they want one thousand?\n\nThat could come from a script, extracting info from a source (maybe a\nlog file) and not stripping leading zeros. If we go this route, I\nthink we should have one release that rejects such numbers first. If\nnobody complains, then we proceed and allow octal in the next release.\n-- \nDuy\n"},{"id":"183832","messageId":"1328371156-4009-1-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328276078-27955-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 00/12] Column display","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-04T15:59:04Z","receivedAt":"2012-02-04T15:59:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"- Turn column bitflag type to unsigned int\n- Get rid of global var git_colopts\n- Get rid of ill-designed print_cell()\n- Refuse to run \"git branch -v --column\" and \"git tag -l -n --column\"\n- Fix \"git status --column --no-color\" always prints ANSI reset sequence\n- Drop OPT_INTEGER hexadecimal support (it's not essential, the column\n  helper functions can pass arguments are decimal)\n- Add some more tests for column display\n\nNguyễn Thái Ngọc Duy (12):\n  Save terminal width before setting up pager\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    |   11 +-\n Makefile                     |    3 +\n builtin.h                    |    1 +\n builtin/branch.c             |   38 +++-\n builtin/column.c             |   62 ++++++\n builtin/commit.c             |    8 +\n builtin/tag.c                |   30 +++-\n column.c                     |  480 ++++++++++++++++++++++++++++++++++++++++++\n column.h                     |   36 +++\n command-list.txt             |    1 +\n git.c                        |    1 +\n help.c                       |   70 ++-----\n pager.c                      |   37 +++-\n parse-options.h              |    2 +\n t/t3200-branch.sh            |   50 +++++\n t/t7004-tag.sh               |   44 ++++\n t/t7508-status.sh            |   24 ++\n t/t9002-column.sh            |  135 ++++++++++++\n wt-status.c                  |   28 +++-\n wt-status.h                  |    1 +\n 25 files changed, 1104 insertions(+), 66 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":"183830","messageId":"1328371156-4009-2-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328371156-4009-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 01/12] Save terminal width before setting up pager","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-04T15:59:05Z","receivedAt":"2012-02-04T15:59:05Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"term_columns() checks for terminal width via ioctl(2). After\nredirecting, stdin is no longer terminal to get terminal width.\n\nCheck terminal width and save it before redirect stdin in\nsetup_pager() and let term_columns() reuse the value.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Makefile |    1 +\n column.h |    6 ++++++\n help.c   |   23 +----------------------\n pager.c  |   35 +++++++++++++++++++++++++++++++++++\n 4 files changed, 43 insertions(+), 22 deletions(-)\n create mode 100644 column.h\n\ndiff --git a/Makefile b/Makefile\nindex c457c34..cbbc699 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2114,6 +2114,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+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.h b/column.h\nnew file mode 100644\nindex 0000000..55d8067\n--- /dev/null\n+++ b/column.h\n@@ -0,0 +1,6 @@\n+#ifndef COLUMN_H\n+#define COLUMN_H\n+\n+extern int term_columns(void);\n+\n+#endif\ndiff --git a/help.c b/help.c\nindex cbbe966..672561b 100644\n--- a/help.c\n+++ b/help.c\n@@ -4,28 +4,7 @@\n #include \"levenshtein.h\"\n #include \"help.h\"\n #include \"common-cmds.h\"\n-\n-/* most GUI terminals set COLUMNS (although some don't export it) */\n-static int term_columns(void)\n-{\n-\tchar *col_string = getenv(\"COLUMNS\");\n-\tint n_cols;\n-\n-\tif (col_string && (n_cols = atoi(col_string)) > 0)\n-\t\treturn n_cols;\n-\n-#ifdef TIOCGWINSZ\n-\t{\n-\t\tstruct winsize ws;\n-\t\tif (!ioctl(1, TIOCGWINSZ, &ws)) {\n-\t\t\tif (ws.ws_col)\n-\t\t\t\treturn ws.ws_col;\n-\t\t}\n-\t}\n-#endif\n-\n-\treturn 80;\n-}\n+#include \"column.h\"\n \n void add_cmdname(struct cmdnames *cmds, const char *name, int len)\n {\ndiff --git a/pager.c b/pager.c\nindex 975955b..772a5a6 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -6,6 +6,21 @@\n #define DEFAULT_PAGER \"less\"\n #endif\n \n+static int spawned_pager;\n+static int max_columns;\n+\n+static int retrieve_terminal_width(void)\n+{\n+#ifdef TIOCGWINSZ\n+\tstruct winsize ws;\n+\tif (ioctl(1, TIOCGWINSZ, &ws))  /* e.g., ENOSYS */\n+\t\treturn 0;\n+\treturn ws.ws_col;\n+#else\n+\treturn 0;\n+#endif\n+}\n+\n /*\n  * This is split up from the rest of git so that we can do\n  * something different on Windows.\n@@ -72,12 +87,17 @@ const char *git_pager(int stdout_is_tty)\n void setup_pager(void)\n {\n \tconst char *pager = git_pager(isatty(1));\n+\tint width;\n \n \tif (!pager)\n \t\treturn;\n \n \tsetenv(\"GIT_PAGER_IN_USE\", \"true\", 1);\n \n+\twidth = retrieve_terminal_width();\n+\tif (width)\n+\t\tmax_columns = width;\n+\n \t/* spawn the pager */\n \tpager_argv[0] = pager;\n \tpager_process.use_shell = 1;\n@@ -110,3 +130,18 @@ int pager_in_use(void)\n \tenv = getenv(\"GIT_PAGER_IN_USE\");\n \treturn env ? git_config_bool(\"GIT_PAGER_IN_USE\", env) : 0;\n }\n+\n+int term_columns()\n+{\n+\tchar *col_string = getenv(\"COLUMNS\");\n+\tint n_cols;\n+\n+\tif (col_string && (n_cols = atoi(col_string)) > 0)\n+\t\treturn n_cols;\n+\n+\tif (spawned_pager && max_columns)\n+\t\treturn max_columns;\n+\n+\tn_cols = retrieve_terminal_width();\n+\treturn n_cols ? n_cols : 80;\n+}\n-- \n1.7.8.36.g69ee2\n"},{"id":"183831","messageId":"1328371156-4009-3-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328371156-4009-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 02/12] column: add API to print items in columns","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-04T15:59:06Z","receivedAt":"2012-02-04T15:59:06Z","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 |    3 ++-\n column.c |   39 +++++++++++++++++++++++++++++++++++++++\n column.h |   13 +++++++++++++\n 3 files changed, 54 insertions(+), 1 deletions(-)\n create mode 100644 column.c\n\ndiff --git a/Makefile b/Makefile\nindex cbbc699..5f0531b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -637,6 +637,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@@ -2114,7 +2115,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-help.o pager.o: column.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\nindex 55d8067..8e4fdaa 100644\n--- a/column.h\n+++ b/column.h\n@@ -1,6 +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":"183834","messageId":"1328371156-4009-4-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328371156-4009-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 03/12] Add git-column and column mode parsing","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-04T15:59:07Z","receivedAt":"2012-02-04T15:59:07Z","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\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                     |  125 ++++++++++++++++++++++++++++++++++++++++++\n column.h                     |    7 ++\n command-list.txt             |    1 +\n git.c                        |    1 +\n parse-options.h              |    2 +\n t/t9002-column.sh            |   27 +++++++++\n 11 files changed, 256 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 3b7680e..039e5ef 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 5f0531b..92700ca 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -766,6 +766,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..c4a0431\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 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..d20cf8f 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,127 @@ 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, set, name_len;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(opts); i++) {\n+\t\tif (opts[i].type == OPTION) {\n+\t\t\tif (len > 2 && !strncmp(arg, \"no\", 2)) {\n+\t\t\t\targ += 2;\n+\t\t\t\tlen -= 2;\n+\t\t\t\tset = 0;\n+\t\t\t}\n+\t\t\telse\n+\t\t\t\tset = 1;\n+\t\t}\n+\n+\t\tname_len = strlen(opts[i].name);\n+\t\tif (len != name_len ||\n+\t\t    strncmp(arg, opts[i].name, name_len))\n+\t\t\tcontinue;\n+\n+\t\tswitch (opts[i].type) {\n+\t\tcase ENABLE: return set_enable_bit(mode, opts[i].value,\n+\t\t\t\t\t\t   stdout_is_tty);\n+\t\tcase MODE: return set_mode(mode, opts[i].value);\n+\t\tcase OPTION: return set_option(mode, opts[i].value, set);\n+\t\tdefault: die(\"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+\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..f48fa81 100644\n--- a/column.h\n+++ b/column.h\n@@ -3,6 +3,7 @@\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 \n struct column_options {\n \tint width;\n@@ -15,5 +16,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":"183833","messageId":"1328371156-4009-5-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328371156-4009-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 04/12] Stop starting pager recursively","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-04T15:59:08Z","receivedAt":"2012-02-04T15:59:08Z","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 772a5a6..e4353dc 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -89,7 +89,7 @@ void setup_pager(void)\n \tconst char *pager = git_pager(isatty(1));\n \tint width;\n \n-\tif (!pager)\n+\tif (!pager || pager_in_use())\n \t\treturn;\n \n \tsetenv(\"GIT_PAGER_IN_USE\", \"true\", 1);\n-- \n1.7.8.36.g69ee2\n"},{"id":"183835","messageId":"1328371156-4009-6-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328371156-4009-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 05/12] column: add columnar layout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-04T15:59:09Z","receivedAt":"2012-02-04T15:59:09Z","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 d20cf8f..be4e07c 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, set, name_len;\n \ndiff --git a/column.h b/column.h\nindex f48fa81..52181d4 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 \n struct column_options {\n \tint width;\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":"183836","messageId":"1328371156-4009-7-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328371156-4009-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 06/12] column: support columns with different widths","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-04T15:59:10Z","receivedAt":"2012-02-04T15:59:10Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\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 |   22 ++++++++++++++\n 3 files changed, 104 insertions(+), 0 deletions(-)\n\ndiff --git a/column.c b/column.c\nindex be4e07c..fcff2fd 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, set, name_len;\n \ndiff --git a/column.h b/column.h\nindex 52181d4..2255851 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 \n struct column_options {\n \tint width;\ndiff --git a/t/t9002-column.sh b/t/t9002-column.sh\nindex cffb029..23d340e 100755\n--- a/t/t9002-column.sh\n+++ b/t/t9002-column.sh\n@@ -71,6 +71,17 @@ EOF\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 +121,15 @@ EOF\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":"183837","messageId":"1328371156-4009-8-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328371156-4009-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 07/12] column: add column.ui for default column output settings","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-04T15:59:11Z","receivedAt":"2012-02-04T15:59:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-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             |   23 ++++++++++++++++++++++-\n column.c                     |   38 ++++++++++++++++++++++++++++++++++++++\n column.h                     |    2 ++\n 5 files changed, 93 insertions(+), 2 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 c4a0431..351375f 100644\n--- a/builtin/column.c\n+++ b/builtin/column.c\n@@ -9,14 +9,21 @@ static const char * const builtin_column_usage[] = {\n \t\"git column [options]\",\n \tNULL\n };\n-static int colopts;\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 fcff2fd..c1233a7 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@@ -22,6 +23,8 @@ struct column_data {\n \tint *width;\t\t\t/* index to the longest row in column */\n };\n \n+unsigned int git_column_mode;\n+\n /* return length of 's' in letters, ANSI escapes stripped */\n static int item_length(int mode, const char *s)\n {\n@@ -356,6 +359,41 @@ 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 (!strcmp(var, key)) {\n+\t\tint ret = git_config_column(colopts, value, -1);\n+\t\tif (ret)\n+\t\t\tdie(\"invalid %s mode %s\", key, value);\n+\t\treturn 0;\n+\t}\n+\treturn 1;\t\t/* go on */\n+}\n+\n+int git_column_config(const char *var, const char *value,\n+\t\t      const char *command, unsigned int *colopts)\n+{\n+\tint ret;\n+\n+\tgit_column_mode &= ~COL_ENABLED_SET;\n+\tret = column_config(var, value, \"column.ui\", colopts);\n+\tif (ret <= 0)\n+\t\treturn ret;\n+\n+\tif (command) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tstrbuf_addf(&sb, \"column.%s\", command);\n+\t\tgit_column_mode &= ~COL_ENABLED_SET;\n+\t\tret = column_config(var, value, sb.buf, colopts);\n+\t\tstrbuf_release(&sb);\n+\t\tif (ret <= 0)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 1;\t\t/* go on */\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 2255851..3d2ed52 100644\n--- a/column.h\n+++ b/column.h\n@@ -23,6 +23,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":"183838","messageId":"1328371156-4009-9-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328371156-4009-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 08/12] help: reuse print_columns() for help -a","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-04T15:59:12Z","receivedAt":"2012-02-04T15:59:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n help.c |   47 +++++++++++++----------------------------------\n 1 files changed, 13 insertions(+), 34 deletions(-)\n\ndiff --git a/help.c b/help.c\nindex 672561b..d6d2e19 100644\n--- a/help.c\n+++ b/help.c\n@@ -4,6 +4,7 @@\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@@ -71,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@@ -207,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@@ -231,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":"183839","messageId":"1328371156-4009-10-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328371156-4009-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 09/12] branch: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-04T15:59:13Z","receivedAt":"2012-02-04T15:59:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-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             |   38 +++++++++++++++++++++++++++++--\n t/t3200-branch.sh            |   50 ++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 99 insertions(+), 4 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 92700ca..061f6e5 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2116,7 +2116,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 7095718..5014b9d 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,9 @@ 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+\tint status = git_column_config(var, value, \"branch\", &colopts);\n+\tif (status <= 0)\n+\t\treturn status;\n \tif (!strcmp(var, \"color.branch\")) {\n \t\tbranch_use_color = git_config_colorbool(var, value);\n \t\treturn 0;\n@@ -474,7 +482,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}\n+\telse\n+\t\tprintf(\"%s\\n\", out.buf);\n \tstrbuf_release(&name);\n \tstrbuf_release(&out);\n }\n@@ -727,6 +740,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 +763,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 = (colopts & ~COL_ENABLED_SET) | COL_ANSI;\n \targc = parse_options(argc, argv, prefix, options, builtin_branch_usage,\n \t\t\t     0);\n \n@@ -761,11 +777,27 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \tif (abbrev == -1)\n \t\tabbrev = DEFAULT_ABBREV;\n \n+\tif (verbose) {\n+\t\t/* only die when --column is given explicitly */\n+\t\tif ((colopts & (COL_ENABLED_SET | COL_ENABLED)) == (COL_ENABLED_SET | COL_ENABLED))\n+\t\t\tdie(_(\"--column and --verbose are incompatible\"));\n+\t\telse\n+\t\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+\telse if (list) {\n+\t\tint ret;\n+\t\tif (verbose)\n+\t\t\tcolopts = 0;\n+\n+\t\tret = print_ref_list(kinds, detached, verbose, abbrev,\n \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\tif (detached)\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex ea82424..9728ee6 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -163,6 +163,56 @@ 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 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 >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":"183840","messageId":"1328371156-4009-11-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328371156-4009-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 10/12] status: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-04T15:59:14Z","receivedAt":"2012-02-04T15:59:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-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             |    8 ++++++++\n t/t7508-status.sh            |   24 ++++++++++++++++++++++++\n wt-status.c                  |   28 ++++++++++++++++++++++++++--\n wt-status.h                  |    1 +\n 7 files changed, 71 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 061f6e5..b2644bc 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2116,7 +2116,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 eba1377..b73211c 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@@ -1138,7 +1140,11 @@ static int parse_status_slot(const char *var, int offset)\n static int git_status_config(const char *k, const char *v, void *cb)\n {\n \tstruct wt_status *s = cb;\n+\tint status;\n \n+\tstatus = git_column_config(k, v, \"status\", &colopts);\n+\tif (status <= 0)\n+\t\treturn status;\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@@ -1204,6 +1210,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@@ -1217,6 +1224,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..63ce7c9 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":"183841","messageId":"1328371156-4009-12-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328371156-4009-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 11/12] column: support piping stdout to external git-column process","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-04T15:59:15Z","receivedAt":"2012-02-04T15:59:15Z","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 c1233a7..4098f03 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@@ -409,3 +410,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()\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 3d2ed52..b9dec64 100644\n--- a/column.h\n+++ b/column.h\n@@ -30,4 +30,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":"183842","messageId":"1328371156-4009-13-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328371156-4009-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 12/12] tag: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-04T15:59:16Z","receivedAt":"2012-02-04T15:59:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt  |    4 ++++\n Documentation/git-tag.txt |   11 ++++++++++-\n Makefile                  |    2 +-\n builtin/tag.c             |   30 +++++++++++++++++++++++++++---\n t/t7004-tag.sh            |   44 ++++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 86 insertions(+), 5 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 53ff5f6..5ead91e 100644\n--- a/Documentation/git-tag.txt\n+++ b/Documentation/git-tag.txt\n@@ -12,7 +12,8 @@ SYNOPSIS\n 'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n \t<tagname> [<commit> | <object>]\n 'git tag' -d <tagname>...\n-'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>...]\n+'git tag' [-n[<num>]] -l [--contains <commit>]\n+\t[--column[=<options>] | --no-column] [<pattern>...]\n 'git tag' -v <tagname>...\n \n DESCRIPTION\n@@ -83,6 +84,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 b2644bc..eb7b6fc 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2116,7 +2116,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 31f02e8..d01f918 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -15,6 +15,7 @@\n #include \"diff.h\"\n #include \"revision.h\"\n #include \"gpg-interface.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@@ -30,6 +31,8 @@ struct tag_filter {\n \tstruct commit_list *with_commit;\n };\n \n+static unsigned int colopts;\n+\n static int match_pattern(const char **patterns, const char *ref)\n {\n \t/* no pattern means match everything */\n@@ -230,6 +233,9 @@ 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+\tstatus = git_column_config(var, value, \"tag\", &colopts);\n+\tif (status <= 0)\n+\t\treturn status;\n \treturn git_default_config(var, value, cb);\n }\n \n@@ -409,6 +415,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@@ -424,6 +431,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opt, 0, sizeof(opt));\n \n+\tcolopts &= ~COL_ENABLED_SET;\n \targc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);\n \n \tif (keyid) {\n@@ -441,9 +449,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+\tif (list && lines != -1) {\n+\t\t/* only die when --column is given explicitly */\n+\t\tif ((colopts & (COL_ENABLED_SET | COL_ENABLED)) == (COL_ENABLED_SET | COL_ENABLED))\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 (lines == -1 && 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 (lines == -1 && 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 e93ac73..579a298 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":"183843","messageId":"1328371886-12544-1-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328371156-4009-4-git-send-email-pclouds@gmail.com","subject":"[PATCH v6 03/12] Add git-column and column mode parsing","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-04T16:11:26Z","receivedAt":"2012-02-04T16:11:26Z","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\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Turn colopts to unsigned int from the beginning.\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                     |  125 ++++++++++++++++++++++++++++++++++++++++++\n column.h                     |    7 ++\n command-list.txt             |    1 +\n git.c                        |    1 +\n parse-options.h              |    2 +\n t/t9002-column.sh            |   27 +++++++++\n 11 files changed, 256 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 3b7680e..039e5ef 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 5f0531b..92700ca 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -766,6 +766,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..d20cf8f 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,127 @@ 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, set, name_len;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(opts); i++) {\n+\t\tif (opts[i].type == OPTION) {\n+\t\t\tif (len > 2 && !strncmp(arg, \"no\", 2)) {\n+\t\t\t\targ += 2;\n+\t\t\t\tlen -= 2;\n+\t\t\t\tset = 0;\n+\t\t\t}\n+\t\t\telse\n+\t\t\t\tset = 1;\n+\t\t}\n+\n+\t\tname_len = strlen(opts[i].name);\n+\t\tif (len != name_len ||\n+\t\t    strncmp(arg, opts[i].name, name_len))\n+\t\t\tcontinue;\n+\n+\t\tswitch (opts[i].type) {\n+\t\tcase ENABLE: return set_enable_bit(mode, opts[i].value,\n+\t\t\t\t\t\t   stdout_is_tty);\n+\t\tcase MODE: return set_mode(mode, opts[i].value);\n+\t\tcase OPTION: return set_option(mode, opts[i].value, set);\n+\t\tdefault: die(\"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+\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..f48fa81 100644\n--- a/column.h\n+++ b/column.h\n@@ -3,6 +3,7 @@\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 \n struct column_options {\n \tint width;\n@@ -15,5 +16,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":"183844","messageId":"1328371958-12596-1-git-send-email-pclouds@gmail.com","threadId":"29535","inReplyTo":"1328371156-4009-8-git-send-email-pclouds@gmail.com","subject":"[PATCH v6 07/12] column: add column.ui for default column output settings","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-02-04T16:12:38Z","receivedAt":"2012-02-04T16:12:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Really get rid of git_colopts (aka git_column_mode in v5). Sorry.\n\n Documentation/config.txt     |   26 ++++++++++++++++++++++++++\n Documentation/git-column.txt |    6 +++++-\n builtin/column.c             |   21 +++++++++++++++++++++\n column.c                     |   36 ++++++++++++++++++++++++++++++++++++\n column.h                     |    2 ++\n 5 files changed, 90 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..351375f 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 fcff2fd..e3cec1c 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@@ -356,6 +357,41 @@ 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 (!strcmp(var, key)) {\n+\t\tint ret = git_config_column(colopts, value, -1);\n+\t\tif (ret)\n+\t\t\tdie(\"invalid %s mode %s\", key, value);\n+\t\treturn 0;\n+\t}\n+\treturn 1;\t\t/* go on */\n+}\n+\n+int git_column_config(const char *var, const char *value,\n+\t\t      const char *command, unsigned int *colopts)\n+{\n+\tint ret;\n+\n+\t*colopts &= ~COL_ENABLED_SET;\n+\tret = column_config(var, value, \"column.ui\", colopts);\n+\tif (ret <= 0)\n+\t\treturn ret;\n+\n+\tif (command) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tstrbuf_addf(&sb, \"column.%s\", command);\n+\t\t*colopts &= ~COL_ENABLED_SET;\n+\t\tret = column_config(var, value, sb.buf, colopts);\n+\t\tstrbuf_release(&sb);\n+\t\tif (ret <= 0)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 1;\t\t/* go on */\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 2255851..3d2ed52 100644\n--- a/column.h\n+++ b/column.h\n@@ -23,6 +23,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":"184036","messageId":"7v7gzz98d7.fsf@alter.siamese.dyndns.org","threadId":"29535","inReplyTo":"1328371156-4009-10-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v5 09/12] branch: add --column","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-06T17:31:16Z","receivedAt":"2012-02-06T17:31:16Z","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> +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 >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\nFor a test like these where you expect whitespaces at the end, please make\nsure that these whitespaces are visible to people who read test scripts,\nby doing something like this instead:\n\n\tsed -e 's/#$//' >expected <<\\EOF\n          a/b/c   #\n          abc     #\n          bam     #\n          bar     #\n          foo     #\n        EOF\n\nThanks.\n"},{"id":"184038","messageId":"7v39an973x.fsf@alter.siamese.dyndns.org","threadId":"29535","inReplyTo":"1328371156-4009-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v5 00/12] Column display","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-06T17:58:26Z","receivedAt":"2012-02-06T17:58:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"I've added two patches on top of this series and queued on 'pu'.\n\nThe first one is a fix for style issues; this is limited to absolute\nminimum and I wouldn't be surprised if I fixed one instance of the same\nclass of style violations just as an example while ignoring bunch of\nsimilar ones.\n\nThe second was to see how \"git branch\" behaves when it sees one absurdly\nlong entry (longer than $COLUMN width) among the normal ones, primarily\ndone just for fun.\n\nYou may want to tease the first one apart and squash them in to where the\nproblem originates. The second one is optional, but as you hinted an\ninterest on coming up with a heuristic to cram more info by making some\noddball entries span multi-column, it might serve as a good starting point \nto think about the possible issues.\n\nThanks for an amusing read.\n"}]}