{"thread":{"id":"26452","subject":"[PATCH 00/16] column output (v2) and git-ls","startedAt":"2011-02-09T12:24:28Z","lastAt":"2011-02-09T12:24:44Z","messageCount":17,"participants":["Nguyễn Thái Ngọc Duy"],"isPatch":true,"patchVersion":1,"patchTotal":16},"messages":[{"id":"160748","messageId":"1297254284-3729-1-git-send-email-pclouds@gmail.com","threadId":"26452","inReplyTo":null,"subject":"[PATCH 00/16] column output (v2) and git-ls","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T12:24:28Z","receivedAt":"2011-02-09T12:24:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"I have fixed most of things Jonathan mentioned in the previous series,\nbut that's not the focus. I resend column output series because it's\nrequired for git-ls patches, a more interesting command.\n\nColumn output patches:\n\n  Move term_columns() to pager.c and save terminal width before pager\n  Add display_columns() to display in column layout\n  display_columns: add COL_MODE_{COLUMN,ROW} mode\n  display_columns: add COL_DENSE to do unequal column layout\n  Add test-column for testing column layout\n  Add core.column\n  parseopt: OPT_COLUMN to set struct column_layout.mode\n  help: reuse display_columns() for help -a\n  tag: add --column\n  branch: add --column\n\nChanges:\n\n - struct column_layout is gone.\n - core.column and --column takes a list of options separated by\n   space/comma, so you can customize your display.\n\nRemaining problems:\n\n - perhaps column.<cmd> config is better than core.column.\n - ansi_length() might misunderstand utf-8/ansi escapes\n - the heuristics to make dense layout\n - --column can also take auto/never/always\n - tests\n\ngit-ls patches:\n\n  Add ls command\n  ls: add --column\n  ls: add --recursive and turn default to non-recursive mode\n  ls: immitate UNIX ls output style\n  ls: strip common directory prefix from output\n  ls: color output\n\nIn short, git-ls is supposed to work as close as possible to UNIX ls,\nwhile still takes advantages of git-ls-files.\n\nI split this into many patches for easier discussion. The code itself\nis still WIP-quality. The design may not be 100% right.\nOh and I'm not sure how to do the color part right. Reading $LS_COLORS\nis scary and does not really fit Git.\n\n .gitignore                   |    2 +\n Documentation/config.txt     |   11 ++\n Documentation/git-branch.txt |    6 +\n Documentation/git-tag.txt    |    9 +-\n Makefile                     |    3 +\n builtin.h                    |    1 +\n builtin/branch.c             |   23 +++-\n builtin/ls-files.c           |  295 +++++++++++++++++++++++++++++++++++++++++-\n builtin/tag.c                |   21 +++-\n cache.h                      |    1 +\n column.c                     |  287 ++++++++++++++++++++++++++++++++++++++++\n column.h                     |   18 +++\n command-list.txt             |    1 +\n config.c                     |    4 +\n environment.c                |    1 +\n git.c                        |    1 +\n help.c                       |   52 +------\n pager.c                      |   33 +++++\n parse-options.h              |    2 +\n t/t9002-column.sh            |  108 +++++++++++++++\n test-column.c                |   57 ++++++++\n 21 files changed, 879 insertions(+), 57 deletions(-)\n create mode 100644 column.c\n create mode 100644 column.h\n create mode 100755 t/t9002-column.sh\n create mode 100644 test-column.c\n\n-- \n1.7.2.2\n"},{"id":"160749","messageId":"1297254284-3729-2-git-send-email-pclouds@gmail.com","threadId":"26452","inReplyTo":"1297254284-3729-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 01/16] Move term_columns() to pager.c and save terminal width before pager","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T12:24:29Z","receivedAt":"2011-02-09T12:24:29Z","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 setup_pager()\nand 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  |   33 +++++++++++++++++++++++++++++++++\n 4 files changed, 41 insertions(+), 22 deletions(-)\n create mode 100644 column.h\n\ndiff --git a/Makefile b/Makefile\nindex 775ee83..ed9e94b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1956,6 +1956,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 http-backend.o: url.h\n+pager.o help.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 7654f1b..768f64c 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 dac358f..dad6329 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -12,6 +12,19 @@\n  */\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 #ifndef WIN32\n static void pager_preexec(void)\n@@ -74,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 \tspawned_pager = 1; /* means we are emitting to terminal */\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@@ -116,3 +134,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.2.2\n"},{"id":"160750","messageId":"1297254284-3729-3-git-send-email-pclouds@gmail.com","threadId":"26452","inReplyTo":"1297254284-3729-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 02/16] Add display_columns() to display in column layout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T12:24:30Z","receivedAt":"2011-02-09T12:24:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Currently it does not do that, just provide the API.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Makefile |    3 ++-\n column.c |   31 +++++++++++++++++++++++++++++++\n column.h |    4 ++++\n 3 files changed, 37 insertions(+), 1 deletions(-)\n create mode 100644 column.c\n\ndiff --git a/Makefile b/Makefile\nindex ed9e94b..88e20de 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -575,6 +575,7 @@ LIB_OBJS += branch.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 += config.o\n@@ -1956,7 +1957,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 http-backend.o: url.h\n-pager.o help.o: column.h\n+column.o pager.o help.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..c7d9a84\n--- /dev/null\n+++ b/column.c\n@@ -0,0 +1,31 @@\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, COL_MODE_PLAIN */\n+static void display_plain(const struct string_list *list, const char *indent)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < list->nr; i++)\n+\t\tprintf(\"%s%s\\n\", indent, list->items[i].string);\n+}\n+\n+void display_columns(const struct string_list *list, int mode, int width, int padding, const char *indent)\n+{\n+\tint real_mode = MODE(mode);\n+\tif (!indent)\n+\t\tindent = \"\";\n+\tif (width <= 1)\n+\t\treal_mode = COL_MODE_PLAIN;\n+\tswitch (real_mode) {\n+\tcase COL_MODE_PLAIN:\n+\t\tdisplay_plain(list, indent);\n+\t\tbreak;\n+\n+\tdefault:\n+\t\tdie(\"BUG: invalid mode %d\", MODE(mode));\n+\t}\n+}\ndiff --git a/column.h b/column.h\nindex 55d8067..da0bf9a 100644\n--- a/column.h\n+++ b/column.h\n@@ -1,6 +1,10 @@\n #ifndef COLUMN_H\n #define COLUMN_H\n \n+#define COL_MODE          0x000F\n+#define COL_MODE_PLAIN         0   /* Single column */\n+\n extern int term_columns(void);\n+extern void display_columns(const struct string_list *list, int mode, int width, int padding, const char *indent);\n \n #endif\n-- \n1.7.2.2\n"},{"id":"160751","messageId":"1297254284-3729-4-git-send-email-pclouds@gmail.com","threadId":"26452","inReplyTo":"1297254284-3729-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 03/16] display_columns: add COL_MODE_{COLUMN,ROW} mode","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T12:24:31Z","receivedAt":"2011-02-09T12:24: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 in UTF-8. If strings contain ANSI escape strings,\nCOL_ANSI must be specified.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n column.c |  107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n column.h |    3 ++\n 2 files changed, 110 insertions(+), 0 deletions(-)\n\ndiff --git a/column.c b/column.c\nindex c7d9a84..d6e2d70 100644\n--- a/column.c\n+++ b/column.c\n@@ -1,8 +1,58 @@\n #include \"cache.h\"\n #include \"column.h\"\n+#include \"parse-options.h\"\n #include \"string-list.h\"\n+#include \"utf8.h\"\n \n #define MODE(mode) ((mode) & COL_MODE)\n+#define XY2SCALAR(mode,x,y,cols,rows) (MODE(mode) == COL_MODE_COLUMN ? (x) * (rows) + (y) : (y) * (cols) + (x))\n+\n+/* return length of 's' in letters, ANSI escapes stripped */\n+static int item_length(const struct string_list *list, 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(const struct string_list *list,\n+\t\t   int mode, int total_width, int padding,\n+\t\t   int *width, int *rows, int *cols)\n+{\n+\tint i;\n+\n+\t*width = 0;\n+\t/* Find maximum column width */\n+\tfor (i = 0; i < list->nr; i++) {\n+\t\tconst char *s = list->items[i].string;\n+\t\tint len = item_length(list, mode, s);\n+\t\tif (*width < len)\n+\t\t\t*width = len;\n+\t}\n+\t*width += padding;\n+\n+\t*cols = total_width / *width;\n+\tif (*cols == 0)\n+\t\t*cols = 1;\n+\n+\t*rows = DIV_ROUND_UP(list->nr, *cols);\n+}\n \n /* Display without layout, COL_MODE_PLAIN */\n static void display_plain(const struct string_list *list, const char *indent)\n@@ -13,6 +63,58 @@ static void display_plain(const struct string_list *list, const char *indent)\n \t\tprintf(\"%s%s\\n\", indent, list->items[i].string);\n }\n \n+static int display_cell(const struct string_list *list, int mode,\n+\t\t\tint *width, int initial_width,\n+\t\t\tconst char *indent, const char *empty_cell,\n+\t\t\tint x, int y, int cols, int rows)\n+{\n+\tconst char *s;\n+\tint i, len, newline;\n+\n+\ti = XY2SCALAR(mode, x, y, cols, rows);\n+\tif (i >= list->nr)\n+\t\treturn -1;\n+\ts = list->items[i].string;\n+\tlen = item_length(list, mode, s);\n+\tif (width[x] < initial_width)\n+\t\tlen += initial_width - width[x];\n+\tif (MODE(mode) == COL_MODE_COLUMN)\n+\t\tnewline = i + rows >= list->nr;\n+\telse\n+\t\tnewline = x == cols - 1 || i == list->nr - 1;\n+\n+\tprintf(\"%s%s%s\",\n+\t\t\tx == 0 ? indent : \"\",\n+\t\t\tlist->items[i].string,\n+\t\t\tnewline ? \"\\n\" : 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+{\n+\tint x, y, i, cols, rows, initial_width, *width;\n+\tchar *empty_cell;\n+\n+\tlayout(list, mode, total_width, padding, &initial_width, &rows, &cols);\n+\twidth = xmalloc(sizeof(*width) * cols);\n+\tfor (i = 0; i < cols; i++)\n+\t\twidth[i] = 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 < rows; y++) {\n+\t\tfor (x = 0; x < cols; x++)\n+\t\t\tif (display_cell(list, mode, width,\n+\t\t\t\t\t initial_width, indent, empty_cell,\n+\t\t\t\t\t x, y, cols, rows))\n+\t\t\t\tbreak;\n+\t}\n+}\n+\n void display_columns(const struct string_list *list, int mode, int width, int padding, const char *indent)\n {\n \tint real_mode = MODE(mode);\n@@ -25,6 +127,11 @@ void display_columns(const struct string_list *list, int mode, int width, int pa\n \t\tdisplay_plain(list, indent);\n \t\tbreak;\n \n+\tcase COL_MODE_ROW:\n+\tcase COL_MODE_COLUMN:\n+\t\tdisplay_table(list, mode, width, padding, indent);\n+\t\tbreak;\n+\n \tdefault:\n \t\tdie(\"BUG: invalid mode %d\", MODE(mode));\n \t}\ndiff --git a/column.h b/column.h\nindex da0bf9a..3ed5731 100644\n--- a/column.h\n+++ b/column.h\n@@ -3,6 +3,9 @@\n \n #define COL_MODE          0x000F\n #define COL_MODE_PLAIN         0   /* Single column */\n+#define COL_MODE_COLUMN        1   /* Fill columns before rows */\n+#define COL_MODE_ROW           2   /* Fill rows before columns */\n+#define COL_ANSI         (1 << 4) /* Remove ANSI escapes from string length */\n \n extern int term_columns(void);\n extern void display_columns(const struct string_list *list, int mode, int width, int padding, const char *indent);\n-- \n1.7.2.2\n"},{"id":"160752","messageId":"1297254284-3729-5-git-send-email-pclouds@gmail.com","threadId":"26452","inReplyTo":"1297254284-3729-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 04/16] display_columns: add COL_DENSE to do unequal column layout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T12:24:32Z","receivedAt":"2011-02-09T12:24:32Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"shrink_columns() calculate column width just enough to contain\ntext and padding.\n\nrelayout() uses shrink_columns() to try adding more columns after\ncolumns are shrunk. However it only try that once.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n column.c |   70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n column.h |    1 +\n 2 files changed, 71 insertions(+), 0 deletions(-)\n\ndiff --git a/column.c b/column.c\nindex d6e2d70..e7facf4 100644\n--- a/column.c\n+++ b/column.c\n@@ -54,6 +54,68 @@ static void layout(const struct string_list *list,\n \t*rows = DIV_ROUND_UP(list->nr, *cols);\n }\n \n+/*\n+ * Tighten columns just wide enough to fit its data and padding\n+ * Return number of chars saved after shrinking.\n+ */\n+static int shrink_columns(const struct string_list *list, int mode,\n+\t\t\t   int *width, int padding, int rows, int cols)\n+{\n+\tint x, y, i, len, item_len, spare = 0;\n+\tconst char *s;\n+\n+\tfor (x = 0; x < cols; x++) {\n+\t\tfor (y = len = 0; y < rows; y++) {\n+\t\t\ti = XY2SCALAR(mode, x, y, cols, rows);\n+\t\t\tif (i >= list->nr)\n+\t\t\t\tbreak;\n+\t\t\ts = list->items[i].string;\n+\t\t\titem_len = item_length(list, mode, s);\n+\t\t\tif (len < item_len)\n+\t\t\t\tlen = item_len;\n+\t\t}\n+\t\tlen += padding;\n+\n+\t\tassert(len <= width[x]);\n+\t\tspare += width[x] - len;\n+\t\twidth[x] = len;\n+\t}\n+\treturn spare;\n+}\n+\n+/* Try to tighten the layout and see if more columns can be added.*/\n+static void relayout(const struct string_list *list,\n+\t\t     int mode, int original_width, int padding, int spare,\n+\t\t     int *initial_width, int **width, int *rows, int *cols)\n+{\n+\tint new_rows, new_cols, new_initial_width;\n+\tint i, *new_width, new_spare, total_width;\n+\n+\t/*\n+\t * Assume all columns have same width, we would need\n+\t * initial_width*cols. But then after shrinking, we have\n+\t * \"spare\" more chars. Assume a new total_width with\n+\t * additional chars, then re-shrink to see if it fits\n+\t * c->width.\n+\t */\n+\ttotal_width = (*initial_width)*(*cols) + spare;\n+\tlayout(list, mode, total_width, padding,\n+\t       &new_initial_width, &new_rows, &new_cols);\n+\tnew_width = xmalloc(sizeof(*new_width) * new_cols);\n+\tfor (i = 0; i < new_cols; i++)\n+\t\tnew_width[i] = new_initial_width;\n+\tnew_spare = shrink_columns(list, mode, new_width, padding, new_rows, new_cols);\n+\n+\t/* Does it fit? */\n+\tif (total_width - new_spare < original_width) {\n+\t\tfree(*width);\n+\t\t*width = new_width;\n+\t\t*initial_width = new_initial_width;\n+\t\t*rows = new_rows;\n+\t\t*cols = new_cols;\n+\t}\n+}\n+\n /* Display without layout, COL_MODE_PLAIN */\n static void display_plain(const struct string_list *list, const char *indent)\n {\n@@ -103,6 +165,14 @@ static void display_table(const struct string_list *list,\n \tfor (i = 0; i < cols; i++)\n \t\twidth[i] = initial_width;\n \n+\tif (mode & COL_DENSE) {\n+\t\tint spare = shrink_columns(list, mode, width, padding, rows, cols);\n+\t\t/* is it worth relayout? */\n+\t\tif (spare >= initial_width/2)\n+\t\t\trelayout(list, mode, total_width, padding, spare,\n+\t\t\t\t &initial_width, &width, &rows, &cols);\n+\t}\n+\n \tempty_cell = xmalloc(initial_width + 1);\n \tmemset(empty_cell, ' ', initial_width);\n \tempty_cell[initial_width] = '\\0';\ndiff --git a/column.h b/column.h\nindex 3ed5731..cef354d 100644\n--- a/column.h\n+++ b/column.h\n@@ -6,6 +6,7 @@\n #define COL_MODE_COLUMN        1   /* Fill columns before rows */\n #define COL_MODE_ROW           2   /* Fill rows before columns */\n #define COL_ANSI         (1 << 4) /* Remove ANSI escapes from string length */\n+#define COL_DENSE        (1 << 5) /* Shrink columns when possible, making space for more columns */\n \n extern int term_columns(void);\n extern void display_columns(const struct string_list *list, int mode, int width, int padding, const char *indent);\n-- \n1.7.2.2\n"},{"id":"160764","messageId":"1297254284-3729-6-git-send-email-pclouds@gmail.com","threadId":"26452","inReplyTo":"1297254284-3729-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 05/16] Add test-column for testing column layout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T12:24:33Z","receivedAt":"2011-02-09T12:24: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 .gitignore        |    1 +\n Makefile          |    1 +\n t/t9002-column.sh |  108 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n test-column.c     |   57 ++++++++++++++++++++++++++++\n 4 files changed, 167 insertions(+), 0 deletions(-)\n create mode 100755 t/t9002-column.sh\n create mode 100644 test-column.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 3dd6ef7..a1a1202 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -162,6 +162,7 @@\n /gitweb/gitweb.cgi\n /gitweb/static/gitweb.min.*\n /test-chmtime\n+/test-column\n /test-ctype\n /test-date\n /test-delta\ndiff --git a/Makefile b/Makefile\nindex 88e20de..a687e73 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -417,6 +417,7 @@ PROGRAM_OBJS += http-backend.o\n PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))\n \n TEST_PROGRAMS_NEED_X += test-chmtime\n+TEST_PROGRAMS_NEED_X += test-column\n TEST_PROGRAMS_NEED_X += test-ctype\n TEST_PROGRAMS_NEED_X += test-date\n TEST_PROGRAMS_NEED_X += test-delta\ndiff --git a/t/t9002-column.sh b/t/t9002-column.sh\nnew file mode 100755\nindex 0000000..1d030b0\n--- /dev/null\n+++ b/t/t9002-column.sh\n@@ -0,0 +1,108 @@\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 '80 columns' '\n+\tcat >expected <<\\EOF &&\n+one    two    three  four   five   six    seven  eight  nine   ten    eleven\n+EOF\n+\tCOLUMNS=80 test-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 test-column < lista > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'width = 1' '\n+\ttest-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+\ttest-column < lista > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, densed' '\n+\tcat >expected <<\\EOF &&\n+one   seven\n+two   eight\n+three nine\n+four  ten\n+five  eleven\n+six\n+EOF\n+\ttest-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+two     eight\n+three   nine\n+four    ten\n+five    eleven\n+six\n+EOF\n+\ttest-column --padding 2 < lista > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, left 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+\ttest-column --left=2 < lista > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_done\ndiff --git a/test-column.c b/test-column.c\nnew file mode 100644\nindex 0000000..b9a5d4b\n--- /dev/null\n+++ b/test-column.c\n@@ -0,0 +1,57 @@\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 [--mode=<mode>] [--width=<width>] [--left-space=<N>] [--right-space=<N>]\",\n+\tNULL\n+};\n+\n+int main(int argc, const char **argv)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\tconst char *modestr = NULL;\n+\tint mode = 0;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint left_space = 0, right_space = 0, padding = 1;\n+\tint term_width = term_columns();\n+\tstruct option options[] = {\n+\t\tOPT_INTEGER(0, \"width\", &term_width, \"Maximum width\"),\n+\t\tOPT_INTEGER(0, \"left\", &left_space, \"Padding space on left border\"),\n+\t\tOPT_INTEGER(0, \"right\", &right_space, \"Padding space on right border\"),\n+\t\tOPT_INTEGER(0, \"padding\", &padding, \"Padding space between columns\"),\n+\t\tOPT_STRING(0, \"mode\", &modestr, \"mode\", \"Which layout mode to use\"),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, \"\", options, builtin_column_usage, 0);\n+\n+\twhile (modestr && *modestr) {\n+\t\tint len = strcspn(modestr, \",\");\n+\t\tif (len == 6 && !strncmp(modestr, \"column\", len))\n+\t\t\tmode |= COL_MODE_COLUMN;\n+\t\telse if (len == 5 && !strncmp(modestr, \"dense\", len))\n+\t\t\tmode |= COL_DENSE;\n+\t\telse if (len == 4 && !strncmp(modestr, \"ansi\", len))\n+\t\t\tmode |= COL_ANSI;\n+\t\telse\n+\t\t\tdie(\"Unknown modestr %s\", modestr);\n+\t\tmodestr += len;\n+\t\twhile (*modestr == ',')\n+\t\t\tmodestr++;\n+\t}\n+\tif (!mode)\n+\t\tmode = COL_MODE_COLUMN;\n+\tif ((mode & COL_MODE) == 0)\n+\t\tdie(\"Invalid mode '%s'\", modestr);\n+\n+\twhile (!strbuf_getline(&sb, stdin, '\\n'))\n+\t\tstring_list_append(&list, sb.buf);\n+\n+\tstrbuf_setlen(&sb, left_space);\n+\tmemset(sb.buf, ' ', left_space);\n+\tdisplay_columns(&list, mode, term_width - right_space - left_space, padding, sb.buf);\n+\treturn 0;\n+}\n-- \n1.7.2.2\n"},{"id":"160753","messageId":"1297254284-3729-7-git-send-email-pclouds@gmail.com","threadId":"26452","inReplyTo":"1297254284-3729-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 06/16] Add core.column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T12:24:34Z","receivedAt":"2011-02-09T12:24:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"FIXME: should probably go as color.* (i.e. column.*)\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt |   11 ++++++++\n cache.h                  |    1 +\n column.c                 |   64 ++++++++++++++++++++++++++++++++++++++++++++++\n column.h                 |    1 +\n config.c                 |    4 +++\n environment.c            |    1 +\n 6 files changed, 82 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex c5e1835..45b5f5b 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -567,6 +567,17 @@ core.sparseCheckout::\n \tEnable \"sparse checkout\" feature. See section \"Sparse checkout\" in\n \tlinkgit:git-read-tree[1] for more information.\n \n+core.column::\n+\tSpecify whether a command should output in columns. Only commands\n+\tthat support `--column` will be affected by this. This variable\n+\tconsists of a list of tokens separated by either spaces or commas:\n+\t`never` (do not output in columns), `auto` (output in columns if\n+\tthe output is to a terminal), `always` (always output in\n+\tcolumns), `column` (fill column before row), `row` (fill row\n+\tbefore column), `dense` (unequal column width).\n+\tSetting `--column` or `--no-column` will override this\n+\tvariable. This option defaults to never.\n+\n add.ignore-errors::\n add.ignoreErrors::\n \tTells 'git add' to continue adding files when some files cannot be\ndiff --git a/cache.h b/cache.h\nindex d83d68c..b370657 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -559,6 +559,7 @@ extern int read_replace_refs;\n extern int fsync_object_files;\n extern int core_preload_index;\n extern int core_apply_sparse_checkout;\n+extern int core_column;\n \n enum safe_crlf {\n \tSAFE_CRLF_FALSE = 0,\ndiff --git a/column.c b/column.c\nindex e7facf4..615a698 100644\n--- a/column.c\n+++ b/column.c\n@@ -206,3 +206,67 @@ void display_columns(const struct string_list *list, int mode, int width, int pa\n \t\tdie(\"BUG: invalid mode %d\", MODE(mode));\n \t}\n }\n+\n+static int parse_column_option(const char *arg, int len, int *mode)\n+{\n+\tint negate = !strncmp(arg, \"no\", 2);\n+\n+\tif (negate)\n+\t\targ += 2;\n+\n+\tif (!negate && !strncmp(arg, \"column\", 6)) {\n+\t\t*mode &= ~COL_MODE;\n+\t\t*mode |= COL_MODE_COLUMN;\n+\t\treturn 0;\n+\t}\n+\telse if (!negate && !strncmp(arg, \"row\", 3)) {\n+\t\t*mode &= ~COL_MODE;\n+\t\t*mode |= COL_MODE_ROW;\n+\t\treturn 0;\n+\t}\n+\telse if (!strncmp(arg, \"dense\", 5)) {\n+\t\tif (negate)\n+\t\t\t*mode &= ~COL_DENSE;\n+\t\telse\n+\t\t\t*mode |= COL_DENSE;\n+\t\treturn 0;\n+\t}\n+\telse\n+\t\treturn error(\"unsupported style '%s'\", arg);\n+\treturn 0;\n+}\n+\n+int git_config_column(const char *var, const char *value, int stdout_is_tty)\n+{\n+\tconst char *sep = \" ,\";\n+\tint enable = 0;\n+\n+\twhile (*value) {\n+\t\tint len = strcspn(value, sep);\n+\t\tif (len) {\n+\t\t\tif (!strncasecmp(value, \"never\", 5))\n+\t\t\t\tenable = 0;\n+\t\t\telse if (!strncasecmp(value, \"always\", 6))\n+\t\t\t\tenable = 1;\n+\t\t\telse if (!strncasecmp(value, \"auto\", 4)) {\n+\t\t\t\tif (stdout_is_tty < 0)\n+\t\t\t\t\tstdout_is_tty = isatty(1);\n+\t\t\t\tif (stdout_is_tty || (pager_in_use() && pager_use_color))\n+\t\t\t\t\tenable = 1;\n+\t\t\t}\n+\t\t\telse if (!parse_column_option(value, len, &core_column))\n+\t\t\t\t;\n+\t\t\telse\n+\t\t\t\treturn -1;\n+\n+\t\t\tvalue += len;\n+\t\t}\n+\t\tvalue += strspn(value, sep);\n+\t}\n+\tif (!enable)\n+\t\tcore_column = 0;\n+\telse if (MODE(core_column) == COL_MODE_PLAIN)\n+\t\tcore_column |= COL_MODE_COLUMN;\n+\n+\treturn 0;\n+}\ndiff --git a/column.h b/column.h\nindex cef354d..0749793 100644\n--- a/column.h\n+++ b/column.h\n@@ -10,5 +10,6 @@\n \n extern int term_columns(void);\n extern void display_columns(const struct string_list *list, int mode, int width, int padding, const char *indent);\n+extern int git_config_column(const char *var, const char *value, int stdout_is_tty);\n \n #endif\ndiff --git a/config.c b/config.c\nindex 625e051..68a432a 100644\n--- a/config.c\n+++ b/config.c\n@@ -9,6 +9,7 @@\n #include \"exec_cmd.h\"\n #include \"strbuf.h\"\n #include \"quote.h\"\n+#include \"column.h\"\n \n #define MAXNAME (256)\n \n@@ -660,6 +661,9 @@ static int git_default_core_config(const char *var, const char *value)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"core.column\"))\n+\t\treturn git_config_column(var, value, -1);\n+\n \t/* Add other config variables here and to Documentation/config.txt. */\n \treturn 0;\n }\ndiff --git a/environment.c b/environment.c\nindex 9564475..b420ea2 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -56,6 +56,7 @@ char *notes_ref_name;\n int grafts_replace_parents = 1;\n int core_apply_sparse_checkout;\n struct startup_info *startup_info;\n+int core_column;\n \n /* Parallel index stat data preload? */\n int core_preload_index = 0;\n-- \n1.7.2.2\n"},{"id":"160754","messageId":"1297254284-3729-8-git-send-email-pclouds@gmail.com","threadId":"26452","inReplyTo":"1297254284-3729-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 07/16] parseopt: OPT_COLUMN to set struct column_layout.mode","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T12:24:35Z","receivedAt":"2011-02-09T12:24: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 column.c        |   15 +++++++++++++++\n column.h        |    3 +++\n parse-options.h |    2 ++\n 3 files changed, 20 insertions(+), 0 deletions(-)\n\ndiff --git a/column.c b/column.c\nindex 615a698..c08a885 100644\n--- a/column.c\n+++ b/column.c\n@@ -270,3 +270,18 @@ int git_config_column(const char *var, const char *value, int stdout_is_tty)\n \n \treturn 0;\n }\n+\n+int parseopt_column_callback(const struct option *opt, const char *arg, int unset)\n+{\n+\tint *mode = opt->value;\n+\tif (unset) {\n+\t\t*mode = COL_MODE_PLAIN;\n+\t\treturn 0;\n+\t}\n+\tif (!arg) {\n+\t\tif (!MODE(*mode))\n+\t\t\t*mode |= COL_MODE_COLUMN;\n+\t\treturn 0;\n+\t}\n+\treturn parse_column_option(arg, strlen(arg), mode);\n+}\ndiff --git a/column.h b/column.h\nindex 0749793..99ec848 100644\n--- a/column.h\n+++ b/column.h\n@@ -12,4 +12,7 @@ extern int term_columns(void);\n extern void display_columns(const struct string_list *list, int mode, int width, int padding, const char *indent);\n extern int git_config_column(const char *var, const char *value, int stdout_is_tty);\n \n+struct option;\n+extern int parseopt_column_callback(const struct option *opt, const char *arg, int unset);\n+\n #endif\ndiff --git a/parse-options.h b/parse-options.h\nindex 31ec5d2..2fed453 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -145,6 +145,8 @@ struct option {\n #define OPT_COLOR_FLAG(s, l, v, h) \\\n \t{ OPTION_CALLBACK, (s), (l), (v), \"when\", (h), PARSE_OPT_OPTARG, \\\n \t\tparse_opt_color_flag_cb, (intptr_t)\"always\" }\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 \n /* parse_options() will filter out the processed options and leave the\n-- \n1.7.2.2\n"},{"id":"160755","messageId":"1297254284-3729-9-git-send-email-pclouds@gmail.com","threadId":"26452","inReplyTo":"1297254284-3729-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 08/16] help: reuse display_columns() for help -a","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T12:24:36Z","receivedAt":"2011-02-09T12:24: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 help.c |   29 +++++++----------------------\n 1 files changed, 7 insertions(+), 22 deletions(-)\n\ndiff --git a/help.c b/help.c\nindex 768f64c..81942f9 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@@ -73,29 +74,13 @@ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)\n \n static void pretty_print_string_list(struct cmdnames *cmds, int longest)\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+\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+\tdisplay_columns(&list, COL_MODE_COLUMN, term_columns(), 1, \"  \");\n+\tstring_list_clear(&list, 0);\n }\n \n static int is_executable(const char *name)\n-- \n1.7.2.2\n"},{"id":"160756","messageId":"1297254284-3729-10-git-send-email-pclouds@gmail.com","threadId":"26452","inReplyTo":"1297254284-3729-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 09/16] tag: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T12:24:37Z","receivedAt":"2011-02-09T12:24:37Z","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/git-tag.txt |    9 ++++++++-\n Makefile                  |    2 +-\n builtin/tag.c             |   21 +++++++++++++++++----\n 3 files changed, 26 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt\nindex 8b169e3..89c55fb 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 [--column[=<options>] | --no-column]\n+\t[--contains <commit>] [<pattern>]\n 'git tag' -v <tagname>...\n \n DESCRIPTION\n@@ -71,6 +72,12 @@ OPTIONS\n \tList tags with names that match the given pattern (or all if no pattern is given).\n \tTyping \"git tag\" without arguments, also lists all tags.\n \n+--column::\n+--no-column::\n+\tWhether show tags in columns. This option is only applicable if `git\n+\ttag` is used to list tags without annotation lines. See core.column\n+\tfor options.\n+\n --contains <commit>::\n \tOnly list tags which contain the specified commit.\n \ndiff --git a/Makefile b/Makefile\nindex a687e73..c9fd9aa 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1958,7 +1958,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 http-backend.o: url.h\n-column.o pager.o help.o: column.h\n+column.o pager.o help.o tag.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 aa1f87d..2b6cae9 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -12,6 +12,8 @@\n #include \"tag.h\"\n #include \"run-command.h\"\n #include \"parse-options.h\"\n+#include \"string-list.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@@ -22,6 +24,7 @@ static const char * const git_tag_usage[] = {\n };\n \n static char signingkey[1000];\n+static struct string_list output;\n \n struct tag_filter {\n \tconst char *pattern;\n@@ -52,7 +55,7 @@ static int show_reference(const char *refname, const unsigned char *sha1,\n \t\t}\n \n \t\tif (!filter->lines) {\n-\t\t\tprintf(\"%s\\n\", refname);\n+\t\t\tstring_list_append(&output, refname);\n \t\t\treturn 0;\n \t\t}\n \t\tprintf(\"%-15s \", refname);\n@@ -361,7 +364,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \tstruct ref_lock *lock;\n \n \tint annotate = 0, sign = 0, force = 0, lines = -1,\n-\t\tlist = 0, delete = 0, verify = 0;\n+\t\tlist = 0, delete = 0, verify = 0, column_mode;\n \tconst char *msgfile = NULL, *keyid = NULL;\n \tstruct msg_arg msg = { 0, STRBUF_INIT };\n \tstruct commit_list *with_commit = NULL;\n@@ -383,6 +386,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING('u', NULL, &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\", &column_mode, \"show tag list in columns\" ),\n \n \t\tOPT_GROUP(\"Tag listing options\"),\n \t\t{\n@@ -395,6 +399,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t};\n \n \tgit_config(git_tag_config, NULL);\n+\tcolumn_mode = core_column;\n+\toutput.strdup_strings = 1;\n \n \targc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);\n \n@@ -413,9 +419,16 @@ 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[0], lines == -1 ? 0 : lines,\n+\tif (list) {\n+\t\tint ret;\n+\n+\t\tif (lines != -1)\n+\t\t\tcolumn_mode = COL_MODE_PLAIN;\n+\t\tret =  list_tags(argv[0], lines == -1 ? 0 : lines,\n \t\t\t\t with_commit);\n+\t\tdisplay_columns(&output, column_mode, term_columns(), 2, \"\");\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.2.2\n"},{"id":"160757","messageId":"1297254284-3729-11-git-send-email-pclouds@gmail.com","threadId":"26452","inReplyTo":"1297254284-3729-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 10/16] branch: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T12:24:38Z","receivedAt":"2011-02-09T12:24: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/git-branch.txt |    6 ++++++\n Makefile                     |    2 +-\n builtin/branch.c             |   23 +++++++++++++++++++----\n 3 files changed, 26 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex 9106d38..8a05784 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -9,6 +9,7 @@ SYNOPSIS\n --------\n [verse]\n 'git branch' [--color[=<when>] | --no-color] [-r | -a]\n+\t[--column[=<options>] | --no-column]\n \t[-v [--abbrev=<length> | --no-abbrev]]\n \t[(--merged | --no-merged | --contains) [<commit>]]\n 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]\n@@ -99,6 +100,11 @@ OPTIONS\n \tdefault to color output.\n \tSame as `--color=never`.\n \n+--column::\n+--no-column::\n+\tWhether whow branches in columns. See core.column for options.\n+\tThis option is ignored in verbose mode.\n+\n -r::\n \tList or delete (if used with -d) the remote-tracking branches.\n \ndiff --git a/Makefile b/Makefile\nindex c9fd9aa..0e459e4 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1958,7 +1958,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 http-backend.o: url.h\n-column.o pager.o help.o tag.o: column.h\n+branch.o column.o pager.o help.o tag.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 9e546e4..d3eaa00 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,8 @@ static enum merge_filter {\n } merge_filter;\n static unsigned char merge_filter_ref[20];\n \n+static struct string_list output;\n+\n static int parse_branch_color_slot(const char *var, int ofs)\n {\n \tif (!strcasecmp(var+ofs, \"plain\"))\n@@ -451,7 +455,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,\n \t\tstrbuf_release(&stat);\n \t\tstrbuf_release(&subject);\n \t}\n-\tprintf(\"%s\\n\", out.buf);\n+\tstring_list_append(&output, out.buf);\n \tstrbuf_release(&name);\n \tstrbuf_release(&out);\n }\n@@ -609,7 +613,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n {\n \tint delete = 0, rename = 0, force_create = 0;\n \tint verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;\n-\tint reflog = 0;\n+\tint reflog = 0, column_mode;\n \tenum branch_track track;\n \tint kinds = REF_LOCAL_BRANCH;\n \tstruct commit_list *with_commit = NULL;\n@@ -660,6 +664,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\", &column_mode, \"list branches in columns\" ),\n \t\tOPT_END(),\n \t};\n \n@@ -686,6 +691,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t}\n \thashcpy(merge_filter_ref, head_sha1);\n \n+\tcolumn_mode = core_column | COL_ANSI;\n+\toutput.strdup_strings = 1;\n+\n \targc = parse_options(argc, argv, prefix, options, builtin_branch_usage,\n \t\t\t     0);\n \tif (!!delete + !!rename + !!force_create > 1)\n@@ -693,8 +701,15 @@ 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 (argc == 0)\n-\t\treturn print_ref_list(kinds, detached, verbose, abbrev, with_commit);\n+\telse if (argc == 0) {\n+\t\tint ret;\n+\t\tif (verbose)\n+\t\t\tcolumn_mode = COL_MODE_PLAIN;\n+\n+\t\tret = print_ref_list(kinds, detached, verbose, abbrev, with_commit);\n+\t\tdisplay_columns(&output, column_mode, term_columns(), 1, \"\");\n+\t\treturn ret;\n+\t}\n \telse if (rename && (argc == 1))\n \t\trename_branch(head, argv[0], rename > 1);\n \telse if (rename && (argc == 2))\n-- \n1.7.2.2\n"},{"id":"160758","messageId":"1297254284-3729-12-git-send-email-pclouds@gmail.com","threadId":"26452","inReplyTo":"1297254284-3729-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 11/16] Add ls command","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T12:24:39Z","receivedAt":"2011-02-09T12:24:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The command is supposed to be git variant of UNIX ls. Currently\nit's just a stripped down version of ls-files with some porcelain touch:\n\n - auto refreshing index\n - setup standard excludes\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore         |    1 +\n Makefile           |    2 +-\n builtin.h          |    1 +\n builtin/ls-files.c |   52 ++++++++++++++++++++++++++++++++++++++++++++++++++++\n command-list.txt   |    1 +\n git.c              |    1 +\n 6 files changed, 57 insertions(+), 1 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex a1a1202..c090d21 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -66,6 +66,7 @@\n /git-instaweb\n /git-log\n /git-lost-found\n+/git-ls\n /git-ls-files\n /git-ls-remote\n /git-ls-tree\ndiff --git a/Makefile b/Makefile\nindex 0e459e4..5efbb1c 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1958,7 +1958,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 http-backend.o: url.h\n-branch.o column.o pager.o help.o tag.o: column.h\n+branch.o builtin/ls-files.o column.o pager.o help.o tag.o: column.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/builtin.h b/builtin.h\nindex 904e067..5aede6e 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -80,6 +80,7 @@ extern int cmd_index_pack(int argc, const char **argv, const char *prefix);\n extern int cmd_init_db(int argc, const char **argv, const char *prefix);\n extern int cmd_log(int argc, const char **argv, const char *prefix);\n extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);\n+extern int cmd_ls(int argc, const char **argv, const char *prefix);\n extern int cmd_ls_files(int argc, const char **argv, const char *prefix);\n extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex fb2d5f4..cab7a10 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -619,3 +619,55 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \n \treturn 0;\n }\n+\n+int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n+{\n+\tstruct dir_struct dir;\n+\tstruct option builtin_ls_files_options[] = {\n+\t\tOPT_BOOLEAN('c', \"cached\", &show_cached,\n+\t\t\t\"show cached files in the output (default)\"),\n+\t\tOPT_BOOLEAN('d', \"deleted\", &show_deleted,\n+\t\t\t\"show deleted files in the output\"),\n+\t\tOPT_BOOLEAN('m', \"modified\", &show_modified,\n+\t\t\t\"show modified files in the output\"),\n+\t\tOPT_BOOLEAN('o', \"others\", &show_others,\n+\t\t\t\"show other files in the output\"),\n+\t\tOPT_BIT('i', \"ignored\", &dir.flags,\n+\t\t\t\"show ignored files in the output\",\n+\t\t\tDIR_SHOW_IGNORED),\n+\t\tOPT_BOOLEAN('k', \"killed\", &show_killed,\n+\t\t\t\"show files on the filesystem that need to be removed\"),\n+\t\tOPT_END()\n+\t};\n+\n+\tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n+\t\tusage_with_options(ls_files_usage, builtin_ls_files_options);\n+\n+\tmemset(&dir, 0, sizeof(dir));\n+\tprefix = cmd_prefix;\n+\tif (prefix)\n+\t\tprefix_len = strlen(prefix);\n+\tgit_config(git_default_config, NULL);\n+\n+\tif (read_cache() < 0)\n+\t\tdie(\"index file corrupt\");\n+\n+\targc = parse_options(argc, argv, prefix, builtin_ls_files_options,\n+\t\t\tls_files_usage, 0);\n+\n+\tif (dir.flags & DIR_SHOW_IGNORED || show_others)\n+\t\tsetup_standard_excludes(&dir);\n+\n+\tpathspec = get_pathspec(prefix, argv);\n+\n+\tif (show_modified)\n+\t\trefresh_index(&the_index, REFRESH_QUIET, pathspec, NULL, NULL);\n+\n+\t/* With no flags, we default to showing the cached files */\n+\tif (!(show_stage | show_deleted | show_others | show_unmerged |\n+\t      show_killed | show_modified | show_resolve_undo))\n+\t\tshow_cached = 1;\n+\n+\tshow_files(&dir);\n+\treturn 0;\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex 95bf18c..15e6770 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -59,6 +59,7 @@ git-instaweb                            ancillaryinterrogators\n gitk                                    mainporcelain\n git-log                                 mainporcelain common\n git-lost-found                          ancillarymanipulators\tdeprecated\n+git-ls                                  mainporcelain\n git-ls-files                            plumbinginterrogators\n git-ls-remote                           plumbinginterrogators\n git-ls-tree                             plumbinginterrogators\ndiff --git a/git.c b/git.c\nindex 68334f6..f8e20d2 100644\n--- a/git.c\n+++ b/git.c\n@@ -357,6 +357,7 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"init\", cmd_init_db },\n \t\t{ \"init-db\", cmd_init_db },\n \t\t{ \"log\", cmd_log, RUN_SETUP },\n+\t\t{ \"ls\", cmd_ls, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"ls-files\", cmd_ls_files, RUN_SETUP },\n \t\t{ \"ls-tree\", cmd_ls_tree, RUN_SETUP },\n \t\t{ \"ls-remote\", cmd_ls_remote, RUN_SETUP_GENTLY },\n-- \n1.7.2.2\n"},{"id":"160759","messageId":"1297254284-3729-13-git-send-email-pclouds@gmail.com","threadId":"26452","inReplyTo":"1297254284-3729-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 12/16] ls: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T12:24:40Z","receivedAt":"2011-02-09T12:24:40Z","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 builtin/ls-files.c |   16 ++++++++++++++++\n 1 files changed, 16 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex cab7a10..3195f75 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -13,6 +13,7 @@\n #include \"parse-options.h\"\n #include \"resolve-undo.h\"\n #include \"string-list.h\"\n+#include \"column.h\"\n \n static int abbrev;\n static int show_deleted;\n@@ -45,8 +46,19 @@ static const char *tag_modified = \"\";\n static const char *tag_skip_worktree = \"\";\n static const char *tag_resolve_undo = \"\";\n \n+static struct string_list output;\n+static int column_mode;\n+\n static void write_name(const char* name, size_t len)\n {\n+\t/* No quoting in column layout. It will be done by the end. */\n+\tif (column_mode & COL_MODE) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tstrbuf_add(&sb, name, len);\n+\t\tstring_list_append(&output, strbuf_detach(&sb, NULL));\n+\t\treturn;\n+\t}\n+\n \twrite_name_quoted_relative(name, len, prefix, prefix_len, stdout,\n \t\t\tline_terminator);\n }\n@@ -637,6 +649,7 @@ int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tDIR_SHOW_IGNORED),\n \t\tOPT_BOOLEAN('k', \"killed\", &show_killed,\n \t\t\t\"show files on the filesystem that need to be removed\"),\n+\t\tOPT_COLUMN(0, \"column\", &column_mode, \"show files in columns\" ),\n \t\tOPT_END()\n \t};\n \n@@ -652,6 +665,8 @@ int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n \tif (read_cache() < 0)\n \t\tdie(\"index file corrupt\");\n \n+\tcolumn_mode = core_column;\n+\n \targc = parse_options(argc, argv, prefix, builtin_ls_files_options,\n \t\t\tls_files_usage, 0);\n \n@@ -669,5 +684,6 @@ int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n \t\tshow_cached = 1;\n \n \tshow_files(&dir);\n+\tdisplay_columns(&output, column_mode, term_columns(), 2, NULL);\n \treturn 0;\n }\n-- \n1.7.2.2\n"},{"id":"160760","messageId":"1297254284-3729-14-git-send-email-pclouds@gmail.com","threadId":"26452","inReplyTo":"1297254284-3729-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 13/16] ls: add --recursive and turn default to non-recursive mode","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T12:24:41Z","receivedAt":"2011-02-09T12:24:41Z","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 This patch should be after 14/16. Otherwise the requirement in path_too_deep()\n is just wrong. Too late to reorder the series for sending.\n\n builtin/ls-files.c |   54 ++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 files changed, 52 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 3195f75..87ee728 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -27,6 +27,7 @@ static int show_killed;\n static int show_valid_bit;\n static int line_terminator = '\\n';\n static int debug_mode;\n+static int depth_limit;\n \n static const char *prefix;\n static int max_prefix_len;\n@@ -63,18 +64,54 @@ static void write_name(const char* name, size_t len)\n \t\t\tline_terminator);\n }\n \n+static const char *path_too_deep(const char *name)\n+{\n+\tint common = 0;\n+\tif (!depth_limit)\n+\t\treturn NULL;\n+\n+\t/* When depth_limit is set, pathspec contains exactly 1 item */\n+\twhile (name[common] &&\n+\t       name[common] == pathspec[0][common])\n+\t\tcommon++;\n+\twhile (common && name[common] != '/')\n+\t\tcommon--;\n+\tif (name[common] == '/')\n+\t\tcommon++;\n+\treturn strchr(name + common, '/');\n+}\n+\n+static int already_shown(const char *name)\n+{\n+\tconst char *last_item;\n+\tint len;\n+\n+\tif (!output.nr)\n+\t\treturn 0;\n+\n+\tlast_item = output.items[output.nr-1].string;\n+\tlen = strlen(last_item);\n+\treturn !strncmp(last_item, name, len) && name[len] == '/';\n+}\n+\n static void show_dir_entry(const char *tag, struct dir_entry *ent)\n {\n \tint len = max_prefix_len;\n+\tconst char *too_deep;\n \n \tif (len >= ent->len)\n \t\tdie(\"git ls-files: internal error - directory entry not superset of prefix\");\n \n+\tif (already_shown(ent->name))\n+\t\treturn;\n+\n+\ttoo_deep = path_too_deep(ent->name);\n \tif (!match_pathspec(pathspec, ent->name, ent->len, len, ps_matched))\n \t\treturn;\n \n+\tlen = too_deep ? too_deep - ent->name : ent->len;\n \tfputs(tag, stdout);\n-\twrite_name(ent->name, ent->len);\n+\twrite_name(ent->name, len);\n }\n \n static void show_other_files(struct dir_struct *dir)\n@@ -141,12 +178,19 @@ static void show_killed_files(struct dir_struct *dir)\n static void show_ce_entry(const char *tag, struct cache_entry *ce)\n {\n \tint len = max_prefix_len;\n+\tconst char *too_deep;\n+\tint namelen;\n \n \tif (len >= ce_namelen(ce))\n \t\tdie(\"git ls-files: internal error - cache entry not superset of prefix\");\n \n+\tif (already_shown(ce->name))\n+\t\treturn;\n+\n+\ttoo_deep = path_too_deep(ce->name);\n \tif (!match_pathspec(pathspec, ce->name, ce_namelen(ce), len, ps_matched))\n \t\treturn;\n+\tnamelen = too_deep ? too_deep - ce->name : ce_namelen(ce);\n \n \tif (tag && *tag && show_valid_bit &&\n \t    (ce->ce_flags & CE_VALID)) {\n@@ -174,7 +218,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)\n \t\t       find_unique_abbrev(ce->sha1,abbrev),\n \t\t       ce_stage(ce));\n \t}\n-\twrite_name(ce->name, ce_namelen(ce));\n+\twrite_name(ce->name, namelen);\n \tif (debug_mode) {\n \t\tprintf(\"  ctime: %d:%d\\n\", ce->ce_ctime.sec, ce->ce_ctime.nsec);\n \t\tprintf(\"  mtime: %d:%d\\n\", ce->ce_mtime.sec, ce->ce_mtime.nsec);\n@@ -635,6 +679,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n {\n \tstruct dir_struct dir;\n+\tint recursive = 0;\n \tstruct option builtin_ls_files_options[] = {\n \t\tOPT_BOOLEAN('c', \"cached\", &show_cached,\n \t\t\t\"show cached files in the output (default)\"),\n@@ -647,6 +692,8 @@ int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n \t\tOPT_BIT('i', \"ignored\", &dir.flags,\n \t\t\t\"show ignored files in the output\",\n \t\t\tDIR_SHOW_IGNORED),\n+\t\tOPT_BOOLEAN('r', \"recursive\", &recursive,\n+\t\t\t    \"list recursively\"),\n \t\tOPT_BOOLEAN('k', \"killed\", &show_killed,\n \t\t\t\"show files on the filesystem that need to be removed\"),\n \t\tOPT_COLUMN(0, \"column\", &column_mode, \"show files in columns\" ),\n@@ -683,6 +730,9 @@ int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n \t      show_killed | show_modified | show_resolve_undo))\n \t\tshow_cached = 1;\n \n+\tif (!recursive)\n+\t\tdepth_limit = 1;\n+\n \tshow_files(&dir);\n \tdisplay_columns(&output, column_mode, term_columns(), 2, NULL);\n \treturn 0;\n-- \n1.7.2.2\n"},{"id":"160761","messageId":"1297254284-3729-15-git-send-email-pclouds@gmail.com","threadId":"26452","inReplyTo":"1297254284-3729-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 14/16] ls: immitate UNIX ls output style","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T12:24:42Z","receivedAt":"2011-02-09T12:24:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Two points:\n\n - When input is a mixed of directories and files, files will be\n   grouped and displayed first. Directory content will follow.\n\n - GNU ls when examining a directory will output files relative to\n   that directory.\n\nIndex does not have directories. There nearest thing to that is\npathspecs that expand to more than one item. So in Git the rules become:\n\n - Multiple match pathspecs is printed with a header, the pathspec,\n   then the content (its expansion). If there is only one pathspec, no\n   header will be printed.\n\n - The common directory prefix of all files from a pathspec will be\n   stripped (not yet implemented)\n\n - One-match pathspecs will be grouped and printed out first like\n   the result of a virtual multiple match pathspec.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Mixing multiple entry types (others and cached) will result in\n duplicates and unsorted output. The command is a porcelain now,\n perhaps we should sort/uniq output?\n\n one_match_pathspec() needs better impl.\n\n builtin/ls-files.c |  101 +++++++++++++++++++++++++++++++++++++++++++++++++---\n 1 files changed, 96 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 87ee728..22e0c87 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -676,10 +676,46 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \treturn 0;\n }\n \n+static int one_match_pathspec(const char *pathspec)\n+{\n+\tstruct stat st;\n+\tint len = strlen(pathspec);\n+\n+\tif (show_cached) {\n+\t\tchar *new_path;\n+\t\tint pos = index_name_pos(&the_index, pathspec, len);\n+\t\tif (pos >= 0)\n+\t\t\treturn 1;\n+\n+\t\tnew_path = xmalloc(len+2);\n+\t\tmemcpy(new_path, pathspec, len);\n+\t\tnew_path[len++] = '/';\n+\t\tnew_path[len] = 0;\n+\t\tpos = index_name_pos(&the_index, pathspec, len);\n+\t\tif (pos >= 0)\n+\t\t\tdie(\"BUG: Wait there are directories in index??\");\n+\t\tpos = -pos-1;\n+\n+\t\t/* dir match */\n+\t\tif (!strncmp(the_index.cache[pos]->name, new_path, len)) {\n+\t\t\tfree(new_path);\n+\t\t\treturn 0;\n+\t\t}\n+\t\tfree(new_path);\n+\t}\n+\n+\tif (!stat(pathspec, &st) && !S_ISDIR(st.st_mode))\n+\t\treturn 1;\n+\n+\treturn 0;\n+}\n+\n int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n {\n+\tint show_pathspec;\n+\tconst char **pathspecs, **one_matches = NULL;\n \tstruct dir_struct dir;\n-\tint recursive = 0;\n+\tint recursive = 0, src, dst, len;\n \tstruct option builtin_ls_files_options[] = {\n \t\tOPT_BOOLEAN('c', \"cached\", &show_cached,\n \t\t\t\"show cached files in the output (default)\"),\n@@ -720,20 +756,75 @@ int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n \tif (dir.flags & DIR_SHOW_IGNORED || show_others)\n \t\tsetup_standard_excludes(&dir);\n \n-\tpathspec = get_pathspec(prefix, argv);\n+\tpathspecs = get_pathspec(prefix, argv);\n \n \tif (show_modified)\n-\t\trefresh_index(&the_index, REFRESH_QUIET, pathspec, NULL, NULL);\n+\t\trefresh_index(&the_index, REFRESH_QUIET, pathspecs, NULL, NULL);\n+\n+\tif (!pathspecs) {\n+\t\tstatic const char *spec[2];\n+\t\tspec[0] = \".\";\n+\t\tspec[1] = NULL;\n+\t\tpathspecs = get_pathspec(prefix, spec);\n+\t}\n \n \t/* With no flags, we default to showing the cached files */\n \tif (!(show_stage | show_deleted | show_others | show_unmerged |\n \t      show_killed | show_modified | show_resolve_undo))\n \t\tshow_cached = 1;\n \n+\t/*\n+\t * Group one-match pathspecs together. Shell expansion may\n+\t * turn foo* to fooa foob/ and fooc. List fooa and fooc, leave\n+\t * foob for later.\n+\t */\n+\tlen = src = dst = 0;\n+\twhile (pathspecs[src]) {\n+\t\tif (one_match_pathspec(pathspecs[src])) {\n+\t\t\tlen++;\n+\t\t\tone_matches = xrealloc(one_matches, sizeof(*one_matches) * (len+1));\n+\t\t\tone_matches[len - 1] = pathspecs[src];\n+\t\t\tsrc++;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (src != dst)\n+\t\t\tpathspecs[dst] = pathspecs[src];\n+\t\tsrc++;\n+\t\tdst++;\n+\t}\n+\tpathspecs[dst] = NULL;\n+\n \tif (!recursive)\n \t\tdepth_limit = 1;\n \n-\tshow_files(&dir);\n-\tdisplay_columns(&output, column_mode, term_columns(), 2, NULL);\n+\tif (len) {\n+\t\tone_matches[len] = NULL;\n+\t\tpathspec = one_matches;\n+\t\tshow_files(&dir);\n+\t\tdisplay_columns(&output, column_mode, term_columns(), 2, NULL);\n+\t\tstring_list_clear(&output, 0);\n+\t\tif (dst)\n+\t\t\tprintf(\"\\n\");\n+\t}\n+\n+\tif (!pathspecs[0])\n+\t\treturn 0;\n+\n+\tshow_pathspec = dst > 1 || len;\n+\tpathspec = xmalloc(sizeof(*pathspec)*2);\n+\tpathspec[1] = NULL;\n+\twhile (*pathspecs) {\n+\t\tpathspec[0] = pathspecs[0];\n+\t\tpathspecs++;\n+\n+\t\tshow_files(&dir);\n+\t\tif (output.nr && show_pathspec)\n+\t\t\tprintf(\"%s:\\n\", pathspec[0]);\n+\n+\t\tdisplay_columns(&output, column_mode, term_columns(), 2, NULL);\n+\t\tstring_list_clear(&output, 0);\n+\t\tif (show_pathspec && pathspecs[0])\n+\t\t\tprintf(\"\\n\");\n+\t}\n \treturn 0;\n }\n-- \n1.7.2.2\n"},{"id":"160763","messageId":"1297254284-3729-16-git-send-email-pclouds@gmail.com","threadId":"26452","inReplyTo":"1297254284-3729-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 15/16] ls: strip common directory prefix from output","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T12:24:43Z","receivedAt":"2011-02-09T12:24:43Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"In GNU ls, files are printed relative to their parent directory.\nWe don't have such thing, so stay as close as we can.\n\nThe result is quite good. If the given pathspec is a directory prefix,\nit will be stripped, strictly following GNU ls behavior. If the pathspec\nhas wildcards, then the command part is stripped. Usually this is the\nprefix part in the pathspec.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/ls-files.c |   37 +++++++++++++++++++++++++++++++++++++\n 1 files changed, 37 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 22e0c87..bc438b2 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -710,6 +710,41 @@ static int one_match_pathspec(const char *pathspec)\n \treturn 0;\n }\n \n+static void remove_common_prefix()\n+{\n+\tint i, len = -1;\n+\tconst char *s1, *s2;\n+\n+\tif (output.nr <= 1)\n+\t\treturn;\n+\n+\t/*\n+\t * Eliminate common prefix. This is caused by a prefix\n+\t * pathspec like \"ls builtin\" or even \"ls 't/t*.sh'\"\n+\t */\n+\n+\ts1 = output.items[output.nr-1].string;\n+\tlen = strlen(s1);\n+\tfor (i = 1; i < output.nr; i++) {\n+\t\tint j = 0;\n+\t\ts1 = output.items[i-1].string;\n+\t\ts2 = output.items[i].string;\n+\t\twhile (j < len && s1[j] == s2[j])\n+\t\t\tj++;\n+\t\tlen = j;\n+\t}\n+\twhile (len && s1[len] != '/')\n+\t\tlen--;\n+\tif (len && s1[len] == '/')\n+\t\tlen++;\n+\tif (len) {\n+\t\tfor (i = 0; i < output.nr; i++) {\n+\t\t\tchar *s = output.items[i].string;\n+\t\t\tmemcpy(s, s+len, strlen(s) - len + 1);\n+\t\t}\n+\t}\n+}\n+\n int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n {\n \tint show_pathspec;\n@@ -801,6 +836,7 @@ int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n \t\tone_matches[len] = NULL;\n \t\tpathspec = one_matches;\n \t\tshow_files(&dir);\n+\t\tremove_common_prefix();\n \t\tdisplay_columns(&output, column_mode, term_columns(), 2, NULL);\n \t\tstring_list_clear(&output, 0);\n \t\tif (dst)\n@@ -821,6 +857,7 @@ int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n \t\tif (output.nr && show_pathspec)\n \t\t\tprintf(\"%s:\\n\", pathspec[0]);\n \n+\t\tremove_common_prefix();\n \t\tdisplay_columns(&output, column_mode, term_columns(), 2, NULL);\n \t\tstring_list_clear(&output, 0);\n \t\tif (show_pathspec && pathspecs[0])\n-- \n1.7.2.2\n"},{"id":"160762","messageId":"1297254284-3729-17-git-send-email-pclouds@gmail.com","threadId":"26452","inReplyTo":"1297254284-3729-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 16/16] ls: color output","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T12:24:44Z","receivedAt":"2011-02-09T12:24:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The rules are currently hardcoded (the first item has highest priority)\n\n - If it's a modified entry, it's bold red.\n - If it's an executable, it's bold green.\n - If it's a directory, it's bold blue.\n\nPersonally I'm happy with just that. But people might want to separate\nother entries from cached ones in \"ls -co\", anyone?\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Bad bad bad hard coding.\n\n builtin/ls-files.c |   59 ++++++++++++++++++++++++++++++++++++++++++++--------\n 1 files changed, 50 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex bc438b2..9c8179a 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -14,6 +14,7 @@\n #include \"resolve-undo.h\"\n #include \"string-list.h\"\n #include \"column.h\"\n+#include \"color.h\"\n \n static int abbrev;\n static int show_deleted;\n@@ -37,6 +38,7 @@ static int error_unmatch;\n static char *ps_matched;\n static const char *with_tree;\n static int exc_given;\n+static int use_color;\n \n static const char *tag_cached = \"\";\n static const char *tag_unmerged = \"\";\n@@ -50,12 +52,18 @@ static const char *tag_resolve_undo = \"\";\n static struct string_list output;\n static int column_mode;\n \n-static void write_name(const char* name, size_t len)\n+static void write_name(const char* name, size_t len, const char *color)\n {\n \t/* No quoting in column layout. It will be done by the end. */\n \tif (column_mode & COL_MODE) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n-\t\tstrbuf_add(&sb, name, len);\n+\t\tif (color && use_color) {\n+\t\t\tstrbuf_addstr(&sb, color);\n+\t\t\tstrbuf_add(&sb, name, len);\n+\t\t\tstrbuf_addstr(&sb, GIT_COLOR_RESET);\n+\t\t}\n+\t\telse\n+\t\t\tstrbuf_add(&sb, name, len);\n \t\tstring_list_append(&output, strbuf_detach(&sb, NULL));\n \t\treturn;\n \t}\n@@ -81,16 +89,29 @@ static const char *path_too_deep(const char *name)\n \treturn strchr(name + common, '/');\n }\n \n+static int ansi_length(const char *s)\n+{\n+\tconst char *p = s;\n+\twhile (p[0] == '\\033')\n+\t\tp += strspn(p + 2, \"0123456789;\") + 3;\n+\treturn p - s;\n+}\n+\n static int already_shown(const char *name)\n {\n \tconst char *last_item;\n-\tint len;\n+\tint len, ansi_len;\n \n \tif (!output.nr)\n \t\treturn 0;\n \n \tlast_item = output.items[output.nr-1].string;\n \tlen = strlen(last_item);\n+\tansi_len = ansi_length(last_item);\n+\tif (ansi_len) {\n+\t\tlast_item += ansi_len;\n+\t\tlen -= ansi_len + strlen(GIT_COLOR_RESET);\n+\t}\n \treturn !strncmp(last_item, name, len) && name[len] == '/';\n }\n \n@@ -98,6 +119,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n {\n \tint len = max_prefix_len;\n \tconst char *too_deep;\n+\tstruct stat st;\n \n \tif (len >= ent->len)\n \t\tdie(\"git ls-files: internal error - directory entry not superset of prefix\");\n@@ -111,7 +133,9 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n \n \tlen = too_deep ? too_deep - ent->name : ent->len;\n \tfputs(tag, stdout);\n-\twrite_name(ent->name, len);\n+\twrite_name(ent->name, len,\n+\t\t   len < ent->len ? GIT_COLOR_BOLD_BLUE :\n+\t\t   (!stat(ent->name, &st) && st.st_mode & S_IXUSR ? GIT_COLOR_BOLD_GREEN : NULL));\n }\n \n static void show_other_files(struct dir_struct *dir)\n@@ -179,6 +203,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)\n {\n \tint len = max_prefix_len;\n \tconst char *too_deep;\n+\tstruct stat st;\n \tint namelen;\n \n \tif (len >= ce_namelen(ce))\n@@ -218,7 +243,10 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)\n \t\t       find_unique_abbrev(ce->sha1,abbrev),\n \t\t       ce_stage(ce));\n \t}\n-\twrite_name(ce->name, namelen);\n+\twrite_name(ce->name, namelen,\n+\t\t   namelen < ce_namelen(ce) ? GIT_COLOR_BOLD_BLUE :\n+\t\t   (!stat(ce->name, &st) && ce_modified(ce, &st, 0) ? GIT_COLOR_BOLD_RED :\n+\t\t    (ce_permissions(ce->ce_mode) == 0755 ? GIT_COLOR_BOLD_GREEN : NULL)));\n \tif (debug_mode) {\n \t\tprintf(\"  ctime: %d:%d\\n\", ce->ce_ctime.sec, ce->ce_ctime.nsec);\n \t\tprintf(\"  mtime: %d:%d\\n\", ce->ce_mtime.sec, ce->ce_mtime.nsec);\n@@ -251,7 +279,7 @@ static void show_ru_info(void)\n \t\t\tprintf(\"%s%06o %s %d\\t\", tag_resolve_undo, ui->mode[i],\n \t\t\t       find_unique_abbrev(ui->sha1[i], abbrev),\n \t\t\t       i + 1);\n-\t\t\twrite_name(path, len);\n+\t\t\twrite_name(path, len, NULL);\n \t\t}\n \t}\n }\n@@ -676,6 +704,16 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \treturn 0;\n }\n \n+static int ls_config(const char *var, const char *value, void *cb)\n+{\n+\tif (!strcmp(var, \"color.ls\"))\n+\t\tuse_color = git_config_colorbool(var, value, -1);\n+\telse\n+\t\treturn git_color_default_config(var, value, cb);\n+\n+\treturn 0;\n+}\n+\n static int one_match_pathspec(const char *pathspec)\n {\n \tstruct stat st;\n@@ -724,11 +762,13 @@ static void remove_common_prefix()\n \t */\n \n \ts1 = output.items[output.nr-1].string;\n-\tlen = strlen(s1);\n+\tlen = strlen(s1) - ansi_length(s1);\n \tfor (i = 1; i < output.nr; i++) {\n \t\tint j = 0;\n \t\ts1 = output.items[i-1].string;\n+\t\ts1 += ansi_length(s1);\n \t\ts2 = output.items[i].string;\n+\t\ts2 += ansi_length(s2);\n \t\twhile (j < len && s1[j] == s2[j])\n \t\t\tj++;\n \t\tlen = j;\n@@ -740,6 +780,7 @@ static void remove_common_prefix()\n \tif (len) {\n \t\tfor (i = 0; i < output.nr; i++) {\n \t\t\tchar *s = output.items[i].string;\n+\t\t\ts += ansi_length(s);\n \t\t\tmemcpy(s, s+len, strlen(s) - len + 1);\n \t\t}\n \t}\n@@ -778,12 +819,12 @@ int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n \tprefix = cmd_prefix;\n \tif (prefix)\n \t\tprefix_len = strlen(prefix);\n-\tgit_config(git_default_config, NULL);\n+\tgit_config(ls_config, NULL);\n \n \tif (read_cache() < 0)\n \t\tdie(\"index file corrupt\");\n \n-\tcolumn_mode = core_column;\n+\tcolumn_mode = core_column | COL_ANSI;\n \n \targc = parse_options(argc, argv, prefix, builtin_ls_files_options,\n \t\t\tls_files_usage, 0);\n@@ -793,7 +834,7 @@ int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n \n \tpathspecs = get_pathspec(prefix, argv);\n \n-\tif (show_modified)\n+\tif (show_modified || use_color)\n \t\trefresh_index(&the_index, REFRESH_QUIET, pathspecs, NULL, NULL);\n \n \tif (!pathspecs) {\n"}]}