{"thread":{"id":"38449","subject":"[PATCH 00/21] nd/list-files updates","startedAt":"2015-01-25T12:37:35Z","lastAt":"2015-01-28T19:03:03Z","messageCount":30,"participants":["Nguyễn Thái Ngọc Duy","Eric Sunshine","Junio C Hamano","Michael Blume","Duy Nguyen"],"isPatch":true,"patchVersion":1,"patchTotal":21},"messages":[{"id":"255256","messageId":"1422189476-7518-1-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":null,"subject":"[PATCH 00/21] nd/list-files updates","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-01-25T12:37:35Z","receivedAt":"2015-01-25T12:37:35Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Changes since 'pu' version:\n\n - refresh_index() in 09/21 is set not to show \"XXX: needs merge\"\n   messages, cluttering \"list-files -u\" output\n - new patch 10/21 to add a default alias 'ls' to 'list-files',\n   of course overridable by the user\n - fix \"list-files -u\" not showing anything because\n   show_files_compact in 16/21 ignores show_unmerged flag\n - directory listing in 17/21 is rewritten to work filtering\n   (e.g. -m, -M...)\n - new patch 21/21 adds tests for the series\n\nNguyễn Thái Ngọc Duy (21):\n  ls_colors.c: add $LS_COLORS parsing code\n  ls_colors.c: parse color.ls.* from config file\n  ls_colors.c: add a function to color a file name\n  ls_colors.c: highlight submodules like directories\n  ls-files: buffer full item in strbuf before printing\n  ls-files: add --color to highlight file names\n  ls-files: add --column\n  ls-files: support --max-depth\n  list-files: a user friendly version of ls-files and more\n  list-files: make alias 'ls' default to 'list-files'\n  list-files: -u does not imply showing stages\n  list-files: add -R/--recursive short for --max-depth=-1\n  list-files: add -1 short for --no-column\n  list-files: add -t back\n  list-files: sort output and remove duplicates\n  list-files: do not show duplicate cached entries\n  list-files: show directories as well as files\n  list-files: add -F/--classify\n  list-files -F: show submodules with the new indicator '&'\n  list-files: -M aka diff-cached\n  t3080: tests for git-list-files\n\nTotal diff against 'pu' is something like this\n\n-- 8< --\ndiff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt\nindex c57129b..223f6fd 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/builtin/ls-files.c b/builtin/ls-files.c\nindex 02a9ac1..b04c712 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -213,6 +213,37 @@ 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_release(&sb2);\n+\t\t\tsb2 = sb3;\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+\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@@ -230,16 +261,31 @@ static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce)\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+\tint len = max_prefix_len, saved_max_depth;\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 \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+\n \tif (tag && *tag && show_valid_bit &&\n \t    (ce->ce_flags & CE_VALID)) {\n \t\tstatic char alttag[4];\n@@ -348,7 +394,7 @@ static void show_files(struct dir_struct *dir)\n \t\t\t\t(ce_skip_worktree(ce) ? tag_skip_worktree : tag_cached), ce);\n \t\t}\n \t}\n-\tif (show_deleted || show_modified || show_diff_cached) {\n+\tif (show_deleted || show_modified) {\n \t\tfor (i = 0; i < active_nr; i++) {\n \t\t\tconst struct cache_entry *ce = active_cache[i];\n \t\t\tstruct stat st;\n@@ -377,45 +423,6 @@ static void show_files(struct dir_struct *dir)\n \t}\n }\n \n-static void show_directories(const struct cache_entry *ce)\n-{\n-\tstatic const char *last_directory;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\tconst char *p = ce->name + prefix_len;\n-\tconst char *sep;\n-\n-\tif (last_directory) {\n-\t\tint len = strlen(last_directory);\n-\t\tif (!strncmp(ce->name, last_directory, len) &&\n-\t\t    ce->name[len] == '/')\n-\t\t\tp += len + 1;\n-\t}\n-\n-\twhile (*p && (sep = strchr(p, '/'))) {\n-\t\tstruct strbuf sb2 = STRBUF_INIT;\n-\t\tstrbuf_reset(&sb);\n-\t\tstrbuf_add(&sb, ce->name, sep - ce->name);\n-\t\tp = sep + 1;\n-\t\tif (!match_pathspec(&pathspec, sb.buf, sb.len,\n-\t\t\t\t    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_release(&sb2);\n-\t\t\tsb2 = sb3;\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\tlast_directory = strbuf_detach(&sb, NULL);\n-\t\tstrbuf_fputs(&sb2, last_directory, NULL);\n-\t\tstrbuf_release(&sb2);\n-\t}\n-}\n-\n static void show_files_compact(struct dir_struct *dir)\n {\n \tint i;\n@@ -430,14 +437,13 @@ 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_stage || 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 \t\tstruct stat st;\n \t\tint err, shown = 0;\n-\t\tif (show_dirs)\n-\t\t\tshow_directories(ce);\n \t\tif ((dir->flags & DIR_SHOW_IGNORED) &&\n \t\t    !ce_excluded(dir, ce))\n \t\t\tcontinue;\n@@ -452,6 +458,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@@ -817,7 +832,6 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\tuse_color = -1;\n \t\tmax_depth = 0;\n \t\tshow_tag = -1;\n-\t\tshow_dirs = 1;\n \t\tgit_config(git_ls_config, NULL);\n \t} else\n \t\tgit_config(git_default_config, NULL);\n@@ -866,6 +880,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@@ -911,7 +927,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\toverlay_tree_on_cache(with_tree, max_prefix);\n \t}\n \tif (porcelain) {\n-\t\trefresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL);\n+\t\trefresh_index(&the_index, REFRESH_QUIET | REFRESH_UNMERGED, &pathspec, NULL, NULL);\n \t\tsetup_pager();\n \t}\n \tif (show_diff_cached)\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);\ndiff --git a/t/t3080-list-files.sh b/t/t3080-list-files.sh\nnew file mode 100755\nindex 0000000..6313dd9\n--- /dev/null\n+++ b/t/t3080-list-files.sh\n@@ -0,0 +1,122 @@\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 | grep -v gitlink >actual &&\n+\ttest_cmp \"$TEST_DIRECTORY\"/t3080/ls_colors 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+'\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+\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+\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\ndiff --git a/t/t3080/color_ls b/t/t3080/color_ls\nnew file mode 100644\nindex 0000000..47f77ad\n--- /dev/null\n+++ b/t/t3080/color_ls\n@@ -0,0 +1,3 @@\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\nnew file mode 100644\nindex 0000000..423c016\n--- /dev/null\n+++ b/t/t3080/ls_colors\n@@ -0,0 +1,2 @@\n+\u001b[32mdir\u001b[m\n+\u001b[31mfile\u001b[m\n-- 8< --\n-- \n2.2.0.84.ge9c7a8a\n"},{"id":"255257","messageId":"1422189476-7518-2-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:36Z","receivedAt":"2015-01-25T12:37:36Z","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.2.0.84.ge9c7a8a\n"},{"id":"255258","messageId":"1422189476-7518-3-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:37Z","receivedAt":"2015-01-25T12:37:37Z","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\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config.txt | 11 +++++++++++\n ls_colors.c              | 26 ++++++++++++++++++++++++++\n 2 files changed, 37 insertions(+)\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..08e7068 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@@ -395,4 +420,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.2.0.84.ge9c7a8a\n"},{"id":"255259","messageId":"1422189476-7518-4-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:38Z","receivedAt":"2015-01-25T12:37:38Z","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 08e7068..7d2e2e0 100644\n--- a/ls_colors.c\n+++ b/ls_colors.c\n@@ -422,3 +422,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.2.0.84.ge9c7a8a\n"},{"id":"255260","messageId":"1422189476-7518-5-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:39Z","receivedAt":"2015-01-25T12:37:39Z","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 7d2e2e0..9259ad3 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@@ -448,6 +452,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.2.0.84.ge9c7a8a\n"},{"id":"255261","messageId":"1422189476-7518-6-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:40Z","receivedAt":"2015-01-25T12:37:40Z","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.2.0.84.ge9c7a8a\n"},{"id":"255262","messageId":"1422189476-7518-7-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:41Z","receivedAt":"2015-01-25T12:37:41Z","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.2.0.84.ge9c7a8a\n"},{"id":"255263","messageId":"1422189476-7518-8-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:42Z","receivedAt":"2015-01-25T12:37:42Z","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.2.0.84.ge9c7a8a\n"},{"id":"255264","messageId":"1422189476-7518-9-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:43Z","receivedAt":"2015-01-25T12:37:43Z","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.2.0.84.ge9c7a8a\n"},{"id":"255265","messageId":"1422189476-7518-10-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:44Z","receivedAt":"2015-01-25T12:37:44Z","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\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..74da715 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.list-files::\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.list-files::\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..eca9407 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, \"list-files\", &colopts);\n+\tif (!strcmp(var, \"color.list-files\")) {\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.2.0.84.ge9c7a8a\n"},{"id":"255266","messageId":"1422189476-7518-11-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:45Z","receivedAt":"2015-01-25T12:37:45Z","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.2.0.84.ge9c7a8a\n"},{"id":"255267","messageId":"1422189476-7518-12-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:46Z","receivedAt":"2015-01-25T12:37:46Z","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 eca9407..f2c29ce 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.2.0.84.ge9c7a8a\n"},{"id":"255268","messageId":"1422189476-7518-13-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:47Z","receivedAt":"2015-01-25T12:37:47Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\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 f2c29ce..010291c 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.2.0.84.ge9c7a8a\n"},{"id":"255269","messageId":"1422189476-7518-14-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:48Z","receivedAt":"2015-01-25T12:37:48Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/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 010291c..a80ac6a 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.2.0.84.ge9c7a8a\n"},{"id":"255270","messageId":"1422189476-7518-15-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:49Z","receivedAt":"2015-01-25T12:37:49Z","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 a80ac6a..b4b24ef 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.2.0.84.ge9c7a8a\n"},{"id":"255272","messageId":"1422189476-7518-16-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:50Z","receivedAt":"2015-01-25T12:37:50Z","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 b4b24ef..154dd9d 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.2.0.84.ge9c7a8a\n"},{"id":"255271","messageId":"1422189476-7518-17-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:51Z","receivedAt":"2015-01-25T12:37:51Z","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 154dd9d..1a1c9c8 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.2.0.84.ge9c7a8a\n"},{"id":"255273","messageId":"1422189476-7518-18-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:52Z","receivedAt":"2015-01-25T12:37:52Z","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\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/ls-files.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++--\n 1 file changed, 50 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 1a1c9c8..29b5c2e 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_release(&sb2);\n+\t\t\tsb2 = 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@@ -194,16 +225,31 @@ static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce)\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+\tint len = max_prefix_len, saved_max_depth;\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 \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+\n \tif (tag && *tag && show_valid_bit &&\n \t    (ce->ce_flags & CE_VALID)) {\n \t\tstatic char alttag[4];\n@@ -575,7 +621,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 +790,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.2.0.84.ge9c7a8a\n"},{"id":"255276","messageId":"1422189476-7518-19-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:53Z","receivedAt":"2015-01-25T12:37:53Z","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 29b5c2e..bbeb4ce 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@@ -706,6 +735,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.2.0.84.ge9c7a8a\n"},{"id":"255274","messageId":"1422189476-7518-20-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:54Z","receivedAt":"2015-01-25T12:37:54Z","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 bbeb4ce..697a307 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.2.0.84.ge9c7a8a\n"},{"id":"255275","messageId":"1422189476-7518-21-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:55Z","receivedAt":"2015-01-25T12:37:55Z","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 697a307..b04c712 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@@ -404,7 +409,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@@ -424,7 +437,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@@ -444,6 +458,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@@ -457,6 +480,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@@ -726,6 +781,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@@ -839,11 +896,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@@ -851,6 +909,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@@ -871,6 +930,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.2.0.84.ge9c7a8a\n"},{"id":"255277","messageId":"1422189476-7518-22-git-send-email-pclouds@gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-25T12:37:56Z","receivedAt":"2015-01-25T12:37:56Z","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 t/t3080-list-files.sh (new +x) | 122 +++++++++++++++++++++++++++++++++++++++++\n t/t3080/color_ls (new)         |   3 +\n t/t3080/ls_colors (new)        |   2 +\n 3 files changed, 127 insertions(+)\n create mode 100755 t/t3080-list-files.sh\n create mode 100644 t/t3080/color_ls\n create mode 100644 t/t3080/ls_colors\n\ndiff --git a/t/t3080-list-files.sh b/t/t3080-list-files.sh\nnew file mode 100755\nindex 0000000..6313dd9\n--- /dev/null\n+++ b/t/t3080-list-files.sh\n@@ -0,0 +1,122 @@\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 | grep -v gitlink >actual &&\n+\ttest_cmp \"$TEST_DIRECTORY\"/t3080/ls_colors 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+'\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+\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+\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\ndiff --git a/t/t3080/color_ls b/t/t3080/color_ls\nnew file mode 100644\nindex 0000000..47f77ad\n--- /dev/null\n+++ b/t/t3080/color_ls\n@@ -0,0 +1,3 @@\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\nnew file mode 100644\nindex 0000000..423c016\n--- /dev/null\n+++ b/t/t3080/ls_colors\n@@ -0,0 +1,2 @@\n+\u001b[32mdir\u001b[m\n+\u001b[31mfile\u001b[m\n-- \n2.2.0.84.ge9c7a8a\n"},{"id":"255278","messageId":"CAPig+cTwcAWUiJGViYDjeRifd2ERojQR+f+cpSB806RwYHoCCw@mail.gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-18-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 17/21] list-files: show directories as well as files","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-01-25T19:16:36Z","receivedAt":"2015-01-25T19:16:36Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Jan 25, 2015 at 7:37 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> The index does not store directories explicitly (except submodules) so\n> we have to figure them out from file list when output lis depth-limited.\n>\n> The function show_as_directory() deliberately generates duplicate\n> directories and expects the previous patch to remove duplicates.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/builtin/ls-files.c b/builtin/ls-files.c\n> index 1a1c9c8..29b5c2e 100644\n> --- a/builtin/ls-files.c\n> +++ b/builtin/ls-files.c\n> @@ -179,6 +181,35 @@ static void show_killed_files(struct dir_struct *dir)\n>         }\n>  }\n>\n> +static int show_as_directory(const struct cache_entry *ce)\n> +{\n> +       struct strbuf sb = STRBUF_INIT;\n> +       const char *p;\n> +\n> +       strbuf_add(&sb, ce->name, ce_namelen(ce));\n> +       while (sb.len && (p = strrchr(sb.buf, '/')) != NULL) {\n> +               struct strbuf sb2 = STRBUF_INIT;\n> +               strbuf_setlen(&sb, p - sb.buf);\n> +               if (!match_pathspec(&pathspec, sb.buf, sb.len,\n> +                                   max_prefix_len, NULL, 1))\n> +                       continue;\n> +               write_name(&sb2, sb.buf);\n> +               if (want_color(use_color)) {\n> +                       struct strbuf sb3 = STRBUF_INIT;\n> +                       color_filename(&sb3, ce->name, sb2.buf, S_IFDIR, 1);\n> +                       strbuf_release(&sb2);\n> +                       sb2 = sb3;\n\nAlthough more expensive, would it be a bit more idiomatic and obvious\nto phrase this as\n\n    strbuf_swap(&sb2, &sb3);\n    strbuf_release(&sb3);\n\nor is it not worth it?\n\n> +               }\n> +               if (show_tag)\n> +                       strbuf_insert(&sb2, 0, tag_cached, strlen(tag_cached));\n> +               strbuf_fputs(&sb2, strbuf_detach(&sb, NULL), NULL);\n\nThe detached strbuf content gets assigned to the 'util' field of the\n'struct string_list output' item and is eventually leaked, however,\nthe program exits soon after. Okay.\n\n> +               strbuf_release(&sb2);\n> +               return 1;\n> +       }\n> +       strbuf_release(&sb);\n> +       return 0;\n> +}\n> +\n>  static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce)\n>  {\n>         struct strbuf quoted = STRBUF_INIT;\n"},{"id":"255279","messageId":"CAPig+cQ8AuoA=ZNaOiZ_JQb-fKtXa-mFhoYQg+eWJxF3eFy9FA@mail.gmail.com","threadId":"38449","inReplyTo":"1422189476-7518-22-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 21/21] t3080: tests for git-list-files","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-01-25T19:20:29Z","receivedAt":"2015-01-25T19:20:29Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Jan 25, 2015 at 7:37 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/t/t3080-list-files.sh b/t/t3080-list-files.sh\n> new file mode 100755\n> index 0000000..6313dd9\n> --- /dev/null\n> +++ b/t/t3080-list-files.sh\n> +test_expect_success 'no dups' '\n> +       echo dirty >>file &&\n\nTo leave a clean slate for subsequent tests, would it make sense to\nrestore 'file' to a clean state via test_when_finished()?\n\n> +       git list-files -m file >actual &&\n> +       echo \"file\" >expected &&\n> +       test_cmp expected actual &&\n> +       git list-files -cm file >actual &&\n> +       echo \"C file\" >expected &&\n> +       test_cmp expected actual &&\n> +       git list-files -tcm file >actual &&\n> +       test_cmp expected actual\n> +'\n> +\n> +test_expect_success 'diff-cached' '\n> +       echo dirty >>file &&\n> +       git add file &&\n\nDitto here?\n\n> +       git list-files -M >actual &&\n> +       echo \"file\" >expected &&\n> +       test_cmp expected actual\n> +'\n"},{"id":"255346","messageId":"xmqqegqgkltz.fsf@gitster.dls.corp.google.com","threadId":"38449","inReplyTo":"1422189476-7518-10-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 09/21] list-files: a user friendly version of ls-files and more","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-01-27T20:30:48Z","receivedAt":"2015-01-27T20:30:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> +color.list-files::\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\nThis violates the configuration variable naming rules; perhaps\nrename it to color.listFiles or something?\n"},{"id":"255349","messageId":"xmqqmw53ki3i.fsf@gitster.dls.corp.google.com","threadId":"38449","inReplyTo":"1422189476-7518-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-01-27T21:51:29Z","receivedAt":"2015-01-27T21:51:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> @@ -194,16 +225,31 @@ static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce)\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> +\tint len = max_prefix_len, saved_max_depth;\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>  \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> +\n\nMy compiler seems to be too stupid to notice that saved_max_depth is\nalways set before it is used, if it gets used and complains.  Sigh.\n\nFor now I am tempted to squash this in.  Note that the original does\nnot seem to restore saved_max_depath when the pathspec does not match\nand function returns in the call to match_pathspec() we have in the\ncode before your patch, which smells like a bug, and the attached\nwould fix it.\n\n builtin/ls-files.c | 33 ++++++++++++++++++++++-----------\n 1 file changed, 22 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 29b5c2e..f28b7e9 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -222,27 +222,38 @@ 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+\tint len = max_prefix_len;\n+\tstatic const int infinite_depth = -1;\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"},{"id":"255356","messageId":"CAO2U3QhA1bGAKmM4OL0VVuOfwaocqmFEsQdZNhSqf_7tkJ+=WQ@mail.gmail.com","threadId":"38449","inReplyTo":"CAPig+cQ8AuoA=ZNaOiZ_JQb-fKtXa-mFhoYQg+eWJxF3eFy9FA@mail.gmail.com","subject":"Re: [PATCH 21/21] t3080: tests for git-list-files","fromName":"Michael Blume","fromEmail":"blume.mike@gmail.com","sentAt":"2015-01-28T04:44:30Z","receivedAt":"2015-01-28T04:44:30Z","isPatch":true,"sender":{"key":"blume.mike@gmail.com","avatar":"https://gravatar.com/avatar/1a7b440e1d942425ff4098ac7fc15b86b30cecaa56e1692a7ef8b5939ba25ea7?d=mp&s=160"},"body":"Test 3 is failing on my mac:\n\nexpecting success:\ntest_config color.ls.file red &&\ntest_config color.ls.directory green &&\ntest_config color.ls.submodule yellow &&\ngit list-files --color=always >actual &&\ntest_cmp \"$TEST_DIRECTORY\"/t3080/color_ls actual\n\n--- /Users/michael.blume/workspace/git/t/t3080/color_ls 2015-01-28\n04:40:23.000000000 +0000\n+++ actual 2015-01-28 04:42:59.000000000 +0000\n@@ -1,3 +1,3 @@\n-dir\n-file\n-gitlink\n+dir\n+file\n+gitlink\nnot ok 3 - color.ls.*\n#\n# test_config color.ls.file red &&\n# test_config color.ls.directory green &&\n# test_config color.ls.submodule yellow &&\n# git list-files --color=always >actual &&\n# test_cmp \"$TEST_DIRECTORY\"/t3080/color_ls actual\n#\n\nOn Sun, Jan 25, 2015 at 11:20 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n> On Sun, Jan 25, 2015 at 7:37 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>> diff --git a/t/t3080-list-files.sh b/t/t3080-list-files.sh\n>> new file mode 100755\n>> index 0000000..6313dd9\n>> --- /dev/null\n>> +++ b/t/t3080-list-files.sh\n>> +test_expect_success 'no dups' '\n>> +       echo dirty >>file &&\n>\n> To leave a clean slate for subsequent tests, would it make sense to\n> restore 'file' to a clean state via test_when_finished()?\n>\n>> +       git list-files -m file >actual &&\n>> +       echo \"file\" >expected &&\n>> +       test_cmp expected actual &&\n>> +       git list-files -cm file >actual &&\n>> +       echo \"C file\" >expected &&\n>> +       test_cmp expected actual &&\n>> +       git list-files -tcm file >actual &&\n>> +       test_cmp expected actual\n>> +'\n>> +\n>> +test_expect_success 'diff-cached' '\n>> +       echo dirty >>file &&\n>> +       git add file &&\n>\n> Ditto here?\n>\n>> +       git list-files -M >actual &&\n>> +       echo \"file\" >expected &&\n>> +       test_cmp expected actual\n>> +'\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n"},{"id":"255386","messageId":"CACsJy8DKJiXpHU4v3vsrgG0dDQ1akGYzfW9cgVJ=mPtdHjsbZw@mail.gmail.com","threadId":"38449","inReplyTo":"CAO2U3QhA1bGAKmM4OL0VVuOfwaocqmFEsQdZNhSqf_7tkJ+=WQ@mail.gmail.com","subject":"Re: [PATCH 21/21] t3080: tests for git-list-files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2015-01-28T10:19:27Z","receivedAt":"2015-01-28T10:19:27Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Jan 28, 2015 at 11:44 AM, Michael Blume <blume.mike@gmail.com> wrote:\n> Test 3 is failing on my mac:\n>\n> expecting success:\n> test_config color.ls.file red &&\n> test_config color.ls.directory green &&\n> test_config color.ls.submodule yellow &&\n> git list-files --color=always >actual &&\n> test_cmp \"$TEST_DIRECTORY\"/t3080/color_ls actual\n>\n> --- /Users/michael.blume/workspace/git/t/t3080/color_ls 2015-01-28\n> 04:40:23.000000000 +0000\n> +++ actual 2015-01-28 04:42:59.000000000 +0000\n> @@ -1,3 +1,3 @@\n> -dir\n> -file\n> -gitlink\n> +dir\n> +file\n> +gitlink\n\nUrgh.. colors do not send well over plain text.. I just realized we\nhave test_decode_color to convert colors to text descriptions.. will\nfix..\n\nAnyway, I think i may know why it fails. I suppose  the env variable\nLS_COLORS is not defined, or defined as empty in your shell?\n-- \nDuy\n"},{"id":"255364","messageId":"CAO2U3Qg_w17uDZhqehJnQQLeRTLXN1Fir=v+RJqcknptqJB-6Q@mail.gmail.com","threadId":"38449","inReplyTo":"CACsJy8DKJiXpHU4v3vsrgG0dDQ1akGYzfW9cgVJ=mPtdHjsbZw@mail.gmail.com","subject":"Re: [PATCH 21/21] t3080: tests for git-list-files","fromName":"Michael Blume","fromEmail":"blume.mike@gmail.com","sentAt":"2015-01-28T17:49:02Z","receivedAt":"2015-01-28T17:49:02Z","isPatch":true,"sender":{"key":"blume.mike@gmail.com","avatar":"https://gravatar.com/avatar/1a7b440e1d942425ff4098ac7fc15b86b30cecaa56e1692a7ef8b5939ba25ea7?d=mp&s=160"},"body":"On Wed, Jan 28, 2015 at 2:19 AM, Duy Nguyen <pclouds@gmail.com> wrote:\n> On Wed, Jan 28, 2015 at 11:44 AM, Michael Blume <blume.mike@gmail.com> wrote:\n>> Test 3 is failing on my mac:\n>>\n>> expecting success:\n>> test_config color.ls.file red &&\n>> test_config color.ls.directory green &&\n>> test_config color.ls.submodule yellow &&\n>> git list-files --color=always >actual &&\n>> test_cmp \"$TEST_DIRECTORY\"/t3080/color_ls actual\n>>\n>> --- /Users/michael.blume/workspace/git/t/t3080/color_ls 2015-01-28\n>> 04:40:23.000000000 +0000\n>> +++ actual 2015-01-28 04:42:59.000000000 +0000\n>> @@ -1,3 +1,3 @@\n>> -dir\n>> -file\n>> -gitlink\n>> +dir\n>> +file\n>> +gitlink\n>\n> Urgh.. colors do not send well over plain text.. I just realized we\n> have test_decode_color to convert colors to text descriptions.. will\n> fix..\n>\n> Anyway, I think i may know why it fails. I suppose  the env variable\n> LS_COLORS is not defined, or defined as empty in your shell?\n> --\n> Duy\n\n\nIn case it helps:\nhttps://www.dropbox.com/s/zsm3xgulmsl3rqm/Screenshot%202015-01-28%2009.46.57.png?dl=0\n\nAlso no, LS_COLORS is not defined\n\n$ echo $LS_COLORS\n\n$\n"},{"id":"255377","messageId":"xmqqa912iv88.fsf@gitster.dls.corp.google.com","threadId":"38449","inReplyTo":"CAO2U3QhA1bGAKmM4OL0VVuOfwaocqmFEsQdZNhSqf_7tkJ+=WQ@mail.gmail.com","subject":"Re: [PATCH 21/21] t3080: tests for git-list-files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-01-28T19:03:03Z","receivedAt":"2015-01-28T19:03:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Michael Blume <blume.mike@gmail.com> writes:\n\n> Test 3 is failing on my mac:\n>\n> expecting success:\n> test_config color.ls.file red &&\n> test_config color.ls.directory green &&\n> test_config color.ls.submodule yellow &&\n> git list-files --color=always >actual &&\n> test_cmp \"$TEST_DIRECTORY\"/t3080/color_ls actual\n>\n> --- /Users/michael.blume/workspace/git/t/t3080/color_ls 2015-01-28\n> 04:40:23.000000000 +0000\n> +++ actual 2015-01-28 04:42:59.000000000 +0000\n> @@ -1,3 +1,3 @@\n> -dir\n> -file\n> -gitlink\n> +dir\n> +file\n> +gitlink\n> not ok 3 - color.ls.*\n\nSame here on Ubuntu.\n\nRegardless of the breakage in the code that violates the expectation\nof this test, I do not think you would want to have test_cmp compare\ncoloured output directly without using test_decode_color.\n"}]}