{"thread":{"id":"39015","subject":"[PATCH v2 00/25] list-files redesign","startedAt":"2015-04-06T13:52:09Z","lastAt":"2015-04-08T02:39:01Z","messageCount":36,"participants":["Nguyễn Thái Ngọc Duy","Duy Nguyen","Eric Sunshine","Junio C Hamano"],"isPatch":true,"patchVersion":2,"patchTotal":25},"messages":[{"id":"259060","messageId":"1428328354-14897-1-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":null,"subject":"[PATCH v2 00/25] list-files redesign","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:09Z","receivedAt":"2015-04-06T13:52:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The UI part is the same (or nearly the same) with the last round. The\ninternal data structure is reorignized to avoid abusing string_list.\nTests and documentation are added back.\n\nNguyễn Thái Ngọc Duy (25):\n  ls_colors.c: add $LS_COLORS parsing code\n  ls_colors.c: parse color.ls.* from config file\n  ls_colors.c: add a function to color a file name\n  ls_colors.c: highlight submodules like directories\n  list-files: command skeleton\n  list-files: show paths relative to cwd\n  list-files: add tag to each entry, filter duplicate tags\n  list-files: add --[no-]column, -C and -1\n  list-files: add --max-depth, -R and default to --max-depth=0\n  list-files: show directories as well as files\n  list-files: add --color\n  list-files: add -F/--classify\n  list-files: new indicator '&' for submodules when -F is used\n  list-files: add --cached and --others\n  list-files: add --ignored\n  list-files: add --unmerged\n  list-files: add file modification options -[admADM]\n  list-files: delete redundant cached entries\n  list-files: make alias 'ls' default to 'list-files'\n  list-files: preload index\n  list-files: reduce match_pathspec calls in matched()\n  list-files: only do diff that is actually useful\n  pathspec: move getenv() code out of prefix_pathspec()\n  list-files: make :(glob) pathspec default\n  list-files: documentation\n\n .gitignore                             |   1 +\n Documentation/config.txt               |  12 +\n Documentation/git-list-files.txt (new) | 115 +++++++\n Makefile                               |   2 +\n builtin.h                              |   1 +\n builtin/list-files.c (new)             | 581 +++++++++++++++++++++++++++++++++\n color.h                                |  10 +\n config.c                               |   8 +\n git.c                                  |   1 +\n ls_colors.c (new)                      | 498 ++++++++++++++++++++++++++++\n pathspec.c                             |  59 ++--\n pathspec.h                             |   1 +\n t/t7013-list-files.sh (new +x)         | 335 +++++++++++++++++++\n wt-status.c                            |  16 +-\n wt-status.h                            |   3 +\n 15 files changed, 1612 insertions(+), 31 deletions(-)\n create mode 100644 Documentation/git-list-files.txt\n create mode 100644 builtin/list-files.c\n create mode 100644 ls_colors.c\n create mode 100755 t/t7013-list-files.sh\n\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259061","messageId":"1428328354-14897-2-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 01/25] ls_colors.c: add $LS_COLORS parsing code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:10Z","receivedAt":"2015-04-06T13:52:10Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Reusing color settings from $LS_COLORS could give a native look and\nfeel on file coloring.\n\nThis code is basically from coreutils.git [1], rewritten to fit Git.\n\nAs this is from GNU ls, the environment variable CLICOLOR is not\ntested. It is to be decided later whether we should ignore $LS_COLORS\nif $CLICOLOR is not set on Mac or FreeBSD.\n\n[1] commit 7326d1f1a67edf21947ae98194f98c38b6e9e527 file\n    src/ls.c. This is the last GPL-2 commit before coreutils turns to\n    GPL-3.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Makefile          |   1 +\n color.h           |   8 ++\n ls_colors.c (new) | 398 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 407 insertions(+)\n create mode 100644 ls_colors.c\n\ndiff --git a/Makefile b/Makefile\nindex 827006b..459121d 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -703,6 +703,7 @@ LIB_OBJS += list-objects.o\n LIB_OBJS += ll-merge.o\n LIB_OBJS += lockfile.o\n LIB_OBJS += log-tree.o\n+LIB_OBJS += ls_colors.o\n LIB_OBJS += mailmap.o\n LIB_OBJS += match-trees.o\n LIB_OBJS += merge.o\ndiff --git a/color.h b/color.h\nindex f5beab1..3eaa5bd 100644\n--- a/color.h\n+++ b/color.h\n@@ -45,6 +45,12 @@ struct strbuf;\n #define GIT_COLOR_BG_MAGENTA\t\"\\033[45m\"\n #define GIT_COLOR_BG_CYAN\t\"\\033[46m\"\n \n+#define GIT_COLOR_WHITE_ON_RED    \"\\033[37;41m\"\n+#define GIT_COLOR_WHITE_ON_BLUE   \"\\033[37;44m\"\n+#define GIT_COLOR_BLACK_ON_YELLOW \"\\033[30;43m\"\n+#define GIT_COLOR_BLUE_ON_GREEN   \"\\033[34;42m\"\n+#define GIT_COLOR_BLACK_ON_GREEN  \"\\033[30;42m\"\n+\n /* A special value meaning \"no color selected\" */\n #define GIT_COLOR_NIL \"NIL\"\n \n@@ -87,4 +93,6 @@ void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb);\n \n int color_is_nil(const char *color);\n \n+void parse_ls_color(void);\n+\n #endif /* COLOR_H */\ndiff --git a/ls_colors.c b/ls_colors.c\nnew file mode 100644\nindex 0000000..e743315\n--- /dev/null\n+++ b/ls_colors.c\n@@ -0,0 +1,398 @@\n+#include \"cache.h\"\n+#include \"color.h\"\n+\n+enum color_ls {\n+\tLS_LC,\t\t\t/* left, unused */\n+\tLS_RC,\t\t\t/* right, unused */\n+\tLS_EC,\t\t\t/* end color, unused */\n+\tLS_RS,\t\t\t/* reset */\n+\tLS_NO,\t\t\t/* normal */\n+\tLS_FL,\t\t\t/* file, default */\n+\tLS_DI,\t\t\t/* directory */\n+\tLS_LN,\t\t\t/* symlink */\n+\n+\tLS_PI,\t\t\t/* pipe */\n+\tLS_SO,\t\t\t/* socket */\n+\tLS_BD,\t\t\t/* block device */\n+\tLS_CD,\t\t\t/* char device */\n+\tLS_MI,\t\t\t/* missing file */\n+\tLS_OR,\t\t\t/* orphaned symlink */\n+\tLS_EX,\t\t\t/* executable */\n+\tLS_DO,\t\t\t/* Solaris door */\n+\n+\tLS_SU,\t\t\t/* setuid */\n+\tLS_SG,\t\t\t/* setgid */\n+\tLS_ST,\t\t\t/* sticky */\n+\tLS_OW,\t\t\t/* other-writable */\n+\tLS_TW,\t\t\t/* ow with sticky */\n+\tLS_CA,\t\t\t/* cap */\n+\tLS_MH,\t\t\t/* multi hardlink */\n+\tLS_CL,\t\t\t/* clear end of line */\n+\n+\tMAX_LS\n+};\n+\n+static char ls_colors[MAX_LS][COLOR_MAXLEN] = {\n+\t\"\",\n+\t\"\",\n+\t\"\",\n+\tGIT_COLOR_RESET,\n+\tGIT_COLOR_NORMAL,\n+\tGIT_COLOR_NORMAL,\n+\tGIT_COLOR_BOLD_BLUE,\n+\tGIT_COLOR_BOLD_CYAN,\n+\n+\tGIT_COLOR_YELLOW,\n+\tGIT_COLOR_BOLD_MAGENTA,\n+\tGIT_COLOR_BOLD_YELLOW,\n+\tGIT_COLOR_BOLD_YELLOW,\n+\tGIT_COLOR_NORMAL,\n+\tGIT_COLOR_NORMAL,\n+\tGIT_COLOR_BOLD_GREEN,\n+\tGIT_COLOR_BOLD_MAGENTA,\n+\n+\tGIT_COLOR_WHITE_ON_RED,\n+\tGIT_COLOR_BLACK_ON_YELLOW,\n+\tGIT_COLOR_WHITE_ON_BLUE,\n+\tGIT_COLOR_BLUE_ON_GREEN,\n+\tGIT_COLOR_BLACK_ON_GREEN,\n+\t\"\",\n+\t\"\",\n+\t\"\"\n+};\n+\n+static const char *const indicator_name[] = {\n+\t\"lc\", \"rc\", \"ec\", \"rs\", \"no\", \"fi\", \"di\", \"ln\",\n+\t\"pi\", \"so\", \"bd\", \"cd\", \"mi\", \"or\", \"ex\", \"do\",\n+\t\"su\", \"sg\", \"st\", \"ow\", \"tw\", \"ca\", \"mh\", \"cl\",\n+\tNULL\n+};\n+\n+struct bin_str {\n+\tsize_t len;\t\t\t/* Number of bytes */\n+\tconst char *string;\t\t/* Pointer to the same */\n+};\n+\n+struct color_ext_type {\n+\tstruct bin_str ext;\t\t/* The extension we're looking for */\n+\tstruct bin_str seq;\t\t/* The sequence to output when we do */\n+\tstruct color_ext_type *next;\t/* Next in list */\n+};\n+\n+static struct color_ext_type *color_ext_list;\n+\n+/*\n+ * When true, in a color listing, color each symlink name according to the\n+ * type of file it points to.  Otherwise, color them according to the `ln'\n+ * directive in LS_COLORS.  Dangling (orphan) symlinks are treated specially,\n+ * regardless.  This is set when `ln=target' appears in LS_COLORS.\n+ */\n+static int color_symlink_as_referent;\n+\n+/*\n+ * Parse a string as part of the LS_COLORS variable; this may involve\n+ * decoding all kinds of escape characters.  If equals_end is set an\n+ * unescaped equal sign ends the string, otherwise only a : or \\0\n+ * does.  Set *OUTPUT_COUNT to the number of bytes output.  Return\n+ * true if successful.\n+ *\n+ * The resulting string is *not* null-terminated, but may contain\n+ * embedded nulls.\n+ *\n+ * Note that both dest and src are char **; on return they point to\n+ * the first free byte after the array and the character that ended\n+ * the input string, respectively.\n+ */\n+static int get_funky_string(char **dest, const char **src, int equals_end,\n+\t\t\t    size_t *output_count)\n+{\n+\tchar num;\t\t\t/* For numerical codes */\n+\tsize_t count;\t\t\t/* Something to count with */\n+\tenum {\n+\t\tST_GND, ST_BACKSLASH, ST_OCTAL, ST_HEX,\n+\t\tST_CARET, ST_END, ST_ERROR\n+\t} state;\n+\tconst char *p;\n+\tchar *q;\n+\n+\tp = *src;\t\t\t/* We don't want to double-indirect */\n+\tq = *dest;\t\t\t/* the whole darn time.  */\n+\n+\tcount = 0;\t\t\t/* No characters counted in yet.  */\n+\tnum = 0;\n+\n+\tstate = ST_GND;\t\t/* Start in ground state.  */\n+\twhile (state < ST_END) {\n+\t\tswitch (state) {\n+\t\tcase ST_GND:\t\t/* Ground state (no escapes) */\n+\t\t\tswitch (*p) {\n+\t\t\tcase ':':\n+\t\t\tcase '\\0':\n+\t\t\t\tstate = ST_END;\t/* End of string */\n+\t\t\t\tbreak;\n+\t\t\tcase '\\\\':\n+\t\t\t\tstate = ST_BACKSLASH; /* Backslash scape sequence */\n+\t\t\t\t++p;\n+\t\t\t\tbreak;\n+\t\t\tcase '^':\n+\t\t\t\tstate = ST_CARET; /* Caret escape */\n+\t\t\t\t++p;\n+\t\t\t\tbreak;\n+\t\t\tcase '=':\n+\t\t\t\tif (equals_end) {\n+\t\t\t\t\tstate = ST_END; /* End */\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\t/* else fall through */\n+\t\t\tdefault:\n+\t\t\t\t*(q++) = *(p++);\n+\t\t\t\t++count;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase ST_BACKSLASH:\t/* Backslash escaped character */\n+\t\t\tswitch (*p) {\n+\t\t\tcase '0':\n+\t\t\tcase '1':\n+\t\t\tcase '2':\n+\t\t\tcase '3':\n+\t\t\tcase '4':\n+\t\t\tcase '5':\n+\t\t\tcase '6':\n+\t\t\tcase '7':\n+\t\t\t\tstate = ST_OCTAL;\t/* Octal sequence */\n+\t\t\t\tnum = *p - '0';\n+\t\t\t\tbreak;\n+\t\t\tcase 'x':\n+\t\t\tcase 'X':\n+\t\t\t\tstate = ST_HEX;\t/* Hex sequence */\n+\t\t\t\tnum = 0;\n+\t\t\t\tbreak;\n+\t\t\tcase 'a':\t\t/* Bell */\n+\t\t\t\tnum = '\\a';\n+\t\t\t\tbreak;\n+\t\t\tcase 'b':\t\t/* Backspace */\n+\t\t\t\tnum = '\\b';\n+\t\t\t\tbreak;\n+\t\t\tcase 'e':\t\t/* Escape */\n+\t\t\t\tnum = 27;\n+\t\t\t\tbreak;\n+\t\t\tcase 'f':\t\t/* Form feed */\n+\t\t\t\tnum = '\\f';\n+\t\t\t\tbreak;\n+\t\t\tcase 'n':\t\t/* Newline */\n+\t\t\t\tnum = '\\n';\n+\t\t\t\tbreak;\n+\t\t\tcase 'r':\t\t/* Carriage return */\n+\t\t\t\tnum = '\\r';\n+\t\t\t\tbreak;\n+\t\t\tcase 't':\t\t/* Tab */\n+\t\t\t\tnum = '\\t';\n+\t\t\t\tbreak;\n+\t\t\tcase 'v':\t\t/* Vtab */\n+\t\t\t\tnum = '\\v';\n+\t\t\t\tbreak;\n+\t\t\tcase '?':\t\t/* Delete */\n+\t\t\t\tnum = 127;\n+\t\t\t\tbreak;\n+\t\t\tcase '_':\t\t/* Space */\n+\t\t\t\tnum = ' ';\n+\t\t\t\tbreak;\n+\t\t\tcase '\\0':\t\t/* End of string */\n+\t\t\t\tstate = ST_ERROR;\t/* Error! */\n+\t\t\t\tbreak;\n+\t\t\tdefault:\t\t/* Escaped character like \\ ^ : = */\n+\t\t\t\tnum = *p;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tif (state == ST_BACKSLASH) {\n+\t\t\t\t*(q++) = num;\n+\t\t\t\t++count;\n+\t\t\t\tstate = ST_GND;\n+\t\t\t}\n+\t\t\t++p;\n+\t\t\tbreak;\n+\n+\t\tcase ST_OCTAL:\t\t/* Octal sequence */\n+\t\t\tif (*p < '0' || *p > '7') {\n+\t\t\t\t*(q++) = num;\n+\t\t\t\t++count;\n+\t\t\t\tstate = ST_GND;\n+\t\t\t} else\n+\t\t\t\tnum = (num << 3) + (*(p++) - '0');\n+\t\t\tbreak;\n+\n+\t\tcase ST_HEX:\t\t/* Hex sequence */\n+\t\t\tswitch (*p) {\n+\t\t\tcase '0':\n+\t\t\tcase '1':\n+\t\t\tcase '2':\n+\t\t\tcase '3':\n+\t\t\tcase '4':\n+\t\t\tcase '5':\n+\t\t\tcase '6':\n+\t\t\tcase '7':\n+\t\t\tcase '8':\n+\t\t\tcase '9':\n+\t\t\t\tnum = (num << 4) + (*(p++) - '0');\n+\t\t\t\tbreak;\n+\t\t\tcase 'a':\n+\t\t\tcase 'b':\n+\t\t\tcase 'c':\n+\t\t\tcase 'd':\n+\t\t\tcase 'e':\n+\t\t\tcase 'f':\n+\t\t\t\tnum = (num << 4) + (*(p++) - 'a') + 10;\n+\t\t\t\tbreak;\n+\t\t\tcase 'A':\n+\t\t\tcase 'B':\n+\t\t\tcase 'C':\n+\t\t\tcase 'D':\n+\t\t\tcase 'E':\n+\t\t\tcase 'F':\n+\t\t\t\tnum = (num << 4) + (*(p++) - 'A') + 10;\n+\t\t\t\tbreak;\n+\t\t\tdefault:\n+\t\t\t\t*(q++) = num;\n+\t\t\t\t++count;\n+\t\t\t\tstate = ST_GND;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase ST_CARET:\t\t/* Caret escape */\n+\t\t\tstate = ST_GND;\t/* Should be the next state... */\n+\t\t\tif (*p >= '@' && *p <= '~') {\n+\t\t\t\t*(q++) = *(p++) & 037;\n+\t\t\t\t++count;\n+\t\t\t} else if (*p == '?') {\n+\t\t\t\t*(q++) = 127;\n+\t\t\t\t++count;\n+\t\t\t} else\n+\t\t\t\tstate = ST_ERROR;\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\tabort();\n+\t\t}\n+\t}\n+\n+\t*dest = q;\n+\t*src = p;\n+\t*output_count = count;\n+\n+\treturn state != ST_ERROR;\n+}\n+\n+void parse_ls_color(void)\n+{\n+\tconst char *p;\t\t\t/* Pointer to character being parsed */\n+\tchar *buf;\t\t\t/* color_buf buffer pointer */\n+\tint state;\t\t\t/* State of parser */\n+\tint ind_no;\t\t\t/* Indicator number */\n+\tchar label[3];\t\t\t/* Indicator label */\n+\tstruct color_ext_type *ext;\t/* Extension we are working on */\n+\tstatic char *color_buf;\n+\tchar *start;\n+\tsize_t len;\n+\n+\tif ((p = getenv(\"LS_COLORS\")) == NULL || *p == '\\0')\n+\t\treturn;\n+\n+\text = NULL;\n+\tstrcpy(label, \"??\");\n+\n+\t/*\n+\t * This is an overly conservative estimate, but any possible\n+\t * LS_COLORS string will *not* generate a color_buf longer\n+\t * than itself, so it is a safe way of allocating a buffer in\n+\t * advance.\n+\t */\n+\tbuf = color_buf = xstrdup(p);\n+\n+\tstate = 1;\n+\twhile (state > 0) {\n+\t\tswitch (state) {\n+\t\tcase 1:\t\t/* First label character */\n+\t\t\tswitch (*p) {\n+\t\t\tcase ':':\n+\t\t\t\t++p;\n+\t\t\t\tbreak;\n+\n+\t\t\tcase '*':\n+\t\t\t\t/*\n+\t\t\t\t * Allocate new extension block and add to head of\n+\t\t\t\t * linked list (this way a later definition will\n+\t\t\t\t * override an earlier one, which can be useful for\n+\t\t\t\t * having terminal-specific defs override global).\n+\t\t\t\t */\n+\n+\t\t\t\text = xmalloc(sizeof(*ext));\n+\t\t\t\text->next = color_ext_list;\n+\t\t\t\tcolor_ext_list = ext;\n+\n+\t\t\t\t++p;\n+\t\t\t\text->ext.string = buf;\n+\n+\t\t\t\tstate = (get_funky_string(&buf, &p, 1, &ext->ext.len)\n+\t\t\t\t\t ? 4 : -1);\n+\t\t\t\tbreak;\n+\n+\t\t\tcase '\\0':\n+\t\t\t\tstate = 0;\t/* Done! */\n+\t\t\t\tbreak;\n+\n+\t\t\tdefault:\t/* Assume it is file type label */\n+\t\t\t\tlabel[0] = *(p++);\n+\t\t\t\tstate = 2;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase 2:\t\t/* Second label character */\n+\t\t\tif (*p) {\n+\t\t\t\tlabel[1] = *(p++);\n+\t\t\t\tstate = 3;\n+\t\t\t} else\n+\t\t\t\tstate = -1;\t/* Error */\n+\t\t\tbreak;\n+\n+\t\tcase 3:\t\t/* Equal sign after indicator label */\n+\t\t\tstate = -1;\t/* Assume failure...  */\n+\t\t\tif (*(p++) != '=')\n+\t\t\t\tbreak;\n+\t\t\tfor (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no) {\n+\t\t\t\tif (!strcmp(label, indicator_name[ind_no])) {\n+\t\t\t\t\tstart = buf;\n+\t\t\t\t\tif (get_funky_string(&buf, &p, 0, &len))\n+\t\t\t\t\t\tstate = 1;\n+\t\t\t\t\telse\n+\t\t\t\t\t\tstate = -1;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (state == -1)\n+\t\t\t\terror(_(\"unrecognized prefix: %s\"), label);\n+\t\t\telse if (ind_no == LS_LN && len == 6 &&\n+\t\t\t\t starts_with(start, \"target\"))\n+\t\t\t\tcolor_symlink_as_referent = 1;\n+\t\t\telse\n+\t\t\t\tsprintf(ls_colors[ind_no], \"\\033[%.*sm\",\n+\t\t\t\t       (int)len, start);\n+\t\t\tbreak;\n+\n+\t\tcase 4:\t\t/* Equal sign after *.ext */\n+\t\t\tif (*(p++) == '=') {\n+\t\t\t\text->seq.string = buf;\n+\t\t\t\tstate = (get_funky_string(&buf, &p, 0, &ext->seq.len)\n+\t\t\t\t\t ? 1 : -1);\n+\t\t\t} else\n+\t\t\t\tstate = -1;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (!strcmp(ls_colors[LS_LN], \"target\"))\n+\t\tcolor_symlink_as_referent = 1;\n+}\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259062","messageId":"1428328354-14897-3-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 02/25] ls_colors.c: parse color.ls.* from config file","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:11Z","receivedAt":"2015-04-06T13:52:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is the second (and preferred) source for color information. This\nwill override $LS_COLORS.\n\nHelped-by: Michael Blume <blume.mike@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config.txt | 11 +++++++++++\n ls_colors.c              | 30 +++++++++++++++++++++++++++++-\n 2 files changed, 40 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 9220725..2090866 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -940,6 +940,17 @@ color.status.<slot>::\n \tto red). The values of these variables may be specified as in\n \tcolor.branch.<slot>.\n \n+color.ls.<slot>::\n+\tUse customized color for file name colorization. If not set\n+\tand the environment variable LS_COLORS is set, color settings\n+\tfrom $LS_COLORS are used. `<slot>` can be `normal`, `file`,\n+\t`directory`, `symlink`, `fifo`, `socket`, `block`, `char`,\n+\t`missing`, `orphan`, `executable`, `door`, `setuid`, `setgid`,\n+\t`sticky`, `otherwritable`, `stickyotherwritable`, `cap`,\n+\t`multihardlink`. The values of these variables may be\n+\tspecified as in color.branch.<slot>.\n+\n+\n color.ui::\n \tThis variable determines the default value for variables such\n \tas `color.diff` and `color.grep` that control the use of color\ndiff --git a/ls_colors.c b/ls_colors.c\nindex e743315..2dc2d39 100644\n--- a/ls_colors.c\n+++ b/ls_colors.c\n@@ -68,6 +68,14 @@ static const char *const indicator_name[] = {\n \tNULL\n };\n \n+static const char * const config_name[] = {\n+\t\"\", \"\", \"\", \"\", \"normal\", \"file\", \"directory\", \"symlink\",\n+\t\"fifo\", \"socket\", \"block\", \"char\", \"missing\", \"orphan\", \"executable\",\n+\t\"door\", \"setuid\", \"setgid\", \"sticky\", \"otherwritable\",\n+\t\"stickyotherwritable\", \"cap\", \"multihardlink\", \"\",\n+\tNULL\n+};\n+\n struct bin_str {\n \tsize_t len;\t\t\t/* Number of bytes */\n \tconst char *string;\t\t/* Pointer to the same */\n@@ -285,6 +293,23 @@ static int get_funky_string(char **dest, const char **src, int equals_end,\n \treturn state != ST_ERROR;\n }\n \n+static int ls_colors_config(const char *var, const char *value, void *cb)\n+{\n+\tint slot;\n+\tif (!starts_with(var, \"color.ls.\"))\n+\t\treturn 0;\n+\tvar += 9;\n+\tfor (slot = 0; config_name[slot]; slot++)\n+\t\tif (!strcasecmp(var, config_name[slot]))\n+\t\t\tbreak;\n+\tif (!config_name[slot])\n+\t\treturn 0;\n+\tif (!value)\n+\t\treturn config_error_nonbool(var);\n+\tcolor_parse(value, ls_colors[slot]);\n+\treturn 0;\n+}\n+\n void parse_ls_color(void)\n {\n \tconst char *p;\t\t\t/* Pointer to character being parsed */\n@@ -297,8 +322,10 @@ void parse_ls_color(void)\n \tchar *start;\n \tsize_t len;\n \n-\tif ((p = getenv(\"LS_COLORS\")) == NULL || *p == '\\0')\n+\tif ((p = getenv(\"LS_COLORS\")) == NULL || *p == '\\0') {\n+\t\tgit_config(ls_colors_config, NULL);\n \t\treturn;\n+\t}\n \n \text = NULL;\n \tstrcpy(label, \"??\");\n@@ -395,4 +422,5 @@ void parse_ls_color(void)\n \n \tif (!strcmp(ls_colors[LS_LN], \"target\"))\n \t\tcolor_symlink_as_referent = 1;\n+\tgit_config(ls_colors_config, NULL);\n }\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259063","messageId":"1428328354-14897-4-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 03/25] ls_colors.c: add a function to color a file name","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:12Z","receivedAt":"2015-04-06T13:52:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The new function is based on print_color_indicator() from commit\n7326d1f1a67edf21947ae98194f98c38b6e9e527 in coreutils.git.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n color.h     |  2 ++\n ls_colors.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 68 insertions(+)\n\ndiff --git a/color.h b/color.h\nindex 3eaa5bd..b6904a3 100644\n--- a/color.h\n+++ b/color.h\n@@ -94,5 +94,7 @@ void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb);\n int color_is_nil(const char *color);\n \n void parse_ls_color(void);\n+void color_filename(struct strbuf *sb, const char *name,\n+\t\t    const char *display_name, mode_t mode, int linkok);\n \n #endif /* COLOR_H */\ndiff --git a/ls_colors.c b/ls_colors.c\nindex 2dc2d39..f84ef0f 100644\n--- a/ls_colors.c\n+++ b/ls_colors.c\n@@ -424,3 +424,69 @@ void parse_ls_color(void)\n \t\tcolor_symlink_as_referent = 1;\n \tgit_config(ls_colors_config, NULL);\n }\n+\n+void color_filename(struct strbuf *sb, const char *name,\n+\t\t    const char *display_name, mode_t mode, int linkok)\n+{\n+\tint type;\n+\tstruct color_ext_type *ext;\t/* Color extension */\n+\n+\tif (S_ISREG(mode)) {\n+\t\ttype = LS_FL;\n+\t\tif ((mode & S_ISUID) != 0)\n+\t\t\ttype = LS_SU;\n+\t\telse if ((mode & S_ISGID) != 0)\n+\t\t\ttype = LS_SG;\n+\t\telse if ((mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)\n+\t\t\ttype = LS_EX;\n+\t} else if (S_ISDIR(mode)) {\n+\t\tif ((mode & S_ISVTX) && (mode & S_IWOTH))\n+\t\t\ttype = LS_TW;\n+\t\telse if ((mode & S_IWOTH) != 0)\n+\t\t\ttype = LS_OW;\n+\t\telse if ((mode & S_ISVTX) != 0)\n+\t\t\ttype = LS_ST;\n+\t\telse\n+\t\t\ttype = LS_DI;\n+\t} else if (S_ISLNK(mode))\n+\t\ttype = (!linkok && *ls_colors[LS_OR]) ? LS_OR : LS_LN;\n+\telse if (S_ISFIFO(mode))\n+\t\ttype = LS_PI;\n+\telse if (S_ISSOCK(mode))\n+\t\ttype = LS_SO;\n+\telse if (S_ISBLK(mode))\n+\t\ttype = LS_BD;\n+\telse if (S_ISCHR(mode))\n+\t\ttype = LS_CD;\n+#ifdef S_ISDOOR\n+\telse if (S_ISDOOR(mode))\n+\t\ttype = LS_DO;\n+#endif\n+\telse\n+\t\t/* Classify a file of some other type as C_ORPHAN.  */\n+\t\ttype = LS_OR;\n+\n+\t/* Check the file's suffix only if still classified as C_FILE.  */\n+\text = NULL;\n+\tif (type == LS_FL) {\n+\t\t/* Test if NAME has a recognized suffix.  */\n+\t\tsize_t len = strlen(name);\n+\t\tconst char *p = name + len;\t\t/* Pointer to final \\0.  */\n+\t\tfor (ext = color_ext_list; ext != NULL; ext = ext->next) {\n+\t\t\tif (ext->ext.len <= len &&\n+\t\t\t    !strncmp(p - ext->ext.len, ext->ext.string, ext->ext.len))\n+\t\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (display_name)\n+\t\tname = display_name;\n+\tif (ext)\n+\t\tstrbuf_addf(sb, \"\\033[%.*sm%s%s\",\n+\t\t\t    (int)ext->seq.len, ext->seq.string,\n+\t\t\t    name, GIT_COLOR_RESET);\n+\telse if (*ls_colors[type])\n+\t\tstrbuf_addf(sb, \"%s%s%s\", ls_colors[type], name, GIT_COLOR_RESET);\n+\telse\n+\t\tstrbuf_addstr(sb, name);\n+}\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259064","messageId":"1428328354-14897-5-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 04/25] ls_colors.c: highlight submodules like directories","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:13Z","receivedAt":"2015-04-06T13:52:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config.txt | 3 ++-\n ls_colors.c              | 8 +++++++-\n 2 files changed, 9 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 2090866..2290c47 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -944,7 +944,8 @@ color.ls.<slot>::\n \tUse customized color for file name colorization. If not set\n \tand the environment variable LS_COLORS is set, color settings\n \tfrom $LS_COLORS are used. `<slot>` can be `normal`, `file`,\n-\t`directory`, `symlink`, `fifo`, `socket`, `block`, `char`,\n+\t`directory`, `submodule`,\n+\t`symlink`, `fifo`, `socket`, `block`, `char`,\n \t`missing`, `orphan`, `executable`, `door`, `setuid`, `setgid`,\n \t`sticky`, `otherwritable`, `stickyotherwritable`, `cap`,\n \t`multihardlink`. The values of these variables may be\ndiff --git a/ls_colors.c b/ls_colors.c\nindex f84ef0f..011a8b9 100644\n--- a/ls_colors.c\n+++ b/ls_colors.c\n@@ -29,6 +29,8 @@ enum color_ls {\n \tLS_MH,\t\t\t/* multi hardlink */\n \tLS_CL,\t\t\t/* clear end of line */\n \n+\tLS_SUBMODULE,\n+\n \tMAX_LS\n };\n \n@@ -58,7 +60,8 @@ static char ls_colors[MAX_LS][COLOR_MAXLEN] = {\n \tGIT_COLOR_BLACK_ON_GREEN,\n \t\"\",\n \t\"\",\n-\t\"\"\n+\t\"\",\n+\tGIT_COLOR_BOLD_BLUE\n };\n \n static const char *const indicator_name[] = {\n@@ -73,6 +76,7 @@ static const char * const config_name[] = {\n \t\"fifo\", \"socket\", \"block\", \"char\", \"missing\", \"orphan\", \"executable\",\n \t\"door\", \"setuid\", \"setgid\", \"sticky\", \"otherwritable\",\n \t\"stickyotherwritable\", \"cap\", \"multihardlink\", \"\",\n+\t\"submodule\",\n \tNULL\n };\n \n@@ -450,6 +454,8 @@ void color_filename(struct strbuf *sb, const char *name,\n \t\t\ttype = LS_DI;\n \t} else if (S_ISLNK(mode))\n \t\ttype = (!linkok && *ls_colors[LS_OR]) ? LS_OR : LS_LN;\n+\telse if (S_ISGITLINK(mode))\n+\t\ttype = LS_SUBMODULE;\n \telse if (S_ISFIFO(mode))\n \t\ttype = LS_PI;\n \telse if (S_ISSOCK(mode))\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259065","messageId":"1428328354-14897-6-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 05/25] list-files: command skeleton","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:14Z","receivedAt":"2015-04-06T13:52:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"list-files is supposed to be the user friendly version of ls-files, or\nan alternative to git-status. Nothing fancy in this patch yet.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore                     |   1 +\n Makefile                       |   1 +\n builtin.h                      |   1 +\n builtin/list-files.c (new)     | 107 +++++++++++++++++++++++++++++++++++++++++\n git.c                          |   1 +\n t/t7013-list-files.sh (new +x) |  35 ++++++++++++++\n 6 files changed, 146 insertions(+)\n create mode 100644 builtin/list-files.c\n create mode 100755 t/t7013-list-files.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex a052419..0534225 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -77,6 +77,7 @@\n /git-interpret-trailers\n /git-instaweb\n /git-log\n+/git-list-files\n /git-ls-files\n /git-ls-remote\n /git-ls-tree\ndiff --git a/Makefile b/Makefile\nindex 459121d..17f52b2 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -832,6 +832,7 @@ BUILTIN_OBJS += builtin/index-pack.o\n BUILTIN_OBJS += builtin/init-db.o\n BUILTIN_OBJS += builtin/interpret-trailers.o\n BUILTIN_OBJS += builtin/log.o\n+BUILTIN_OBJS += builtin/list-files.o\n BUILTIN_OBJS += builtin/ls-files.o\n BUILTIN_OBJS += builtin/ls-remote.o\n BUILTIN_OBJS += builtin/ls-tree.o\ndiff --git a/builtin.h b/builtin.h\nindex b87df70..afc29e7 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -76,6 +76,7 @@ extern int cmd_init_db(int argc, const char **argv, const char *prefix);\n extern int cmd_interpret_trailers(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_list_files(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/list-files.c b/builtin/list-files.c\nnew file mode 100644\nindex 0000000..8b74d79\n--- /dev/null\n+++ b/builtin/list-files.c\n@@ -0,0 +1,107 @@\n+#include \"cache.h\"\n+#include \"builtin.h\"\n+#include \"parse-options.h\"\n+#include \"pathspec.h\"\n+#include \"dir.h\"\n+\n+enum item_type {\n+\tFROM_INDEX\n+};\n+\n+struct item {\n+\tenum item_type type;\n+\tconst char *path;\n+\tconst struct cache_entry *ce;\n+};\n+\n+struct item_list {\n+\tstruct item *items;\n+\tint nr, alloc;\n+};\n+\n+static struct pathspec pathspec;\n+static const char *prefix;\n+static int prefix_length;\n+\n+static const char * const ls_usage[] = {\n+\tN_(\"git list-files [options] [<pathspec>...]\"),\n+\tNULL\n+};\n+\n+struct option ls_options[] = {\n+\tOPT_END()\n+};\n+\n+static void populate_cached_entries(struct item_list *result,\n+\t\t\t\t    const struct index_state *istate)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < istate->cache_nr; i++) {\n+\t\tconst struct cache_entry *ce = istate->cache[i];\n+\t\tstruct item *item;\n+\n+\t\tif (!match_pathspec(&pathspec, ce->name, ce_namelen(ce),\n+\t\t\t\t    0, NULL,\n+\t\t\t\t    S_ISDIR(ce->ce_mode) ||\n+\t\t\t\t    S_ISGITLINK(ce->ce_mode)))\n+\t\t\tcontinue;\n+\n+\t\tALLOC_GROW(result->items, result->nr + 1, result->alloc);\n+\t\titem = result->items + result->nr++;\n+\t\titem->type = FROM_INDEX;\n+\t\titem->path = ce->name;\n+\t\titem->ce = ce;\n+\t}\n+}\n+\n+static void display(const struct item_list *result)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < result->nr; i++) {\n+\t\tconst struct item *item = result->items + i;\n+\n+\t\tprintf(\"%s\\n\", item->path);\n+\t}\n+}\n+\n+static int ls_config(const char *var, const char *value, void *cb)\n+{\n+\treturn git_default_config(var, value, cb);\n+}\n+\n+int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n+{\n+\tstruct item_list result;\n+\n+\tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n+\t\tusage_with_options(ls_usage, ls_options);\n+\n+\tprefix = cmd_prefix;\n+\tif (prefix)\n+\t\tprefix_length = strlen(prefix);\n+\n+\tif (read_cache() < 0)\n+\t\tdie(_(\"index file corrupt\"));\n+\n+\tgit_config(ls_config, NULL);\n+\n+\targc = parse_options(argc, argv, prefix, ls_options, ls_usage, 0);\n+\n+\tparse_pathspec(&pathspec, 0,\n+\t\t       PATHSPEC_PREFER_CWD |\n+\t\t       PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,\n+\t\t       cmd_prefix, argv);\n+\tpathspec.max_depth = 0;\n+\tpathspec.recursive = 1;\n+\n+\trefresh_index(&the_index, REFRESH_QUIET | REFRESH_UNMERGED,\n+\t\t      &pathspec, NULL, NULL);\n+\n+\tmemset(&result, 0, sizeof(result));\n+\tpopulate_cached_entries(&result, &the_index);\n+\tdisplay(&result);\n+\t/* free-ing result seems unnecessary */\n+\treturn 0;\n+}\ndiff --git a/git.c b/git.c\nindex 18fbf79..ae7fe77 100644\n--- a/git.c\n+++ b/git.c\n@@ -418,6 +418,7 @@ static struct cmd_struct commands[] = {\n \t{ \"init\", cmd_init_db, NO_SETUP },\n \t{ \"init-db\", cmd_init_db, NO_SETUP },\n \t{ \"interpret-trailers\", cmd_interpret_trailers, RUN_SETUP },\n+\t{ \"list-files\", cmd_list_files, RUN_SETUP | USE_PAGER | NEED_WORK_TREE },\n \t{ \"log\", cmd_log, RUN_SETUP },\n \t{ \"ls-files\", cmd_ls_files, RUN_SETUP },\n \t{ \"ls-remote\", cmd_ls_remote, RUN_SETUP_GENTLY },\ndiff --git a/t/t7013-list-files.sh b/t/t7013-list-files.sh\nnew file mode 100755\nindex 0000000..f43776e\n--- /dev/null\n+++ b/t/t7013-list-files.sh\n@@ -0,0 +1,35 @@\n+#!/bin/sh\n+\n+test_description='list-files'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\tmkdir sa sa/sb sc &&\n+\ttouch a b c sa/a sa/sb/b sc/c &&\n+\tgit add .\n+'\n+\n+test_expect_success 'list-files from index' '\n+\tgit list-files >actual &&\n+\tcat >expect <<-\\EOF &&\n+\ta\n+\tb\n+\tc\n+\tsa/a\n+\tsa/sb/b\n+\tsc/c\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'list-files selectively from index' '\n+\tgit list-files \"*a\" >actual &&\n+\tcat >expect <<-\\EOF &&\n+\ta\n+\tsa/a\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259067","messageId":"1428328354-14897-7-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 06/25] list-files: show paths relative to cwd","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:15Z","receivedAt":"2015-04-06T13:52:15Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c  |  9 ++++++++-\n t/t7013-list-files.sh | 24 ++++++++++++++++++++++++\n 2 files changed, 32 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 8b74d79..51ff19d 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -3,6 +3,7 @@\n #include \"parse-options.h\"\n #include \"pathspec.h\"\n #include \"dir.h\"\n+#include \"quote.h\"\n \n enum item_type {\n \tFROM_INDEX\n@@ -57,13 +58,19 @@ static void populate_cached_entries(struct item_list *result,\n \n static void display(const struct item_list *result)\n {\n+\tstruct strbuf quoted = STRBUF_INIT;\n \tint i;\n \n+\tif (!prefix_length)\n+\t\tprefix = NULL;\n+\n \tfor (i = 0; i < result->nr; i++) {\n \t\tconst struct item *item = result->items + i;\n \n-\t\tprintf(\"%s\\n\", item->path);\n+\t\tquote_path_relative(item->path, prefix, &quoted);\n+\t\tprintf(\"%s\\n\", quoted.buf);\n \t}\n+\tstrbuf_release(&quoted);\n }\n \n static int ls_config(const char *var, const char *value, void *cb)\ndiff --git a/t/t7013-list-files.sh b/t/t7013-list-files.sh\nindex f43776e..a5e555d 100755\n--- a/t/t7013-list-files.sh\n+++ b/t/t7013-list-files.sh\n@@ -32,4 +32,28 @@ test_expect_success 'list-files selectively from index' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'list-files from subdir ' '\n+\t(\n+\tcd sa &&\n+\tgit list-files >actual &&\n+\tcat >expect <<-\\EOF &&\n+\ta\n+\tsb/b\n+\tEOF\n+\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'list-files from subdir (2)' '\n+\t(\n+\tcd sa &&\n+\tgit list-files ../a sb >actual &&\n+\tcat >expect <<-\\EOF &&\n+\t../a\n+\tsb/b\n+\tEOF\n+\ttest_cmp expect actual\n+\t)\n+'\n+\n test_done\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259066","messageId":"1428328354-14897-8-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 07/25] list-files: add tag to each entry, filter duplicate tags","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:16Z","receivedAt":"2015-04-06T13:52:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"All entries have a two-letter tag. If all entries have the same tags,\ntags are not displayed.\n\nThe outcome before and after this patch is the same. But it will be\nuseful in future when there are more than one type of entry.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 63 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 51ff19d..ec9ca17 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -12,17 +12,20 @@ enum item_type {\n struct item {\n \tenum item_type type;\n \tconst char *path;\n+\tchar tag[2];\n \tconst struct cache_entry *ce;\n };\n \n struct item_list {\n \tstruct item *items;\n \tint nr, alloc;\n+\tint tag_pos, tag_len;\n };\n \n static struct pathspec pathspec;\n static const char *prefix;\n static int prefix_length;\n+static int show_tag = -1;\n \n static const char * const ls_usage[] = {\n \tN_(\"git list-files [options] [<pathspec>...]\"),\n@@ -30,6 +33,7 @@ static const char * const ls_usage[] = {\n };\n \n struct option ls_options[] = {\n+\tOPT_BOOL(0, \"tag\", &show_tag, N_(\"show tags\")),\n \tOPT_END()\n };\n \n@@ -52,10 +56,61 @@ static void populate_cached_entries(struct item_list *result,\n \t\titem = result->items + result->nr++;\n \t\titem->type = FROM_INDEX;\n \t\titem->path = ce->name;\n+\t\titem->tag[0] = ' ';\n+\t\titem->tag[1] = ' ';\n \t\titem->ce = ce;\n \t}\n }\n \n+static void cleanup_tags(struct item_list *result)\n+{\n+\tint i, same_1 = 1, same_2 = 1;\n+\n+\tif (!show_tag) {\n+\t\tresult->tag_pos = 0;\n+\t\tresult->tag_len = 0;\n+\t\treturn;\n+\t}\n+\tif (show_tag > 0) {\n+\t\tresult->tag_pos = 0;\n+\t\tresult->tag_len = 2;\n+\t\treturn;\n+\t}\n+\n+\tfor (i = 1; i < result->nr && (same_1 || same_2); i++) {\n+\t\tconst char *s0 = result->items[i - 1].tag;\n+\t\tconst char *s1 = result->items[i].tag;\n+\n+\t\tsame_1 = same_1 && s0[0] == s1[0];\n+\t\tsame_2 = same_2 && s0[1] == s1[1];\n+\t}\n+\n+\tif (same_1 && same_2) {\n+\t\tresult->tag_pos = 0;\n+\t\tresult->tag_len = 0;\n+\t} else if (same_1) {\n+\t\tresult->tag_pos = 1;\n+\t\tresult->tag_len = 1;\n+\t} else if (same_2) {\n+\t\tresult->tag_pos = 0;\n+\t\tresult->tag_len = 1;\n+\t} else {\n+\t\tresult->tag_pos = 0;\n+\t\tresult->tag_len = 2;\n+\t}\n+}\n+\n+/* this is similar to quote_path_relative() except it does not clear sb */\n+static void quote_item(struct strbuf *out, const struct item *item)\n+{\n+\tstatic struct strbuf sb = STRBUF_INIT;\n+\tconst char *rel;\n+\n+\tstrbuf_reset(&sb);\n+\trel = relative_path(item->path, prefix, &sb);\n+\tquote_c_style(rel, out, NULL, 0);\n+}\n+\n static void display(const struct item_list *result)\n {\n \tstruct strbuf quoted = STRBUF_INIT;\n@@ -67,7 +122,13 @@ static void display(const struct item_list *result)\n \tfor (i = 0; i < result->nr; i++) {\n \t\tconst struct item *item = result->items + i;\n \n-\t\tquote_path_relative(item->path, prefix, &quoted);\n+\t\tstrbuf_reset(&quoted);\n+\t\tif (result->tag_len) {\n+\t\t\tstrbuf_add(&quoted, item->tag + result->tag_pos,\n+\t\t\t\t   result->tag_len);\n+\t\t\tstrbuf_addch(&quoted, ' ');\n+\t\t}\n+\t\tquote_item(&quoted, item);\n \t\tprintf(\"%s\\n\", quoted.buf);\n \t}\n \tstrbuf_release(&quoted);\n@@ -108,6 +169,7 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \n \tmemset(&result, 0, sizeof(result));\n \tpopulate_cached_entries(&result, &the_index);\n+\tcleanup_tags(&result);\n \tdisplay(&result);\n \t/* free-ing result seems unnecessary */\n \treturn 0;\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259068","messageId":"1428328354-14897-9-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 08/25] list-files: add --[no-]column, -C and -1","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:17Z","receivedAt":"2015-04-06T13:52:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c  | 22 +++++++++++++++++++++-\n t/t7013-list-files.sh | 27 +++++++++++++++++++++++++++\n 2 files changed, 48 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex ec9ca17..8913770 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -4,6 +4,7 @@\n #include \"pathspec.h\"\n #include \"dir.h\"\n #include \"quote.h\"\n+#include \"column.h\"\n \n enum item_type {\n \tFROM_INDEX\n@@ -26,6 +27,7 @@ static struct pathspec pathspec;\n static const char *prefix;\n static int prefix_length;\n static int show_tag = -1;\n+static unsigned int colopts;\n \n static const char * const ls_usage[] = {\n \tN_(\"git list-files [options] [<pathspec>...]\"),\n@@ -34,6 +36,9 @@ static const char * const ls_usage[] = {\n \n struct option ls_options[] = {\n \tOPT_BOOL(0, \"tag\", &show_tag, N_(\"show tags\")),\n+\tOPT_COLUMN('C', \"column\", &colopts, N_(\"show files in columns\")),\n+\tOPT_SET_INT('1', NULL, &colopts,\n+\t\t    N_(\"shortcut for --no-column\"), COL_PARSEOPT),\n \tOPT_END()\n };\n \n@@ -114,6 +119,7 @@ static void quote_item(struct strbuf *out, const struct item *item)\n static void display(const struct item_list *result)\n {\n \tstruct strbuf quoted = STRBUF_INIT;\n+\tstruct string_list s = STRING_LIST_INIT_DUP;\n \tint i;\n \n \tif (!prefix_length)\n@@ -129,13 +135,26 @@ static void display(const struct item_list *result)\n \t\t\tstrbuf_addch(&quoted, ' ');\n \t\t}\n \t\tquote_item(&quoted, item);\n-\t\tprintf(\"%s\\n\", quoted.buf);\n+\t\tif (column_active(colopts))\n+\t\t\tstring_list_append(&s, quoted.buf);\n+\t\telse\n+\t\t\tprintf(\"%s\\n\", quoted.buf);\n+\t}\n+\n+\tif (column_active(colopts)) {\n+\t\tstruct column_options copts;\n+\t\tmemset(&copts, 0, sizeof(copts));\n+\t\tcopts.padding = 2;\n+\t\tprint_columns(&s, colopts, &copts);\n+\t\tstring_list_clear(&s, 0);\n \t}\n \tstrbuf_release(&quoted);\n }\n \n static int ls_config(const char *var, const char *value, void *cb)\n {\n+\tif (starts_with(var, \"column.\"))\n+\t\treturn git_column_config(var, value, \"listfiles\", &colopts);\n \treturn git_default_config(var, value, cb);\n }\n \n@@ -163,6 +182,7 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t       cmd_prefix, argv);\n \tpathspec.max_depth = 0;\n \tpathspec.recursive = 1;\n+\tfinalize_colopts(&colopts, -1);\n \n \trefresh_index(&the_index, REFRESH_QUIET | REFRESH_UNMERGED,\n \t\t      &pathspec, NULL, NULL);\ndiff --git a/t/t7013-list-files.sh b/t/t7013-list-files.sh\nindex a5e555d..b43245c 100755\n--- a/t/t7013-list-files.sh\n+++ b/t/t7013-list-files.sh\n@@ -23,6 +23,33 @@ test_expect_success 'list-files from index' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'column output' '\n+\tCOLUMNS=20 git list-files --column=always >actual &&\n+\tcat >expected <<-\\EOF &&\n+\ta        sa/a\n+\tb        sa/sb/b\n+\tc        sc/c\n+\tEOF\n+\ttest_cmp expected actual &&\n+\tCOLUMNS=20 git -c column.listfiles=always list-files >actual &&\n+\tcat >expected <<-\\EOF &&\n+\ta        sa/a\n+\tb        sa/sb/b\n+\tc        sc/c\n+\tEOF\n+\ttest_cmp expected actual &&\n+\tCOLUMNS=20 git -c column.listfiles=always list-files -1 >actual &&\n+\tcat >expected <<-\\EOF &&\n+\ta\n+\tb\n+\tc\n+\tsa/a\n+\tsa/sb/b\n+\tsc/c\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'list-files selectively from index' '\n \tgit list-files \"*a\" >actual &&\n \tcat >expect <<-\\EOF &&\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259069","messageId":"1428328354-14897-10-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 09/25] list-files: add --max-depth, -R and default to --max-depth=0","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:18Z","receivedAt":"2015-04-06T13:52:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c  |  9 ++++++++-\n t/t7013-list-files.sh | 40 ++++++++++++++++++++++++++++++++--------\n 2 files changed, 40 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 8913770..4599cf0 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -28,6 +28,7 @@ static const char *prefix;\n static int prefix_length;\n static int show_tag = -1;\n static unsigned int colopts;\n+static int max_depth;\n \n static const char * const ls_usage[] = {\n \tN_(\"git list-files [options] [<pathspec>...]\"),\n@@ -39,6 +40,11 @@ struct option ls_options[] = {\n \tOPT_COLUMN('C', \"column\", &colopts, N_(\"show files in columns\")),\n \tOPT_SET_INT('1', NULL, &colopts,\n \t\t    N_(\"shortcut for --no-column\"), COL_PARSEOPT),\n+\t{ OPTION_INTEGER, 0, \"max-depth\", &max_depth, N_(\"depth\"),\n+\t  N_(\"descend at most <depth> levels\"), PARSE_OPT_NONEG,\n+\t  NULL, 1 },\n+\tOPT_SET_INT('R', \"recursive\", &max_depth,\n+\t\t    N_(\"shortcut for --max-depth=-1\"), -1),\n \tOPT_END()\n };\n \n@@ -178,9 +184,10 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \n \tparse_pathspec(&pathspec, 0,\n \t\t       PATHSPEC_PREFER_CWD |\n+\t\t       (max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0) |\n \t\t       PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,\n \t\t       cmd_prefix, argv);\n-\tpathspec.max_depth = 0;\n+\tpathspec.max_depth = max_depth;\n \tpathspec.recursive = 1;\n \tfinalize_colopts(&colopts, -1);\n \ndiff --git a/t/t7013-list-files.sh b/t/t7013-list-files.sh\nindex b43245c..89930c7 100755\n--- a/t/t7013-list-files.sh\n+++ b/t/t7013-list-files.sh\n@@ -10,8 +10,8 @@ test_expect_success 'setup' '\n \tgit add .\n '\n \n-test_expect_success 'list-files from index' '\n-\tgit list-files >actual &&\n+test_expect_success 'list-files -R from index' '\n+\tgit list-files -R >actual &&\n \tcat >expect <<-\\EOF &&\n \ta\n \tb\n@@ -23,22 +23,34 @@ test_expect_success 'list-files from index' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'list-files from index' '\n+\tgit list-files --max-depth=0 >actual &&\n+\tcat >expect <<-\\EOF &&\n+\ta\n+\tb\n+\tc\n+\tEOF\n+\ttest_cmp expect actual &&\n+\tgit list-files >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'column output' '\n-\tCOLUMNS=20 git list-files --column=always >actual &&\n+\tCOLUMNS=20 git list-files -R --column=always >actual &&\n \tcat >expected <<-\\EOF &&\n \ta        sa/a\n \tb        sa/sb/b\n \tc        sc/c\n \tEOF\n \ttest_cmp expected actual &&\n-\tCOLUMNS=20 git -c column.listfiles=always list-files >actual &&\n+\tCOLUMNS=20 git -c column.listfiles=always list-files -R >actual &&\n \tcat >expected <<-\\EOF &&\n \ta        sa/a\n \tb        sa/sb/b\n \tc        sc/c\n \tEOF\n \ttest_cmp expected actual &&\n-\tCOLUMNS=20 git -c column.listfiles=always list-files -1 >actual &&\n+\tCOLUMNS=20 git -c column.listfiles=always list-files -1R >actual &&\n \tcat >expected <<-\\EOF &&\n \ta\n \tb\n@@ -51,7 +63,7 @@ test_expect_success 'column output' '\n '\n \n test_expect_success 'list-files selectively from index' '\n-\tgit list-files \"*a\" >actual &&\n+\tgit list-files -R \"*a\" >actual &&\n \tcat >expect <<-\\EOF &&\n \ta\n \tsa/a\n@@ -59,10 +71,22 @@ test_expect_success 'list-files selectively from index' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success '--max-depth' '\n+\tgit list-files --max-depth=1 >actual &&\n+\tcat >expected <<-\\EOF &&\n+\ta\n+\tb\n+\tc\n+\tsa/a\n+\tsc/c\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'list-files from subdir ' '\n \t(\n \tcd sa &&\n-\tgit list-files >actual &&\n+\tgit list-files -R >actual &&\n \tcat >expect <<-\\EOF &&\n \ta\n \tsb/b\n@@ -74,7 +98,7 @@ test_expect_success 'list-files from subdir ' '\n test_expect_success 'list-files from subdir (2)' '\n \t(\n \tcd sa &&\n-\tgit list-files ../a sb >actual &&\n+\tgit list-files -R ../a sb >actual &&\n \tcat >expect <<-\\EOF &&\n \t../a\n \tsb/b\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259070","messageId":"1428328354-14897-11-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 10/25] list-files: show directories as well as files","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:19Z","receivedAt":"2015-04-06T13:52:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The index does not store directories explicitly (except submodules) so\nwe have to figure them out from file list when output lis depth-limited.\n\nThe function add_directory() can generate duplicate entries, which is\ncleaned up before displaying.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c  | 94 +++++++++++++++++++++++++++++++++++++++++++++++----\n t/t7013-list-files.sh |  3 ++\n 2 files changed, 91 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 4599cf0..a60ab98 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -7,7 +7,8 @@\n #include \"column.h\"\n \n enum item_type {\n-\tFROM_INDEX\n+\tFROM_INDEX,\n+\tIS_DIR\n };\n \n struct item {\n@@ -24,11 +25,13 @@ struct item_list {\n };\n \n static struct pathspec pathspec;\n+static struct pathspec recursive_pathspec;\n static const char *prefix;\n static int prefix_length;\n static int show_tag = -1;\n static unsigned int colopts;\n static int max_depth;\n+static int show_dirs;\n \n static const char * const ls_usage[] = {\n \tN_(\"git list-files [options] [<pathspec>...]\"),\n@@ -48,6 +51,81 @@ struct option ls_options[] = {\n \tOPT_END()\n };\n \n+static int compare_item(const void *a_, const void *b_)\n+{\n+\tconst struct item *a = a_;\n+\tconst struct item *b = b_;\n+\treturn strcmp(a->path, b->path);\n+}\n+\n+static void free_item(struct item *item)\n+{\n+\tswitch (item->type) {\n+\tcase IS_DIR:\n+\t\tfree((char*)item->path);\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+}\n+\n+static void remove_duplicates(struct item_list *list)\n+{\n+\tint src, dst;\n+\n+\tif (list->nr <= 1)\n+\t\treturn;\n+\tqsort(list->items, list->nr, sizeof(*list->items), compare_item);\n+\tfor (src = dst = 1; src < list->nr; src++) {\n+\t\tif (!compare_item(list->items + dst - 1, list->items + src))\n+\t\t\tfree_item(list->items + src);\n+\t\telse\n+\t\t\tlist->items[dst++] = list->items[src];\n+\t}\n+\tlist->nr = dst;\n+}\n+\n+static int add_directory(struct item_list *result,\n+\t\t\t const char *name)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tstruct item *item;\n+\tconst char *p;\n+\n+\tstrbuf_addstr(&sb, name);\n+\twhile (sb.len && (p = strrchr(sb.buf, '/')) != NULL) {\n+\t\tstrbuf_setlen(&sb, p - sb.buf);\n+\t\tif (!match_pathspec(&pathspec, sb.buf, sb.len, 0, NULL, 1))\n+\t\t\tcontinue;\n+\n+\t\tALLOC_GROW(result->items, result->nr + 1, result->alloc);\n+\t\titem = result->items + result->nr++;\n+\t\titem->type = IS_DIR;\n+\t\titem->path = strbuf_detach(&sb, NULL);\n+\t\titem->tag[0] = ' ';\n+\t\titem->tag[1] = ' ';\n+\t\treturn 1;\n+\t}\n+\tstrbuf_release(&sb);\n+\treturn 0;\n+}\n+\n+static int matched(struct item_list *result, const char *name, int mode)\n+{\n+\tint len = strlen(name);\n+\n+\tif (!match_pathspec(&recursive_pathspec, name, len, 0, NULL,\n+\t\t\t    S_ISDIR(mode) || S_ISGITLINK(mode)))\n+\t\treturn 0;\n+\n+\tif (show_dirs && strchr(name, '/') &&\n+\t    !match_pathspec(&pathspec, name, len, 0, NULL, 1) &&\n+\t    add_directory(result, name))\n+\t\treturn 0;\n+\n+\treturn 1;\n+}\n+\n static void populate_cached_entries(struct item_list *result,\n \t\t\t\t    const struct index_state *istate)\n {\n@@ -57,10 +135,7 @@ static void populate_cached_entries(struct item_list *result,\n \t\tconst struct cache_entry *ce = istate->cache[i];\n \t\tstruct item *item;\n \n-\t\tif (!match_pathspec(&pathspec, ce->name, ce_namelen(ce),\n-\t\t\t\t    0, NULL,\n-\t\t\t\t    S_ISDIR(ce->ce_mode) ||\n-\t\t\t\t    S_ISGITLINK(ce->ce_mode)))\n+\t\tif (!matched(result, ce->name, ce->ce_mode))\n \t\t\tcontinue;\n \n \t\tALLOC_GROW(result->items, result->nr + 1, result->alloc);\n@@ -71,6 +146,10 @@ static void populate_cached_entries(struct item_list *result,\n \t\titem->tag[1] = ' ';\n \t\titem->ce = ce;\n \t}\n+\n+\tif (!show_dirs)\n+\t\treturn;\n+\tremove_duplicates(result);\n }\n \n static void cleanup_tags(struct item_list *result)\n@@ -189,10 +268,13 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t       cmd_prefix, argv);\n \tpathspec.max_depth = max_depth;\n \tpathspec.recursive = 1;\n+\tshow_dirs = max_depth >= 0;\n+\tcopy_pathspec(&recursive_pathspec, &pathspec);\n+\trecursive_pathspec.max_depth = -1;\n \tfinalize_colopts(&colopts, -1);\n \n \trefresh_index(&the_index, REFRESH_QUIET | REFRESH_UNMERGED,\n-\t\t      &pathspec, NULL, NULL);\n+\t\t      &recursive_pathspec, NULL, NULL);\n \n \tmemset(&result, 0, sizeof(result));\n \tpopulate_cached_entries(&result, &the_index);\ndiff --git a/t/t7013-list-files.sh b/t/t7013-list-files.sh\nindex 89930c7..a4916d8 100755\n--- a/t/t7013-list-files.sh\n+++ b/t/t7013-list-files.sh\n@@ -29,6 +29,8 @@ test_expect_success 'list-files from index' '\n \ta\n \tb\n \tc\n+\tsa\n+\tsc\n \tEOF\n \ttest_cmp expect actual &&\n \tgit list-files >actual &&\n@@ -78,6 +80,7 @@ test_expect_success '--max-depth' '\n \tb\n \tc\n \tsa/a\n+\tsa/sb\n \tsc/c\n \tEOF\n \ttest_cmp expected actual\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259071","messageId":"1428328354-14897-12-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 11/25] list-files: add --color","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:20Z","receivedAt":"2015-04-06T13:52:20Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c  | 30 +++++++++++++++++++++++++++++-\n t/t7013-list-files.sh | 44 ++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 73 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex a60ab98..02a088d 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -5,6 +5,7 @@\n #include \"dir.h\"\n #include \"quote.h\"\n #include \"column.h\"\n+#include \"color.h\"\n \n enum item_type {\n \tFROM_INDEX,\n@@ -32,6 +33,7 @@ static int show_tag = -1;\n static unsigned int colopts;\n static int max_depth;\n static int show_dirs;\n+static int use_color = -1;\n \n static const char * const ls_usage[] = {\n \tN_(\"git list-files [options] [<pathspec>...]\"),\n@@ -48,6 +50,7 @@ struct option ls_options[] = {\n \t  NULL, 1 },\n \tOPT_SET_INT('R', \"recursive\", &max_depth,\n \t\t    N_(\"shortcut for --max-depth=-1\"), -1),\n+\tOPT__COLOR(&use_color, N_(\"show color\")),\n \tOPT_END()\n };\n \n@@ -69,6 +72,17 @@ static void free_item(struct item *item)\n \t}\n }\n \n+static mode_t get_mode(const struct item *item)\n+{\n+\tswitch (item->type) {\n+\tcase IS_DIR:\n+\t\treturn S_IFDIR;\n+\tcase FROM_INDEX:\n+\t\treturn item->ce->ce_mode;\n+\t}\n+\treturn S_IFREG;\n+}\n+\n static void remove_duplicates(struct item_list *list)\n {\n \tint src, dst;\n@@ -220,6 +234,13 @@ static void display(const struct item_list *result)\n \t\t\tstrbuf_addch(&quoted, ' ');\n \t\t}\n \t\tquote_item(&quoted, item);\n+\t\tif (want_color(use_color)) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\t\tstrbuf_swap(&sb, &quoted);\n+\t\t\tcolor_filename(&quoted, item->path, sb.buf,\n+\t\t\t\t       get_mode(item), 1);\n+\t\t\tstrbuf_release(&sb);\n+\t\t}\n \t\tif (column_active(colopts))\n \t\t\tstring_list_append(&s, quoted.buf);\n \t\telse\n@@ -240,7 +261,11 @@ static int ls_config(const char *var, const char *value, void *cb)\n {\n \tif (starts_with(var, \"column.\"))\n \t\treturn git_column_config(var, value, \"listfiles\", &colopts);\n-\treturn git_default_config(var, value, cb);\n+\tif (!strcmp(var, \"color.listfiles\")) {\n+\t\tuse_color = git_config_colorbool(var, value);\n+\t\treturn 0;\n+\t}\n+\treturn git_color_default_config(var, value, cb);\n }\n \n int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n@@ -261,6 +286,9 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \n \targc = parse_options(argc, argv, prefix, ls_options, ls_usage, 0);\n \n+\tif (want_color(use_color))\n+\t\tparse_ls_color();\n+\n \tparse_pathspec(&pathspec, 0,\n \t\t       PATHSPEC_PREFER_CWD |\n \t\t       (max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0) |\ndiff --git a/t/t7013-list-files.sh b/t/t7013-list-files.sh\nindex a4916d8..cdbc34a 100755\n--- a/t/t7013-list-files.sh\n+++ b/t/t7013-list-files.sh\n@@ -110,4 +110,48 @@ test_expect_success 'list-files from subdir (2)' '\n \t)\n '\n \n+test_expect_success 'setup 2' '\n+\tgit init 2 &&\n+\t(\n+\tcd 2 &&\n+\tmkdir dir &&\n+\ttouch file dir/file &&\n+\tgit init gitlink &&\n+\t( cd gitlink && test_commit foo ) &&\n+\tgit add file dir/file gitlink &&\n+\tgit commit -qm1\n+\t)\n+'\n+\n+test_expect_success 'LS_COLORS env variable' '\n+\t(\n+\tcd 2 &&\n+\tLS_COLORS=\"rs=0:fi=31:di=32\" &&\n+\texport LS_COLORS &&\n+\tgit list-files --color=always | test_decode_color | \\\n+\t\tgrep -v gitlink >actual &&\n+\tcat >expected <<-\\EOF &&\n+\t<GREEN>dir<RESET>\n+\t<RED>file<RESET>\n+\tEOF\n+\ttest_cmp expected actual\n+\t)\n+'\n+\n+test_expect_success 'color.ls.*' '\n+\t(\n+\tcd 2 &&\n+\ttest_config color.ls.file red &&\n+\ttest_config color.ls.directory green &&\n+\ttest_config color.ls.submodule yellow &&\n+\tgit list-files --color=always | test_decode_color >actual &&\n+\tcat >expected <<-\\EOF &&\n+\t<GREEN>dir<RESET>\n+\t<RED>file<RESET>\n+\t<YELLOW>gitlink<RESET>\n+\tEOF\n+\ttest_cmp expected actual\n+\t)\n+'\n+\n test_done\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259072","messageId":"1428328354-14897-13-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 12/25] list-files: add -F/--classify","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:21Z","receivedAt":"2015-04-06T13:52:21Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This appends an indicator after the file name if it's executable, a\ndirectory and so on, like in GNU ls. In fact append_indicator() is a\nrewrite from get_type_indicator() in coreutils.git commit\n7326d1f1a67edf21947ae98194f98c38b6e9e527.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c  | 27 +++++++++++++++++++++++++++\n t/t7013-list-files.sh | 13 +++++++++++++\n 2 files changed, 40 insertions(+)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 02a088d..31f1f25 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -34,6 +34,7 @@ static unsigned int colopts;\n static int max_depth;\n static int show_dirs;\n static int use_color = -1;\n+static int show_indicator;\n \n static const char * const ls_usage[] = {\n \tN_(\"git list-files [options] [<pathspec>...]\"),\n@@ -51,6 +52,8 @@ struct option ls_options[] = {\n \tOPT_SET_INT('R', \"recursive\", &max_depth,\n \t\t    N_(\"shortcut for --max-depth=-1\"), -1),\n \tOPT__COLOR(&use_color, N_(\"show color\")),\n+\tOPT_BOOL('F', \"classify\", &show_indicator,\n+\t\t N_(\"append indicator (one of */=>@|) to entries\")),\n \tOPT_END()\n };\n \n@@ -204,6 +207,28 @@ static void cleanup_tags(struct item_list *result)\n \t}\n }\n \n+static void append_indicator(struct strbuf *sb, mode_t mode)\n+{\n+\tchar c = 0;\n+\tif (S_ISREG(mode)) {\n+\t\tif (mode & (S_IXUSR | S_IXGRP | S_IXOTH))\n+\t\t\tc = '*';\n+\t} else if (S_ISDIR(mode))\n+\t\tc = '/';\n+\telse if (S_ISLNK(mode))\n+\t\tc = '@';\n+\telse if (S_ISFIFO(mode))\n+\t\tc = '|';\n+\telse if (S_ISSOCK(mode))\n+\t\tc = '=';\n+#ifdef S_ISDOOR\n+\telse if (S_ISDOOR(mode))\n+\t\tc = '>';\n+#endif\n+\tif (c)\n+\t\tstrbuf_addch(sb, c);\n+}\n+\n /* this is similar to quote_path_relative() except it does not clear sb */\n static void quote_item(struct strbuf *out, const struct item *item)\n {\n@@ -241,6 +266,8 @@ static void display(const struct item_list *result)\n \t\t\t\t       get_mode(item), 1);\n \t\t\tstrbuf_release(&sb);\n \t\t}\n+\t\tif (show_indicator)\n+\t\t\tappend_indicator(&quoted, get_mode(item));\n \t\tif (column_active(colopts))\n \t\t\tstring_list_append(&s, quoted.buf);\n \t\telse\ndiff --git a/t/t7013-list-files.sh b/t/t7013-list-files.sh\nindex cdbc34a..16d000e 100755\n--- a/t/t7013-list-files.sh\n+++ b/t/t7013-list-files.sh\n@@ -154,4 +154,17 @@ test_expect_success 'color.ls.*' '\n \t)\n '\n \n+test_expect_success '--classify' '\n+\t(\n+\tcd 2 &&\n+\tgit list-files -F >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tdir/\n+\tfile\n+\tgitlink\n+\tEOF\n+\ttest_cmp expected actual\n+\t)\n+'\n+\n test_done\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259073","messageId":"1428328354-14897-14-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 13/25] list-files: new indicator '&' for submodules when -F is used","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:22Z","receivedAt":"2015-04-06T13:52:22Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c  | 2 ++\n t/t7013-list-files.sh | 2 +-\n 2 files changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 31f1f25..7c18848 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -221,6 +221,8 @@ static void append_indicator(struct strbuf *sb, mode_t mode)\n \t\tc = '|';\n \telse if (S_ISSOCK(mode))\n \t\tc = '=';\n+\telse if (S_ISGITLINK(mode))\n+\t\tc = '&';\n #ifdef S_ISDOOR\n \telse if (S_ISDOOR(mode))\n \t\tc = '>';\ndiff --git a/t/t7013-list-files.sh b/t/t7013-list-files.sh\nindex 16d000e..37dd403 100755\n--- a/t/t7013-list-files.sh\n+++ b/t/t7013-list-files.sh\n@@ -161,7 +161,7 @@ test_expect_success '--classify' '\n \tcat >expected <<-\\EOF &&\n \tdir/\n \tfile\n-\tgitlink\n+\tgitlink&\n \tEOF\n \ttest_cmp expected actual\n \t)\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259075","messageId":"1428328354-14897-15-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 14/25] list-files: add --cached and --others","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:23Z","receivedAt":"2015-04-06T13:52:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"If no filter options are specified, --cached is the default.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c  | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++-\n t/t7013-list-files.sh | 38 ++++++++++++++++++++++++-\n 2 files changed, 115 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 7c18848..3cbd30d 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -6,9 +6,11 @@\n #include \"quote.h\"\n #include \"column.h\"\n #include \"color.h\"\n+#include \"wt-status.h\"\n \n enum item_type {\n \tFROM_INDEX,\n+\tFROM_WORKTREE,\n \tIS_DIR\n };\n \n@@ -17,6 +19,7 @@ struct item {\n \tconst char *path;\n \tchar tag[2];\n \tconst struct cache_entry *ce;\n+\tstruct stat st;\n };\n \n struct item_list {\n@@ -35,6 +38,7 @@ static int max_depth;\n static int show_dirs;\n static int use_color = -1;\n static int show_indicator;\n+static int show_cached, show_untracked;\n \n static const char * const ls_usage[] = {\n \tN_(\"git list-files [options] [<pathspec>...]\"),\n@@ -42,6 +46,13 @@ static const char * const ls_usage[] = {\n };\n \n struct option ls_options[] = {\n+\tOPT_GROUP(N_(\"Filter options\")),\n+\tOPT_BOOL('c', \"cached\", &show_cached,\n+\t\t N_(\"show cached files (default)\")),\n+\tOPT_BOOL('o', \"others\", &show_untracked,\n+\t\t N_(\"show untracked files\")),\n+\n+\tOPT_GROUP(N_(\"Other\")),\n \tOPT_BOOL(0, \"tag\", &show_tag, N_(\"show tags\")),\n \tOPT_COLUMN('C', \"column\", &colopts, N_(\"show files in columns\")),\n \tOPT_SET_INT('1', NULL, &colopts,\n@@ -82,6 +93,8 @@ static mode_t get_mode(const struct item *item)\n \t\treturn S_IFDIR;\n \tcase FROM_INDEX:\n \t\treturn item->ce->ce_mode;\n+\tcase FROM_WORKTREE:\n+\t\treturn item->st.st_mode;\n \t}\n \treturn S_IFREG;\n }\n@@ -148,6 +161,9 @@ static void populate_cached_entries(struct item_list *result,\n {\n \tint i;\n \n+\tif (!show_cached)\n+\t\treturn;\n+\n \tfor (i = 0; i < istate->cache_nr; i++) {\n \t\tconst struct cache_entry *ce = istate->cache[i];\n \t\tstruct item *item;\n@@ -169,6 +185,63 @@ static void populate_cached_entries(struct item_list *result,\n \tremove_duplicates(result);\n }\n \n+static void add_wt_item(struct item_list *result,\n+\t\t\tenum item_type type,\n+\t\t\tconst char *path,\n+\t\t\tconst char *tag,\n+\t\t\tconst struct stat *st)\n+{\n+\tstruct item *item;\n+\n+\tALLOC_GROW(result->items, result->nr + 1, result->alloc);\n+\titem = result->items + result->nr++;\n+\titem->type = type;\n+\titem->path = path;\n+\tmemcpy(item->tag, tag, sizeof(item->tag));\n+\tmemcpy(&item->st, &st, sizeof(st));\n+}\n+\n+static void populate_untracked(struct item_list *result,\n+\t\t\t       const struct string_list *untracked)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < untracked->nr; i++) {\n+\t\tconst char *name = untracked->items[i].string;\n+\t\tstruct stat st;\n+\n+\t\tif (lstat(name, &st))\n+\t\t\t/* color_filename() treats this as an orphan file */\n+\t\t\tst.st_mode = 0;\n+\n+\t\tif (!matched(result, name, st.st_mode))\n+\t\t\tcontinue;\n+\n+\t\tadd_wt_item(result, FROM_WORKTREE, name, \"??\", &st);\n+\t}\n+}\n+\n+static void wt_status_populate(struct item_list *result,\n+\t\t\t       struct index_state *istate)\n+{\n+\tstruct wt_status ws;\n+\n+\tif (!show_untracked)\n+\t\treturn;\n+\n+\twt_status_prepare(&ws);\n+\tcopy_pathspec(&ws.pathspec, &recursive_pathspec);\n+\tws.relative_paths = 0;\n+\tws.use_color = 0;\n+\tws.fp = NULL;\n+\twt_status_collect(&ws);\n+\n+\tif (show_untracked)\n+\t\tpopulate_untracked(result, &ws.untracked);\n+\n+\tremove_duplicates(result);\n+}\n+\n static void cleanup_tags(struct item_list *result)\n {\n \tint i, same_1 = 1, same_2 = 1;\n@@ -178,7 +251,7 @@ static void cleanup_tags(struct item_list *result)\n \t\tresult->tag_len = 0;\n \t\treturn;\n \t}\n-\tif (show_tag > 0) {\n+\tif (show_tag > 0 || show_cached + show_untracked > 1) {\n \t\tresult->tag_pos = 0;\n \t\tresult->tag_len = 2;\n \t\treturn;\n@@ -315,6 +388,9 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \n \targc = parse_options(argc, argv, prefix, ls_options, ls_usage, 0);\n \n+\tif (!show_cached && !show_untracked)\n+\t\tshow_cached = 1;\n+\n \tif (want_color(use_color))\n \t\tparse_ls_color();\n \n@@ -335,6 +411,7 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \n \tmemset(&result, 0, sizeof(result));\n \tpopulate_cached_entries(&result, &the_index);\n+\twt_status_populate(&result, &the_index);\n \tcleanup_tags(&result);\n \tdisplay(&result);\n \t/* free-ing result seems unnecessary */\ndiff --git a/t/t7013-list-files.sh b/t/t7013-list-files.sh\nindex 37dd403..53dc745 100755\n--- a/t/t7013-list-files.sh\n+++ b/t/t7013-list-files.sh\n@@ -7,7 +7,41 @@ test_description='list-files'\n test_expect_success 'setup' '\n \tmkdir sa sa/sb sc &&\n \ttouch a b c sa/a sa/sb/b sc/c &&\n-\tgit add .\n+\tgit add a sa/a &&\n+\tgit commit -m initial &&\n+\tgit add . &&\n+\techo foo >.git/info/exclude &&\n+\ttouch foo bar sa/foo sa/bar\n+'\n+\n+test_expect_success 'list-files --others' '\n+\tgit list-files --others >actual &&\n+\tcat >expect <<-\\EOF &&\n+\t?? actual\n+\t?? bar\n+\t   sa\n+\tEOF\n+\ttest_cmp expect actual &&\n+\tgit list-files --others --cached >actual &&\n+\tcat >expect <<-\\EOF &&\n+\t   a\n+\t?? actual\n+\t   b\n+\t?? bar\n+\t   c\n+\t?? expect\n+\t   sa\n+\t   sc\n+\tEOF\n+\ttest_cmp expect actual &&\n+\tgit list-files --others -R >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tactual\n+\tbar\n+\texpect\n+\tsa/bar\n+\tEOF\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'list-files -R from index' '\n@@ -34,6 +68,8 @@ test_expect_success 'list-files from index' '\n \tEOF\n \ttest_cmp expect actual &&\n \tgit list-files >actual &&\n+\ttest_cmp expect actual &&\n+\tgit list-files --cached >actual &&\n \ttest_cmp expect actual\n '\n \n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259074","messageId":"1428328354-14897-16-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 15/25] list-files: add --ignored","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:24Z","receivedAt":"2015-04-06T13:52:24Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c  | 22 ++++++++++++++++------\n t/t7013-list-files.sh | 15 +++++++++++++++\n 2 files changed, 31 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 3cbd30d..31991a4 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -39,6 +39,7 @@ static int show_dirs;\n static int use_color = -1;\n static int show_indicator;\n static int show_cached, show_untracked;\n+static int show_ignored;\n \n static const char * const ls_usage[] = {\n \tN_(\"git list-files [options] [<pathspec>...]\"),\n@@ -51,6 +52,8 @@ struct option ls_options[] = {\n \t\t N_(\"show cached files (default)\")),\n \tOPT_BOOL('o', \"others\", &show_untracked,\n \t\t N_(\"show untracked files\")),\n+\tOPT_BOOL('i', \"ignored\", &show_ignored,\n+\t\t N_(\"show ignored files\")),\n \n \tOPT_GROUP(N_(\"Other\")),\n \tOPT_BOOL(0, \"tag\", &show_tag, N_(\"show tags\")),\n@@ -202,7 +205,8 @@ static void add_wt_item(struct item_list *result,\n }\n \n static void populate_untracked(struct item_list *result,\n-\t\t\t       const struct string_list *untracked)\n+\t\t\t       const struct string_list *untracked,\n+\t\t\t       const char *tag)\n {\n \tint i;\n \n@@ -217,7 +221,7 @@ static void populate_untracked(struct item_list *result,\n \t\tif (!matched(result, name, st.st_mode))\n \t\t\tcontinue;\n \n-\t\tadd_wt_item(result, FROM_WORKTREE, name, \"??\", &st);\n+\t\tadd_wt_item(result, FROM_WORKTREE, name, tag, &st);\n \t}\n }\n \n@@ -226,18 +230,22 @@ static void wt_status_populate(struct item_list *result,\n {\n \tstruct wt_status ws;\n \n-\tif (!show_untracked)\n+\tif (!show_untracked && !show_ignored)\n \t\treturn;\n \n \twt_status_prepare(&ws);\n \tcopy_pathspec(&ws.pathspec, &recursive_pathspec);\n+\tif (show_ignored)\n+\t\tws.show_ignored_files = 1;\n \tws.relative_paths = 0;\n \tws.use_color = 0;\n \tws.fp = NULL;\n \twt_status_collect(&ws);\n \n \tif (show_untracked)\n-\t\tpopulate_untracked(result, &ws.untracked);\n+\t\tpopulate_untracked(result, &ws.untracked, \"??\");\n+\tif (show_ignored)\n+\t\tpopulate_untracked(result, &ws.ignored, \"!!\");\n \n \tremove_duplicates(result);\n }\n@@ -251,7 +259,9 @@ static void cleanup_tags(struct item_list *result)\n \t\tresult->tag_len = 0;\n \t\treturn;\n \t}\n-\tif (show_tag > 0 || show_cached + show_untracked > 1) {\n+\n+\tif (show_tag > 0 ||\n+\t    show_cached + show_untracked + show_ignored > 1) {\n \t\tresult->tag_pos = 0;\n \t\tresult->tag_len = 2;\n \t\treturn;\n@@ -388,7 +398,7 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \n \targc = parse_options(argc, argv, prefix, ls_options, ls_usage, 0);\n \n-\tif (!show_cached && !show_untracked)\n+\tif (!show_cached && !show_untracked && !show_ignored)\n \t\tshow_cached = 1;\n \n \tif (want_color(use_color))\ndiff --git a/t/t7013-list-files.sh b/t/t7013-list-files.sh\nindex 53dc745..6b76d1f 100755\n--- a/t/t7013-list-files.sh\n+++ b/t/t7013-list-files.sh\n@@ -44,6 +44,21 @@ test_expect_success 'list-files --others' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'list-files --others' '\n+\tgit list-files --ignored >actual &&\n+\tcat >expect <<-\\EOF &&\n+\t!! foo\n+\t   sa\n+\tEOF\n+\ttest_cmp expect actual &&\n+\tgit list-files --ignored -R >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tfoo\n+\tsa/foo\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'list-files -R from index' '\n \tgit list-files -R >actual &&\n \tcat >expect <<-\\EOF &&\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259076","messageId":"1428328354-14897-17-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 16/25] list-files: add --unmerged","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:25Z","receivedAt":"2015-04-06T13:52:25Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c  | 50 +++++++++++++++++++++++++++++++++++++++++++++-----\n t/t7013-list-files.sh | 35 +++++++++++++++++++++++++++++++++++\n 2 files changed, 80 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 31991a4..fe15417 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -11,7 +11,8 @@\n enum item_type {\n \tFROM_INDEX,\n \tFROM_WORKTREE,\n-\tIS_DIR\n+\tIS_DIR,\n+\tIS_UNMERGED\n };\n \n struct item {\n@@ -38,7 +39,7 @@ static int max_depth;\n static int show_dirs;\n static int use_color = -1;\n static int show_indicator;\n-static int show_cached, show_untracked;\n+static int show_cached, show_untracked, show_unmerged;\n static int show_ignored;\n \n static const char * const ls_usage[] = {\n@@ -54,6 +55,8 @@ struct option ls_options[] = {\n \t\t N_(\"show untracked files\")),\n \tOPT_BOOL('i', \"ignored\", &show_ignored,\n \t\t N_(\"show ignored files\")),\n+\tOPT_BOOL('u', \"unmerged\", &show_unmerged,\n+\t\t N_(\"show unmerged files\")),\n \n \tOPT_GROUP(N_(\"Other\")),\n \tOPT_BOOL(0, \"tag\", &show_tag, N_(\"show tags\")),\n@@ -97,6 +100,7 @@ static mode_t get_mode(const struct item *item)\n \tcase FROM_INDEX:\n \t\treturn item->ce->ce_mode;\n \tcase FROM_WORKTREE:\n+\tcase IS_UNMERGED:\n \t\treturn item->st.st_mode;\n \t}\n \treturn S_IFREG;\n@@ -225,12 +229,46 @@ static void populate_untracked(struct item_list *result,\n \t}\n }\n \n+static void populate_unmerged(struct item_list *result,\n+\t\t\t      const struct string_list *change)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < change->nr; i++) {\n+\t\tconst struct string_list_item *it = change->items + i;\n+\t\tstruct wt_status_change_data *d = it->util;\n+\t\tconst char *name = it->string;\n+\t\tconst char *tag;\n+\t\tstruct stat st;\n+\n+\t\tswitch (d->stagemask) {\n+\t\tcase 1: tag = \"DD\"; break; /* both deleted */\n+\t\tcase 2: tag = \"AU\"; break; /* added by us */\n+\t\tcase 3: tag = \"UD\"; break; /* deleted by them */\n+\t\tcase 4: tag = \"UA\"; break; /* added by them */\n+\t\tcase 5: tag = \"DU\"; break; /* deleted by us */\n+\t\tcase 6: tag = \"AA\"; break; /* both added */\n+\t\tcase 7: tag = \"UU\"; break; /* both modified */\n+\t\tdefault: continue;\n+\t\t}\n+\n+\t\tif (lstat(name, &st))\n+\t\t\t/* color_filename() treats this as an orphan file */\n+\t\t\tst.st_mode = 0;\n+\n+\t\tif (!matched(result, name, st.st_mode))\n+\t\t\tcontinue;\n+\n+\t\tadd_wt_item(result, IS_UNMERGED, name, tag, &st);\n+\t}\n+}\n+\n static void wt_status_populate(struct item_list *result,\n \t\t\t       struct index_state *istate)\n {\n \tstruct wt_status ws;\n \n-\tif (!show_untracked && !show_ignored)\n+\tif (!show_untracked && !show_ignored && !show_unmerged)\n \t\treturn;\n \n \twt_status_prepare(&ws);\n@@ -246,6 +284,8 @@ static void wt_status_populate(struct item_list *result,\n \t\tpopulate_untracked(result, &ws.untracked, \"??\");\n \tif (show_ignored)\n \t\tpopulate_untracked(result, &ws.ignored, \"!!\");\n+\tif (show_unmerged)\n+\t\tpopulate_unmerged(result, &ws.change);\n \n \tremove_duplicates(result);\n }\n@@ -260,7 +300,7 @@ static void cleanup_tags(struct item_list *result)\n \t\treturn;\n \t}\n \n-\tif (show_tag > 0 ||\n+\tif (show_tag > 0 || show_unmerged ||\n \t    show_cached + show_untracked + show_ignored > 1) {\n \t\tresult->tag_pos = 0;\n \t\tresult->tag_len = 2;\n@@ -398,7 +438,7 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \n \targc = parse_options(argc, argv, prefix, ls_options, ls_usage, 0);\n \n-\tif (!show_cached && !show_untracked && !show_ignored)\n+\tif (!show_cached && !show_untracked && !show_ignored && !show_unmerged)\n \t\tshow_cached = 1;\n \n \tif (want_color(use_color))\ndiff --git a/t/t7013-list-files.sh b/t/t7013-list-files.sh\nindex 6b76d1f..7fe9673 100755\n--- a/t/t7013-list-files.sh\n+++ b/t/t7013-list-files.sh\n@@ -218,4 +218,39 @@ test_expect_success '--classify' '\n \t)\n '\n \n+test_expect_success 'list-files unmerged' '\n+\t(\n+\tadd_stage() {\n+\t\techo \"100644 $1 $2\t$3\" | git update-index --index-info\n+\t}\n+\tgit init 3 &&\n+\tcd 3 &&\n+\ttest_commit 1 &&\n+\tSHA1=`echo 1 | git hash-object -w --stdin` &&\n+\tadd_stage $SHA1 1 deleted-by-both &&\n+\tadd_stage $SHA1 2 added-by-us &&\n+\tadd_stage $SHA1 1 deleted-by-them &&\n+\tadd_stage $SHA1 2 deleted-by-them &&\n+\tadd_stage $SHA1 3 added-by-them &&\n+\tadd_stage $SHA1 3 deleted-by-us &&\n+\tadd_stage $SHA1 1 deleted-by-us &&\n+\tadd_stage $SHA1 2 added-by-both &&\n+\tadd_stage $SHA1 3 added-by-both &&\n+\tadd_stage $SHA1 1 modified-by-both &&\n+\tadd_stage $SHA1 2 modified-by-both &&\n+\tadd_stage $SHA1 3 modified-by-both &&\n+\tgit list-files -u >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tAA added-by-both\n+\tUA added-by-them\n+\tAU added-by-us\n+\tDD deleted-by-both\n+\tUD deleted-by-them\n+\tDU deleted-by-us\n+\tUU modified-by-both\n+\tEOF\n+\ttest_cmp expected actual\n+\t)\n+'\n+\n test_done\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259077","messageId":"1428328354-14897-18-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 17/25] list-files: add file modification options -[admADM]","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:26Z","receivedAt":"2015-04-06T13:52:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c  | 73 ++++++++++++++++++++++++++++++++++++++++++++++++---\n t/t7013-list-files.sh | 62 +++++++++++++++++++++++++++++++++++++++++++\n wt-status.c           |  8 +++---\n wt-status.h           |  1 +\n 4 files changed, 137 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex fe15417..14ffd62 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -11,6 +11,7 @@\n enum item_type {\n \tFROM_INDEX,\n \tFROM_WORKTREE,\n+\tFROM_DIFF,\n \tIS_DIR,\n \tIS_UNMERGED\n };\n@@ -39,8 +40,10 @@ static int max_depth;\n static int show_dirs;\n static int use_color = -1;\n static int show_indicator;\n-static int show_cached, show_untracked, show_unmerged;\n+static int show_cached, show_untracked, show_unmerged, show_changed;\n static int show_ignored;\n+static int show_added, show_deleted, show_modified;\n+static int show_wt_added, show_wt_deleted, show_wt_modified;\n \n static const char * const ls_usage[] = {\n \tN_(\"git list-files [options] [<pathspec>...]\"),\n@@ -57,6 +60,18 @@ struct option ls_options[] = {\n \t\t N_(\"show ignored files\")),\n \tOPT_BOOL('u', \"unmerged\", &show_unmerged,\n \t\t N_(\"show unmerged files\")),\n+\tOPT_BOOL('a', \"added\", &show_added,\n+\t\t N_(\"show added files compared to HEAD\")),\n+\tOPT_BOOL('d', \"deleted\", &show_deleted,\n+\t\t N_(\"show deleted files compared to HEAD\")),\n+\tOPT_BOOL('m', \"modified\", &show_modified,\n+\t\t N_(\"show modified files compared to HEAD\")),\n+\tOPT_BOOL('A', \"wt-added\", &show_wt_added,\n+\t\t N_(\"show added files in worktree\")),\n+\tOPT_BOOL('D', \"wt-deleted\", &show_wt_deleted,\n+\t\t N_(\"show deleted files in worktree\")),\n+\tOPT_BOOL('M', \"wt-modified\", &show_wt_modified,\n+\t\t N_(\"show modified files on worktree\")),\n \n \tOPT_GROUP(N_(\"Other\")),\n \tOPT_BOOL(0, \"tag\", &show_tag, N_(\"show tags\")),\n@@ -100,6 +115,7 @@ static mode_t get_mode(const struct item *item)\n \tcase FROM_INDEX:\n \t\treturn item->ce->ce_mode;\n \tcase FROM_WORKTREE:\n+\tcase FROM_DIFF:\n \tcase IS_UNMERGED:\n \t\treturn item->st.st_mode;\n \t}\n@@ -263,18 +279,59 @@ static void populate_unmerged(struct item_list *result,\n \t}\n }\n \n+static void populate_changed(struct item_list *result,\n+\t\t\t     const struct string_list *change)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < change->nr; i++) {\n+\t\tconst struct string_list_item *it = change->items + i;\n+\t\tstruct wt_status_change_data *d = it->util;\n+\t\tconst char *name = it->string;\n+\t\tstruct stat st;\n+\t\tchar tag[2];\n+\n+\t\tswitch (d->stagemask)\n+\t\t\tcontinue;\n+\n+\t\ttag[0] = d->index_status ? d->index_status : ' ';\n+\t\ttag[1] = d->worktree_status ? d->worktree_status : ' ';\n+\n+\t\tif ((show_added       && tag[0] == 'A') ||\n+\t\t    (show_deleted     && tag[0] == 'D') ||\n+\t\t    (show_modified    && tag[0] != ' ') ||\n+\t\t    (show_wt_added    && tag[1] == 'A') ||\n+\t\t    (show_wt_deleted  && tag[1] == 'D') ||\n+\t\t    (show_wt_modified && tag[1] != ' '))\n+\t\t\t;\t/* keep going */\n+\t\telse\n+\t\t\tcontinue;\n+\n+\t\tif (lstat(name, &st))\n+\t\t\t/* color_filename() treats this as an orphan file */\n+\t\t\tst.st_mode = 0;\n+\n+\t\tif (!matched(result, name, st.st_mode))\n+\t\t\tcontinue;\n+\n+\t\tadd_wt_item(result, FROM_DIFF, name, tag, &st);\n+\t}\n+}\n+\n static void wt_status_populate(struct item_list *result,\n \t\t\t       struct index_state *istate)\n {\n \tstruct wt_status ws;\n \n-\tif (!show_untracked && !show_ignored && !show_unmerged)\n+\tif (!show_untracked && !show_ignored &&\n+\t    !show_unmerged && !show_changed)\n \t\treturn;\n \n \twt_status_prepare(&ws);\n \tcopy_pathspec(&ws.pathspec, &recursive_pathspec);\n \tif (show_ignored)\n \t\tws.show_ignored_files = 1;\n+\tws.no_rename = 1;\n \tws.relative_paths = 0;\n \tws.use_color = 0;\n \tws.fp = NULL;\n@@ -286,6 +343,8 @@ static void wt_status_populate(struct item_list *result,\n \t\tpopulate_untracked(result, &ws.ignored, \"!!\");\n \tif (show_unmerged)\n \t\tpopulate_unmerged(result, &ws.change);\n+\tif (show_changed)\n+\t\tpopulate_changed(result, &ws.change);\n \n \tremove_duplicates(result);\n }\n@@ -301,7 +360,9 @@ static void cleanup_tags(struct item_list *result)\n \t}\n \n \tif (show_tag > 0 || show_unmerged ||\n-\t    show_cached + show_untracked + show_ignored > 1) {\n+\t    show_cached + show_untracked + show_ignored +\n+\t    show_added + show_deleted + show_wt_added + show_wt_deleted +\n+\t    show_modified + show_wt_modified > 1) {\n \t\tresult->tag_pos = 0;\n \t\tresult->tag_len = 2;\n \t\treturn;\n@@ -438,7 +499,11 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \n \targc = parse_options(argc, argv, prefix, ls_options, ls_usage, 0);\n \n-\tif (!show_cached && !show_untracked && !show_ignored && !show_unmerged)\n+\tshow_changed =\n+\t\tshow_added || show_deleted || show_modified ||\n+\t\tshow_wt_added || show_wt_deleted || show_wt_modified;\n+\tif (!show_cached && !show_untracked && !show_ignored &&\n+\t    !show_unmerged && !show_changed)\n \t\tshow_cached = 1;\n \n \tif (want_color(use_color))\ndiff --git a/t/t7013-list-files.sh b/t/t7013-list-files.sh\nindex 7fe9673..c747453 100755\n--- a/t/t7013-list-files.sh\n+++ b/t/t7013-list-files.sh\n@@ -253,4 +253,66 @@ test_expect_success 'list-files unmerged' '\n \t)\n '\n \n+test_expect_success 'list-files --added' '\n+\tgit list-files -a >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tA b\n+\tA c\n+\t  sa\n+\t  sc\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'list-files --modified' '\n+\techo modified >>a &&\n+\tgit add a &&\n+\tgit list-files --modified >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tM a\n+\tA b\n+\tA c\n+\t  sa\n+\t  sc\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'list-files --deleted' '\n+\tgit rm --cached a &&\n+\tgit list-files --deleted >actual &&\n+\tcat >expected <<-\\EOF &&\n+\ta\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'list-files --wt-modifed --wt-deleted' '\n+\trm b &&\n+\tgit list-files --wt-deleted >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tb\n+\tEOF\n+\ttest_cmp expected actual &&\n+\techo foo >>c &&\n+\tgit list-files --wt-modified >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tD b\n+\tM c\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'list-files --wt-modifed --modified' '\n+\tgit list-files -mM >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tD  a\n+\tAD b\n+\tAM c\n+\t   sa\n+\t   sc\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n test_done\ndiff --git a/wt-status.c b/wt-status.c\nindex cdbc8d7..ba9b56c 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -537,9 +537,11 @@ static void wt_status_collect_changes_index(struct wt_status *s)\n \trev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;\n \trev.diffopt.format_callback = wt_status_collect_updated_cb;\n \trev.diffopt.format_callback_data = s;\n-\trev.diffopt.detect_rename = 1;\n-\trev.diffopt.rename_limit = 200;\n-\trev.diffopt.break_opt = 0;\n+\tif (!s->no_rename) {\n+\t\trev.diffopt.detect_rename = 1;\n+\t\trev.diffopt.rename_limit = 200;\n+\t\trev.diffopt.break_opt = 0;\n+\t}\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_index(&rev, 1);\n }\ndiff --git a/wt-status.h b/wt-status.h\nindex 283a9fe..dc94f35 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -55,6 +55,7 @@ struct wt_status {\n \tint relative_paths;\n \tint submodule_summary;\n \tint show_ignored_files;\n+\tint no_rename;\n \tenum untracked_status_type show_untracked_files;\n \tconst char *ignore_submodule_arg;\n \tchar color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259080","messageId":"1428328354-14897-19-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 18/25] list-files: delete redundant cached entries","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:27Z","receivedAt":"2015-04-06T13:52:27Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"When both --cached and one of -amdAMD is used together we may have two\nentries of the same path, e.g. \"  foo\" and \"MM foo\". In this case it's\npretty clear that \"foo\" must be tracked, no need to display \"   foo\".\nThe new function does that.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c  | 12 ++++++++++--\n t/t7013-list-files.sh | 12 ++++++++++++\n 2 files changed, 22 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 14ffd62..31c2336 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -93,7 +93,10 @@ static int compare_item(const void *a_, const void *b_)\n {\n \tconst struct item *a = a_;\n \tconst struct item *b = b_;\n-\treturn strcmp(a->path, b->path);\n+\tint ret = strcmp(a->path, b->path);\n+\tif (ret)\n+\t\treturn ret;\n+\treturn strncmp(a->tag, b->tag, 2);\n }\n \n static void free_item(struct item *item)\n@@ -132,7 +135,12 @@ static void remove_duplicates(struct item_list *list)\n \tfor (src = dst = 1; src < list->nr; src++) {\n \t\tif (!compare_item(list->items + dst - 1, list->items + src))\n \t\t\tfree_item(list->items + src);\n-\t\telse\n+\t\telse if ((list->items[dst - 1].tag[0] == ' ' &&\n+\t\t\t  list->items[dst - 1].tag[1] == ' ' &&\n+\t\t\t  !strcmp(list->items[src].path, list->items[dst - 1].path))) {\n+\t\t\tfree_item(list->items + dst - 1);\n+\t\t\tlist->items[dst - 1] = list->items[src];\n+\t\t} else\n \t\t\tlist->items[dst++] = list->items[src];\n \t}\n \tlist->nr = dst;\ndiff --git a/t/t7013-list-files.sh b/t/t7013-list-files.sh\nindex c747453..392cce1 100755\n--- a/t/t7013-list-files.sh\n+++ b/t/t7013-list-files.sh\n@@ -315,4 +315,16 @@ test_expect_success 'list-files --wt-modifed --modified' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'list-files -mMc' '\n+\tgit list-files -mMc >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tD  a\n+\tAD b\n+\tAM c\n+\t   sa\n+\t   sc\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259078","messageId":"1428328354-14897-20-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 19/25] list-files: make alias 'ls' default to 'list-files'","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:28Z","receivedAt":"2015-04-06T13:52:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n config.c | 8 ++++++++\n 1 file changed, 8 insertions(+)\n\ndiff --git a/config.c b/config.c\nindex 15a2983..16209c6 100644\n--- a/config.c\n+++ b/config.c\n@@ -40,6 +40,10 @@ static struct config_source *cf;\n \n static int zlib_compression_seen;\n \n+static const char *builtin_config =\n+\t\"[alias]\\n\"\n+\t\"\tls = list-files\\n\";\n+\n /*\n  * Default config_set that contains key-value pairs from the usual set of config\n  * config files (i.e repo specific .git/config, user wide ~/.gitconfig, XDG\n@@ -1175,6 +1179,10 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n \n \thome_config_paths(&user_config, &xdg_config, \"config\");\n \n+\tif (git_config_system())\n+\t\tgit_config_from_buf(fn, \"<builtin>\", builtin_config,\n+\t\t\t\t    strlen(builtin_config), data);\n+\n \tif (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) {\n \t\tret += git_config_from_file(fn, git_etc_gitconfig(),\n \t\t\t\t\t    data);\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259079","messageId":"1428328354-14897-21-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 20/25] list-files: preload index","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:29Z","receivedAt":"2015-04-06T13:52:29Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Try to reduce refresh time. Note that we still need another\nread_cache() at top top, before parse_pathspec() because this\n*_SLASH_CHEAP needs the index loaded.\n\nOne day read_index() should be improved to notice that the on-disk\nversion is the same as the one in memory and skip I/O entirely..\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 31c2336..948b8e6 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -529,6 +529,7 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \trecursive_pathspec.max_depth = -1;\n \tfinalize_colopts(&colopts, -1);\n \n+\tread_cache_preload(&recursive_pathspec);\n \trefresh_index(&the_index, REFRESH_QUIET | REFRESH_UNMERGED,\n \t\t      &recursive_pathspec, NULL, NULL);\n \n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259081","messageId":"1428328354-14897-22-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 21/25] list-files: reduce match_pathspec calls in matched()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:30Z","receivedAt":"2015-04-06T13:52:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"match_pathspec() is expensive and the loop of match_pathspec() in\nadd_directory() is even more so. Luckily the output string list is\nusually sorted so if we add a directory \"builtin\", chances are\nbuiltin/foo follows right after. Perform a cheaper strncasecmp() in\nthis case.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c | 49 +++++++++++++++++++++++++++++++++++++------------\n 1 file changed, 37 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 948b8e6..228c39b 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -146,8 +146,8 @@ static void remove_duplicates(struct item_list *list)\n \tlist->nr = dst;\n }\n \n-static int add_directory(struct item_list *result,\n-\t\t\t const char *name)\n+static const char *add_directory(struct item_list *result,\n+\t\t\t\t const char *name)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct item *item;\n@@ -165,24 +165,37 @@ static int add_directory(struct item_list *result,\n \t\titem->path = strbuf_detach(&sb, NULL);\n \t\titem->tag[0] = ' ';\n \t\titem->tag[1] = ' ';\n-\t\treturn 1;\n+\t\treturn item->path;\n \t}\n \tstrbuf_release(&sb);\n-\treturn 0;\n+\treturn NULL;\n }\n \n-static int matched(struct item_list *result, const char *name, int mode)\n+static int matched(struct item_list *result, const char *name, int mode,\n+\t\t   const char **last_directory, int *last_dir_len)\n {\n \tint len = strlen(name);\n \n+\tif (*last_dir_len && len > *last_dir_len) {\n+\t\tif (!strncasecmp(name, *last_directory, *last_dir_len) &&\n+\t\t    name[*last_dir_len] == '/')\n+\t\t\treturn 0;\n+\t\t*last_dir_len = 0;\n+\t}\n+\n \tif (!match_pathspec(&recursive_pathspec, name, len, 0, NULL,\n \t\t\t    S_ISDIR(mode) || S_ISGITLINK(mode)))\n \t\treturn 0;\n \n \tif (show_dirs && strchr(name, '/') &&\n-\t    !match_pathspec(&pathspec, name, len, 0, NULL, 1) &&\n-\t    add_directory(result, name))\n-\t\treturn 0;\n+\t    !match_pathspec(&pathspec, name, len, 0, NULL, 1)) {\n+\t\tconst char *p = add_directory(result, name);\n+\t\tif (p) {\n+\t\t\t*last_directory = p;\n+\t\t\t*last_dir_len = strlen(p);\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n \n \treturn 1;\n }\n@@ -190,6 +203,8 @@ static int matched(struct item_list *result, const char *name, int mode)\n static void populate_cached_entries(struct item_list *result,\n \t\t\t\t    const struct index_state *istate)\n {\n+\tconst char *last_directory;\n+\tint last_dir_len = 0;\n \tint i;\n \n \tif (!show_cached)\n@@ -199,7 +214,8 @@ static void populate_cached_entries(struct item_list *result,\n \t\tconst struct cache_entry *ce = istate->cache[i];\n \t\tstruct item *item;\n \n-\t\tif (!matched(result, ce->name, ce->ce_mode))\n+\t\tif (!matched(result, ce->name, ce->ce_mode,\n+\t\t\t     &last_directory, &last_dir_len))\n \t\t\tcontinue;\n \n \t\tALLOC_GROW(result->items, result->nr + 1, result->alloc);\n@@ -236,6 +252,8 @@ static void populate_untracked(struct item_list *result,\n \t\t\t       const struct string_list *untracked,\n \t\t\t       const char *tag)\n {\n+\tconst char *last_directory;\n+\tint last_dir_len = 0;\n \tint i;\n \n \tfor (i = 0; i < untracked->nr; i++) {\n@@ -246,7 +264,8 @@ static void populate_untracked(struct item_list *result,\n \t\t\t/* color_filename() treats this as an orphan file */\n \t\t\tst.st_mode = 0;\n \n-\t\tif (!matched(result, name, st.st_mode))\n+\t\tif (!matched(result, name, st.st_mode,\n+\t\t\t     &last_directory, &last_dir_len))\n \t\t\tcontinue;\n \n \t\tadd_wt_item(result, FROM_WORKTREE, name, tag, &st);\n@@ -256,6 +275,8 @@ static void populate_untracked(struct item_list *result,\n static void populate_unmerged(struct item_list *result,\n \t\t\t      const struct string_list *change)\n {\n+\tconst char *last_directory;\n+\tint last_dir_len = 0;\n \tint i;\n \n \tfor (i = 0; i < change->nr; i++) {\n@@ -280,7 +301,8 @@ static void populate_unmerged(struct item_list *result,\n \t\t\t/* color_filename() treats this as an orphan file */\n \t\t\tst.st_mode = 0;\n \n-\t\tif (!matched(result, name, st.st_mode))\n+\t\tif (!matched(result, name, st.st_mode,\n+\t\t\t     &last_directory, &last_dir_len))\n \t\t\tcontinue;\n \n \t\tadd_wt_item(result, IS_UNMERGED, name, tag, &st);\n@@ -290,6 +312,8 @@ static void populate_unmerged(struct item_list *result,\n static void populate_changed(struct item_list *result,\n \t\t\t     const struct string_list *change)\n {\n+\tconst char *last_directory;\n+\tint last_dir_len = 0;\n \tint i;\n \n \tfor (i = 0; i < change->nr; i++) {\n@@ -319,7 +343,8 @@ static void populate_changed(struct item_list *result,\n \t\t\t/* color_filename() treats this as an orphan file */\n \t\t\tst.st_mode = 0;\n \n-\t\tif (!matched(result, name, st.st_mode))\n+\t\tif (!matched(result, name, st.st_mode,\n+\t\t\t     &last_directory, &last_dir_len))\n \t\t\tcontinue;\n \n \t\tadd_wt_item(result, FROM_DIFF, name, tag, &st);\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259082","messageId":"1428328354-14897-23-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 22/25] list-files: only do diff that is actually useful","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:31Z","receivedAt":"2015-04-06T13:52:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c | 12 ++++++++++++\n wt-status.c          |  8 ++++++++\n wt-status.h          |  2 ++\n 3 files changed, 22 insertions(+)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 228c39b..fc9c8d4 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -365,6 +365,10 @@ static void wt_status_populate(struct item_list *result,\n \tif (show_ignored)\n \t\tws.show_ignored_files = 1;\n \tws.no_rename = 1;\n+\tws.show_index_changes = show_unmerged || show_added ||\n+\t\tshow_modified || show_deleted;\n+\tws.show_worktree_changes = show_unmerged || show_wt_added ||\n+\t\tshow_wt_modified || show_wt_deleted;\n \tws.relative_paths = 0;\n \tws.use_color = 0;\n \tws.fp = NULL;\n@@ -538,6 +542,14 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \tif (!show_cached && !show_untracked && !show_ignored &&\n \t    !show_unmerged && !show_changed)\n \t\tshow_cached = 1;\n+\tif (show_unmerged) {\n+\t\tint i;\n+\t\tfor (i = 0; i < the_index.cache_nr; i++)\n+\t\t\tif (ce_stage(the_index.cache[i]))\n+\t\t\t\tbreak;\n+\t\tif (i == the_index.cache_nr)\n+\t\t\tshow_unmerged = 0;\n+\t}\n \n \tif (want_color(use_color))\n \t\tparse_ls_color();\ndiff --git a/wt-status.c b/wt-status.c\nindex ba9b56c..ce149b9 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -126,6 +126,8 @@ void wt_status_prepare(struct wt_status *s)\n \tmemcpy(s->color_palette, default_wt_status_colors,\n \t       sizeof(default_wt_status_colors));\n \ts->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;\n+\ts->show_index_changes = 1;\n+\ts->show_worktree_changes = 1;\n \ts->use_color = -1;\n \ts->relative_paths = 1;\n \ts->branch = resolve_refdup(\"HEAD\", 0, sha1, NULL);\n@@ -493,6 +495,9 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)\n {\n \tstruct rev_info rev;\n \n+\tif (!s->show_worktree_changes)\n+\t\treturn;\n+\n \tinit_revisions(&rev, NULL);\n \tsetup_revisions(0, NULL, &rev, NULL);\n \trev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;\n@@ -514,6 +519,9 @@ static void wt_status_collect_changes_index(struct wt_status *s)\n \tstruct rev_info rev;\n \tstruct setup_revision_opt opt;\n \n+\tif (!s->show_index_changes)\n+\t\treturn;\n+\n \tinit_revisions(&rev, NULL);\n \tmemset(&opt, 0, sizeof(opt));\n \topt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;\ndiff --git a/wt-status.h b/wt-status.h\nindex dc94f35..1d7ad3a 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -57,6 +57,8 @@ struct wt_status {\n \tint show_ignored_files;\n \tint no_rename;\n \tenum untracked_status_type show_untracked_files;\n+\tint show_index_changes;\n+\tint show_worktree_changes;\n \tconst char *ignore_submodule_arg;\n \tchar color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];\n \tunsigned colopts;\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259083","messageId":"1428328354-14897-24-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 23/25] pathspec: move getenv() code out of prefix_pathspec()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:32Z","receivedAt":"2015-04-06T13:52:32Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n pathspec.c | 55 +++++++++++++++++++++++++++----------------------------\n 1 file changed, 27 insertions(+), 28 deletions(-)\n\ndiff --git a/pathspec.c b/pathspec.c\nindex 9304ee3..5573127 100644\n--- a/pathspec.c\n+++ b/pathspec.c\n@@ -88,6 +88,13 @@ static void prefix_short_magic(struct strbuf *sb, int prefixlen,\n \tstrbuf_addf(sb, \",prefix:%d)\", prefixlen);\n }\n \n+struct global_flags {\n+\tunsigned literal : 1;\n+\tunsigned glob : 1;\n+\tunsigned noglob : 1;\n+\tunsigned icase : 1;\n+};\n+\n /*\n  * Take an element of a pathspec and check for magic signatures.\n  * Append the result to the prefix. Return the magic bitmap.\n@@ -105,39 +112,20 @@ static unsigned prefix_pathspec(struct pathspec_item *item,\n \t\t\t\tunsigned *p_short_magic,\n \t\t\t\tconst char **raw, unsigned flags,\n \t\t\t\tconst char *prefix, int prefixlen,\n-\t\t\t\tconst char *elt)\n+\t\t\t\tconst char *elt,\n+\t\t\t\tconst struct global_flags *global)\n {\n-\tstatic int literal_global = -1;\n-\tstatic int glob_global = -1;\n-\tstatic int noglob_global = -1;\n-\tstatic int icase_global = -1;\n \tunsigned magic = 0, short_magic = 0, global_magic = 0;\n \tconst char *copyfrom = elt, *long_magic_end = NULL;\n \tchar *match;\n \tint i, pathspec_prefix = -1;\n \n-\tif (literal_global < 0)\n-\t\tliteral_global = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0);\n-\tif (literal_global)\n+\tif (global->literal)\n \t\tglobal_magic |= PATHSPEC_LITERAL;\n-\n-\tif (glob_global < 0)\n-\t\tglob_global = git_env_bool(GIT_GLOB_PATHSPECS_ENVIRONMENT, 0);\n-\tif (glob_global)\n+\tif (global->glob)\n \t\tglobal_magic |= PATHSPEC_GLOB;\n-\n-\tif (noglob_global < 0)\n-\t\tnoglob_global = git_env_bool(GIT_NOGLOB_PATHSPECS_ENVIRONMENT, 0);\n-\n-\tif (glob_global && noglob_global)\n-\t\tdie(_(\"global 'glob' and 'noglob' pathspec settings are incompatible\"));\n-\n-\n-\tif (icase_global < 0)\n-\t\ticase_global = git_env_bool(GIT_ICASE_PATHSPECS_ENVIRONMENT, 0);\n-\tif (icase_global)\n+\tif (global->icase)\n \t\tglobal_magic |= PATHSPEC_ICASE;\n-\n \tif ((global_magic & PATHSPEC_LITERAL) &&\n \t    (global_magic & ~PATHSPEC_LITERAL))\n \t\tdie(_(\"global 'literal' pathspec setting is incompatible \"\n@@ -146,7 +134,8 @@ static unsigned prefix_pathspec(struct pathspec_item *item,\n \tif (flags & PATHSPEC_LITERAL_PATH)\n \t\tglobal_magic = 0;\n \n-\tif (elt[0] != ':' || literal_global ||\n+\n+\tif (elt[0] != ':' || global->literal ||\n \t    (flags & PATHSPEC_LITERAL_PATH)) {\n \t\t; /* nothing to do */\n \t} else if (elt[1] == '(') {\n@@ -213,7 +202,7 @@ static unsigned prefix_pathspec(struct pathspec_item *item,\n \t*p_short_magic = short_magic;\n \n \t/* --noglob-pathspec adds :(literal) _unless_ :(glob) is specified */\n-\tif (noglob_global && !(magic & PATHSPEC_GLOB))\n+\tif (global->noglob && !(magic & PATHSPEC_GLOB))\n \t\tglobal_magic |= PATHSPEC_LITERAL;\n \n \t/* --glob-pathspec is overridden by :(literal) */\n@@ -247,7 +236,7 @@ static unsigned prefix_pathspec(struct pathspec_item *item,\n \t */\n \tif (flags & PATHSPEC_PREFIX_ORIGIN) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n-\t\tif (prefixlen && !literal_global) {\n+\t\tif (prefixlen && !global->literal) {\n \t\t\t/* Preserve the actual prefix length of each pattern */\n \t\t\tif (short_magic)\n \t\t\t\tprefix_short_magic(&sb, prefixlen, short_magic);\n@@ -365,6 +354,7 @@ void parse_pathspec(struct pathspec *pathspec,\n \tstruct pathspec_item *item;\n \tconst char *entry = argv ? *argv : NULL;\n \tint i, n, prefixlen, nr_exclude = 0;\n+\tstruct global_flags global;\n \n \tmemset(pathspec, 0, sizeof(*pathspec));\n \n@@ -401,6 +391,14 @@ void parse_pathspec(struct pathspec *pathspec,\n \t\treturn;\n \t}\n \n+\tmemset(&global, 0, sizeof(global));\n+\tglobal.literal = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0) != 0;\n+\tglobal.glob    = git_env_bool(GIT_GLOB_PATHSPECS_ENVIRONMENT,    0) != 0;\n+\tglobal.noglob  = git_env_bool(GIT_NOGLOB_PATHSPECS_ENVIRONMENT,  0) != 0;\n+\tglobal.icase   = git_env_bool(GIT_ICASE_PATHSPECS_ENVIRONMENT,   0) != 0;\n+\tif (global.glob && global.noglob)\n+\t\tdie(_(\"global 'glob' and 'noglob' pathspec settings are incompatible\"));\n+\n \tn = 0;\n \twhile (argv[n])\n \t\tn++;\n@@ -416,7 +414,8 @@ void parse_pathspec(struct pathspec *pathspec,\n \n \t\titem[i].magic = prefix_pathspec(item + i, &short_magic,\n \t\t\t\t\t\targv + i, flags,\n-\t\t\t\t\t\tprefix, prefixlen, entry);\n+\t\t\t\t\t\tprefix, prefixlen, entry,\n+\t\t\t\t\t\t&global);\n \t\tif ((flags & PATHSPEC_LITERAL_PATH) &&\n \t\t    !(magic_mask & PATHSPEC_LITERAL))\n \t\t\titem[i].magic |= PATHSPEC_LITERAL;\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259085","messageId":"1428328354-14897-25-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 24/25] list-files: make :(glob) pathspec default","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:33Z","receivedAt":"2015-04-06T13:52:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c  | 1 +\n pathspec.c            | 4 ++++\n pathspec.h            | 1 +\n t/t7013-list-files.sh | 7 ++++++-\n 4 files changed, 12 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex fc9c8d4..c85bb55 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -557,6 +557,7 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \tparse_pathspec(&pathspec, 0,\n \t\t       PATHSPEC_PREFER_CWD |\n \t\t       (max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0) |\n+\t\t       PATHSPEC_DEFAULT_GLOB |\n \t\t       PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,\n \t\t       cmd_prefix, argv);\n \tpathspec.max_depth = max_depth;\ndiff --git a/pathspec.c b/pathspec.c\nindex 5573127..3770c26 100644\n--- a/pathspec.c\n+++ b/pathspec.c\n@@ -396,6 +396,10 @@ void parse_pathspec(struct pathspec *pathspec,\n \tglobal.glob    = git_env_bool(GIT_GLOB_PATHSPECS_ENVIRONMENT,    0) != 0;\n \tglobal.noglob  = git_env_bool(GIT_NOGLOB_PATHSPECS_ENVIRONMENT,  0) != 0;\n \tglobal.icase   = git_env_bool(GIT_ICASE_PATHSPECS_ENVIRONMENT,   0) != 0;\n+\tif ((flags & PATHSPEC_DEFAULT_GLOB) &&\n+\t    !getenv(GIT_GLOB_PATHSPECS_ENVIRONMENT) &&\n+\t    !getenv(GIT_NOGLOB_PATHSPECS_ENVIRONMENT))\n+\t\tglobal.glob = 1;\n \tif (global.glob && global.noglob)\n \t\tdie(_(\"global 'glob' and 'noglob' pathspec settings are incompatible\"));\n \ndiff --git a/pathspec.h b/pathspec.h\nindex 0c11262..7e4058f 100644\n--- a/pathspec.h\n+++ b/pathspec.h\n@@ -67,6 +67,7 @@ struct pathspec {\n  * allowed, then it will automatically set for every pathspec.\n  */\n #define PATHSPEC_LITERAL_PATH (1<<8)\n+#define PATHSPEC_DEFAULT_GLOB (1<<9) /* :(glob) by default */\n \n extern void parse_pathspec(struct pathspec *pathspec,\n \t\t\t   unsigned magic_mask,\ndiff --git a/t/t7013-list-files.sh b/t/t7013-list-files.sh\nindex 392cce1..4a9368e 100755\n--- a/t/t7013-list-files.sh\n+++ b/t/t7013-list-files.sh\n@@ -116,11 +116,16 @@ test_expect_success 'column output' '\n '\n \n test_expect_success 'list-files selectively from index' '\n-\tgit list-files -R \"*a\" >actual &&\n+\tgit list-files -R \"**/a\" >actual &&\n \tcat >expect <<-\\EOF &&\n \ta\n \tsa/a\n \tEOF\n+\ttest_cmp expect actual &&\n+\tgit list-files -R \"*a\" >actual &&\n+\tcat >expect <<-\\EOF &&\n+\ta\n+\tEOF\n \ttest_cmp expect actual\n '\n \n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259084","messageId":"1428328354-14897-26-git-send-email-pclouds@gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 25/25] list-files: documentation","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:52:34Z","receivedAt":"2015-04-06T13:52:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-list-files.txt (new) | 115 +++++++++++++++++++++++++++++++++\n 1 file changed, 115 insertions(+)\n create mode 100644 Documentation/git-list-files.txt\n\ndiff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt\nnew file mode 100644\nindex 0000000..9b9edce\n--- /dev/null\n+++ b/Documentation/git-list-files.txt\n@@ -0,0 +1,115 @@\n+git-list-files(1)\n+===============\n+\n+NAME\n+----\n+git-list-files - List files\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git list-files [options] [<pathspec>...]\n+\n+DESCRIPTION\n+-----------\n+List files (by default in current working directory) that are in the\n+index. Depending on the chosen options, maybe only modified files in\n+working tree are shown, or untracked files... The builtin alias \"ls\"\n+is set to \"list-files\".\n+\n+OPTIONS\n+-------\n+-c::\n+--cached::\n+\tShow cached files (default)\n+\n+-d::\n+--deleted::\n+\tShow deleted files in index, compared to HEAD\n+\n+-a::\n+--added::\n+\tShow added files in index, compared to HEAD\n+\n+-m::\n+--modified::\n+\tShow modified files in index, compared to HEAD. This implies\n+\t--deleted and --added\n+\n+-D::\n+--wt-deleted::\n+\tShow deleted files in working directory\n+\n+-A::\n+--wt-added::\n+\tShow added files in working directory\n+\n+-M::\n+--wt-modified::\n+\tShow modified files in working directory. This implies\n+\t--wt-deleted and --wt-added\n+\n+-o::\n+--others::\n+\tShow untracked files (and only unignored ones unless -i is\n+\tspecified)\n+\n+-i::\n+--ignored::\n+\tShow only ignored files. When showing files in the index,\n+\tprint only those matched by an exclude pattern. When showing\n+\t\"other\" files, show only those matched by an exclude pattern.\n+\n+-u::\n+--unmerged::\n+\tShow unmerged files\n+\n+-F::\n+--classify::\n+\tAppend indicator (one of `*/=>@|&`, which is executable,\n+\tdirectory, socket, Solaris door, symlink, fifo, or submodule\n+\trespectively) to entries.\n+\n+--color[=<when>]::\n+--no-color::\n+\tColor file names. The value must be `always`, `never`, or\n+\t`auto`. `--no-color` is equivalent to\n+\t`--color=never`. `--color` is equivalent to\n+\t`--color=auto`. See configuration variable `color.list-files`\n+\tfor the default settings.\n+\n+--column[=<options>]::\n+--no-column::\n+-1::\n+\tDisplay files in columns. See configuration variable column.ui\n+\tfor option syntax. `--column` and `--no-column` without options\n+\tare equivalent to 'always' and 'never' respectively.\n+\t`-1` is a shortcut for --no-column.\n+\n+--max-depth=<depth>::\n+--recursive::\n+-R::\n+\tFor each <pathspec> given on command line, descend at most <depth>\n+\tlevels of directories. A negative value means no limit.\n+\tThis option is ignored if <pathspec> contains active wildcards.\n+\tIn other words if \"a*\" matches a directory named \"a*\",\n+\t\"*\" is matched literally so --max-depth is still effective.\n+\t`-R` or `--recursive` is equivalent of `--max-depth=-1`\n+\t(infinite recursion).  The default is `--max-depth=0`.\n+\n+--tag::\n+--no-tag::\n+\tUsually when tags are displayed by the left of the file name if\n+\tthere are more than one file selection. Use either of these options\n+\tto force always or never display tags.\n+\n+<pathspec>::\n+\tFiles to show. :(glob) magic is enabled by default.\n+\n+SEE ALSO\n+--------\n+linkgit:git-ls-files[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"259086","messageId":"CACsJy8B1M1wwGFgcguaq1_p0qMPCiooKq-mhYT+mPfENdE9ttg@mail.gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 00/25] list-files redesign","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2015-04-06T13:58:16Z","receivedAt":"2015-04-06T13:58:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Apr 6, 2015 at 8:52 PM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> The UI part is the same (or nearly the same) with the last round. The\n> internal data structure is reorignized to avoid abusing string_list.\n> Tests and documentation are added back.\n\nIt's supposed to be a reply to the last round [1] but I missed\n--in-reply-to and screwed it up. Sorry.\n\n[1] http://permalink.gmane.org/gmane.comp.version-control.git/265142\n-- \nDuy\n"},{"id":"259096","messageId":"CAPig+cS=41Y+c-vz7h8=jWEw_9ZKWT4JJ-boERCMyRzq=x_p+g@mail.gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-8-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 07/25] list-files: add tag to each entry, filter duplicate tags","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-04-06T21:32:48Z","receivedAt":"2015-04-06T21:32:48Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Apr 6, 2015 at 9:52 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> All entries have a two-letter tag. If all entries have the same tags,\n> tags are not displayed.\n>\n> The outcome before and after this patch is the same. But it will be\n> useful in future when there are more than one type of entry.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/builtin/list-files.c b/builtin/list-files.c\n> index 51ff19d..ec9ca17 100644\n> --- a/builtin/list-files.c\n> +++ b/builtin/list-files.c\n> @@ -52,10 +56,61 @@ static void populate_cached_entries(struct item_list *result,\n> +static void cleanup_tags(struct item_list *result)\n> +{\n> +       int i, same_1 = 1, same_2 = 1;\n> +\n> +       if (!show_tag) {\n> +               result->tag_pos = 0;\n> +               result->tag_len = 0;\n> +               return;\n> +       }\n> [...]\n> +}\n> +\n> +/* this is similar to quote_path_relative() except it does not clear sb */\n\nThis comment is a bit confusing since it talks about 'sb', which is a\nlocal variable, rather than the intended 'out'. Perhaps rephrase:\n\n    /* similar to quote_path_relative() but does not clear 'out' */\n\n> +static void quote_item(struct strbuf *out, const struct item *item)\n> +{\n> +       static struct strbuf sb = STRBUF_INIT;\n> +       const char *rel;\n> +\n> +       strbuf_reset(&sb);\n> +       rel = relative_path(item->path, prefix, &sb);\n> +       quote_c_style(rel, out, NULL, 0);\n> +}\n> +\n>  static void display(const struct item_list *result)\n>  {\n>         struct strbuf quoted = STRBUF_INIT;\n"},{"id":"259097","messageId":"CAPig+cRwHu+OznoCNcXLM-+YZmiYNtXKs2PF6Lm+So1TdkoJ4g@mail.gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-12-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 11/25] list-files: add --color","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-04-06T21:33:36Z","receivedAt":"2015-04-06T21:33:36Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Apr 6, 2015 at 9:52 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/t/t7013-list-files.sh b/t/t7013-list-files.sh\n> index a4916d8..cdbc34a 100755\n> --- a/t/t7013-list-files.sh\n> +++ b/t/t7013-list-files.sh\n> @@ -110,4 +110,48 @@ test_expect_success 'list-files from subdir (2)' '\n>         )\n>  '\n>\n> +test_expect_success 'setup 2' '\n> +       git init 2 &&\n> +       (\n> +       cd 2 &&\n> +       mkdir dir &&\n> +       touch file dir/file &&\n> +       git init gitlink &&\n> +       ( cd gitlink && test_commit foo ) &&\n> +       git add file dir/file gitlink &&\n> +       git commit -qm1\n\nMinor: Why -q?\n\n> +       )\n> +'\n> +\n> +test_expect_success 'LS_COLORS env variable' '\n> +       (\n> +       cd 2 &&\n> +       LS_COLORS=\"rs=0:fi=31:di=32\" &&\n> +       export LS_COLORS &&\n> +       git list-files --color=always | test_decode_color | \\\n> +               grep -v gitlink >actual &&\n> +       cat >expected <<-\\EOF &&\n> +       <GREEN>dir<RESET>\n> +       <RED>file<RESET>\n> +       EOF\n> +       test_cmp expected actual\n> +       )\n> +'\n> +\n> +test_expect_success 'color.ls.*' '\n> +       (\n> +       cd 2 &&\n> +       test_config color.ls.file red &&\n> +       test_config color.ls.directory green &&\n> +       test_config color.ls.submodule yellow &&\n> +       git list-files --color=always | test_decode_color >actual &&\n> +       cat >expected <<-\\EOF &&\n> +       <GREEN>dir<RESET>\n> +       <RED>file<RESET>\n> +       <YELLOW>gitlink<RESET>\n> +       EOF\n> +       test_cmp expected actual\n> +       )\n> +'\n> +\n>  test_done\n> --\n> 2.3.0.rc1.137.g477eb31\n"},{"id":"259098","messageId":"CAPig+cQrzNT8pL9CiLKxJu0+qEmefeoUV8j2_7Ax9_JzGm4Q3Q@mail.gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-16-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 15/25] list-files: add --ignored","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-04-06T21:34:17Z","receivedAt":"2015-04-06T21:34:17Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Apr 6, 2015 at 9:52 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/builtin/list-files.c b/builtin/list-files.c\n> index 3cbd30d..31991a4 100644\n> --- a/builtin/list-files.c\n> +++ b/builtin/list-files.c\n> @@ -39,6 +39,7 @@ static int show_dirs;\n>  static int use_color = -1;\n>  static int show_indicator;\n>  static int show_cached, show_untracked;\n> +static int show_ignored;\n>\n>  static const char * const ls_usage[] = {\n>         N_(\"git list-files [options] [<pathspec>...]\"),\n> @@ -51,6 +52,8 @@ struct option ls_options[] = {\n>                  N_(\"show cached files (default)\")),\n>         OPT_BOOL('o', \"others\", &show_untracked,\n>                  N_(\"show untracked files\")),\n> +       OPT_BOOL('i', \"ignored\", &show_ignored,\n> +                N_(\"show ignored files\")),\n>\n>         OPT_GROUP(N_(\"Other\")),\n>         OPT_BOOL(0, \"tag\", &show_tag, N_(\"show tags\")),\n> @@ -202,7 +205,8 @@ static void add_wt_item(struct item_list *result,\n>  }\n>\n>  static void populate_untracked(struct item_list *result,\n> -                              const struct string_list *untracked)\n> +                              const struct string_list *untracked,\n> +                              const char *tag)\n\nMinor: I could easily see patch 14/25 declaring populate_untracked()\nthis way in the first place...\n\n>  {\n>         int i;\n>\n> @@ -217,7 +221,7 @@ static void populate_untracked(struct item_list *result,\n>                 if (!matched(result, name, st.st_mode))\n>                         continue;\n>\n> -               add_wt_item(result, FROM_WORKTREE, name, \"??\", &st);\n> +               add_wt_item(result, FROM_WORKTREE, name, tag, &st);\n\n...since doing so would eliminate this change from 15/25 and...\n(continued below)\n\n>         }\n>  }\n>\n> @@ -226,18 +230,22 @@ static void wt_status_populate(struct item_list *result,\n>  {\n>         struct wt_status ws;\n>\n> -       if (!show_untracked)\n> +       if (!show_untracked && !show_ignored)\n>                 return;\n>\n>         wt_status_prepare(&ws);\n>         copy_pathspec(&ws.pathspec, &recursive_pathspec);\n> +       if (show_ignored)\n> +               ws.show_ignored_files = 1;\n\nSimpler:\n\n    ws.show_ignored_files = show_ignored;\n\n>         ws.relative_paths = 0;\n>         ws.use_color = 0;\n>         ws.fp = NULL;\n>         wt_status_collect(&ws);\n>\n>         if (show_untracked)\n> -               populate_untracked(result, &ws.untracked);\n> +               populate_untracked(result, &ws.untracked, \"??\");\n\n(continued from above) ...and this change.\n\nMore below.\n\n> +       if (show_ignored)\n> +               populate_untracked(result, &ws.ignored, \"!!\");\n>\n>         remove_duplicates(result);\n>  }\n> @@ -251,7 +259,9 @@ static void cleanup_tags(struct item_list *result)\n>                 result->tag_len = 0;\n>                 return;\n>         }\n> -       if (show_tag > 0 || show_cached + show_untracked > 1) {\n> +\n> +       if (show_tag > 0 ||\n> +           show_cached + show_untracked + show_ignored > 1) {\n>                 result->tag_pos = 0;\n>                 result->tag_len = 2;\n>                 return;\n> @@ -388,7 +398,7 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n>\n>         argc = parse_options(argc, argv, prefix, ls_options, ls_usage, 0);\n>\n> -       if (!show_cached && !show_untracked)\n> +       if (!show_cached && !show_untracked && !show_ignored)\n>                 show_cached = 1;\n>\n>         if (want_color(use_color))\n> diff --git a/t/t7013-list-files.sh b/t/t7013-list-files.sh\n> index 53dc745..6b76d1f 100755\n> --- a/t/t7013-list-files.sh\n> +++ b/t/t7013-list-files.sh\n> @@ -44,6 +44,21 @@ test_expect_success 'list-files --others' '\n>         test_cmp expect actual\n>  '\n>\n> +test_expect_success 'list-files --others' '\n\ns/others/ignored/\n\n> +       git list-files --ignored >actual &&\n> +       cat >expect <<-\\EOF &&\n> +       !! foo\n> +          sa\n> +       EOF\n> +       test_cmp expect actual &&\n> +       git list-files --ignored -R >actual &&\n> +       cat >expect <<-\\EOF &&\n> +       foo\n> +       sa/foo\n> +       EOF\n> +       test_cmp expect actual\n> +'\n> +\n>  test_expect_success 'list-files -R from index' '\n>         git list-files -R >actual &&\n>         cat >expect <<-\\EOF &&\n> --\n> 2.3.0.rc1.137.g477eb31\n"},{"id":"259099","messageId":"CAPig+cTs-rXcohvrm40ThSGtGYZvquF2q=jR245LyKsNW+NAQw@mail.gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-17-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 16/25] list-files: add --unmerged","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-04-06T21:34:41Z","receivedAt":"2015-04-06T21:34:41Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Apr 6, 2015 at 9:52 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/builtin/list-files.c b/builtin/list-files.c\n> index 31991a4..fe15417 100644\n> --- a/builtin/list-files.c\n> +++ b/builtin/list-files.c\n> @@ -225,12 +229,46 @@ static void populate_untracked(struct item_list *result,\n>         }\n>  }\n>\n> +static void populate_unmerged(struct item_list *result,\n> +                             const struct string_list *change)\n> +{\n> +       int i;\n> +\n> +       for (i = 0; i < change->nr; i++) {\n> +               const struct string_list_item *it = change->items + i;\n> +               struct wt_status_change_data *d = it->util;\n> +               const char *name = it->string;\n> +               const char *tag;\n> +               struct stat st;\n> +\n> +               switch (d->stagemask) {\n> +               case 1: tag = \"DD\"; break; /* both deleted */\n> +               case 2: tag = \"AU\"; break; /* added by us */\n> +               case 3: tag = \"UD\"; break; /* deleted by them */\n> +               case 4: tag = \"UA\"; break; /* added by them */\n> +               case 5: tag = \"DU\"; break; /* deleted by us */\n> +               case 6: tag = \"AA\"; break; /* both added */\n> +               case 7: tag = \"UU\"; break; /* both modified */\n> +               default: continue;\n\nDoes the 'default' case represent a \"cannot happen\" situation? If so,\ndoes it deserve some sort of \"BUG\" diagnostic message or something?\n\nMore below.\n\n> +               }\n> +\n> +               if (lstat(name, &st))\n> +                       /* color_filename() treats this as an orphan file */\n> +                       st.st_mode = 0;\n> +\n> +               if (!matched(result, name, st.st_mode))\n> +                       continue;\n> +\n> +               add_wt_item(result, IS_UNMERGED, name, tag, &st);\n> +       }\n> +}\n> +\n> diff --git a/t/t7013-list-files.sh b/t/t7013-list-files.sh\n> index 6b76d1f..7fe9673 100755\n> --- a/t/t7013-list-files.sh\n> +++ b/t/t7013-list-files.sh\n> @@ -218,4 +218,39 @@ test_expect_success '--classify' '\n>         )\n>  '\n>\n> +test_expect_success 'list-files unmerged' '\n> +       (\n> +       add_stage() {\n> +               echo \"100644 $1 $2      $3\" | git update-index --index-info\n> +       }\n> +       git init 3 &&\n> +       cd 3 &&\n> +       test_commit 1 &&\n> +       SHA1=`echo 1 | git hash-object -w --stdin` &&\n\n    SHA1=$(echo 1 | git hash-object -w --stdin) &&\n\n> +       add_stage $SHA1 1 deleted-by-both &&\n> +       add_stage $SHA1 2 added-by-us &&\n> +       add_stage $SHA1 1 deleted-by-them &&\n> +       add_stage $SHA1 2 deleted-by-them &&\n> +       add_stage $SHA1 3 added-by-them &&\n> +       add_stage $SHA1 3 deleted-by-us &&\n> +       add_stage $SHA1 1 deleted-by-us &&\n> +       add_stage $SHA1 2 added-by-both &&\n> +       add_stage $SHA1 3 added-by-both &&\n> +       add_stage $SHA1 1 modified-by-both &&\n> +       add_stage $SHA1 2 modified-by-both &&\n> +       add_stage $SHA1 3 modified-by-both &&\n> +       git list-files -u >actual &&\n> +       cat >expected <<-\\EOF &&\n> +       AA added-by-both\n> +       UA added-by-them\n> +       AU added-by-us\n> +       DD deleted-by-both\n> +       UD deleted-by-them\n> +       DU deleted-by-us\n> +       UU modified-by-both\n> +       EOF\n> +       test_cmp expected actual\n> +       )\n> +'\n> +\n>  test_done\n> --\n> 2.3.0.rc1.137.g477eb31\n"},{"id":"259100","messageId":"CAPig+cSXj47+EzvNC0HOpDD5OM7y7mbzu+dp_fFEgN+9-fuEyw@mail.gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-18-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 17/25] list-files: add file modification options -[admADM]","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-04-06T21:34:58Z","receivedAt":"2015-04-06T21:34:58Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Apr 6, 2015 at 9:52 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/wt-status.c b/wt-status.c\n> index cdbc8d7..ba9b56c 100644\n> --- a/wt-status.c\n> +++ b/wt-status.c\n> @@ -537,9 +537,11 @@ static void wt_status_collect_changes_index(struct wt_status *s)\n>         rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;\n>         rev.diffopt.format_callback = wt_status_collect_updated_cb;\n>         rev.diffopt.format_callback_data = s;\n> -       rev.diffopt.detect_rename = 1;\n> -       rev.diffopt.rename_limit = 200;\n> -       rev.diffopt.break_opt = 0;\n> +       if (!s->no_rename) {\n\nDouble-negatives are difficult. Perhaps call this 'detect_rename'?\n\n> +               rev.diffopt.detect_rename = 1;\n> +               rev.diffopt.rename_limit = 200;\n> +               rev.diffopt.break_opt = 0;\n> +       }\n>         copy_pathspec(&rev.prune_data, &s->pathspec);\n>         run_diff_index(&rev, 1);\n>  }\n> diff --git a/wt-status.h b/wt-status.h\n> index 283a9fe..dc94f35 100644\n> --- a/wt-status.h\n> +++ b/wt-status.h\n> @@ -55,6 +55,7 @@ struct wt_status {\n>         int relative_paths;\n>         int submodule_summary;\n>         int show_ignored_files;\n> +       int no_rename;\n>         enum untracked_status_type show_untracked_files;\n>         const char *ignore_submodule_arg;\n>         char color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];\n> --\n> 2.3.0.rc1.137.g477eb31\n"},{"id":"259101","messageId":"CAPig+cTDHf5Wh92SLHfQ0_GRY0cbpz+ALfiukJJHed6d=QmLdg@mail.gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-19-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 18/25] list-files: delete redundant cached entries","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-04-06T21:35:24Z","receivedAt":"2015-04-06T21:35:24Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Apr 6, 2015 at 9:52 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> When both --cached and one of -amdAMD is used together we may have two\n> entries of the same path, e.g. \"  foo\" and \"MM foo\". In this case it's\n> pretty clear that \"foo\" must be tracked, no need to display \"   foo\".\n> The new function does that.\n>\n> Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/builtin/list-files.c b/builtin/list-files.c\n> index 14ffd62..31c2336 100644\n> --- a/builtin/list-files.c\n> +++ b/builtin/list-files.c\n> @@ -93,7 +93,10 @@ static int compare_item(const void *a_, const void *b_)\n>  {\n>         const struct item *a = a_;\n>         const struct item *b = b_;\n> -       return strcmp(a->path, b->path);\n> +       int ret = strcmp(a->path, b->path);\n> +       if (ret)\n> +               return ret;\n> +       return strncmp(a->tag, b->tag, 2);\n>  }\n>\n>  static void free_item(struct item *item)\n> @@ -132,7 +135,12 @@ static void remove_duplicates(struct item_list *list)\n>         for (src = dst = 1; src < list->nr; src++) {\n>                 if (!compare_item(list->items + dst - 1, list->items + src))\n>                         free_item(list->items + src);\n> -               else\n> +               else if ((list->items[dst - 1].tag[0] == ' ' &&\n> +                         list->items[dst - 1].tag[1] == ' ' &&\n> +                         !strcmp(list->items[src].path, list->items[dst - 1].path))) {\n> +                       free_item(list->items + dst - 1);\n> +                       list->items[dst - 1] = list->items[src];\n\nI was wondering if you could drop this backward-patching case by\nhaving tag==\"  \" items sort after tag=\"xx\" items and just fold out the\ntag==\"  \" items normally in the preceding 'if', however, when I\nstarted coding it, I found that the resulting code wasn't any more\npleasant.\n\n> +               } else\n>                         list->items[dst++] = list->items[src];\n>         }\n>         list->nr = dst;\n"},{"id":"259102","messageId":"CAPig+cTfSvCyfRB9TiEd8X8KT=--iU3sJyWrH9xSPk0mwAo-Pw@mail.gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-21-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 20/25] list-files: preload index","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-04-06T21:35:50Z","receivedAt":"2015-04-06T21:35:50Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Apr 6, 2015 at 9:52 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Try to reduce refresh time. Note that we still need another\n> read_cache() at top top, before parse_pathspec() because this\n\ns/top top/the/top/\n\n> *_SLASH_CHEAP needs the index loaded.\n>\n> One day read_index() should be improved to notice that the on-disk\n> version is the same as the one in memory and skip I/O entirely..\n\ns/\\.\\.$/./\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  builtin/list-files.c | 1 +\n>  1 file changed, 1 insertion(+)\n>\n> diff --git a/builtin/list-files.c b/builtin/list-files.c\n> index 31c2336..948b8e6 100644\n> --- a/builtin/list-files.c\n> +++ b/builtin/list-files.c\n> @@ -529,6 +529,7 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n>         recursive_pathspec.max_depth = -1;\n>         finalize_colopts(&colopts, -1);\n>\n> +       read_cache_preload(&recursive_pathspec);\n>         refresh_index(&the_index, REFRESH_QUIET | REFRESH_UNMERGED,\n>                       &recursive_pathspec, NULL, NULL);\n>\n> --\n> 2.3.0.rc1.137.g477eb31\n"},{"id":"259103","messageId":"CAPig+cR+-j6HJffMjhWiVjd1tVKaQKe9-dgJMtOcB_7AeKE9UQ@mail.gmail.com","threadId":"39015","inReplyTo":"1428328354-14897-26-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 25/25] list-files: documentation","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-04-06T21:37:09Z","receivedAt":"2015-04-06T21:37:09Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Apr 6, 2015 at 9:52 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt\n> new file mode 100644\n> index 0000000..9b9edce\n> --- /dev/null\n> +++ b/Documentation/git-list-files.txt\n> @@ -0,0 +1,115 @@\n> +git-list-files(1)\n> +===============\n> +\n> +NAME\n> +----\n> +git-list-files - List files\n> +\n> +SYNOPSIS\n> +--------\n> +[verse]\n> +'git list-files [options] [<pathspec>...]\n> +\n> +DESCRIPTION\n> +-----------\n> +List files (by default in current working directory) that are in the\n> +index. Depending on the chosen options, maybe only modified files in\n> +working tree are shown, or untracked files... The builtin alias \"ls\"\n> +is set to \"list-files\".\n> +\n> +OPTIONS\n> +-------\n> +-c::\n> +--cached::\n> +       Show cached files (default)\n\nHere and elsewhere: s/$/./\n\n> +-d::\n> +--deleted::\n> +       Show deleted files in index, compared to HEAD\n> +\n> +-a::\n> +--added::\n> +       Show added files in index, compared to HEAD\n> +\n> +-m::\n> +--modified::\n> +       Show modified files in index, compared to HEAD. This implies\n> +       --deleted and --added\n\ns/--deleted/`--deleted`/\ns/--added/`--added`/\n\n> +-D::\n> +--wt-deleted::\n> +       Show deleted files in working directory\n> +\n> +-A::\n> +--wt-added::\n> +       Show added files in working directory\n> +\n> +-M::\n> +--wt-modified::\n> +       Show modified files in working directory. This implies\n> +       --wt-deleted and --wt-added\n\ns/--wt-deleted/`--wt-deleted`/\ns/--wt-added/`--wt-added`/\n\n> +-o::\n> +--others::\n> +       Show untracked files (and only unignored ones unless -i is\n\ns/-i/`-i`/ ...or... s/-i/`--ignored`/\n\n> +       specified)\n> +\n> +-i::\n> +--ignored::\n> +       Show only ignored files. When showing files in the index,\n> +       print only those matched by an exclude pattern. When showing\n> +       \"other\" files, show only those matched by an exclude pattern.\n> +\n> +-u::\n> +--unmerged::\n> +       Show unmerged files\n> +\n> +-F::\n> +--classify::\n> +       Append indicator (one of `*/=>@|&`, which is executable,\n> +       directory, socket, Solaris door, symlink, fifo, or submodule\n> +       respectively) to entries.\n> +\n> +--color[=<when>]::\n> +--no-color::\n> +       Color file names. The value must be `always`, `never`, or\n> +       `auto`. `--no-color` is equivalent to\n> +       `--color=never`. `--color` is equivalent to\n> +       `--color=auto`. See configuration variable `color.list-files`\n\nIs 'color.list-files' documented anywhere?\n\n> +       for the default settings.\n> +\n> +--column[=<options>]::\n> +--no-column::\n> +-1::\n> +       Display files in columns. See configuration variable column.ui\n\ns/column.ui/`column.ui`/\n\n> +       for option syntax. `--column` and `--no-column` without options\n> +       are equivalent to 'always' and 'never' respectively.\n> +       `-1` is a shortcut for --no-column.\n\ns/--no-column/`--no-column`/\n\n> +--max-depth=<depth>::\n> +--recursive::\n> +-R::\n> +       For each <pathspec> given on command line, descend at most <depth>\n> +       levels of directories. A negative value means no limit.\n> +       This option is ignored if <pathspec> contains active wildcards.\n> +       In other words if \"a*\" matches a directory named \"a*\",\n> +       \"*\" is matched literally so --max-depth is still effective.\n\ns/--max-depth/`--max-depth`/\n\n> +       `-R` or `--recursive` is equivalent of `--max-depth=-1`\n> +       (infinite recursion).  The default is `--max-depth=0`.\n> +\n> +--tag::\n> +--no-tag::\n> +       Usually when tags are displayed by the left of the file name if\n> +       there are more than one file selection. Use either of these options\n> +       to force always or never display tags.\n\nThis is somewhat difficult to parse. Perhaps it could be rephrased\nsomething like this:\n\n    If the disposition of listed files differs from one another, then\n    tags (\"D\" deleted, \"M\" modified, etc.) are displayed in front of\n    the file names; otherwise tags are suppressed. These options\n    override the the behavior and explicitly show or suppress tags,\n    respectively.\n\n> +\n> +<pathspec>::\n> +       Files to show. :(glob) magic is enabled by default.\n> +\n> +SEE ALSO\n> +--------\n> +linkgit:git-ls-files[1]\n> +\n> +GIT\n> +---\n> +Part of the linkgit:git[1] suite\n> --\n> 2.3.0.rc1.137.g477eb31\n"},{"id":"259161","messageId":"xmqqzj6jbayi.fsf@gitster.dls.corp.google.com","threadId":"39015","inReplyTo":"CAPig+cTDHf5Wh92SLHfQ0_GRY0cbpz+ALfiukJJHed6d=QmLdg@mail.gmail.com","subject":"Re: [PATCH 18/25] list-files: delete redundant cached entries","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-04-08T02:39:01Z","receivedAt":"2015-04-08T02:39:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n> On Mon, Apr 6, 2015 at 9:52 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>> When both --cached and one of -amdAMD is used together we may have two\n>> entries of the same path, e.g. \"  foo\" and \"MM foo\". In this case it's\n>> pretty clear that \"foo\" must be tracked, no need to display \"   foo\".\n>> The new function does that.\n>>\n>> Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>> diff --git a/builtin/list-files.c b/builtin/list-files.c\n>> index 14ffd62..31c2336 100644\n>> --- a/builtin/list-files.c\n>> +++ b/builtin/list-files.c\n>> @@ -93,7 +93,10 @@ static int compare_item(const void *a_, const void *b_)\n>>  {\n>>         const struct item *a = a_;\n>>         const struct item *b = b_;\n>> -       return strcmp(a->path, b->path);\n>> +       int ret = strcmp(a->path, b->path);\n>> +       if (ret)\n>> +               return ret;\n>> +       return strncmp(a->tag, b->tag, 2);\n>>  }\n>>\n>>  static void free_item(struct item *item)\n>> @@ -132,7 +135,12 @@ static void remove_duplicates(struct item_list *list)\n>>         for (src = dst = 1; src < list->nr; src++) {\n>>                 if (!compare_item(list->items + dst - 1, list->items + src))\n>>                         free_item(list->items + src);\n>> -               else\n>> +               else if ((list->items[dst - 1].tag[0] == ' ' &&\n>> +                         list->items[dst - 1].tag[1] == ' ' &&\n>> +                         !strcmp(list->items[src].path, list->items[dst - 1].path))) {\n>> +                       free_item(list->items + dst - 1);\n>> +                       list->items[dst - 1] = list->items[src];\n>\n> I was wondering if you could drop this backward-patching case by\n> having tag==\"  \" items sort after tag=\"xx\" items and just fold out the\n> tag==\"  \" items normally in the preceding 'if', however, when I\n> started coding it, I found that the resulting code wasn't any more\n> pleasant.\n\n\nYou may read from wt-status and then index (or the other way around)\ninto the set of files to be listed---instead of deduping at the end,\nyou should logically do \"add in from index only for entries we did\nnot see in wt-status output\".  So some deduping is necessary.\n\nBut I actually think that the worst aspect of the series this part\nof the code shows is the same issue I raised in the previous review\nround in the approach, which has not been addressed at all as far as\nI can see in this round.\n\nWhy does tag[] record the semantic meaning in textual form?  Isn't\nthis a sympotom that shows the code is turning the information into\ntextual form way too early, which makes the code very unpleasant to\nread and hard to maintain and modify.\n"}]}