{"thread":{"id":"38526","subject":"[PATCH 00/21] nd/list-files updates","startedAt":"2015-02-08T09:01:18Z","lastAt":"2015-02-09T22:28:33Z","messageCount":23,"participants":["Nguyễn Thái Ngọc Duy","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":21},"messages":[{"id":"255740","messageId":"1423386099-19994-1-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":null,"subject":"[PATCH 00/21] nd/list-files updates","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:18Z","receivedAt":"2015-02-08T09:01:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"- 02/21: fix a bug that ignores config keys if $LS_COLORS is not defined\n- 09/21: use listFiles instead of list-files in config keys\n- 17/21: incorporate match_pathspec changes from Junio,\n    use strbuf_swap in show_as_directory()\n- 21/21: use test_decode_color and test_when_finished in t3080\n\nFull diff of changes is\n\n-- 8< --\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 74da715..2dc4788 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -940,7 +940,7 @@ color.status.<slot>::\n \tto red). The values of these variables may be specified as in\n \tcolor.branch.<slot>.\n \n-color.list-files::\n+color.listFiles::\n \tA boolean to enable/disable color in the output of\n \tlinkgit:git-list-files[1]. May be set to `always`, `false` (or\n \t`never`) or `auto` (or `true`), in which case colors are used\n@@ -1018,7 +1018,7 @@ column.clean::\n \tSpecify the layout when list items in `git clean -i`, which always\n \tshows files and directories in columns. See `column.ui` for details.\n \n-column.list-files::\n+column.listFiles::\n \tSpecify whether to output tag listing in `git list-files` in columns.\n \tSee `column.ui` for details.\n \ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex b04c712..b5e1a59 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -229,8 +229,8 @@ static int show_as_directory(const struct cache_entry *ce)\n \t\tif (want_color(use_color)) {\n \t\t\tstruct strbuf sb3 = STRBUF_INIT;\n \t\t\tcolor_filename(&sb3, ce->name, sb2.buf, S_IFDIR, 1);\n-\t\t\tstrbuf_release(&sb2);\n-\t\t\tsb2 = sb3;\n+\t\t\tstrbuf_swap(&sb2, &sb3);\n+\t\t\tstrbuf_release(&sb3);\n \t\t}\n \t\tif (show_tag)\n \t\t\tstrbuf_insert(&sb2, 0, tag_cached, strlen(tag_cached));\n@@ -258,33 +258,41 @@ static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce)\n \tstrbuf_release(&quoted);\n }\n \n+static int match_pathspec_with_depth(struct pathspec *ps,\n+\t\t\t\t     const char *name, int namelen,\n+\t\t\t\t     int prefix, char *seen, int is_dir,\n+\t\t\t\t     const int *custom_depth)\n+{\n+\tint saved_depth = ps->max_depth;\n+\tint result;\n+\n+\tif (custom_depth)\n+\t\tps->max_depth = *custom_depth;\n+\tresult = match_pathspec(ps, name, namelen, prefix, seen, is_dir);\n+\tif (custom_depth)\n+\t\tps->max_depth = saved_depth;\n+\treturn result;\n+}\n+\n static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n {\n \tstatic struct strbuf sb = STRBUF_INIT;\n-\tint len = max_prefix_len, saved_max_depth;\n+\tstatic const int infinite_depth = -1;\n+\tint len = max_prefix_len;\n \n \tif (len >= ce_namelen(ce))\n \t\tdie(\"git ls-files: internal error - cache entry not superset of prefix\");\n \n-\tif (show_dirs) {\n-\t\t/* ignore depth to catch dirs that contain matched entries */\n-\t\tsaved_max_depth = pathspec.max_depth;\n-\t\tpathspec.max_depth = -1;\n-\t}\n-\n-\tif (!match_pathspec(&pathspec, ce->name, ce_namelen(ce),\n-\t\t\t    len, ps_matched,\n-\t\t\t    S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode)))\n+\tif (!match_pathspec_with_depth(&pathspec, ce->name, ce_namelen(ce),\n+\t\t\t\t       len, ps_matched,\n+\t\t\t\t       S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode),\n+\t\t\t\t       show_dirs ? &infinite_depth : NULL))\n \t\treturn;\n \n-\tif (show_dirs) {\n-\t\tpathspec.max_depth = saved_max_depth;\n-\t\tif (strchr(ce->name, '/') &&\n-\t\t    !match_pathspec(&pathspec, ce->name, ce_namelen(ce),\n-\t\t\t\t    prefix_len, NULL, 1) &&\n-\t\t    show_as_directory(ce))\n-\t\t\treturn;\n-\t}\n+\tif (show_dirs &&strchr(ce->name, '/') &&\n+\t    !match_pathspec(&pathspec, ce->name, ce_namelen(ce), prefix_len, NULL, 1) &&\n+\t    show_as_directory(ce))\n+\t\treturn;\n \n \tif (tag && *tag && show_valid_bit &&\n \t    (ce->ce_flags & CE_VALID)) {\n@@ -697,8 +705,8 @@ static int option_parse_exclude_standard(const struct option *opt,\n static int git_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, \"list-files\", &colopts);\n-\tif (!strcmp(var, \"color.list-files\")) {\n+\t\treturn git_column_config(var, value, \"listfiles\", &colopts);\n+\tif (!strcmp(var, \"color.listfiles\")) {\n \t\tuse_color = git_config_colorbool(var, value);\n \t\treturn 0;\n \t}\ndiff --git a/ls_colors.c b/ls_colors.c\nindex 9259ad3..011a8b9 100644\n--- a/ls_colors.c\n+++ b/ls_colors.c\n@@ -326,8 +326,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, \"??\");\ndiff --git a/t/t3080-list-files.sh b/t/t3080-list-files.sh\nindex 6313dd9..01b9662 100755\n--- a/t/t3080-list-files.sh\n+++ b/t/t3080-list-files.sh\n@@ -15,16 +15,26 @@ test_expect_success 'setup' '\n \n test_expect_success 'LS_COLORS env variable' '\n \tLS_COLORS=\"rs=0:fi=31:di=32\" \\\n-\t\tgit list-files --color=always | grep -v gitlink >actual &&\n-\ttest_cmp \"$TEST_DIRECTORY\"/t3080/ls_colors actual\n+\t\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 '\n \n test_expect_success 'color.ls.*' '\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 >actual &&\n-\ttest_cmp \"$TEST_DIRECTORY\"/t3080/color_ls actual\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 '\n \n test_expect_success 'column output' '\n@@ -78,6 +88,7 @@ test_expect_success 'globbing' '\n '\n \n test_expect_success 'no dups' '\n+\ttest_when_finished \"git checkout file\" &&\n \techo dirty >>file &&\n \tgit list-files -m file >actual &&\n \techo \"file\" >expected &&\n@@ -100,6 +111,7 @@ test_expect_success '--classify' '\n '\n \n test_expect_success 'diff-cached' '\n+\ttest_when_finished \"git checkout file\" &&\n \techo dirty >>file &&\n \tgit add file &&\n \tgit list-files -M >actual &&\ndiff --git a/t/t3080/color_ls b/t/t3080/color_ls\ndeleted file mode 100644\nindex 47f77ad..0000000\n--- a/t/t3080/color_ls\n+++ /dev/null\n@@ -1,3 +0,0 @@\n-\u001b[32mdir\u001b[m\n-\u001b[31mfile\u001b[m\n-\u001b[33mgitlink\u001b[m\ndiff --git a/t/t3080/ls_colors b/t/t3080/ls_colors\ndeleted file mode 100644\nindex 423c016..0000000\n--- a/t/t3080/ls_colors\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-\u001b[32mdir\u001b[m\n-\u001b[31mfile\u001b[m\n-- 8< --\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"255742","messageId":"1423386099-19994-2-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 01/21] ls_colors.c: add $LS_COLORS parsing code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:19Z","receivedAt":"2015-02-08T09:01:19Z","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":"255741","messageId":"1423386099-19994-3-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 02/21] ls_colors.c: parse color.ls.* from config file","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:20Z","receivedAt":"2015-02-08T09:01:20Z","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>\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":"255743","messageId":"1423386099-19994-4-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 03/21] 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-02-08T09:01:21Z","receivedAt":"2015-02-08T09:01:21Z","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":"255744","messageId":"1423386099-19994-5-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 04/21] ls_colors.c: highlight submodules like directories","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:22Z","receivedAt":"2015-02-08T09:01: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>\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":"255745","messageId":"1423386099-19994-6-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 05/21] ls-files: buffer full item in strbuf before printing","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:23Z","receivedAt":"2015-02-08T09:01:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Buffering so that we can manipulate the strings (e.g. coloring)\nfurther before finally printing them.\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 builtin/ls-files.c | 48 +++++++++++++++++++++++++++++++++++-------------\n 1 file changed, 35 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 99cee20..b6f0d9f 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -47,18 +47,30 @@ static const char *tag_modified = \"\";\n static const char *tag_skip_worktree = \"\";\n static const char *tag_resolve_undo = \"\";\n \n-static void write_name(const char *name)\n+static void write_name(struct strbuf *sb, const char *name)\n {\n \t/*\n \t * With \"--full-name\", prefix_len=0; this caller needs to pass\n \t * an empty string in that case (a NULL is good for \"\").\n \t */\n-\twrite_name_quoted_relative(name, prefix_len ? prefix : NULL,\n-\t\t\t\t   stdout, line_terminator);\n+\tconst char *real_prefix = prefix_len ? prefix : NULL;\n+\tif (!line_terminator) {\n+\t\tstruct strbuf sb2 = STRBUF_INIT;\n+\t\tstrbuf_addstr(sb, relative_path(name, real_prefix, &sb2));\n+\t\tstrbuf_release(&sb2);\n+\t} else\n+\t\tquote_path_relative(name, real_prefix, sb);\n+\tstrbuf_addch(sb, line_terminator);\n+}\n+\n+static void strbuf_fputs(struct strbuf *sb, FILE *fp)\n+{\n+\tfwrite(sb->buf, sb->len, 1, fp);\n }\n \n static void show_dir_entry(const char *tag, struct dir_entry *ent)\n {\n+\tstatic struct strbuf sb = STRBUF_INIT;\n \tint len = max_prefix_len;\n \n \tif (len >= ent->len)\n@@ -67,8 +79,10 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n \tif (!dir_path_match(ent, &pathspec, len, ps_matched))\n \t\treturn;\n \n-\tfputs(tag, stdout);\n-\twrite_name(ent->name);\n+\tstrbuf_reset(&sb);\n+\tstrbuf_addstr(&sb, tag);\n+\twrite_name(&sb, ent->name);\n+\tstrbuf_fputs(&sb, stdout);\n }\n \n static void show_other_files(struct dir_struct *dir)\n@@ -134,6 +148,7 @@ static void show_killed_files(struct dir_struct *dir)\n \n static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n {\n+\tstatic struct strbuf sb = STRBUF_INIT;\n \tint len = max_prefix_len;\n \n \tif (len >= ce_namelen(ce))\n@@ -161,16 +176,18 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n \t\ttag = alttag;\n \t}\n \n+\tstrbuf_reset(&sb);\n \tif (!show_stage) {\n-\t\tfputs(tag, stdout);\n+\t\tstrbuf_addstr(&sb, tag);\n \t} else {\n-\t\tprintf(\"%s%06o %s %d\\t\",\n-\t\t       tag,\n-\t\t       ce->ce_mode,\n-\t\t       find_unique_abbrev(ce->sha1,abbrev),\n-\t\t       ce_stage(ce));\n+\t\tstrbuf_addf(&sb, \"%s%06o %s %d\\t\",\n+\t\t\t    tag,\n+\t\t\t    ce->ce_mode,\n+\t\t\t    find_unique_abbrev(ce->sha1, abbrev),\n+\t\t\t    ce_stage(ce));\n \t}\n-\twrite_name(ce->name);\n+\twrite_name(&sb, ce->name);\n+\tstrbuf_fputs(&sb, stdout);\n \tif (debug_mode) {\n \t\tconst struct stat_data *sd = &ce->ce_stat_data;\n \n@@ -206,7 +223,12 @@ static void show_ru_info(void)\n \t\t\tprintf(\"%s%06o %s %d\\t\", tag_resolve_undo, ui->mode[i],\n \t\t\t       find_unique_abbrev(ui->sha1[i], abbrev),\n \t\t\t       i + 1);\n-\t\t\twrite_name(path);\n+\t\t\t/*\n+\t\t\t * With \"--full-name\", prefix_len=0; this caller needs to pass\n+\t\t\t * an empty string in that case (a NULL is good for \"\").\n+\t\t\t */\n+\t\t\twrite_name_quoted_relative(path, prefix_len ? prefix : NULL,\n+\t\t\t\t\t\t   stdout, line_terminator);\n \t\t}\n \t}\n }\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"255746","messageId":"1423386099-19994-7-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 06/21] ls-files: add --color to highlight file names","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:24Z","receivedAt":"2015-02-08T09:01: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>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/git-ls-files.txt |  7 +++++++\n builtin/ls-files.c             | 38 +++++++++++++++++++++++++++++++++++---\n 2 files changed, 42 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt\nindex e26f01f..148f226 100644\n--- a/Documentation/git-ls-files.txt\n+++ b/Documentation/git-ls-files.txt\n@@ -147,6 +147,13 @@ a space) at the start of each line:\n \tpossible for manual inspection; the exact format may change at\n \tany time.\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`.\n+\n \\--::\n \tDo not interpret any more arguments as options.\n \ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex b6f0d9f..0ee4f19 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -14,6 +14,7 @@\n #include \"resolve-undo.h\"\n #include \"string-list.h\"\n #include \"pathspec.h\"\n+#include \"color.h\"\n \n static int abbrev;\n static int show_deleted;\n@@ -27,6 +28,7 @@ static int show_killed;\n static int show_valid_bit;\n static int line_terminator = '\\n';\n static int debug_mode;\n+static int use_color;\n \n static const char *prefix;\n static int max_prefix_len;\n@@ -60,7 +62,6 @@ static void write_name(struct strbuf *sb, const char *name)\n \t\tstrbuf_release(&sb2);\n \t} else\n \t\tquote_path_relative(name, real_prefix, sb);\n-\tstrbuf_addch(sb, line_terminator);\n }\n \n static void strbuf_fputs(struct strbuf *sb, FILE *fp)\n@@ -68,6 +69,21 @@ static void strbuf_fputs(struct strbuf *sb, FILE *fp)\n \tfwrite(sb->buf, sb->len, 1, fp);\n }\n \n+static void write_dir_entry(struct strbuf *sb, const struct dir_entry *ent)\n+{\n+\tstruct strbuf quoted = STRBUF_INIT;\n+\tstruct stat st;\n+\tif (stat(ent->name, &st))\n+\t\tst.st_mode = 0;\n+\twrite_name(&quoted, ent->name);\n+\tif (want_color(use_color))\n+\t\tcolor_filename(sb, ent->name, quoted.buf, st.st_mode, 1);\n+\telse\n+\t\tstrbuf_addbuf(sb, &quoted);\n+\tstrbuf_addch(sb, line_terminator);\n+\tstrbuf_release(&quoted);\n+}\n+\n static void show_dir_entry(const char *tag, struct dir_entry *ent)\n {\n \tstatic struct strbuf sb = STRBUF_INIT;\n@@ -81,7 +97,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n \n \tstrbuf_reset(&sb);\n \tstrbuf_addstr(&sb, tag);\n-\twrite_name(&sb, ent->name);\n+\twrite_dir_entry(&sb, ent);\n \tstrbuf_fputs(&sb, stdout);\n }\n \n@@ -146,6 +162,18 @@ static void show_killed_files(struct dir_struct *dir)\n \t}\n }\n \n+static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce)\n+{\n+\tstruct strbuf quoted = STRBUF_INIT;\n+\twrite_name(&quoted, ce->name);\n+\tif (want_color(use_color))\n+\t\tcolor_filename(sb, ce->name, quoted.buf, ce->ce_mode, 1);\n+\telse\n+\t\tstrbuf_addbuf(sb, &quoted);\n+\tstrbuf_addch(sb, line_terminator);\n+\tstrbuf_release(&quoted);\n+}\n+\n static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n {\n \tstatic struct strbuf sb = STRBUF_INIT;\n@@ -186,7 +214,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n \t\t\t    find_unique_abbrev(ce->sha1, abbrev),\n \t\t\t    ce_stage(ce));\n \t}\n-\twrite_name(&sb, ce->name);\n+\twrite_ce_name(&sb, ce);\n \tstrbuf_fputs(&sb, stdout);\n \tif (debug_mode) {\n \t\tconst struct stat_data *sd = &ce->ce_stat_data;\n@@ -523,6 +551,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tN_(\"if any <file> is not in the index, treat this as an error\")),\n \t\tOPT_STRING(0, \"with-tree\", &with_tree, N_(\"tree-ish\"),\n \t\t\tN_(\"pretend that paths removed since <tree-ish> are still present\")),\n+\t\tOPT__COLOR(&use_color, N_(\"show color\")),\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n \t\tOPT_END()\n@@ -570,6 +599,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \tif (require_work_tree && !is_inside_work_tree())\n \t\tsetup_work_tree();\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       PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"255747","messageId":"1423386099-19994-8-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 07/21] ls-files: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:25Z","receivedAt":"2015-02-08T09:01: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>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/git-ls-files.txt |  6 ++++++\n builtin/ls-files.c             | 28 ++++++++++++++++++++++++++++\n 2 files changed, 34 insertions(+)\n\ndiff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt\nindex 148f226..99328b9 100644\n--- a/Documentation/git-ls-files.txt\n+++ b/Documentation/git-ls-files.txt\n@@ -154,6 +154,12 @@ a space) at the start of each line:\n \t`--color=never`. `--color` is equivalent to\n \t`--color=auto`.\n \n+--column[=<options>]::\n+--no-column::\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+\n \\--::\n \tDo not interpret any more arguments as options.\n \ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 0ee4f19..44e5628 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -15,6 +15,7 @@\n #include \"string-list.h\"\n #include \"pathspec.h\"\n #include \"color.h\"\n+#include \"column.h\"\n \n static int abbrev;\n static int show_deleted;\n@@ -29,6 +30,7 @@ static int show_valid_bit;\n static int line_terminator = '\\n';\n static int debug_mode;\n static int use_color;\n+static unsigned int colopts;\n \n static const char *prefix;\n static int max_prefix_len;\n@@ -39,6 +41,7 @@ static char *ps_matched;\n static const char *with_tree;\n static int exc_given;\n static int exclude_args;\n+static struct string_list output = STRING_LIST_INIT_NODUP;\n \n static const char *tag_cached = \"\";\n static const char *tag_unmerged = \"\";\n@@ -66,6 +69,10 @@ static void write_name(struct strbuf *sb, const char *name)\n \n static void strbuf_fputs(struct strbuf *sb, FILE *fp)\n {\n+\tif (column_active(colopts)) {\n+\t\tstring_list_append(&output, strbuf_detach(sb, NULL));\n+\t\treturn;\n+\t}\n \tfwrite(sb->buf, sb->len, 1, fp);\n }\n \n@@ -552,6 +559,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\tOPT_STRING(0, \"with-tree\", &with_tree, N_(\"tree-ish\"),\n \t\t\tN_(\"pretend that paths removed since <tree-ish> are still present\")),\n \t\tOPT__COLOR(&use_color, N_(\"show color\")),\n+\t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n \t\tOPT_END()\n@@ -596,6 +604,18 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \tif (dir.exclude_per_dir)\n \t\texc_given = 1;\n \n+\tfinalize_colopts(&colopts, -1);\n+\tif (explicitly_enable_column(colopts)) {\n+\t\tif (!line_terminator)\n+\t\t\tdie(_(\"--column and -z are incompatible\"));\n+\t\tif (show_resolve_undo)\n+\t\t\tdie(_(\"--column and --resolve-undo are incompatible\"));\n+\t\tif (debug_mode)\n+\t\t\tdie(_(\"--column and --debug are incompatible\"));\n+\t}\n+\tif (column_active(colopts))\n+\t\tline_terminator = 0;\n+\n \tif (require_work_tree && !is_inside_work_tree())\n \t\tsetup_work_tree();\n \n@@ -638,6 +658,14 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \tif (show_resolve_undo)\n \t\tshow_ru_info();\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(&output, colopts, &copts);\n+\t\tstring_list_clear(&output, 0);\n+\t}\n+\n \tif (ps_matched) {\n \t\tint bad;\n \t\tbad = report_path_error(ps_matched, &pathspec, prefix);\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"255748","messageId":"1423386099-19994-9-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 08/21] ls-files: support --max-depth","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:26Z","receivedAt":"2015-02-08T09:01: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>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/git-ls-files.txt | 7 +++++++\n builtin/ls-files.c             | 7 +++++++\n 2 files changed, 14 insertions(+)\n\ndiff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt\nindex 99328b9..3d921eb 100644\n--- a/Documentation/git-ls-files.txt\n+++ b/Documentation/git-ls-files.txt\n@@ -160,6 +160,13 @@ a space) at the start of each line:\n \tfor option syntax. `--column` and `--no-column` without options\n \tare equivalent to 'always' and 'never' respectively.\n \n+--max-depth=<depth>::\n+\tFor each <pathspec> given on command line, descend at most <depth>\n+\tlevels of directories. A negative value means no limit (default).\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+\n \\--::\n \tDo not interpret any more arguments as options.\n \ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 44e5628..09a6b8d 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -503,6 +503,7 @@ static int option_parse_exclude_standard(const struct option *opt,\n int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n {\n \tint require_work_tree = 0, show_tag = 0, i;\n+\tint max_depth = -1;\n \tconst char *max_prefix;\n \tstruct dir_struct dir;\n \tstruct exclude_list *el;\n@@ -560,6 +561,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tN_(\"pretend that paths removed since <tree-ish> are still present\")),\n \t\tOPT__COLOR(&use_color, N_(\"show color\")),\n \t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n+\t\t{ OPTION_INTEGER, 0, \"max-depth\", &max_depth, N_(\"depth\"),\n+\t\t\tN_(\"descend at most <depth> levels\"), PARSE_OPT_NONEG,\n+\t\t\tNULL, 1 },\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n \t\tOPT_END()\n@@ -624,8 +628,11 @@ int cmd_ls_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       prefix, argv);\n+\tpathspec.max_depth = max_depth;\n+\tpathspec.recursive = 1;\n \n \t/* Find common prefix for all pathspec's */\n \tmax_prefix = common_prefix(&pathspec);\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"255749","messageId":"1423386099-19994-10-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 09/21] list-files: a user friendly version of ls-files and more","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:27Z","receivedAt":"2015-02-08T09:01:27Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is more user friendly version of ls-files:\n\n * it's automatically colored and columnized\n * it refreshes the index like all porcelain commands\n * it defaults to non-recursive behavior like ls\n * :(glob) is on by default so '*.c' means a.c but not a/b.c, use\n   '**/*.c' for that.\n * auto pager\n\nThe name 'ls' is not taken. It is left for the user to make an alias\nwith better default options.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore                             |  1 +\n Documentation/config.txt               | 10 +++++\n Documentation/git-list-files.txt (new) | 80 ++++++++++++++++++++++++++++++++++\n Makefile                               |  1 +\n builtin/ls-files.c                     | 69 +++++++++++++++++++++++++++--\n command-list.txt                       |  1 +\n git.c                                  |  1 +\n 7 files changed, 159 insertions(+), 4 deletions(-)\n create mode 100644 Documentation/git-list-files.txt\n\ndiff --git a/.gitignore b/.gitignore\nindex a052419..9727ecc 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -76,6 +76,7 @@\n /git-init-db\n /git-interpret-trailers\n /git-instaweb\n+/git-list-files\n /git-log\n /git-ls-files\n /git-ls-remote\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 2290c47..2dc4788 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -940,6 +940,12 @@ color.status.<slot>::\n \tto red). The values of these variables may be specified as in\n \tcolor.branch.<slot>.\n \n+color.listFiles::\n+\tA boolean to enable/disable color in the output of\n+\tlinkgit:git-list-files[1]. May be set to `always`, `false` (or\n+\t`never`) or `auto` (or `true`), in which case colors are used\n+\tonly when the output is to a terminal. Defaults to false.\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@@ -1012,6 +1018,10 @@ column.clean::\n \tSpecify the layout when list items in `git clean -i`, which always\n \tshows files and directories in columns. See `column.ui` for details.\n \n+column.listFiles::\n+\tSpecify whether to output tag listing in `git list-files` in columns.\n+\tSee `column.ui` for details.\n+\n column.status::\n \tSpecify whether to output untracked files in `git status` in columns.\n \tSee `column.ui` for details.\ndiff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt\nnew file mode 100644\nindex 0000000..3039e1e\n--- /dev/null\n+++ b/Documentation/git-list-files.txt\n@@ -0,0 +1,80 @@\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...\n+\n+OPTIONS\n+-------\n+-c::\n+--cached::\n+\tShow cached files (default)\n+\n+-d::\n+--deleted::\n+\tShow cached files that are deleted on working directory\n+\n+-m::\n+--modified::\n+\tShow cached files that have modification on working directory\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+--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+\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+\n+--max-depth=<depth>::\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+\tThe default is `--max-depth=0`.\n+\n+<pathspec>::\n+\tFiles to show. :(glob) magic is enabled and recursion disabled\n+\tby default.\n+\n+SEE ALSO\n+--------\n+linkgit:git-ls-files[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Makefile b/Makefile\nindex 459121d..23a0751 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -587,6 +587,7 @@ BUILT_INS += git-cherry-pick$X\n BUILT_INS += git-format-patch$X\n BUILT_INS += git-fsck-objects$X\n BUILT_INS += git-init$X\n+BUILT_INS += git-list-files$X\n BUILT_INS += git-merge-subtree$X\n BUILT_INS += git-show$X\n BUILT_INS += git-stage$X\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 09a6b8d..d55b5e7 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -31,6 +31,7 @@ static int line_terminator = '\\n';\n static int debug_mode;\n static int use_color;\n static unsigned int colopts;\n+static int porcelain;\n \n static const char *prefix;\n static int max_prefix_len;\n@@ -459,6 +460,11 @@ static const char * const ls_files_usage[] = {\n \tNULL\n };\n \n+static const char * const ls_usage[] = {\n+\tN_(\"git list-files [options] [<file>...]\"),\n+\tNULL\n+};\n+\n static int option_parse_z(const struct option *opt,\n \t\t\t  const char *arg, int unset)\n {\n@@ -500,6 +506,17 @@ static int option_parse_exclude_standard(const struct option *opt,\n \treturn 0;\n }\n \n+static int git_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+\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_ls_files(int argc, const char **argv, const char *cmd_prefix)\n {\n \tint require_work_tree = 0, show_tag = 0, i;\n@@ -568,21 +585,61 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\tOPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n \t\tOPT_END()\n \t};\n+\tstruct option builtin_ls_options[] = {\n+\t\tOPT_BOOL('c', \"cached\", &show_cached,\n+\t\t\tN_(\"show cached files (default)\")),\n+\t\tOPT_BOOL('d', \"deleted\", &show_deleted,\n+\t\t\tN_(\"show cached files that are deleted on working directory\")),\n+\t\tOPT_BOOL('m', \"modified\", &show_modified,\n+\t\t\tN_(\"show cached files that have modification on working directory\")),\n+\t\tOPT_BOOL('o', \"others\", &show_others,\n+\t\t\tN_(\"show untracked files\")),\n+\t\tOPT_BIT('i', \"ignored\", &dir.flags,\n+\t\t\tN_(\"show ignored files\"),\n+\t\t\tDIR_SHOW_IGNORED),\n+\t\tOPT_BOOL('u', \"unmerged\", &show_unmerged,\n+\t\t\tN_(\"show unmerged files\")),\n+\t\tOPT__COLOR(&use_color, N_(\"show color\")),\n+\t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n+\t\t{ OPTION_INTEGER, 0, \"max-depth\", &max_depth, N_(\"depth\"),\n+\t\t\tN_(\"descend at most <depth> levels\"), PARSE_OPT_NONEG,\n+\t\t\tNULL, 1 },\n+\t\tOPT__ABBREV(&abbrev),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *options;\n+\tconst char * const *help_usage;\n \n+\tif (!strcmp(argv[0], \"list-files\")) {\n+\t\thelp_usage = ls_usage;\n+\t\toptions = builtin_ls_options;\n+\t\tporcelain = 1;\n+\t} else {\n+\t\thelp_usage = ls_files_usage;\n+\t\toptions = builtin_ls_files_options;\n+\t}\n \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n-\t\tusage_with_options(ls_files_usage, builtin_ls_files_options);\n+\t\tusage_with_options(help_usage, options);\n \n \tmemset(&dir, 0, sizeof(dir));\n \tprefix = cmd_prefix;\n \tif (prefix)\n \t\tprefix_len = strlen(prefix);\n-\tgit_config(git_default_config, NULL);\n+\n+\tif (porcelain) {\n+\t\tsetenv(GIT_GLOB_PATHSPECS_ENVIRONMENT, \"1\", 1);\n+\t\texc_given = 1;\n+\t\tsetup_standard_excludes(&dir);\n+\t\tuse_color = -1;\n+\t\tmax_depth = 0;\n+\t\tgit_config(git_ls_config, NULL);\n+\t} else\n+\t\tgit_config(git_default_config, NULL);\n \n \tif (read_cache() < 0)\n \t\tdie(\"index file corrupt\");\n \n-\targc = parse_options(argc, argv, prefix, builtin_ls_files_options,\n-\t\t\tls_files_usage, 0);\n+\targc = parse_options(argc, argv, prefix, options, help_usage, 0);\n \tel = add_exclude_list(&dir, EXC_CMDL, \"--exclude option\");\n \tfor (i = 0; i < exclude_list.nr; i++) {\n \t\tadd_exclude(exclude_list.items[i].string, \"\", 0, el, --exclude_args);\n@@ -661,6 +718,10 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tdie(\"ls-files --with-tree is incompatible with -s or -u\");\n \t\toverlay_tree_on_cache(with_tree, max_prefix);\n \t}\n+\tif (porcelain) {\n+\t\trefresh_index(&the_index, REFRESH_QUIET | REFRESH_UNMERGED, &pathspec, NULL, NULL);\n+\t\tsetup_pager();\n+\t}\n \tshow_files(&dir);\n \tif (show_resolve_undo)\n \t\tshow_ru_info();\ndiff --git a/command-list.txt b/command-list.txt\nindex f1eae08..32101de 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -64,6 +64,7 @@ git-init                                mainporcelain common\n git-instaweb                            ancillaryinterrogators\n git-interpret-trailers                  purehelpers\n gitk                                    mainporcelain\n+git-list-files                          mainporcelain\n git-log                                 mainporcelain common\n git-ls-files                            plumbinginterrogators\n git-ls-remote                           plumbinginterrogators\ndiff --git a/git.c b/git.c\nindex 18fbf79..617c083 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_ls_files, RUN_SETUP },\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 },\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"255750","messageId":"1423386099-19994-11-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 10/21] list-files: make alias 'ls' default to 'list-files'","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:28Z","receivedAt":"2015-02-08T09:01: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 Documentation/git-list-files.txt | 3 ++-\n config.c                         | 8 ++++++++\n 2 files changed, 10 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt\nindex 3039e1e..2182a38 100644\n--- a/Documentation/git-list-files.txt\n+++ b/Documentation/git-list-files.txt\n@@ -14,7 +14,8 @@ 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...\n+working tree are shown, or untracked files... The builtin alias \"ls\"\n+is set to \"list-files\".\n \n OPTIONS\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":"255751","messageId":"1423386099-19994-12-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 11/21] list-files: -u does not imply showing stages","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:29Z","receivedAt":"2015-02-08T09:01:29Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Showing full index entry information is something for ls-files\nonly. The users of \"git list-files\" may just want to know what entries\nare not unmerged.\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 builtin/ls-files.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex d55b5e7..c5468c7 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -656,7 +656,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t}\n \tif (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed)\n \t\trequire_work_tree = 1;\n-\tif (show_unmerged)\n+\tif (show_unmerged && !porcelain)\n \t\t/*\n \t\t * There's no point in showing unmerged unless\n \t\t * you also show the stage information.\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"255752","messageId":"1423386099-19994-13-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 12/21] list-files: add -R/--recursive short for --max-depth=-1","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:30Z","receivedAt":"2015-02-08T09:01:30Z","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/git-list-files.txt | 4 ++++\n builtin/ls-files.c               | 2 ++\n 2 files changed, 6 insertions(+)\n\ndiff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt\nindex 2182a38..8d285c1 100644\n--- a/Documentation/git-list-files.txt\n+++ b/Documentation/git-list-files.txt\n@@ -46,6 +46,10 @@ OPTIONS\n --unmerged::\n \tShow unmerged files\n \n+-R::\n+--recursive::\n+\tEquivalent of `--max-depth=-1` (infinite recursion).\n+\n --color[=<when>]::\n --no-color::\n \tColor file names. The value must be `always`, `never`, or\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex c5468c7..adc31f6 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -594,6 +594,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tN_(\"show cached files that have modification on working directory\")),\n \t\tOPT_BOOL('o', \"others\", &show_others,\n \t\t\tN_(\"show untracked files\")),\n+\t\tOPT_SET_INT('R', \"recursive\", &max_depth,\n+\t\t\t    N_(\"shortcut for --max-depth=-1\"), -1),\n \t\tOPT_BIT('i', \"ignored\", &dir.flags,\n \t\t\tN_(\"show ignored files\"),\n \t\t\tDIR_SHOW_IGNORED),\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"255753","messageId":"1423386099-19994-14-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 13/21] list-files: add -1 short for --no-column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:31Z","receivedAt":"2015-02-08T09:01: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>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/git-list-files.txt | 3 +++\n builtin/ls-files.c               | 2 ++\n 2 files changed, 5 insertions(+)\n\ndiff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt\nindex 8d285c1..1c0c877 100644\n--- a/Documentation/git-list-files.txt\n+++ b/Documentation/git-list-files.txt\n@@ -50,6 +50,9 @@ OPTIONS\n --recursive::\n \tEquivalent of `--max-depth=-1` (infinite recursion).\n \n+-1::\n+\tEquivalent of --no-column.\n+\n --color[=<when>]::\n --no-color::\n \tColor file names. The value must be `always`, `never`, or\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex adc31f6..62aa64f 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -603,6 +603,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tN_(\"show unmerged files\")),\n \t\tOPT__COLOR(&use_color, N_(\"show color\")),\n \t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n+\t\tOPT_SET_INT('1', NULL, &colopts,\n+\t\t\t    N_(\"shortcut for --no-column\"), COL_PARSEOPT),\n \t\t{ OPTION_INTEGER, 0, \"max-depth\", &max_depth, N_(\"depth\"),\n \t\t\tN_(\"descend at most <depth> levels\"), PARSE_OPT_NONEG,\n \t\t\tNULL, 1 },\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"255754","messageId":"1423386099-19994-15-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 14/21] list-files: add -t back","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:32Z","receivedAt":"2015-02-08T09:01:32Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Tag \"H\" (cached) is not shown though because it's usually the majority\nand becomes noise. Not showing it makes the other tags stand out. -t\nis on by default if more than one file category is selected.\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 Documentation/git-list-files.txt |  6 ++++++\n builtin/ls-files.c               | 27 +++++++++++++++++----------\n 2 files changed, 23 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt\nindex 1c0c877..9d4c127 100644\n--- a/Documentation/git-list-files.txt\n+++ b/Documentation/git-list-files.txt\n@@ -46,6 +46,12 @@ OPTIONS\n --unmerged::\n \tShow unmerged files\n \n+-t::\n+--[no-]tag::\n+\tShow a tag to indicate file type. Automatically turned on with\n+\tmultiple file selections. See linkgit::git-ls-files[1] option\n+\t`-t` for more information.\n+\n -R::\n --recursive::\n \tEquivalent of `--max-depth=-1` (infinite recursion).\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 62aa64f..0e7cbb9 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -596,6 +596,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tN_(\"show untracked files\")),\n \t\tOPT_SET_INT('R', \"recursive\", &max_depth,\n \t\t\t    N_(\"shortcut for --max-depth=-1\"), -1),\n+\t\tOPT_BOOL('t', \"tag\", &show_tag,\n+\t\t\tN_(\"identify the file status with tags\")),\n \t\tOPT_BIT('i', \"ignored\", &dir.flags,\n \t\t\tN_(\"show ignored files\"),\n \t\t\tDIR_SHOW_IGNORED),\n@@ -636,6 +638,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\tsetup_standard_excludes(&dir);\n \t\tuse_color = -1;\n \t\tmax_depth = 0;\n+\t\tshow_tag = -1;\n \t\tgit_config(git_ls_config, NULL);\n \t} else\n \t\tgit_config(git_default_config, NULL);\n@@ -648,16 +651,6 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \tfor (i = 0; i < exclude_list.nr; i++) {\n \t\tadd_exclude(exclude_list.items[i].string, \"\", 0, el, --exclude_args);\n \t}\n-\tif (show_tag || show_valid_bit) {\n-\t\ttag_cached = \"H \";\n-\t\ttag_unmerged = \"M \";\n-\t\ttag_removed = \"R \";\n-\t\ttag_modified = \"C \";\n-\t\ttag_other = \"? \";\n-\t\ttag_killed = \"K \";\n-\t\ttag_skip_worktree = \"S \";\n-\t\ttag_resolve_undo = \"U \";\n-\t}\n \tif (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed)\n \t\trequire_work_tree = 1;\n \tif (show_unmerged && !porcelain)\n@@ -711,6 +704,20 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t      show_killed || show_modified || show_resolve_undo))\n \t\tshow_cached = 1;\n \n+\tif (show_tag == -1)\n+\t\tshow_tag = (show_cached + show_deleted + show_others +\n+\t\t\t    show_unmerged + show_killed + show_modified) > 1;\n+\tif (show_tag || show_valid_bit) {\n+\t\ttag_cached = porcelain ? \"  \" : \"H \";\n+\t\ttag_unmerged = \"M \";\n+\t\ttag_removed = \"R \";\n+\t\ttag_modified = \"C \";\n+\t\ttag_other = \"? \";\n+\t\ttag_killed = \"K \";\n+\t\ttag_skip_worktree = \"S \";\n+\t\ttag_resolve_undo = \"U \";\n+\t}\n+\n \tif (max_prefix)\n \t\tprune_cache(max_prefix);\n \tif (with_tree) {\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"255755","messageId":"1423386099-19994-16-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 15/21] list-files: sort output and remove duplicates","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:33Z","receivedAt":"2015-02-08T09:01:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"When you mix different file types, with ls-files you may get separate\nlisting. For example, \"ls-files -cm\" will show file \"abc\" twice: one\nas part of cached list, one of modified list. With \"ls\" (and this\npatch) they will be in a single sorted list (easier for the eye).\n\nDuplicate entries are also removed. Note that display content is\ncompared, so if you have \"-t\" on, or you color file types differently,\nyou will get duplicate textual entries. This is good imo.\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 builtin/ls-files.c | 36 ++++++++++++++++++++++++------------\n 1 file changed, 24 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 0e7cbb9..8f10ab9 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -53,6 +53,13 @@ static const char *tag_modified = \"\";\n static const char *tag_skip_worktree = \"\";\n static const char *tag_resolve_undo = \"\";\n \n+static int compare_output(const void *a_, const void *b_)\n+{\n+\tconst struct string_list_item *a = a_;\n+\tconst struct string_list_item *b = b_;\n+\treturn strcmp(a->util, b->util);\n+}\n+\n static void write_name(struct strbuf *sb, const char *name)\n {\n \t/*\n@@ -68,10 +75,12 @@ static void write_name(struct strbuf *sb, const char *name)\n \t\tquote_path_relative(name, real_prefix, sb);\n }\n \n-static void strbuf_fputs(struct strbuf *sb, FILE *fp)\n+static void strbuf_fputs(struct strbuf *sb, const char *full_name, FILE *fp)\n {\n-\tif (column_active(colopts)) {\n-\t\tstring_list_append(&output, strbuf_detach(sb, NULL));\n+\tif (column_active(colopts) || porcelain) {\n+\t\tstruct string_list_item *it;\n+\t\tit = string_list_append(&output, strbuf_detach(sb, NULL));\n+\t\tit->util = (void *)full_name;\n \t\treturn;\n \t}\n \tfwrite(sb->buf, sb->len, 1, fp);\n@@ -106,7 +115,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n \tstrbuf_reset(&sb);\n \tstrbuf_addstr(&sb, tag);\n \twrite_dir_entry(&sb, ent);\n-\tstrbuf_fputs(&sb, stdout);\n+\tstrbuf_fputs(&sb, ent->name, stdout);\n }\n \n static void show_other_files(struct dir_struct *dir)\n@@ -223,7 +232,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n \t\t\t    ce_stage(ce));\n \t}\n \twrite_ce_name(&sb, ce);\n-\tstrbuf_fputs(&sb, stdout);\n+\tstrbuf_fputs(&sb, ce->name, stdout);\n \tif (debug_mode) {\n \t\tconst struct stat_data *sd = &ce->ce_stat_data;\n \n@@ -524,6 +533,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \tconst char *max_prefix;\n \tstruct dir_struct dir;\n \tstruct exclude_list *el;\n+\tstruct column_options copts;\n \tstruct string_list exclude_list = STRING_LIST_INIT_NODUP;\n \tstruct option builtin_ls_files_options[] = {\n \t\t{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,\n@@ -671,7 +681,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\tif (debug_mode)\n \t\t\tdie(_(\"--column and --debug are incompatible\"));\n \t}\n-\tif (column_active(colopts))\n+\tif (column_active(colopts) || porcelain)\n \t\tline_terminator = 0;\n \n \tif (require_work_tree && !is_inside_work_tree())\n@@ -737,13 +747,15 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \tif (show_resolve_undo)\n \t\tshow_ru_info();\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(&output, colopts, &copts);\n-\t\tstring_list_clear(&output, 0);\n+\tmemset(&copts, 0, sizeof(copts));\n+\tcopts.padding = 2;\n+\tif (porcelain) {\n+\t\tqsort(output.items, output.nr, sizeof(*output.items),\n+\t\t      compare_output);\n+\t\tstring_list_remove_duplicates(&output, 0);\n \t}\n+\tprint_columns(&output, colopts, &copts);\n+\tstring_list_clear(&output, 0);\n \n \tif (ps_matched) {\n \t\tint bad;\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"255756","messageId":"1423386099-19994-17-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 16/21] list-files: do not show duplicate cached entries","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:34Z","receivedAt":"2015-02-08T09:01:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"With the current show_files() \"list-files -tcm\" will show\n\n  foo.c\nM foo.c\n\nThe first item is redundant. If \"foo.c\" is modified, we know it's in\nthe cache. Introduce show_files_compact to do that because ls-files is\nplumbing and scripts may already depend on current display behavior.\n\nAnother difference in show_files_compact() is it does not show\nskip-worktree (aka outside sparse checkout) entries anymore, which\nmakes sense in porcelain context.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/ls-files.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 51 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 8f10ab9..457d067 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -333,6 +333,53 @@ static void show_files(struct dir_struct *dir)\n \t}\n }\n \n+static void show_files_compact(struct dir_struct *dir)\n+{\n+\tint i;\n+\n+\t/* For cached/deleted files we don't need to even do the readdir */\n+\tif (show_others || show_killed) {\n+\t\tif (!show_others)\n+\t\t\tdir->flags |= DIR_COLLECT_KILLED_ONLY;\n+\t\tfill_directory(dir, &pathspec);\n+\t\tif (show_others)\n+\t\t\tshow_other_files(dir);\n+\t\tif (show_killed)\n+\t\t\tshow_killed_files(dir);\n+\t}\n+\tif (!(show_cached || show_unmerged || show_deleted || show_modified))\n+\t\treturn;\n+\tfor (i = 0; i < active_nr; i++) {\n+\t\tconst struct cache_entry *ce = active_cache[i];\n+\t\tstruct stat st;\n+\t\tint err, shown = 0;\n+\t\tif ((dir->flags & DIR_SHOW_IGNORED) &&\n+\t\t    !ce_excluded(dir, ce))\n+\t\t\tcontinue;\n+\t\tif (show_unmerged && !ce_stage(ce))\n+\t\t\tcontinue;\n+\t\tif (ce->ce_flags & CE_UPDATE)\n+\t\t\tcontinue;\n+\t\tif (ce_skip_worktree(ce))\n+\t\t\tcontinue;\n+\t\terr = lstat(ce->name, &st);\n+\t\tif (show_deleted && err) {\n+\t\t\tshow_ce_entry(tag_removed, ce);\n+\t\t\tshown = 1;\n+\t\t}\n+\t\tif (show_modified && (err || ce_modified(ce, &st, 0))) {\n+\t\t\tshow_ce_entry(tag_modified, ce);\n+\t\t\tshown = 1;\n+\t\t}\n+\t\tif (ce_stage(ce)) {\n+\t\t\tshow_ce_entry(tag_unmerged, ce);\n+\t\t\tshown = 1;\n+\t\t}\n+\t\tif (!shown && show_cached)\n+\t\t\tshow_ce_entry(tag_cached, ce);\n+\t}\n+}\n+\n /*\n  * Prune the index to only contain stuff starting with \"prefix\"\n  */\n@@ -743,7 +790,10 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\trefresh_index(&the_index, REFRESH_QUIET | REFRESH_UNMERGED, &pathspec, NULL, NULL);\n \t\tsetup_pager();\n \t}\n-\tshow_files(&dir);\n+\tif (porcelain)\n+\t\tshow_files_compact(&dir);\n+\telse\n+\t\tshow_files(&dir);\n \tif (show_resolve_undo)\n \t\tshow_ru_info();\n \n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"255757","messageId":"1423386099-19994-18-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 17/21] list-files: show directories as well as files","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:35Z","receivedAt":"2015-02-08T09:01:35Z","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 show_as_directory() deliberately generates duplicate\ndirectories and expects the previous patch to remove duplicates.\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/ls-files.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++----\n 1 file changed, 60 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 457d067..6be08fb 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -27,6 +27,8 @@ static int show_resolve_undo;\n static int show_modified;\n static int show_killed;\n static int show_valid_bit;\n+static int show_tag;\n+static int show_dirs;\n static int line_terminator = '\\n';\n static int debug_mode;\n static int use_color;\n@@ -179,6 +181,35 @@ static void show_killed_files(struct dir_struct *dir)\n \t}\n }\n \n+static int show_as_directory(const struct cache_entry *ce)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tconst char *p;\n+\n+\tstrbuf_add(&sb, ce->name, ce_namelen(ce));\n+\twhile (sb.len && (p = strrchr(sb.buf, '/')) != NULL) {\n+\t\tstruct strbuf sb2 = STRBUF_INIT;\n+\t\tstrbuf_setlen(&sb, p - sb.buf);\n+\t\tif (!match_pathspec(&pathspec, sb.buf, sb.len,\n+\t\t\t\t    max_prefix_len, NULL, 1))\n+\t\t\tcontinue;\n+\t\twrite_name(&sb2, sb.buf);\n+\t\tif (want_color(use_color)) {\n+\t\t\tstruct strbuf sb3 = STRBUF_INIT;\n+\t\t\tcolor_filename(&sb3, ce->name, sb2.buf, S_IFDIR, 1);\n+\t\t\tstrbuf_swap(&sb2, &sb3);\n+\t\t\tstrbuf_release(&sb3);\n+\t\t}\n+\t\tif (show_tag)\n+\t\t\tstrbuf_insert(&sb2, 0, tag_cached, strlen(tag_cached));\n+\t\tstrbuf_fputs(&sb2, strbuf_detach(&sb, NULL), NULL);\n+\t\tstrbuf_release(&sb2);\n+\t\treturn 1;\n+\t}\n+\tstrbuf_release(&sb);\n+\treturn 0;\n+}\n+\n static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce)\n {\n \tstruct strbuf quoted = STRBUF_INIT;\n@@ -191,17 +222,40 @@ static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce)\n \tstrbuf_release(&quoted);\n }\n \n+static int match_pathspec_with_depth(struct pathspec *ps,\n+\t\t\t\t     const char *name, int namelen,\n+\t\t\t\t     int prefix, char *seen, int is_dir,\n+\t\t\t\t     const int *custom_depth)\n+{\n+\tint saved_depth = ps->max_depth;\n+\tint result;\n+\n+\tif (custom_depth)\n+\t\tps->max_depth = *custom_depth;\n+\tresult = match_pathspec(ps, name, namelen, prefix, seen, is_dir);\n+\tif (custom_depth)\n+\t\tps->max_depth = saved_depth;\n+\treturn result;\n+}\n+\n static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n {\n \tstatic struct strbuf sb = STRBUF_INIT;\n+\tstatic const int infinite_depth = -1;\n \tint len = max_prefix_len;\n \n \tif (len >= ce_namelen(ce))\n \t\tdie(\"git ls-files: internal error - cache entry not superset of prefix\");\n \n-\tif (!match_pathspec(&pathspec, ce->name, ce_namelen(ce),\n-\t\t\t    len, ps_matched,\n-\t\t\t    S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode)))\n+\tif (!match_pathspec_with_depth(&pathspec, ce->name, ce_namelen(ce),\n+\t\t\t\t       len, ps_matched,\n+\t\t\t\t       S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode),\n+\t\t\t\t       show_dirs ? &infinite_depth : NULL))\n+\t\treturn;\n+\n+\tif (show_dirs &&strchr(ce->name, '/') &&\n+\t    !match_pathspec(&pathspec, ce->name, ce_namelen(ce), prefix_len, NULL, 1) &&\n+\t    show_as_directory(ce))\n \t\treturn;\n \n \tif (tag && *tag && show_valid_bit &&\n@@ -575,7 +629,7 @@ static int git_ls_config(const char *var, const char *value, void *cb)\n \n int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n {\n-\tint require_work_tree = 0, show_tag = 0, i;\n+\tint require_work_tree = 0, i;\n \tint max_depth = -1;\n \tconst char *max_prefix;\n \tstruct dir_struct dir;\n@@ -744,6 +798,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t       prefix, argv);\n \tpathspec.max_depth = max_depth;\n \tpathspec.recursive = 1;\n+\tshow_dirs = porcelain && max_depth != -1;\n+\n \n \t/* Find common prefix for all pathspec's */\n \tmax_prefix = common_prefix(&pathspec);\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"255758","messageId":"1423386099-19994-19-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 18/21] list-files: add -F/--classify","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:36Z","receivedAt":"2015-02-08T09:01:36Z","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 Documentation/git-list-files.txt |  6 ++++++\n builtin/ls-files.c               | 31 +++++++++++++++++++++++++++++++\n 2 files changed, 37 insertions(+)\n\ndiff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt\nindex 9d4c127..475c6da 100644\n--- a/Documentation/git-list-files.txt\n+++ b/Documentation/git-list-files.txt\n@@ -52,6 +52,12 @@ OPTIONS\n \tmultiple file selections. See linkgit::git-ls-files[1] option\n \t`-t` for more information.\n \n+-F::\n+--classify::\n+\tAppend indicator (one of `*/=>@|`, which is executable,\n+\tdirectory, socket, Solaris door, symlink, or fifo\n+\trespectively) to entries.\n+\n -R::\n --recursive::\n \tEquivalent of `--max-depth=-1` (infinite recursion).\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 6be08fb..9e444ff 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -29,6 +29,7 @@ static int show_killed;\n static int show_valid_bit;\n static int show_tag;\n static int show_dirs;\n+static int show_indicator;\n static int line_terminator = '\\n';\n static int debug_mode;\n static int use_color;\n@@ -77,6 +78,28 @@ static void write_name(struct strbuf *sb, const char *name)\n \t\tquote_path_relative(name, real_prefix, sb);\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 static void strbuf_fputs(struct strbuf *sb, const char *full_name, FILE *fp)\n {\n \tif (column_active(colopts) || porcelain) {\n@@ -99,6 +122,8 @@ static void write_dir_entry(struct strbuf *sb, const struct dir_entry *ent)\n \t\tcolor_filename(sb, ent->name, quoted.buf, st.st_mode, 1);\n \telse\n \t\tstrbuf_addbuf(sb, &quoted);\n+\tif (show_indicator && st.st_mode)\n+\t\tappend_indicator(sb, st.st_mode);\n \tstrbuf_addch(sb, line_terminator);\n \tstrbuf_release(&quoted);\n }\n@@ -202,6 +227,8 @@ static int show_as_directory(const struct cache_entry *ce)\n \t\t}\n \t\tif (show_tag)\n \t\t\tstrbuf_insert(&sb2, 0, tag_cached, strlen(tag_cached));\n+\t\tif (show_indicator)\n+\t\t\tappend_indicator(&sb2, S_IFDIR);\n \t\tstrbuf_fputs(&sb2, strbuf_detach(&sb, NULL), NULL);\n \t\tstrbuf_release(&sb2);\n \t\treturn 1;\n@@ -218,6 +245,8 @@ static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce)\n \t\tcolor_filename(sb, ce->name, quoted.buf, ce->ce_mode, 1);\n \telse\n \t\tstrbuf_addbuf(sb, &quoted);\n+\tif (show_indicator)\n+\t\tappend_indicator(sb, ce->ce_mode);\n \tstrbuf_addch(sb, line_terminator);\n \tstrbuf_release(&quoted);\n }\n@@ -714,6 +743,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tDIR_SHOW_IGNORED),\n \t\tOPT_BOOL('u', \"unmerged\", &show_unmerged,\n \t\t\tN_(\"show unmerged files\")),\n+\t\tOPT_BOOL('F', \"classify\", &show_indicator,\n+\t\t\t N_(\"append indicator (one of */=>@|) to entries\")),\n \t\tOPT__COLOR(&use_color, N_(\"show color\")),\n \t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n \t\tOPT_SET_INT('1', NULL, &colopts,\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"255759","messageId":"1423386099-19994-20-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 19/21] list-files -F: show submodules with the new indicator '&'","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:37Z","receivedAt":"2015-02-08T09:01:37Z","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/git-list-files.txt | 4 ++--\n builtin/ls-files.c               | 2 ++\n 2 files changed, 4 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt\nindex 475c6da..223f6fd 100644\n--- a/Documentation/git-list-files.txt\n+++ b/Documentation/git-list-files.txt\n@@ -54,8 +54,8 @@ OPTIONS\n \n -F::\n --classify::\n-\tAppend indicator (one of `*/=>@|`, which is executable,\n-\tdirectory, socket, Solaris door, symlink, or fifo\n+\tAppend indicator (one of `*/=>@|&`, which is executable,\n+\tdirectory, socket, Solaris door, symlink, fifo, or submodule\n \trespectively) to entries.\n \n -R::\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 9e444ff..d3540d1 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -92,6 +92,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 = '>';\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"255760","messageId":"1423386099-19994-21-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 20/21] list-files: -M aka diff-cached","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:38Z","receivedAt":"2015-02-08T09:01:38Z","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/ls-files.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++---\n 1 file changed, 64 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex d3540d1..b5e1a59 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -16,6 +16,9 @@\n #include \"pathspec.h\"\n #include \"color.h\"\n #include \"column.h\"\n+#include \"diff.h\"\n+#include \"diffcore.h\"\n+#include \"revision.h\"\n \n static int abbrev;\n static int show_deleted;\n@@ -25,6 +28,7 @@ static int show_stage;\n static int show_unmerged;\n static int show_resolve_undo;\n static int show_modified;\n+static int show_diff_cached;\n static int show_killed;\n static int show_valid_bit;\n static int show_tag;\n@@ -53,6 +57,7 @@ static const char *tag_removed = \"\";\n static const char *tag_other = \"\";\n static const char *tag_killed = \"\";\n static const char *tag_modified = \"\";\n+static const char *tag_diff_cached = \"\";\n static const char *tag_skip_worktree = \"\";\n static const char *tag_resolve_undo = \"\";\n \n@@ -412,7 +417,15 @@ static void show_files(struct dir_struct *dir)\n \t\t\terr = lstat(ce->name, &st);\n \t\t\tif (show_deleted && err)\n \t\t\t\tshow_ce_entry(tag_removed, ce);\n-\t\t\tif (show_modified && ce_modified(ce, &st, 0))\n+\t\t\tif (show_diff_cached && (ce->ce_flags & CE_MATCHED)) {\n+\t\t\t\tshow_ce_entry(tag_diff_cached, ce);\n+\t\t\t\t/*\n+\t\t\t\t * if we don't clear, it'll confuse write_ce_name()\n+\t\t\t\t * when show_ce_entry(tag_modified, ce) is called\n+\t\t\t\t */\n+\t\t\t\tactive_cache[i]->ce_flags &= ~CE_MATCHED;\n+\t\t\t}\n+\t\t\tif (show_modified && (err || ce_modified(ce, &st, 0)))\n \t\t\t\tshow_ce_entry(tag_modified, ce);\n \t\t}\n \t}\n@@ -432,7 +445,8 @@ static void show_files_compact(struct dir_struct *dir)\n \t\tif (show_killed)\n \t\t\tshow_killed_files(dir);\n \t}\n-\tif (!(show_cached || show_unmerged || show_deleted || show_modified))\n+\tif (!(show_cached || show_unmerged || show_deleted ||\n+\t      show_modified || show_diff_cached))\n \t\treturn;\n \tfor (i = 0; i < active_nr; i++) {\n \t\tconst struct cache_entry *ce = active_cache[i];\n@@ -452,6 +466,15 @@ static void show_files_compact(struct dir_struct *dir)\n \t\t\tshow_ce_entry(tag_removed, ce);\n \t\t\tshown = 1;\n \t\t}\n+\t\tif (show_diff_cached && (ce->ce_flags & CE_MATCHED)) {\n+\t\t\tshow_ce_entry(tag_diff_cached, ce);\n+\t\t\tshown = 1;\n+\t\t\t/*\n+\t\t\t * if we don't clear, it'll confuse write_ce_name()\n+\t\t\t * when show_ce_entry(tag_modified, ce) is called\n+\t\t\t */\n+\t\t\tactive_cache[i]->ce_flags &= ~CE_MATCHED;\n+\t\t}\n \t\tif (show_modified && (err || ce_modified(ce, &st, 0))) {\n \t\t\tshow_ce_entry(tag_modified, ce);\n \t\t\tshown = 1;\n@@ -465,6 +488,38 @@ static void show_files_compact(struct dir_struct *dir)\n \t}\n }\n \n+static void mark_diff_cached(struct diff_queue_struct *q,\n+\t\t\t     struct diff_options *options,\n+\t\t\t     void *data)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < q->nr; i++) {\n+\t\tstruct diff_filepair *p = q->queue[i];\n+\t\tint pos = cache_name_pos(p->two->path, strlen(p->two->path));\n+\t\tif (pos < 0)\n+\t\t\tcontinue;\n+\t\tactive_cache[pos]->ce_flags |= CE_MATCHED;\n+\t}\n+}\n+\n+static void diff_cached(struct pathspec *pathspec)\n+{\n+\tstruct rev_info rev;\n+\tconst char *argv[] = { \"ls-files\", \"HEAD\", NULL };\n+\n+\tinit_revisions(&rev, NULL);\n+\tsetup_revisions(2, argv, &rev, NULL);\n+\n+\trev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;\n+\trev.diffopt.format_callback = mark_diff_cached;\n+\trev.diffopt.detect_rename = 1;\n+\trev.diffopt.rename_limit = 200;\n+\trev.diffopt.break_opt = 0;\n+\tcopy_pathspec(&rev.prune_data, pathspec);\n+\trun_diff_index(&rev, 1);\n+}\n+\n /*\n  * Prune the index to only contain stuff starting with \"prefix\"\n  */\n@@ -734,6 +789,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tN_(\"show cached files that are deleted on working directory\")),\n \t\tOPT_BOOL('m', \"modified\", &show_modified,\n \t\t\tN_(\"show cached files that have modification on working directory\")),\n+\t\tOPT_BOOL('M', \"modified\", &show_diff_cached,\n+\t\t\tN_(\"show modified files in the cache\")),\n \t\tOPT_BOOL('o', \"others\", &show_others,\n \t\t\tN_(\"show untracked files\")),\n \t\tOPT_SET_INT('R', \"recursive\", &max_depth,\n@@ -847,11 +904,12 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \n \t/* With no flags, we default to showing the cached files */\n \tif (!(show_stage || show_deleted || show_others || show_unmerged ||\n-\t      show_killed || show_modified || show_resolve_undo))\n+\t      show_killed || show_modified || show_resolve_undo || show_diff_cached))\n \t\tshow_cached = 1;\n \n \tif (show_tag == -1)\n \t\tshow_tag = (show_cached + show_deleted + show_others +\n+\t\t\t    show_diff_cached +\n \t\t\t    show_unmerged + show_killed + show_modified) > 1;\n \tif (show_tag || show_valid_bit) {\n \t\ttag_cached = porcelain ? \"  \" : \"H \";\n@@ -859,6 +917,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\ttag_removed = \"R \";\n \t\ttag_modified = \"C \";\n \t\ttag_other = \"? \";\n+\t\ttag_diff_cached = \"X \";\n \t\ttag_killed = \"K \";\n \t\ttag_skip_worktree = \"S \";\n \t\ttag_resolve_undo = \"U \";\n@@ -879,6 +938,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\trefresh_index(&the_index, REFRESH_QUIET | REFRESH_UNMERGED, &pathspec, NULL, NULL);\n \t\tsetup_pager();\n \t}\n+\tif (show_diff_cached)\n+\t\tdiff_cached(&pathspec);\n \tif (porcelain)\n \t\tshow_files_compact(&dir);\n \telse\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"255761","messageId":"1423386099-19994-22-git-send-email-pclouds@gmail.com","threadId":"38526","inReplyTo":"1423386099-19994-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 21/21] t3080: tests for git-list-files","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-02-08T09:01:39Z","receivedAt":"2015-02-08T09:01:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Helped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t3080-list-files.sh (new +x) | 134 +++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 134 insertions(+)\n create mode 100755 t/t3080-list-files.sh\n\ndiff --git a/t/t3080-list-files.sh b/t/t3080-list-files.sh\nnew file mode 100755\nindex 0000000..01b9662\n--- /dev/null\n+++ b/t/t3080-list-files.sh\n@@ -0,0 +1,134 @@\n+#!/bin/sh\n+\n+test_description='git list-files test'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\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+'\n+\n+test_expect_success 'LS_COLORS env variable' '\n+\tLS_COLORS=\"rs=0:fi=31:di=32\" \\\n+\t\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+'\n+\n+test_expect_success 'color.ls.*' '\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+'\n+\n+test_expect_success 'column output' '\n+\tCOLUMNS=20 git list-files --column=always >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tdir      gitlink\n+\tfile\n+\tEOF\n+\ttest_cmp expected actual &&\n+\tgit list-files -1 >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tdir\n+\tfile\n+\tgitlink\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '--max-depth' '\n+\tgit list-files --max-depth=1 >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tdir/file\n+\tfile\n+\tgitlink\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'recursive' '\n+\tgit list-files -R >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tdir/file\n+\tfile\n+\tgitlink\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'globbing' '\n+\tgit list-files \"f*\" >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tfile\n+\tEOF\n+\ttest_cmp expected actual &&\n+\tgit list-files \"**/f*\" >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tdir/file\n+\tfile\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'no dups' '\n+\ttest_when_finished \"git checkout file\" &&\n+\techo dirty >>file &&\n+\tgit list-files -m file >actual &&\n+\techo \"file\" >expected &&\n+\ttest_cmp expected actual &&\n+\tgit list-files -cm file >actual &&\n+\techo \"C file\" >expected &&\n+\ttest_cmp expected actual &&\n+\tgit list-files -tcm file >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '--classify' '\n+\tgit list-files -F >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tdir/\n+\tfile\n+\tgitlink&\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'diff-cached' '\n+\ttest_when_finished \"git checkout file\" &&\n+\techo dirty >>file &&\n+\tgit add file &&\n+\tgit list-files -M >actual &&\n+\techo \"file\" >expected &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'unmerged files' '\n+\tgit ls-files --stage file >index-info &&\n+\tsed \"s/ 0/ 2/;s/file/unmerged/\" index-info | git update-index --index-info &&\n+\tsed \"s/ 0/ 3/;s,file,dir/unmerged,\" index-info | git update-index --index-info &&\n+\tgit list-files -u >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tdir\n+\tunmerged\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_done\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"255866","messageId":"xmqqiofawwha.fsf@gitster.dls.corp.google.com","threadId":"38526","inReplyTo":"1423386099-19994-18-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 17/21] list-files: show directories as well as files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-02-09T22:28:33Z","receivedAt":"2015-02-09T22:28:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> +\tif (show_dirs &&strchr(ce->name, '/') &&\n\nOops.  Will fix it up locally.\n"}]}