{"thread":{"id":"38762","subject":"[PATCH 00/16] nd/list-files redesign","startedAt":"2015-03-09T10:18:18Z","lastAt":"2015-03-15T23:41:08Z","messageCount":29,"participants":["Nguyễn Thái Ngọc Duy","Eric Sunshine","Duy Nguyen","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":16},"messages":[{"id":"257376","messageId":"1425896314-10941-1-git-send-email-pclouds@gmail.com","threadId":"38762","inReplyTo":null,"subject":"[PATCH 00/16] nd/list-files redesign","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-09T10:18:18Z","receivedAt":"2015-03-09T10:18:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Feb 24, 2015 at 2:00 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Michael J Gruber <git@drmicha.warpmail.net> writes:\n>\n>> status, status -s and the like are in an ordinary user's tool box.\n>> ls-files isn't, at least not with \"-t\", which we even mark as deprecated.\n>>\n>> That makes me wonder, though, how difficult it would be to\n>> wt_status_collect_unchanged() and to leverage the status machinery\n>> rather than ls-files.\n>\n> Good point.  wt-status feels like a much better infrastructure to\n> build on than \"ls-files -t\", which should die ;-).  Especially if\n> the command is interested in showing the state of the working tree\n> files relative to the tree of HEAD, as \"ls-files\" is purely between\n> the index and the working tree.\n\nSo here it is. Except the first 4 patches identical with\n'nd/list-files' in 'pu' and not reposted here, the rest is rewritten\nusing wt-status.c as the base.  Documentation and tests are missing.\nProbably bugs lurking too. I just want to check if the UI makes sense.\nFull series is available at\n\n    https://github.com/pclouds/git/commits/ls\n\nif you want to pull and try out quickly. With this we got:\n\nusage: git list-files [options] [<pathspec>...]\n\nFilter options\n    -c, --cached          show cached files (default)\n    -o, --others          show untracked files\n    -i, --ignored         show ignored files\n    -u, --unmerged        show unmerged files\n    -a, --added           show added files compared to HEAD\n    -d, --deleted         show deleted files compared to HEAD\n    -m, --modified        show modified files compared to HEAD\n    -A, --wt-added        show added files in worktree\n    -D, --wt-deleted      show deleted files in worktree\n    -M, --wt-modified     show modified files on worktree\n\nOther\n    -C, --column[=<style>]\n                          show files in columns\n    -1                    shortcut for --no-column\n    --max-depth <depth>   descend at most <depth> levels\n    -R, --recursive       shortcut for --max-depth=-1\n    --color[=<when>]      show color\n    -F, --classify        append indicator (one of */=>@|) to entries\n\nAny filter option can be combined with others. Status code is the same\nwith 'git status --porcelain'. In fact options -[iouadmADM] are\nbasically the 'git status --porcelain' with bells and whistles.\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"257377","messageId":"1425896314-10941-2-git-send-email-pclouds@gmail.com","threadId":"38762","inReplyTo":"1425896314-10941-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 01/16] list-files: command skeleton","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-09T10:18:19Z","receivedAt":"2015-03-09T10:18:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"list-files is supposed to be the user friendly version of ls-files, or\nan alternative to git-status. Nothing fancy in this patch yet.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore                 |  1 +\n Makefile                   |  1 +\n builtin.h                  |  1 +\n builtin/list-files.c (new) | 96 ++++++++++++++++++++++++++++++++++++++++++++++\n git.c                      |  1 +\n 5 files changed, 100 insertions(+)\n create mode 100644 builtin/list-files.c\n\ndiff --git a/.gitignore b/.gitignore\nindex a052419..0534225 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -77,6 +77,7 @@\n /git-interpret-trailers\n /git-instaweb\n /git-log\n+/git-list-files\n /git-ls-files\n /git-ls-remote\n /git-ls-tree\ndiff --git a/Makefile b/Makefile\nindex 459121d..17f52b2 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -832,6 +832,7 @@ BUILTIN_OBJS += builtin/index-pack.o\n BUILTIN_OBJS += builtin/init-db.o\n BUILTIN_OBJS += builtin/interpret-trailers.o\n BUILTIN_OBJS += builtin/log.o\n+BUILTIN_OBJS += builtin/list-files.o\n BUILTIN_OBJS += builtin/ls-files.o\n BUILTIN_OBJS += builtin/ls-remote.o\n BUILTIN_OBJS += builtin/ls-tree.o\ndiff --git a/builtin.h b/builtin.h\nindex b87df70..afc29e7 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -76,6 +76,7 @@ extern int cmd_init_db(int argc, const char **argv, const char *prefix);\n extern int cmd_interpret_trailers(int argc, const char **argv, const char *prefix);\n extern int cmd_log(int argc, const char **argv, const char *prefix);\n extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);\n+extern int cmd_list_files(int argc, const char **argv, const char *prefix);\n extern int cmd_ls_files(int argc, const char **argv, const char *prefix);\n extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nnew file mode 100644\nindex 0000000..ac33f13\n--- /dev/null\n+++ b/builtin/list-files.c\n@@ -0,0 +1,96 @@\n+#include \"cache.h\"\n+#include \"builtin.h\"\n+#include \"parse-options.h\"\n+#include \"pathspec.h\"\n+#include \"dir.h\"\n+\n+static struct pathspec pathspec;\n+static const char *prefix;\n+static int prefix_length;\n+\n+static const char * const ls_usage[] = {\n+\tN_(\"git list-files [options] [<pathspec>...]\"),\n+\tNULL\n+};\n+\n+struct option ls_options[] = {\n+\tOPT_END()\n+};\n+\n+static void add_one(struct string_list *result, const char *name)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tstruct string_list_item *item;\n+\n+\tstrbuf_addstr(&sb, name);\n+\titem = string_list_append(result, strbuf_detach(&sb, NULL));\n+\titem->util = (char *)name;\n+}\n+\n+static void populate_cached_entries(struct string_list *result,\n+\t\t\t\t    const struct index_state *istate)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < istate->cache_nr; i++) {\n+\t\tconst struct cache_entry *ce = istate->cache[i];\n+\n+\t\tif (!match_pathspec(&pathspec, ce->name, ce_namelen(ce),\n+\t\t\t\t    0, NULL,\n+\t\t\t\t    S_ISDIR(ce->ce_mode) ||\n+\t\t\t\t    S_ISGITLINK(ce->ce_mode)))\n+\t\t\tcontinue;\n+\n+\t\tadd_one(result, ce->name);\n+\t}\n+}\n+\n+static void display(const struct string_list *result)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < result->nr; i++) {\n+\t\tconst struct string_list_item *s = result->items + i;\n+\n+\t\tprintf(\"%s\\n\", s->string);\n+\t}\n+}\n+\n+static int ls_config(const char *var, const char *value, void *cb)\n+{\n+\treturn git_default_config(var, value, cb);\n+}\n+\n+int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n+{\n+\tstruct string_list result = STRING_LIST_INIT_NODUP;\n+\n+\tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n+\t\tusage_with_options(ls_usage, ls_options);\n+\n+\tprefix = cmd_prefix;\n+\tif (prefix)\n+\t\tprefix_length = strlen(prefix);\n+\n+\tif (read_cache() < 0)\n+\t\tdie(_(\"index file corrupt\"));\n+\n+\tgit_config(ls_config, NULL);\n+\n+\targc = parse_options(argc, argv, prefix, ls_options, ls_usage, 0);\n+\n+\tparse_pathspec(&pathspec, 0,\n+\t\t       PATHSPEC_PREFER_CWD |\n+\t\t       PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,\n+\t\t       cmd_prefix, argv);\n+\tpathspec.max_depth = 0;\n+\tpathspec.recursive = 1;\n+\n+\trefresh_index(&the_index, REFRESH_QUIET | REFRESH_UNMERGED,\n+\t\t      &pathspec, NULL, NULL);\n+\n+\tpopulate_cached_entries(&result, &the_index);\n+\tdisplay(&result);\n+\tstring_list_clear(&result, 0);\n+\treturn 0;\n+}\ndiff --git a/git.c b/git.c\nindex 18fbf79..ae7fe77 100644\n--- a/git.c\n+++ b/git.c\n@@ -418,6 +418,7 @@ static struct cmd_struct commands[] = {\n \t{ \"init\", cmd_init_db, NO_SETUP },\n \t{ \"init-db\", cmd_init_db, NO_SETUP },\n \t{ \"interpret-trailers\", cmd_interpret_trailers, RUN_SETUP },\n+\t{ \"list-files\", cmd_list_files, RUN_SETUP | USE_PAGER | NEED_WORK_TREE },\n \t{ \"log\", cmd_log, RUN_SETUP },\n \t{ \"ls-files\", cmd_ls_files, RUN_SETUP },\n \t{ \"ls-remote\", cmd_ls_remote, RUN_SETUP_GENTLY },\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"257378","messageId":"1425896314-10941-3-git-send-email-pclouds@gmail.com","threadId":"38762","inReplyTo":"1425896314-10941-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 02/16] list-files: make :(glob) pathspec default","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-09T10:18:20Z","receivedAt":"2015-03-09T10:18:20Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex ac33f13..b99f2b7 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -65,6 +65,8 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n {\n \tstruct string_list result = STRING_LIST_INIT_NODUP;\n \n+\tsetenv(GIT_GLOB_PATHSPECS_ENVIRONMENT, \"1\", 0);\n+\n \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n \t\tusage_with_options(ls_usage, ls_options);\n \n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"257380","messageId":"1425896314-10941-4-git-send-email-pclouds@gmail.com","threadId":"38762","inReplyTo":"1425896314-10941-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 03/16] list-files: show paths relative to cwd","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-09T10:18:21Z","receivedAt":"2015-03-09T10:18:21Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex b99f2b7..c444a53 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -3,6 +3,7 @@\n #include \"parse-options.h\"\n #include \"pathspec.h\"\n #include \"dir.h\"\n+#include \"quote.h\"\n \n static struct pathspec pathspec;\n static const char *prefix;\n@@ -22,7 +23,7 @@ static void add_one(struct string_list *result, const char *name)\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct string_list_item *item;\n \n-\tstrbuf_addstr(&sb, name);\n+\tquote_path_relative(name, prefix_length ? prefix : NULL, &sb);\n \titem = string_list_append(result, strbuf_detach(&sb, NULL));\n \titem->util = (char *)name;\n }\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"257386","messageId":"1425896314-10941-5-git-send-email-pclouds@gmail.com","threadId":"38762","inReplyTo":"1425896314-10941-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 04/16] list-files: add tag to each entry, filter duplicate tags","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-09T10:18:22Z","receivedAt":"2015-03-09T10:18:22Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"All entry strings start with two-letter tag and a space. If all\nentries have the same tags, tags are not displayed.\n\nThe outcome before and after this patch is the same. But it will be\nuseful in future when there are more than one type of entry.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c | 40 ++++++++++++++++++++++++++++++++++++++--\n 1 file changed, 38 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex c444a53..18af65c 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -18,12 +18,16 @@ struct option ls_options[] = {\n \tOPT_END()\n };\n \n-static void add_one(struct string_list *result, const char *name)\n+static void add_one(struct string_list *result, const char *name,\n+\t\t    const char *tag)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct string_list_item *item;\n \n \tquote_path_relative(name, prefix_length ? prefix : NULL, &sb);\n+\tstrbuf_insert(&sb, 0, \"   \", 3);\n+\tsb.buf[0] = tag[0];\n+\tsb.buf[1] = tag[1];\n \titem = string_list_append(result, strbuf_detach(&sb, NULL));\n \titem->util = (char *)name;\n }\n@@ -42,7 +46,38 @@ static void populate_cached_entries(struct string_list *result,\n \t\t\t\t    S_ISGITLINK(ce->ce_mode)))\n \t\t\tcontinue;\n \n-\t\tadd_one(result, ce->name);\n+\t\tadd_one(result, ce->name, \"  \");\n+\t}\n+}\n+\n+static void cleanup_tags(struct string_list *result)\n+{\n+\tint i, same_1 = 1, same_2 = 1, pos, len;\n+\n+\tfor (i = 1; i < result->nr && (same_1 || same_2); i++) {\n+\t\tconst char *s0 = result->items[i - 1].string;\n+\t\tconst char *s1 = result->items[i].string;\n+\n+\t\tsame_1 = same_1 && s0[0] == s1[0];\n+\t\tsame_2 = same_2 && s0[1] == s1[1];\n+\t}\n+\n+\tif (same_1 && same_2) {\n+\t\tpos = 0;\n+\t\tlen = 3;\n+\t} else if (same_1) {\n+\t\tpos = 0;\n+\t\tlen = 1;\n+\t} else if (same_2) {\n+\t\tpos = 1;\n+\t\tlen = 1;\n+\t} else\n+\t\treturn;\n+\n+\tfor (i = 0; i < result->nr; i++) {\n+\t\tchar *s = result->items[i].string;\n+\t\tint length = strlen(s);\n+\t\tmemmove(s + pos, s + pos + len, length - len + 1);\n \t}\n }\n \n@@ -93,6 +128,7 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t      &pathspec, NULL, NULL);\n \n \tpopulate_cached_entries(&result, &the_index);\n+\tcleanup_tags(&result);\n \tdisplay(&result);\n \tstring_list_clear(&result, 0);\n \treturn 0;\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"257379","messageId":"1425896314-10941-6-git-send-email-pclouds@gmail.com","threadId":"38762","inReplyTo":"1425896314-10941-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 05/16] list-files: add --[no-]column, -C and -1","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-09T10:18:23Z","receivedAt":"2015-03-09T10:18:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c | 16 ++++++++++++++++\n 1 file changed, 16 insertions(+)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 18af65c..95a2e19 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -4,10 +4,12 @@\n #include \"pathspec.h\"\n #include \"dir.h\"\n #include \"quote.h\"\n+#include \"column.h\"\n \n static struct pathspec pathspec;\n static const char *prefix;\n static int prefix_length;\n+static unsigned int colopts;\n \n static const char * const ls_usage[] = {\n \tN_(\"git list-files [options] [<pathspec>...]\"),\n@@ -15,6 +17,9 @@ static const char * const ls_usage[] = {\n };\n \n struct option ls_options[] = {\n+\tOPT_COLUMN('C', \"column\", &colopts, N_(\"show files in columns\")),\n+\tOPT_SET_INT('1', NULL, &colopts,\n+\t\t    N_(\"shortcut for --no-column\"), COL_PARSEOPT),\n \tOPT_END()\n };\n \n@@ -85,6 +90,14 @@ static void display(const struct string_list *result)\n {\n \tint i;\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(result, colopts, &copts);\n+\t\treturn;\n+\t}\n+\n \tfor (i = 0; i < result->nr; i++) {\n \t\tconst struct string_list_item *s = result->items + i;\n \n@@ -94,6 +107,8 @@ static void display(const struct string_list *result)\n \n static int ls_config(const char *var, const char *value, void *cb)\n {\n+\tif (starts_with(var, \"column.\"))\n+\t\treturn git_column_config(var, value, \"listfiles\", &colopts);\n \treturn git_default_config(var, value, cb);\n }\n \n@@ -123,6 +138,7 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t       cmd_prefix, argv);\n \tpathspec.max_depth = 0;\n \tpathspec.recursive = 1;\n+\tfinalize_colopts(&colopts, -1);\n \n \trefresh_index(&the_index, REFRESH_QUIET | REFRESH_UNMERGED,\n \t\t      &pathspec, NULL, NULL);\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"257382","messageId":"1425896314-10941-7-git-send-email-pclouds@gmail.com","threadId":"38762","inReplyTo":"1425896314-10941-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 06/16] list-files: add --max-depth and -R","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-09T10:18:24Z","receivedAt":"2015-03-09T10:18:24Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c | 9 ++++++++-\n 1 file changed, 8 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 95a2e19..9a55ea6 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -10,6 +10,7 @@ static struct pathspec pathspec;\n static const char *prefix;\n static int prefix_length;\n static unsigned int colopts;\n+static int max_depth;\n \n static const char * const ls_usage[] = {\n \tN_(\"git list-files [options] [<pathspec>...]\"),\n@@ -20,6 +21,11 @@ struct option ls_options[] = {\n \tOPT_COLUMN('C', \"column\", &colopts, N_(\"show files in columns\")),\n \tOPT_SET_INT('1', NULL, &colopts,\n \t\t    N_(\"shortcut for --no-column\"), COL_PARSEOPT),\n+\t{ OPTION_INTEGER, 0, \"max-depth\", &max_depth, N_(\"depth\"),\n+\t  N_(\"descend at most <depth> levels\"), PARSE_OPT_NONEG,\n+\t  NULL, 1 },\n+\tOPT_SET_INT('R', \"recursive\", &max_depth,\n+\t\t    N_(\"shortcut for --max-depth=-1\"), -1),\n \tOPT_END()\n };\n \n@@ -134,9 +140,10 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \n \tparse_pathspec(&pathspec, 0,\n \t\t       PATHSPEC_PREFER_CWD |\n+\t\t       (max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0) |\n \t\t       PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,\n \t\t       cmd_prefix, argv);\n-\tpathspec.max_depth = 0;\n+\tpathspec.max_depth = max_depth;\n \tpathspec.recursive = 1;\n \tfinalize_colopts(&colopts, -1);\n \n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"257381","messageId":"1425896314-10941-8-git-send-email-pclouds@gmail.com","threadId":"38762","inReplyTo":"1425896314-10941-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 07/16] list-files: show directories as well as files","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-09T10:18:25Z","receivedAt":"2015-03-09T10:18:25Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The index does not store directories explicitly (except submodules) so\nwe have to figure them out from file list when output lis depth-limited.\n\nThe function add_directory() can generate duplicate entries, which is\ncleaned up before displaying.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++-----\n 1 file changed, 57 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 9a55ea6..dc865a1 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -7,10 +7,12 @@\n #include \"column.h\"\n \n static struct pathspec pathspec;\n+static struct pathspec recursive_pathspec;\n static const char *prefix;\n static int prefix_length;\n static unsigned int colopts;\n static int max_depth;\n+static int show_dirs;\n \n static const char * const ls_usage[] = {\n \tN_(\"git list-files [options] [<pathspec>...]\"),\n@@ -43,6 +45,51 @@ static void add_one(struct string_list *result, const char *name,\n \titem->util = (char *)name;\n }\n \n+static int add_directory(struct string_list *result,\n+\t\t\t const char *name)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tconst char *p;\n+\n+\tstrbuf_add(&sb, name, strlen(name));\n+\twhile (sb.len && (p = strrchr(sb.buf, '/')) != NULL) {\n+\t\tstrbuf_setlen(&sb, p - sb.buf);\n+\t\tif (!match_pathspec(&pathspec, sb.buf, sb.len, 0, NULL, 1))\n+\t\t\tcontinue;\n+\t\tadd_one(result, sb.buf, \"  \");\n+\t\t/*\n+\t\t * sb.buf is leaked, but because this command is\n+\t\t * short-lived anyway so it does not matter much\n+\t\t */\n+\t\treturn 1;\n+\t}\n+\tstrbuf_release(&sb);\n+\treturn 0;\n+}\n+\n+static int matched(struct string_list *result, const char *name, int mode)\n+{\n+\tint len = strlen(name);\n+\n+\tif (!match_pathspec(&recursive_pathspec, name, len, 0, NULL,\n+\t\t\t    S_ISDIR(mode) || S_ISGITLINK(mode)))\n+\t\treturn 0;\n+\n+\tif (show_dirs && strchr(name, '/') &&\n+\t    !match_pathspec(&pathspec, name, len, 0, NULL, 1) &&\n+\t    add_directory(result, name))\n+\t\treturn 0;\n+\n+\treturn 1;\n+}\n+\n+static 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 populate_cached_entries(struct string_list *result,\n \t\t\t\t    const struct index_state *istate)\n {\n@@ -51,14 +98,16 @@ static void populate_cached_entries(struct string_list *result,\n \tfor (i = 0; i < istate->cache_nr; i++) {\n \t\tconst struct cache_entry *ce = istate->cache[i];\n \n-\t\tif (!match_pathspec(&pathspec, ce->name, ce_namelen(ce),\n-\t\t\t\t    0, NULL,\n-\t\t\t\t    S_ISDIR(ce->ce_mode) ||\n-\t\t\t\t    S_ISGITLINK(ce->ce_mode)))\n+\t\tif (!matched(result, ce->name, ce->ce_mode))\n \t\t\tcontinue;\n \n \t\tadd_one(result, ce->name, \"  \");\n \t}\n+\n+\tif (!show_dirs)\n+\t\treturn;\n+\tqsort(result->items, result->nr, sizeof(*result->items), compare_output);\n+\tstring_list_remove_duplicates(result, 0);\n }\n \n static void cleanup_tags(struct string_list *result)\n@@ -145,10 +194,13 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t       cmd_prefix, argv);\n \tpathspec.max_depth = max_depth;\n \tpathspec.recursive = 1;\n+\tshow_dirs = max_depth >= 0;\n+\tcopy_pathspec(&recursive_pathspec, &pathspec);\n+\trecursive_pathspec.max_depth = -1;\n \tfinalize_colopts(&colopts, -1);\n \n \trefresh_index(&the_index, REFRESH_QUIET | REFRESH_UNMERGED,\n-\t\t      &pathspec, NULL, NULL);\n+\t\t      &recursive_pathspec, NULL, NULL);\n \n \tpopulate_cached_entries(&result, &the_index);\n \tcleanup_tags(&result);\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"257383","messageId":"1425896314-10941-9-git-send-email-pclouds@gmail.com","threadId":"38762","inReplyTo":"1425896314-10941-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 08/16] list-files: add --color","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-09T10:18:26Z","receivedAt":"2015-03-09T10:18:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c | 24 ++++++++++++++++++++----\n 1 file changed, 20 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex dc865a1..316f59c 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -5,6 +5,7 @@\n #include \"dir.h\"\n #include \"quote.h\"\n #include \"column.h\"\n+#include \"color.h\"\n \n static struct pathspec pathspec;\n static struct pathspec recursive_pathspec;\n@@ -13,6 +14,7 @@ static int prefix_length;\n static unsigned int colopts;\n static int max_depth;\n static int show_dirs;\n+static int use_color = -1;\n \n static const char * const ls_usage[] = {\n \tN_(\"git list-files [options] [<pathspec>...]\"),\n@@ -28,16 +30,23 @@ struct option ls_options[] = {\n \t  NULL, 1 },\n \tOPT_SET_INT('R', \"recursive\", &max_depth,\n \t\t    N_(\"shortcut for --max-depth=-1\"), -1),\n+\tOPT__COLOR(&use_color, N_(\"show color\")),\n \tOPT_END()\n };\n \n-static void add_one(struct string_list *result, const char *name,\n+static void add_one(struct string_list *result, const char *name, int mode,\n \t\t    const char *tag)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct string_list_item *item;\n \n \tquote_path_relative(name, prefix_length ? prefix : NULL, &sb);\n+\tif (want_color(use_color)) {\n+\t\tstruct strbuf quoted = STRBUF_INIT;\n+\t\tstrbuf_swap(&sb, &quoted);\n+\t\tcolor_filename(&sb, name, quoted.buf, mode, 1);\n+\t\tstrbuf_release(&quoted);\n+\t}\n \tstrbuf_insert(&sb, 0, \"   \", 3);\n \tsb.buf[0] = tag[0];\n \tsb.buf[1] = tag[1];\n@@ -56,7 +65,7 @@ static int add_directory(struct string_list *result,\n \t\tstrbuf_setlen(&sb, p - sb.buf);\n \t\tif (!match_pathspec(&pathspec, sb.buf, sb.len, 0, NULL, 1))\n \t\t\tcontinue;\n-\t\tadd_one(result, sb.buf, \"  \");\n+\t\tadd_one(result, sb.buf, S_IFDIR, \"  \");\n \t\t/*\n \t\t * sb.buf is leaked, but because this command is\n \t\t * short-lived anyway so it does not matter much\n@@ -101,7 +110,7 @@ static void populate_cached_entries(struct string_list *result,\n \t\tif (!matched(result, ce->name, ce->ce_mode))\n \t\t\tcontinue;\n \n-\t\tadd_one(result, ce->name, \"  \");\n+\t\tadd_one(result, ce->name, ce->ce_mode, \"  \");\n \t}\n \n \tif (!show_dirs)\n@@ -164,7 +173,11 @@ static int ls_config(const char *var, const char *value, void *cb)\n {\n \tif (starts_with(var, \"column.\"))\n \t\treturn git_column_config(var, value, \"listfiles\", &colopts);\n-\treturn git_default_config(var, value, cb);\n+\tif (!strcmp(var, \"color.listfiles\")) {\n+\t\tuse_color = git_config_colorbool(var, value);\n+\t\treturn 0;\n+\t}\n+\treturn git_color_default_config(var, value, cb);\n }\n \n int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n@@ -187,6 +200,9 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \n \targc = parse_options(argc, argv, prefix, ls_options, ls_usage, 0);\n \n+\tif (want_color(use_color))\n+\t\tparse_ls_color();\n+\n \tparse_pathspec(&pathspec, 0,\n \t\t       PATHSPEC_PREFER_CWD |\n \t\t       (max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0) |\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"257385","messageId":"1425896314-10941-10-git-send-email-pclouds@gmail.com","threadId":"38762","inReplyTo":"1425896314-10941-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 09/16] list-files: add -F/--classify","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-09T10:18:27Z","receivedAt":"2015-03-09T10:18:27Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This appends an indicator after the file name if it's executable, a\ndirectory and so on, like in GNU ls. In fact append_indicator() is a\nrewrite from get_type_indicator() in coreutils.git commit\n7326d1f1a67edf21947ae98194f98c38b6e9e527.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c | 27 +++++++++++++++++++++++++++\n 1 file changed, 27 insertions(+)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 316f59c..97fa8bf 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -15,6 +15,7 @@ static unsigned int colopts;\n static int max_depth;\n static int show_dirs;\n static int use_color = -1;\n+static int show_indicator;\n \n static const char * const ls_usage[] = {\n \tN_(\"git list-files [options] [<pathspec>...]\"),\n@@ -31,9 +32,33 @@ struct option ls_options[] = {\n \tOPT_SET_INT('R', \"recursive\", &max_depth,\n \t\t    N_(\"shortcut for --max-depth=-1\"), -1),\n \tOPT__COLOR(&use_color, N_(\"show color\")),\n+\tOPT_BOOL('F', \"classify\", &show_indicator,\n+\t\t N_(\"append indicator (one of */=>@|) to entries\")),\n \tOPT_END()\n };\n \n+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 add_one(struct string_list *result, const char *name, int mode,\n \t\t    const char *tag)\n {\n@@ -47,6 +72,8 @@ static void add_one(struct string_list *result, const char *name, int mode,\n \t\tcolor_filename(&sb, name, quoted.buf, mode, 1);\n \t\tstrbuf_release(&quoted);\n \t}\n+\tif (show_indicator)\n+\t\tappend_indicator(&sb, mode);\n \tstrbuf_insert(&sb, 0, \"   \", 3);\n \tsb.buf[0] = tag[0];\n \tsb.buf[1] = tag[1];\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"257384","messageId":"1425896314-10941-11-git-send-email-pclouds@gmail.com","threadId":"38762","inReplyTo":"1425896314-10941-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 10/16] list-files: new indicator '&' for submodules when -F is used","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-09T10:18:28Z","receivedAt":"2015-03-09T10:18:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 97fa8bf..eb51e7a 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -51,6 +51,8 @@ static void append_indicator(struct strbuf *sb, mode_t mode)\n \t\tc = '|';\n \telse if (S_ISSOCK(mode))\n \t\tc = '=';\n+\telse if (S_ISGITLINK(mode))\n+\t\tc = '&';\n #ifdef S_ISDOOR\n \telse if (S_ISDOOR(mode))\n \t\tc = '>';\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"257387","messageId":"1425896314-10941-12-git-send-email-pclouds@gmail.com","threadId":"38762","inReplyTo":"1425896314-10941-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 11/16] list-files: add --cached and --others","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-09T10:18:29Z","receivedAt":"2015-03-09T10:18:29Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"If no filter options are specified, --cached is the default.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 62 insertions(+)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex eb51e7a..93364b9 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -6,6 +6,7 @@\n #include \"quote.h\"\n #include \"column.h\"\n #include \"color.h\"\n+#include \"wt-status.h\"\n \n static struct pathspec pathspec;\n static struct pathspec recursive_pathspec;\n@@ -16,6 +17,7 @@ static int max_depth;\n static int show_dirs;\n static int use_color = -1;\n static int show_indicator;\n+static int show_cached, show_untracked;\n \n static const char * const ls_usage[] = {\n \tN_(\"git list-files [options] [<pathspec>...]\"),\n@@ -23,6 +25,13 @@ static const char * const ls_usage[] = {\n };\n \n struct option ls_options[] = {\n+\tOPT_GROUP(N_(\"Filter options\")),\n+\tOPT_BOOL('c', \"cached\", &show_cached,\n+\t\t N_(\"show cached files (default)\")),\n+\tOPT_BOOL('o', \"others\", &show_untracked,\n+\t\t N_(\"show untracked files\")),\n+\n+\tOPT_GROUP(N_(\"Other\")),\n \tOPT_COLUMN('C', \"column\", &colopts, N_(\"show files in columns\")),\n \tOPT_SET_INT('1', NULL, &colopts,\n \t\t    N_(\"shortcut for --no-column\"), COL_PARSEOPT),\n@@ -133,6 +142,9 @@ static void populate_cached_entries(struct string_list *result,\n {\n \tint i;\n \n+\tif (!show_cached)\n+\t\treturn;\n+\n \tfor (i = 0; i < istate->cache_nr; i++) {\n \t\tconst struct cache_entry *ce = istate->cache[i];\n \n@@ -148,10 +160,56 @@ static void populate_cached_entries(struct string_list *result,\n \tstring_list_remove_duplicates(result, 0);\n }\n \n+static void populate_untracked(struct string_list *result,\n+\t\t\t       const struct string_list *untracked)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < untracked->nr; i++) {\n+\t\tconst struct string_list_item *item = untracked->items + i;\n+\t\tconst char *name = item->string;\n+\t\tstruct stat st;\n+\n+\t\tif (lstat(name, &st))\n+\t\t\t/* color_filename() treats this as an orphan file */\n+\t\t\tst.st_mode = 0;\n+\n+\t\tif (!matched(result, name, st.st_mode))\n+\t\t\tcontinue;\n+\n+\t\tadd_one(result, name, st.st_mode, \"??\");\n+\t}\n+}\n+\n+static void wt_status_populate(struct string_list *result,\n+\t\t\t       struct index_state *istate)\n+{\n+\tstruct wt_status ws;\n+\n+\tif (!show_untracked)\n+\t\treturn;\n+\n+\twt_status_prepare(&ws);\n+\tcopy_pathspec(&ws.pathspec, &recursive_pathspec);\n+\tws.relative_paths = 0;\n+\tws.use_color = 0;\n+\tws.fp = NULL;\n+\twt_status_collect(&ws);\n+\n+\tif (show_untracked)\n+\t\tpopulate_untracked(result, &ws.untracked);\n+\n+\tqsort(result->items, result->nr, sizeof(*result->items), compare_output);\n+\tstring_list_remove_duplicates(result, 0);\n+}\n+\n static void cleanup_tags(struct string_list *result)\n {\n \tint i, same_1 = 1, same_2 = 1, pos, len;\n \n+\tif (show_cached + show_untracked > 1)\n+\t\treturn;\n+\n \tfor (i = 1; i < result->nr && (same_1 || same_2); i++) {\n \t\tconst char *s0 = result->items[i - 1].string;\n \t\tconst char *s1 = result->items[i].string;\n@@ -229,6 +287,9 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \n \targc = parse_options(argc, argv, prefix, ls_options, ls_usage, 0);\n \n+\tif (!show_cached && !show_untracked)\n+\t\tshow_cached = 1;\n+\n \tif (want_color(use_color))\n \t\tparse_ls_color();\n \n@@ -248,6 +309,7 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t      &recursive_pathspec, NULL, NULL);\n \n \tpopulate_cached_entries(&result, &the_index);\n+\twt_status_populate(&result, &the_index);\n \tcleanup_tags(&result);\n \tdisplay(&result);\n \tstring_list_clear(&result, 0);\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"257388","messageId":"1425896314-10941-13-git-send-email-pclouds@gmail.com","threadId":"38762","inReplyTo":"1425896314-10941-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 12/16] list-files: add --ignored","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-09T10:18:30Z","receivedAt":"2015-03-09T10:18:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c | 20 ++++++++++++++------\n 1 file changed, 14 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 93364b9..fbacad9 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -18,6 +18,7 @@ static int show_dirs;\n static int use_color = -1;\n static int show_indicator;\n static int show_cached, show_untracked;\n+static int show_ignored;\n \n static const char * const ls_usage[] = {\n \tN_(\"git list-files [options] [<pathspec>...]\"),\n@@ -30,6 +31,8 @@ struct option ls_options[] = {\n \t\t N_(\"show cached files (default)\")),\n \tOPT_BOOL('o', \"others\", &show_untracked,\n \t\t N_(\"show untracked files\")),\n+\tOPT_BOOL('i', \"ignored\", &show_ignored,\n+\t\t N_(\"show ignored files\")),\n \n \tOPT_GROUP(N_(\"Other\")),\n \tOPT_COLUMN('C', \"column\", &colopts, N_(\"show files in columns\")),\n@@ -161,7 +164,8 @@ static void populate_cached_entries(struct string_list *result,\n }\n \n static void populate_untracked(struct string_list *result,\n-\t\t\t       const struct string_list *untracked)\n+\t\t\t       const struct string_list *untracked,\n+\t\t\t       const char *tag)\n {\n \tint i;\n \n@@ -177,7 +181,7 @@ static void populate_untracked(struct string_list *result,\n \t\tif (!matched(result, name, st.st_mode))\n \t\t\tcontinue;\n \n-\t\tadd_one(result, name, st.st_mode, \"??\");\n+\t\tadd_one(result, name, st.st_mode, tag);\n \t}\n }\n \n@@ -186,18 +190,22 @@ static void wt_status_populate(struct string_list *result,\n {\n \tstruct wt_status ws;\n \n-\tif (!show_untracked)\n+\tif (!show_untracked && !show_ignored)\n \t\treturn;\n \n \twt_status_prepare(&ws);\n \tcopy_pathspec(&ws.pathspec, &recursive_pathspec);\n+\tif (show_ignored)\n+\t\tws.show_ignored_files = 1;\n \tws.relative_paths = 0;\n \tws.use_color = 0;\n \tws.fp = NULL;\n \twt_status_collect(&ws);\n \n \tif (show_untracked)\n-\t\tpopulate_untracked(result, &ws.untracked);\n+\t\tpopulate_untracked(result, &ws.untracked, \"??\");\n+\tif (show_ignored)\n+\t\tpopulate_untracked(result, &ws.ignored, \"!!\");\n \n \tqsort(result->items, result->nr, sizeof(*result->items), compare_output);\n \tstring_list_remove_duplicates(result, 0);\n@@ -207,7 +215,7 @@ static void cleanup_tags(struct string_list *result)\n {\n \tint i, same_1 = 1, same_2 = 1, pos, len;\n \n-\tif (show_cached + show_untracked > 1)\n+\tif (show_cached + show_untracked + show_ignored > 1)\n \t\treturn;\n \n \tfor (i = 1; i < result->nr && (same_1 || same_2); i++) {\n@@ -287,7 +295,7 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \n \targc = parse_options(argc, argv, prefix, ls_options, ls_usage, 0);\n \n-\tif (!show_cached && !show_untracked)\n+\tif (!show_cached && !show_untracked && !show_ignored)\n \t\tshow_cached = 1;\n \n \tif (want_color(use_color))\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"257389","messageId":"1425896314-10941-14-git-send-email-pclouds@gmail.com","threadId":"38762","inReplyTo":"1425896314-10941-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 13/16] list-files: add --unmerged","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-09T10:18:31Z","receivedAt":"2015-03-09T10:18:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c | 47 +++++++++++++++++++++++++++++++++++++++++++----\n 1 file changed, 43 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex fbacad9..a82410d 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -17,7 +17,7 @@ static int max_depth;\n static int show_dirs;\n static int use_color = -1;\n static int show_indicator;\n-static int show_cached, show_untracked;\n+static int show_cached, show_untracked, show_unmerged;\n static int show_ignored;\n \n static const char * const ls_usage[] = {\n@@ -33,6 +33,8 @@ struct option ls_options[] = {\n \t\t N_(\"show untracked files\")),\n \tOPT_BOOL('i', \"ignored\", &show_ignored,\n \t\t N_(\"show ignored files\")),\n+\tOPT_BOOL('u', \"unmerged\", &show_unmerged,\n+\t\t N_(\"show unmerged files\")),\n \n \tOPT_GROUP(N_(\"Other\")),\n \tOPT_COLUMN('C', \"column\", &colopts, N_(\"show files in columns\")),\n@@ -185,12 +187,46 @@ static void populate_untracked(struct string_list *result,\n \t}\n }\n \n+static void populate_unmerged(struct string_list *result,\n+\t\t\t      const struct string_list *change)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < change->nr; i++) {\n+\t\tconst struct string_list_item *item = change->items + i;\n+\t\tstruct wt_status_change_data *d = item->util;\n+\t\tconst char *name = item->string;\n+\t\tconst char *tag;\n+\t\tstruct stat st;\n+\n+\t\tswitch (d->stagemask) {\n+\t\tcase 1: tag = \"DD\"; break; /* both deleted */\n+\t\tcase 2: tag = \"AU\"; break; /* added by us */\n+\t\tcase 3: tag = \"UD\"; break; /* deleted by them */\n+\t\tcase 4: tag = \"UA\"; break; /* added by them */\n+\t\tcase 5: tag = \"DU\"; break; /* deleted by us */\n+\t\tcase 6: tag = \"AA\"; break; /* both added */\n+\t\tcase 7: tag = \"UU\"; break; /* both modified */\n+\t\tdefault: continue;\n+\t\t}\n+\n+\t\tif (lstat(name, &st))\n+\t\t\t/* color_filename() treats this as an orphan file */\n+\t\t\tst.st_mode = 0;\n+\n+\t\tif (!matched(result, name, st.st_mode))\n+\t\t\tcontinue;\n+\n+\t\tadd_one(result, name, st.st_mode, tag);\n+\t}\n+}\n+\n static void wt_status_populate(struct string_list *result,\n \t\t\t       struct index_state *istate)\n {\n \tstruct wt_status ws;\n \n-\tif (!show_untracked && !show_ignored)\n+\tif (!show_untracked && !show_ignored && !show_unmerged)\n \t\treturn;\n \n \twt_status_prepare(&ws);\n@@ -206,6 +242,8 @@ static void wt_status_populate(struct string_list *result,\n \t\tpopulate_untracked(result, &ws.untracked, \"??\");\n \tif (show_ignored)\n \t\tpopulate_untracked(result, &ws.ignored, \"!!\");\n+\tif (show_unmerged)\n+\t\tpopulate_unmerged(result, &ws.change);\n \n \tqsort(result->items, result->nr, sizeof(*result->items), compare_output);\n \tstring_list_remove_duplicates(result, 0);\n@@ -215,7 +253,8 @@ static void cleanup_tags(struct string_list *result)\n {\n \tint i, same_1 = 1, same_2 = 1, pos, len;\n \n-\tif (show_cached + show_untracked + show_ignored > 1)\n+\tif (show_cached + show_untracked + show_ignored > 1 ||\n+\t    show_unmerged)\n \t\treturn;\n \n \tfor (i = 1; i < result->nr && (same_1 || same_2); i++) {\n@@ -295,7 +334,7 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \n \targc = parse_options(argc, argv, prefix, ls_options, ls_usage, 0);\n \n-\tif (!show_cached && !show_untracked && !show_ignored)\n+\tif (!show_cached && !show_untracked && !show_ignored && !show_unmerged)\n \t\tshow_cached = 1;\n \n \tif (want_color(use_color))\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"257390","messageId":"1425896314-10941-15-git-send-email-pclouds@gmail.com","threadId":"38762","inReplyTo":"1425896314-10941-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 14/16] list-files: add file modification options -[admADM]","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-09T10:18:32Z","receivedAt":"2015-03-09T10:18:32Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++----\n 1 file changed, 66 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex a82410d..74836f6 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -17,8 +17,10 @@ static int max_depth;\n static int show_dirs;\n static int use_color = -1;\n static int show_indicator;\n-static int show_cached, show_untracked, show_unmerged;\n+static int show_cached, show_untracked, show_unmerged, show_changed;\n static int show_ignored;\n+static int show_added, show_deleted, show_modified;\n+static int show_wt_added, show_wt_deleted, show_wt_modified;\n \n static const char * const ls_usage[] = {\n \tN_(\"git list-files [options] [<pathspec>...]\"),\n@@ -35,6 +37,18 @@ struct option ls_options[] = {\n \t\t N_(\"show ignored files\")),\n \tOPT_BOOL('u', \"unmerged\", &show_unmerged,\n \t\t N_(\"show unmerged files\")),\n+\tOPT_BOOL('a', \"added\", &show_added,\n+\t\t N_(\"show added files compared to HEAD\")),\n+\tOPT_BOOL('d', \"deleted\", &show_deleted,\n+\t\t N_(\"show deleted files compared to HEAD\")),\n+\tOPT_BOOL('m', \"modified\", &show_modified,\n+\t\t N_(\"show modified files compared to HEAD\")),\n+\tOPT_BOOL('A', \"wt-added\", &show_wt_added,\n+\t\t N_(\"show added files in worktree\")),\n+\tOPT_BOOL('D', \"wt-deleted\", &show_wt_deleted,\n+\t\t N_(\"show deleted files in worktree\")),\n+\tOPT_BOOL('M', \"wt-modified\", &show_wt_modified,\n+\t\t N_(\"show modified files on worktree\")),\n \n \tOPT_GROUP(N_(\"Other\")),\n \tOPT_COLUMN('C', \"column\", &colopts, N_(\"show files in columns\")),\n@@ -221,12 +235,52 @@ static void populate_unmerged(struct string_list *result,\n \t}\n }\n \n+static void populate_changed(struct string_list *result,\n+\t\t\t     const struct string_list *change)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < change->nr; i++) {\n+\t\tconst struct string_list_item *item = change->items + i;\n+\t\tstruct wt_status_change_data *d = item->util;\n+\t\tconst char *name = item->string;\n+\t\tstruct stat st;\n+\t\tchar tag[2];\n+\n+\t\tswitch (d->stagemask)\n+\t\t\tcontinue;\n+\n+\t\ttag[0] = d->index_status ? d->index_status : ' ';\n+\t\ttag[1] = d->worktree_status ? d->worktree_status : ' ';\n+\n+\t\tif ((show_added       && tag[0] == 'A') ||\n+\t\t    (show_deleted     && tag[0] == 'D') ||\n+\t\t    (show_modified    && tag[0] != ' ') ||\n+\t\t    (show_wt_added    && tag[1] == 'A') ||\n+\t\t    (show_wt_deleted  && tag[1] == 'D') ||\n+\t\t    (show_wt_modified && tag[1] != ' '))\n+\t\t\t;\t/* keep going */\n+\t\telse\n+\t\t\tcontinue;\n+\n+\t\tif (lstat(name, &st))\n+\t\t\t/* color_filename() treats this as an orphan file */\n+\t\t\tst.st_mode = 0;\n+\n+\t\tif (!matched(result, name, st.st_mode))\n+\t\t\tcontinue;\n+\n+\t\tadd_one(result, name, st.st_mode, tag);\n+\t}\n+}\n+\n static void wt_status_populate(struct string_list *result,\n \t\t\t       struct index_state *istate)\n {\n \tstruct wt_status ws;\n \n-\tif (!show_untracked && !show_ignored && !show_unmerged)\n+\tif (!show_untracked && !show_ignored &&\n+\t    !show_unmerged && !show_changed)\n \t\treturn;\n \n \twt_status_prepare(&ws);\n@@ -244,6 +298,8 @@ static void wt_status_populate(struct string_list *result,\n \t\tpopulate_untracked(result, &ws.ignored, \"!!\");\n \tif (show_unmerged)\n \t\tpopulate_unmerged(result, &ws.change);\n+\tif (show_changed)\n+\t\tpopulate_changed(result, &ws.change);\n \n \tqsort(result->items, result->nr, sizeof(*result->items), compare_output);\n \tstring_list_remove_duplicates(result, 0);\n@@ -253,8 +309,9 @@ static void cleanup_tags(struct string_list *result)\n {\n \tint i, same_1 = 1, same_2 = 1, pos, len;\n \n-\tif (show_cached + show_untracked + show_ignored > 1 ||\n-\t    show_unmerged)\n+\tif (show_cached + show_untracked + show_ignored +\n+\t    show_added + show_deleted + show_wt_added + show_wt_deleted > 1 ||\n+\t    show_unmerged || show_modified || show_wt_modified)\n \t\treturn;\n \n \tfor (i = 1; i < result->nr && (same_1 || same_2); i++) {\n@@ -334,7 +391,11 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \n \targc = parse_options(argc, argv, prefix, ls_options, ls_usage, 0);\n \n-\tif (!show_cached && !show_untracked && !show_ignored && !show_unmerged)\n+\tshow_changed =\n+\t\tshow_added || show_deleted || show_modified ||\n+\t\tshow_wt_added || show_wt_deleted || show_wt_modified;\n+\tif (!show_cached && !show_untracked && !show_ignored &&\n+\t    !show_unmerged && !show_changed)\n \t\tshow_cached = 1;\n \n \tif (want_color(use_color))\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"257391","messageId":"1425896314-10941-16-git-send-email-pclouds@gmail.com","threadId":"38762","inReplyTo":"1425896314-10941-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 15/16] list-files: delete redundant cached entries","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-09T10:18:33Z","receivedAt":"2015-03-09T10:18:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"When both --cached and one of -amdAMD is used together we may have two\nentries of the same path, e.g. \"  foo\" and \"MM foo\". In this case it's\npretty clear that \"foo\" must be tracked, no need to display \"   foo\".\nThe new function does that.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/list-files.c | 34 +++++++++++++++++++++++++++++++++-\n 1 file changed, 33 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/list-files.c b/builtin/list-files.c\nindex 74836f6..49fb820 100644\n--- a/builtin/list-files.c\n+++ b/builtin/list-files.c\n@@ -153,7 +153,10 @@ 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+\tint ret = strcmp(a->util, b->util);\n+\tif (ret)\n+\t\treturn ret;\n+\treturn strncmp(a->string, b->string, 2);\n }\n \n static void populate_cached_entries(struct string_list *result,\n@@ -305,6 +308,34 @@ static void wt_status_populate(struct string_list *result,\n \tstring_list_remove_duplicates(result, 0);\n }\n \n+static void delete_duplicate_cached_entries(struct string_list *result)\n+{\n+\tstruct string_list_item *src, *dst;\n+\n+\tif (show_unmerged || !show_cached || !show_changed)\n+\t\treturn;\n+\n+\tsrc = dst = result->items;\n+\twhile (src + 1 < result->items + result->nr) {\n+\t\tconst char *s0 = dst->string;\n+\t\tconst char *s1 = src[1].string;\n+\n+\t\tif (s0[0] == ' ' && s0[1] == ' ' &&\n+\t\t    !strcmp(s0 + 3, s1 + 3)) {\n+\t\t\tsrc++;\n+\t\t} else {\n+\t\t\tdst++;\n+\t\t\tsrc++;\n+\t\t}\n+\t\tif (src != dst)\n+\t\t\t*dst = *src;\n+\t}\n+\tif (src != dst)\n+\t\t*dst = *src;\n+\tresult->nr = dst - result->items;\n+\n+}\n+\n static void cleanup_tags(struct string_list *result)\n {\n \tint i, same_1 = 1, same_2 = 1, pos, len;\n@@ -418,6 +449,7 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n \n \tpopulate_cached_entries(&result, &the_index);\n \twt_status_populate(&result, &the_index);\n+\tdelete_duplicate_cached_entries(&result);\n \tcleanup_tags(&result);\n \tdisplay(&result);\n \tstring_list_clear(&result, 0);\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"257392","messageId":"1425896314-10941-17-git-send-email-pclouds@gmail.com","threadId":"38762","inReplyTo":"1425896314-10941-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 16/16] list-files: make alias 'ls' default to 'list-files'","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-09T10:18:34Z","receivedAt":"2015-03-09T10:18:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n config.c | 8 ++++++++\n 1 file changed, 8 insertions(+)\n\ndiff --git a/config.c b/config.c\nindex 15a2983..16209c6 100644\n--- a/config.c\n+++ b/config.c\n@@ -40,6 +40,10 @@ static struct config_source *cf;\n \n static int zlib_compression_seen;\n \n+static const char *builtin_config =\n+\t\"[alias]\\n\"\n+\t\"\tls = list-files\\n\";\n+\n /*\n  * Default config_set that contains key-value pairs from the usual set of config\n  * config files (i.e repo specific .git/config, user wide ~/.gitconfig, XDG\n@@ -1175,6 +1179,10 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n \n \thome_config_paths(&user_config, &xdg_config, \"config\");\n \n+\tif (git_config_system())\n+\t\tgit_config_from_buf(fn, \"<builtin>\", builtin_config,\n+\t\t\t\t    strlen(builtin_config), data);\n+\n \tif (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) {\n \t\tret += git_config_from_file(fn, git_etc_gitconfig(),\n \t\t\t\t\t    data);\n-- \n2.3.0.rc1.137.g477eb31\n"},{"id":"257444","messageId":"CAPig+cSCTJH-otgWAQxwjwkw0SbP4OFXyDhUZs=0bzoL7B32ig@mail.gmail.com","threadId":"38762","inReplyTo":"1425896314-10941-8-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 07/16] list-files: show directories as well as files","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-03-10T06:23:48Z","receivedAt":"2015-03-10T06:23:48Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Monday, March 9, 2015, 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\ns/lis/is/\n\n> The function add_directory() can generate duplicate entries, which is\n> cleaned up before displaying.\n> ---\n> diff --git a/builtin/list-files.c b/builtin/list-files.c\n> index 9a55ea6..dc865a1 100644\n> --- a/builtin/list-files.c\n> +++ b/builtin/list-files.c\n> @@ -43,6 +45,51 @@ static void add_one(struct string_list *result, const char *name,\n>         item->util = (char *)name;\n>  }\n>\n> +static int add_directory(struct string_list *result,\n> +                        const char *name)\n> +{\n> +       struct strbuf sb = STRBUF_INIT;\n> +       const char *p;\n> +\n> +       strbuf_add(&sb, name, strlen(name));\n\nstrbuf_addstr() perhaps?\n\n> +       while (sb.len && (p = strrchr(sb.buf, '/')) != NULL) {\n> +               strbuf_setlen(&sb, p - sb.buf);\n> +               if (!match_pathspec(&pathspec, sb.buf, sb.len, 0, NULL, 1))\n> +                       continue;\n> +               add_one(result, sb.buf, \"  \");\n> +               /*\n> +                * sb.buf is leaked, but because this command is\n> +                * short-lived anyway so it does not matter much\n> +                */\n> +               return 1;\n> +       }\n> +       strbuf_release(&sb);\n> +       return 0;\n> +}\n"},{"id":"257445","messageId":"CAPig+cS6F6aD_3fLfmBHx+4CxRRrAyLDM3C2HRE55a3S9mJs=A@mail.gmail.com","threadId":"38762","inReplyTo":"1425896314-10941-16-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 15/16] list-files: delete redundant cached entries","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-03-10T06:28:54Z","receivedAt":"2015-03-10T06:28:54Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Monday, March 9, 2015, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> When both --cached and one of -amdAMD is used together we may have two\n> entries of the same path, e.g. \"  foo\" and \"MM foo\". In this case it's\n> pretty clear that \"foo\" must be tracked, no need to display \"   foo\".\n> The new function does that.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/builtin/list-files.c b/builtin/list-files.c\n> index 74836f6..49fb820 100644\n> --- a/builtin/list-files.c\n> +++ b/builtin/list-files.c\n> @@ -305,6 +308,34 @@ static void wt_status_populate(struct string_list *result,\n>         string_list_remove_duplicates(result, 0);\n>  }\n>\n> +static void delete_duplicate_cached_entries(struct string_list *result)\n> +{\n> +       struct string_list_item *src, *dst;\n> +\n> +       if (show_unmerged || !show_cached || !show_changed)\n> +               return;\n> +\n> +       src = dst = result->items;\n> +       while (src + 1 < result->items + result->nr) {\n> +               const char *s0 = dst->string;\n> +               const char *s1 = src[1].string;\n> +\n> +               if (s0[0] == ' ' && s0[1] == ' ' &&\n> +                   !strcmp(s0 + 3, s1 + 3)) {\n> +                       src++;\n> +               } else {\n> +                       dst++;\n> +                       src++;\n> +               }\n> +               if (src != dst)\n> +                       *dst = *src;\n> +       }\n> +       if (src != dst)\n> +               *dst = *src;\n\nDo you want to take some action here (and just above) to ensure that\nthe item being overwritten gets deleted properly rather than leaked?\n\n> +       result->nr = dst - result->items;\n> +\n> +}\n> +\n"},{"id":"257446","messageId":"CACsJy8CU=gmaLL1Nvsai-gtH=Qz876ciiX7LTFgV2-zBRRz1ww@mail.gmail.com","threadId":"38762","inReplyTo":"CAPig+cSCTJH-otgWAQxwjwkw0SbP4OFXyDhUZs=0bzoL7B32ig@mail.gmail.com","subject":"Re: [PATCH 07/16] list-files: show directories as well as files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-10T06:39:58Z","receivedAt":"2015-03-10T06:39:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Mar 10, 2015 at 1:23 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n>> +static int add_directory(struct string_list *result,\n>> +                        const char *name)\n>> +{\n>> +       struct strbuf sb = STRBUF_INIT;\n>> +       const char *p;\n>> +\n>> +       strbuf_add(&sb, name, strlen(name));\n>\n> strbuf_addstr() perhaps?\n\nYeah. The previous version of this function takes string length as an\nargument, but then I dropped that and used strlen() instead. Thanks\nfor catching.\n-- \nDuy\n"},{"id":"257600","messageId":"xmqq1tku2akz.fsf@gitster.dls.corp.google.com","threadId":"38762","inReplyTo":"1425896314-10941-2-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 01/16] list-files: command skeleton","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-03-12T21:02:36Z","receivedAt":"2015-03-12T21:02:36Z","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> list-files is supposed to be the user friendly version of ls-files, or\n> an alternative to git-status. Nothing fancy in this patch yet.\n\nThe result of applying this patch alone will not give us anything\nfancy, but the patch itself is interesting ;-)\n\n> +static void populate_cached_entries(struct string_list *result,\n> +\t\t\t\t    const struct index_state *istate)\n> +{\n> +\tint i;\n> +\n> +\tfor (i = 0; i < istate->cache_nr; i++) {\n> +\t\tconst struct cache_entry *ce = istate->cache[i];\n> +\n> +\t\tif (!match_pathspec(&pathspec, ce->name, ce_namelen(ce),\n> +\t\t\t\t    0, NULL,\n> +\t\t\t\t    S_ISDIR(ce->ce_mode) ||\n> +\t\t\t\t    S_ISGITLINK(ce->ce_mode)))\n\nBecause we won't tell the user \"You gave me Mkaefile but that did\nnot match\" when \"git list-files Mkaefile\" does not produce anything,\nwe do not need to pass seen[] down from here.\n\n> +\tprefix = cmd_prefix;\n> +\tif (prefix)\n> +\t\tprefix_length = strlen(prefix);\n> +\n> +\tif (read_cache() < 0)\n> +\t\tdie(_(\"index file corrupt\"));\n> +\n> +\tgit_config(ls_config, NULL);\n> +\n> +\targc = parse_options(argc, argv, prefix, ls_options, ls_usage, 0);\n> +\n> +\tparse_pathspec(&pathspec, 0,\n> +\t\t       PATHSPEC_PREFER_CWD |\n> +\t\t       PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,\n> +\t\t       cmd_prefix, argv);\n> +\tpathspec.max_depth = 0;\n> +\tpathspec.recursive = 1;\n> +\n> +\trefresh_index(&the_index, REFRESH_QUIET | REFRESH_UNMERGED,\n> +\t\t      &pathspec, NULL, NULL);\n\nIt would be better to do read-cache-preload, instead of read-cache,\nif you are going to immediately refresh.  That is what \"git status\"\ndoes.\n\n> +\tpopulate_cached_entries(&result, &the_index);\n> +\tdisplay(&result);\n> +\tstring_list_clear(&result, 0);\n> +\treturn 0;\n> +}\n> diff --git a/git.c b/git.c\n> index 18fbf79..ae7fe77 100644\n> --- a/git.c\n> +++ b/git.c\n> @@ -418,6 +418,7 @@ static struct cmd_struct commands[] = {\n>  \t{ \"init\", cmd_init_db, NO_SETUP },\n>  \t{ \"init-db\", cmd_init_db, NO_SETUP },\n>  \t{ \"interpret-trailers\", cmd_interpret_trailers, RUN_SETUP },\n> +\t{ \"list-files\", cmd_list_files, RUN_SETUP | USE_PAGER | NEED_WORK_TREE },\n\nThanks.\n"},{"id":"257601","messageId":"xmqqwq2l2a70.fsf@gitster.dls.corp.google.com","threadId":"38762","inReplyTo":"1425896314-10941-3-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 02/16] list-files: make :(glob) pathspec default","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-03-12T21:10:59Z","receivedAt":"2015-03-12T21:10:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  builtin/list-files.c | 2 ++\n>  1 file changed, 2 insertions(+)\n>\n> diff --git a/builtin/list-files.c b/builtin/list-files.c\n> index ac33f13..b99f2b7 100644\n> --- a/builtin/list-files.c\n> +++ b/builtin/list-files.c\n> @@ -65,6 +65,8 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n>  {\n>  \tstruct string_list result = STRING_LIST_INIT_NODUP;\n>  \n> +\tsetenv(GIT_GLOB_PATHSPECS_ENVIRONMENT, \"1\", 0);\n> +\n>  \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n>  \t\tusage_with_options(ls_usage, ls_options);\n\nYikes.\n\nI do not have enough info at this step in the series to judge if it\nis sensible to force the :(glob) interpretation as default, but is\nit something we would want to do commonly to flip the default per\nGit subcommand?  If so, using the environment feels like a clunky\nway to do that.\n\nHow about a two-patch clean-up before this step?\n\n (1) remove the handling of literal_global and friends that peek\n     into various environment variables from prefix_pathspec(),\n     which is a function that is repeatedly called for each pathspec\n     element given from the command line, and move that logic to\n     parse_pathspec(); pass necessary information down to\n     prefix_pathspec() as parameter(s);\n\n (2) allow parse_pathspec() so that the caller can say \"the default,\n     when there is no environment variable given by the end user to\n     tell us otherwise, is to :(glob)\".\n"},{"id":"257602","messageId":"xmqqsid929rb.fsf@gitster.dls.corp.google.com","threadId":"38762","inReplyTo":"1425896314-10941-4-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 03/16] list-files: show paths relative to cwd","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-03-12T21:20:24Z","receivedAt":"2015-03-12T21:20:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  builtin/list-files.c | 3 ++-\n>  1 file changed, 2 insertions(+), 1 deletion(-)\n>\n> diff --git a/builtin/list-files.c b/builtin/list-files.c\n> index b99f2b7..c444a53 100644\n> --- a/builtin/list-files.c\n> +++ b/builtin/list-files.c\n> @@ -3,6 +3,7 @@\n>  #include \"parse-options.h\"\n>  #include \"pathspec.h\"\n>  #include \"dir.h\"\n> +#include \"quote.h\"\n>  \n>  static struct pathspec pathspec;\n>  static const char *prefix;\n> @@ -22,7 +23,7 @@ static void add_one(struct string_list *result, const char *name)\n>  \tstruct strbuf sb = STRBUF_INIT;\n>  \tstruct string_list_item *item;\n>  \n> -\tstrbuf_addstr(&sb, name);\n> +\tquote_path_relative(name, prefix_length ? prefix : NULL, &sb);\n>  \titem = string_list_append(result, strbuf_detach(&sb, NULL));\n>  \titem->util = (char *)name;\n>  }\n\nHmph, wouldn't it make it more cumbersome and problematic to do\nthings like this here in add_one() phase?  I am imagining that the\nendgame of this program will be\n\n    - populate_cached_entries() reads from the data source (at this\n      step, there is just \"the index\"), calling add_one() whose\n      responsibility is to record the paths that are interesting\n      to an in-core structure;\n\n    - perform some interesting filtering, annotating, ordering,\n      etc. (at this step, there is none) yet to come;\n\n    - display() will iterate over the result and then format the\n      result.\n\nFor example, if you do the \"quote\" thing too early, don't codepaths\nin the later phases have to worry about item->string not matching\nthe original pathname anymore?  If you want to do something like\n\"/bin/ls -t\", you may have to lstat() the paths for each item, but\nif these store a path relative to the prefix, wouldn't you have to\nprepend the prefix again before running lstat()?\n\nI am just wondering if this prefix-stripping and quoting belongs to\nthe output phase, not the input phase.\n"},{"id":"257603","messageId":"xmqqoanx29ei.fsf@gitster.dls.corp.google.com","threadId":"38762","inReplyTo":"xmqqsid929rb.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 03/16] list-files: show paths relative to cwd","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-03-12T21:28:05Z","receivedAt":"2015-03-12T21:28:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>>  static struct pathspec pathspec;\n>>  static const char *prefix;\n>> @@ -22,7 +23,7 @@ static void add_one(struct string_list *result, const char *name)\n>>  \tstruct strbuf sb = STRBUF_INIT;\n>>  \tstruct string_list_item *item;\n>>  \n>> -\tstrbuf_addstr(&sb, name);\n>> +\tquote_path_relative(name, prefix_length ? prefix : NULL, &sb);\n>>  \titem = string_list_append(result, strbuf_detach(&sb, NULL));\n>>  \titem->util = (char *)name;\n>>  }\n>\n> Hmph, wouldn't it make it more cumbersome and problematic to do\n> things like this here in add_one() phase?  I am imagining that the\n> endgame of this program will be\n>\n>     - populate_cached_entries() reads from the data source (at this\n>       step, there is just \"the index\"), calling add_one() whose\n>       responsibility is to record the paths that are interesting\n>       to an in-core structure;\n>\n>     - perform some interesting filtering, annotating, ordering,\n>       etc. (at this step, there is none) yet to come;\n>\n>     - display() will iterate over the result and then format the\n>       result.\n>\n> For example, if you do the \"quote\" thing too early, don't codepaths\n> in the later phases have to worry about item->string not matching\n> the original pathname anymore?  If you want to do something like\n> \"/bin/ls -t\", you may have to lstat() the paths for each item, but\n> if these store a path relative to the prefix, wouldn't you have to\n> prepend the prefix again before running lstat()?\n>\n> I am just wondering if this prefix-stripping and quoting belongs to\n> the output phase, not the input phase.\n\nHmph, another interpretation of this patch is that your item->string\nare not the true filenames but the result of applying some\ninteresting processing to the filenames and the true filenames are\nkept in item->util.  Is that what is going on?\n\nIf that is the case, it sort of makes sense to me, even though it\nwould feel a bit unusual way to use the string-list.\n\nThanks.\n"},{"id":"257604","messageId":"xmqqk2yl28fw.fsf@gitster.dls.corp.google.com","threadId":"38762","inReplyTo":"1425896314-10941-5-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 04/16] list-files: add tag to each entry, filter duplicate tags","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-03-12T21:48:51Z","receivedAt":"2015-03-12T21:48:51Z","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> All entry strings start with two-letter tag and a space. If all\n> entries have the same tags, tags are not displayed.\n>\n> The outcome before and after this patch is the same. But it will be\n> useful in future when there are more than one type of entry.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  builtin/list-files.c | 40 ++++++++++++++++++++++++++++++++++++++--\n>  1 file changed, 38 insertions(+), 2 deletions(-)\n>\n> diff --git a/builtin/list-files.c b/builtin/list-files.c\n> index c444a53..18af65c 100644\n> --- a/builtin/list-files.c\n> +++ b/builtin/list-files.c\n> @@ -18,12 +18,16 @@ struct option ls_options[] = {\n>  \tOPT_END()\n>  };\n>  \n> -static void add_one(struct string_list *result, const char *name)\n> +static void add_one(struct string_list *result, const char *name,\n> +\t\t    const char *tag)\n>  {\n>  \tstruct strbuf sb = STRBUF_INIT;\n>  \tstruct string_list_item *item;\n>  \n>  \tquote_path_relative(name, prefix_length ? prefix : NULL, &sb);\n> +\tstrbuf_insert(&sb, 0, \"   \", 3);\n> +\tsb.buf[0] = tag[0];\n> +\tsb.buf[1] = tag[1];\n>  \titem = string_list_append(result, strbuf_detach(&sb, NULL));\n>  \titem->util = (char *)name;\n>  }\n> @@ -42,7 +46,38 @@ static void populate_cached_entries(struct string_list *result,\n>  \t\t\t\t    S_ISGITLINK(ce->ce_mode)))\n>  \t\t\tcontinue;\n>  \n> -\t\tadd_one(result, ce->name);\n> +\t\tadd_one(result, ce->name, \"  \");\n> +\t}\n> +}\n> +\n> +static void cleanup_tags(struct string_list *result)\n> +{\n> +\tint i, same_1 = 1, same_2 = 1, pos, len;\n> +\n> +\tfor (i = 1; i < result->nr && (same_1 || same_2); i++) {\n> +\t\tconst char *s0 = result->items[i - 1].string;\n> +\t\tconst char *s1 = result->items[i].string;\n> +\n> +\t\tsame_1 = same_1 && s0[0] == s1[0];\n> +\t\tsame_2 = same_2 && s0[1] == s1[1];\n> +\t}\n> +\n> +\tif (same_1 && same_2) {\n> +\t\tpos = 0;\n> +\t\tlen = 3;\n> +\t} else if (same_1) {\n> +\t\tpos = 0;\n> +\t\tlen = 1;\n> +\t} else if (same_2) {\n> +\t\tpos = 1;\n> +\t\tlen = 1;\n> +\t} else\n> +\t\treturn;\n> +\n> +\tfor (i = 0; i < result->nr; i++) {\n> +\t\tchar *s = result->items[i].string;\n> +\t\tint length = strlen(s);\n> +\t\tmemmove(s + pos, s + pos + len, length - len + 1);\n>  \t}\n>  }\n\nHmm, I wonder if a different implementation strategy would produce a\ncode that is better for longer term maintenance and readability.\nFor example, how much pain would be involved if we later find that\nwe would want three \"tag\" letters per entry and wanted to add\nsupport for that third tag by modifying this code?\n\nInstead of half-formatted result in item->string and then inspect\nand update the already formatted string at the textual level, why\nnot invert the keys and values of the table and arrange things this\nway instead:\n\n * the \"table\" is expressed as a string-list, as this series does;\n\n * the keys to the table, item->string, is the original pathname;\n\n * the values in the table, item->util, is a pointer to a structure\n   that allows implementation of list-files a more meaningful access\n   to the information (as opposed to \"the first column of formatted\n   text output means X, the second column means Y\"), perhaps like\n\n    struct list_item {\n\tenum {\n            LS_IS_FILE, LS_IS_DIRECTORY, LS_IS_SYMLINK,\tLS_IS_SUBMODULE\n\t} kind;\n\tunsigned changed_from_index:1,\n                 changed_from_HEAD:1;\n\tstruct cache_time mtime;\n    };\n\n * Internal processing is done to the value found in item->util, and\n   the textual output is created by formatting what is in the\n   *((struct list_item *)item->util) at the output phase.\n\nA hypothetical \"we need more tags\" case would then involve adding a\nnew field (could be a bitfield \"breaks_build:1\") to the list_item\nstructure with a reasonable default, keeping the existing codepath\nthat does not care about the new field intact and updating only the\noutput phase, which would be a lot less painful, no?\n\n> @@ -93,6 +128,7 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n>  \t\t      &pathspec, NULL, NULL);\n>  \n>  \tpopulate_cached_entries(&result, &the_index);\n> +\tcleanup_tags(&result);\n>  \tdisplay(&result);\n>  \tstring_list_clear(&result, 0);\n>  \treturn 0;\n"},{"id":"257672","messageId":"CACsJy8DWe2rCzQivA7HZAkQ==s7OyxpP=UkvsxRzvZ4PtFRyJg@mail.gmail.com","threadId":"38762","inReplyTo":"xmqqwq2l2a70.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 02/16] list-files: make :(glob) pathspec default","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-14T11:21:10Z","receivedAt":"2015-03-14T11:21:10Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Mar 13, 2015 at 4:10 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>>  builtin/list-files.c | 2 ++\n>>  1 file changed, 2 insertions(+)\n>>\n>> diff --git a/builtin/list-files.c b/builtin/list-files.c\n>> index ac33f13..b99f2b7 100644\n>> --- a/builtin/list-files.c\n>> +++ b/builtin/list-files.c\n>> @@ -65,6 +65,8 @@ int cmd_list_files(int argc, const char **argv, const char *cmd_prefix)\n>>  {\n>>       struct string_list result = STRING_LIST_INIT_NODUP;\n>>\n>> +     setenv(GIT_GLOB_PATHSPECS_ENVIRONMENT, \"1\", 0);\n>> +\n>>       if (argc == 2 && !strcmp(argv[1], \"-h\"))\n>>               usage_with_options(ls_usage, ls_options);\n>\n> Yikes.\n>\n> I do not have enough info at this step in the series to judge if it\n> is sensible to force the :(glob) interpretation as default, but is\n> it something we would want to do commonly to flip the default per\n> Git subcommand?\n\nAs this new 'ls' imitates the unix version, I try to hide this subtle\ndifference of git. Perhaps this is a premature move because we may\nwant to make :(glob) default for most commands at 3.0 or something,\nexcept plumbing commands.\n\n> If so, using the environment feels like a clunkyway to do that.\n>\n> How about a two-patch clean-up before this step?\n>\n>  (1) remove the handling of literal_global and friends that peek\n>      into various environment variables from prefix_pathspec(),\n>      which is a function that is repeatedly called for each pathspec\n>      element given from the command line, and move that logic to\n>      parse_pathspec(); pass necessary information down to\n>      prefix_pathspec() as parameter(s);\n>\n>  (2) allow parse_pathspec() so that the caller can say \"the default,\n>      when there is no environment variable given by the end user to\n>      tell us otherwise, is to :(glob)\".\n>\n\nSounds like a good thing to do anyway. Will do.\n-- \nDuy\n"},{"id":"257674","messageId":"CACsJy8CjcCuDC8X9E5iko2b8xykBAwMY1JupYx1dzpnf5p5ghg@mail.gmail.com","threadId":"38762","inReplyTo":"xmqqoanx29ei.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 03/16] list-files: show paths relative to cwd","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-14T11:25:41Z","receivedAt":"2015-03-14T11:25:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Mar 13, 2015 at 4:28 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>>>  static struct pathspec pathspec;\n>>>  static const char *prefix;\n>>> @@ -22,7 +23,7 @@ static void add_one(struct string_list *result, const char *name)\n>>>      struct strbuf sb = STRBUF_INIT;\n>>>      struct string_list_item *item;\n>>>\n>>> -    strbuf_addstr(&sb, name);\n>>> +    quote_path_relative(name, prefix_length ? prefix : NULL, &sb);\n>>>      item = string_list_append(result, strbuf_detach(&sb, NULL));\n>>>      item->util = (char *)name;\n>>>  }\n>>\n>> Hmph, wouldn't it make it more cumbersome and problematic to do\n>> things like this here in add_one() phase?  I am imagining that the\n>> endgame of this program will be\n>>\n>>     - populate_cached_entries() reads from the data source (at this\n>>       step, there is just \"the index\"), calling add_one() whose\n>>       responsibility is to record the paths that are interesting\n>>       to an in-core structure;\n>>\n>>     - perform some interesting filtering, annotating, ordering,\n>>       etc. (at this step, there is none) yet to come;\n>>\n>>     - display() will iterate over the result and then format the\n>>       result.\n>>\n>> For example, if you do the \"quote\" thing too early, don't codepaths\n>> in the later phases have to worry about item->string not matching\n>> the original pathname anymore?  If you want to do something like\n>> \"/bin/ls -t\", you may have to lstat() the paths for each item, but\n>> if these store a path relative to the prefix, wouldn't you have to\n>> prepend the prefix again before running lstat()?\n>>\n>> I am just wondering if this prefix-stripping and quoting belongs to\n>> the output phase, not the input phase.\n>\n> Hmph, another interpretation of this patch is that your item->string\n> are not the true filenames but the result of applying some\n> interesting processing to the filenames and the true filenames are\n> kept in item->util.  Is that what is going on?\n\nExactly. We would need to sort and stuff later on, so true filenames\nare preserved in util->item. A cleaner way is perhaps carry all\nmetadata in item->util and item->string remains true filename, then do\nall the formatting, coloring for all strings just before displaying.\nIt seems a lot varying data to carry around.\n\n> If that is the case, it sort of makes sense to me, even though it\n> would feel a bit unusual way to use the string-list.\n>\n> Thanks.\n\n\n\n-- \nDuy\n"},{"id":"257750","messageId":"xmqqh9tmt0zt.fsf@gitster.dls.corp.google.com","threadId":"38762","inReplyTo":"CACsJy8CjcCuDC8X9E5iko2b8xykBAwMY1JupYx1dzpnf5p5ghg@mail.gmail.com","subject":"Re: [PATCH 03/16] list-files: show paths relative to cwd","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-03-15T21:16:38Z","receivedAt":"2015-03-15T21:16:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> Exactly. We would need to sort and stuff later on, so true filenames\n> are preserved in util->item. A cleaner way is perhaps carry all\n> metadata in item->util and item->string remains true filename, then do\n> all the formatting, coloring for all strings just before displaying.\n\nI guess we are then in agreement with my review comment on [04/16].\n\nThanks.\n"},{"id":"257759","messageId":"CACsJy8CX0WJryLV5C6=0pV=Ssgp3uxpF0pYX5Q1eKHOedAFOzg@mail.gmail.com","threadId":"38762","inReplyTo":"xmqqh9tmt0zt.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 03/16] list-files: show paths relative to cwd","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2015-03-15T23:41:08Z","receivedAt":"2015-03-15T23:41:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Mar 16, 2015 at 4:16 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Duy Nguyen <pclouds@gmail.com> writes:\n>\n>> Exactly. We would need to sort and stuff later on, so true filenames\n>> are preserved in util->item. A cleaner way is perhaps carry all\n>> metadata in item->util and item->string remains true filename, then do\n>> all the formatting, coloring for all strings just before displaying.\n>\n> I guess we are then in agreement with my review comment on [04/16].\n\nYes. I'm redoing it. I will probably stop using string-list as well\n(it's inherited from the old code), just an array of struct.\n-- \nDuy\n"}]}