{"thread":{"id":"46918","subject":"[PATCH 0/6] Teach Status options around showing ignored files","startedAt":"2017-10-05T20:55:07Z","lastAt":"2017-10-31T03:16:09Z","messageCount":49,"participants":["jameson.miller81@gmail.com","Jonathan Nieder","Jameson Miller","Stefan Beller","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":6},"messages":[{"id":"329802","messageId":"20171005205443.206900-1-jameson.miller81@gmail.com","threadId":"46918","inReplyTo":null,"subject":"[PATCH 0/6] Teach Status options around showing ignored files","fromName":"","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-05T20:54:37Z","receivedAt":"2017-10-05T20:55:07Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"From: Jameson Miller <jamill@microsoft.com>\n\nThis patch series is the second part of [1], which was split into 2\nparts. The first part, added an optimization in the directory listing\nlogic to not scan the contents of ignored directories and was merged\nto master with commit 5aaa7fd3. This patch series includes the second\npart to expose additional arguments to the --ignored option on the\nstatus command.\n\nThis patch series teaches the status command more options to control\nwhich ignored files are reported independently of the which untracked\nfiles are reported by allowing the `--ignored` option to take\nadditional arguments.  Currently, the shown ignored files are linked\nto how untracked files are reported. Both ignored and untracked files\nare controlled by arguments to `--untracked-files` option. This makes\nit impossible to show all untracked files individually, but show\nignored \"files and directories\" (that is, ignored directories are\nshown as 1 entry, instead of having all contents shown).\n    \nOur application (Visual Studio) has a specific set of requirements\nabout how it wants untracked / ignored files reported by git status.\nIt requires all untracked files listed individually. It would like\nignored files and directories that explicity match an exclude pattern\nlisted. If an ignored directory matches an exclude pattern, then the\npath of the directory should be returned. If a directory does not\nmatch an exclude pattern, but all of its contents are ignored, then we\nwant the contained files reported instead of the directory.\n    \nThe reason for controlling these behaviors separately is that there\ncan be a significant performance impact to scanning the contents of\nexcluded directories. Additionally, knowing whether a directory\nexplicitly matches an exclude pattern can help the application make\ndecisions about how to handle the directory. If an ignored directory\nitself matches an exclude pattern, then the application will know that\nany files underneath the directory must be ignored as well.\n    \nAs a more concrete example, on Windows, Visual Studio creates a bin/\nand obj/ directory inside of the project where it writes all .obj and\nbinary build output files. Normal usage is to explicitly ignore these\n2 directory names in the .gitignore file (rather than or in addition\nto *.obj). We just want to see the \"bin/\" and \"obj/\" paths regardless\nof which \"--untracked-files\" flag is passed in. Additionally, if we\nknow the bin/ and obj/ directories are ignored, then we can ignore any\nfiles changes we notice underneath these paths, as we know they do not\naffect the output of stats.\n\n[PATCH v1 0/1] Teach status to show ignored directories\n[1] https://public-inbox.org/git/20170810184936.239542-1-jamill@microsoft.com/\n\nJameson Miller (6):\n  Teach status options around showing ignored files\n  Update documentation for new directory and status logic\n  Add tests for git status `--ignored=matching`\n  Expand support for ignored arguments on status\n  Add tests around status handling of ignored arguments\n  Handle unsupported combination status arguments\n\n Documentation/git-status.txt                      |  22 ++-\n Documentation/technical/api-directory-listing.txt |  28 +++-\n builtin/commit.c                                  |  32 +++-\n dir.c                                             |  44 ++++-\n dir.h                                             |   3 +-\n t/t7519-ignored-mode.sh                           | 195 ++++++++++++++++++++++\n wt-status.c                                       |  11 +-\n wt-status.h                                       |   8 +-\n 8 files changed, 325 insertions(+), 18 deletions(-)\n create mode 100755 t/t7519-ignored-mode.sh\n\n-- \n2.13.6\n\n"},{"id":"329803","messageId":"20171005205443.206900-4-jameson.miller81@gmail.com","threadId":"46918","inReplyTo":"20171005205443.206900-1-jameson.miller81@gmail.com","subject":"[PATCH 3/6] Add tests for git status `--ignored=matching`","fromName":"","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-05T20:54:40Z","receivedAt":"2017-10-05T20:55:09Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"From: Jameson Miller <jamill@microsoft.com>\n\nAdd tests for new ignored mode (matching) when showing all untracked\nfiles.\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n t/t7519-ignored-mode.sh | 132 ++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 132 insertions(+)\n create mode 100755 t/t7519-ignored-mode.sh\n\ndiff --git a/t/t7519-ignored-mode.sh b/t/t7519-ignored-mode.sh\nnew file mode 100755\nindex 0000000000..a8c35d1cbc\n--- /dev/null\n+++ b/t/t7519-ignored-mode.sh\n@@ -0,0 +1,132 @@\n+#!/bin/sh\n+\n+test_description='git status collapse ignored'\n+\n+. ./test-lib.sh\n+\n+# commit initial ignore file\n+test_expect_success 'setup initial commit and ignore file' '\n+\tcat >.gitignore <<-\\EOF &&\n+\t*.ign\n+\tignored_dir/\n+\t!*.unignore\n+\tEOF\n+\tgit add . &&\n+\ttest_tick &&\n+\tgit commit -m \"Initial commit\"\n+'\n+\n+# Test status behavior on folder with ignored files\n+test_expect_success 'Verify behavior of status on folders with ignored files' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! dir/ignored/ignored_1.ign\n+\t! dir/ignored/ignored_2.ign\n+\t! ignored/ignored_1.ign\n+\t! ignored/ignored_2.ign\n+\tEOF\n+\n+\tmkdir -p ignored dir/ignored &&\n+\ttouch ignored/ignored_1.ign ignored/ignored_2.ign \\\n+\t\tdir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+# Test status behavior on folder with tracked and ignored files\n+test_expect_success 'Verify status on folder with tracked & ignored files' '\n+\ttest_when_finished \"git clean -fdx && git reset HEAD~1 --hard\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! dir/tracked_ignored/ignored_1.ign\n+\t! dir/tracked_ignored/ignored_2.ign\n+\t! tracked_ignored/ignored_1.ign\n+\t! tracked_ignored/ignored_2.ign\n+\tEOF\n+\n+\tmkdir -p tracked_ignored dir/tracked_ignored &&\n+\ttouch tracked_ignored/tracked_1 tracked_ignored/tracked_2 \\\n+\t\ttracked_ignored/ignored_1.ign tracked_ignored/ignored_2.ign \\\n+\t\tdir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 \\\n+\t\tdir/tracked_ignored/ignored_1.ign dir/tracked_ignored/ignored_2.ign &&\n+\n+\tgit add tracked_ignored/tracked_1 tracked_ignored/tracked_2 \\\n+\t\tdir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 &&\n+\ttest_tick &&\n+\tgit commit -m \"commit tracked files\" &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+# Test status behavior on folder with untracked and ignored files\n+test_expect_success 'Verify status matching ignored files on folder with tracked & ignored files' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? dir/untracked_ignored/untracked_1\n+\t? dir/untracked_ignored/untracked_2\n+\t? expect\n+\t? output\n+\t? untracked_ignored/untracked_1\n+\t? untracked_ignored/untracked_2\n+\t! dir/untracked_ignored/ignored_1.ign\n+\t! dir/untracked_ignored/ignored_2.ign\n+\t! untracked_ignored/ignored_1.ign\n+\t! untracked_ignored/ignored_2.ign\n+\tEOF\n+\n+\tmkdir -p untracked_ignored dir/untracked_ignored &&\n+\ttouch untracked_ignored/untracked_1 untracked_ignored/untracked_2 \\\n+\t\tuntracked_ignored/ignored_1.ign untracked_ignored/ignored_2.ign \\\n+\t\tdir/untracked_ignored/untracked_1 dir/untracked_ignored/untracked_2 \\\n+\t\tdir/untracked_ignored/ignored_1.ign dir/untracked_ignored/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+# Test status behavior on ignored folder\n+test_expect_success 'Verify status matching ignored files on ignored folder' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! ignored_dir/\n+\tEOF\n+\n+\tmkdir ignored_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+# Test status behavior on ignored folder with tracked file\n+test_expect_success 'Verify status behavior on ignored folder containing tracked file' '\n+\ttest_when_finished \"git clean -fdx && git reset HEAD~1 --hard\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! ignored_dir/ignored_1\n+\t! ignored_dir/ignored_1.ign\n+\t! ignored_dir/ignored_2\n+\t! ignored_dir/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \\\n+\t\tignored_dir/tracked &&\n+\tgit add -f ignored_dir/tracked &&\n+\ttest_tick &&\n+\tgit commit -m \"Force add file in ignored directory\" &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_done\n-- \n2.13.6\n\n"},{"id":"329804","messageId":"20171005205443.206900-6-jameson.miller81@gmail.com","threadId":"46918","inReplyTo":"20171005205443.206900-1-jameson.miller81@gmail.com","subject":"[PATCH 5/6] Add tests around status handling of ignored arguments","fromName":"","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-05T20:54:42Z","receivedAt":"2017-10-05T20:55:11Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"From: Jameson Miller <jamill@microsoft.com>\n\nAdd tests for status handling of '--ignored=matching` and\n`--untracked-files=normal`.\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n t/t7519-ignored-mode.sh | 63 +++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 63 insertions(+)\n\ndiff --git a/t/t7519-ignored-mode.sh b/t/t7519-ignored-mode.sh\nindex a8c35d1cbc..7292ec7d06 100755\n--- a/t/t7519-ignored-mode.sh\n+++ b/t/t7519-ignored-mode.sh\n@@ -129,4 +129,67 @@ test_expect_success 'Verify status behavior on ignored folder containing tracked\n \ttest_i18ncmp expect output\n '\n \n+# Test ignored matching behavior with --untracked=normal\n+test_expect_success 'Verify matching ignored files with --untracked-files=normal' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t? untracked_dir/\n+\t! ignored_dir/\n+\t! ignored_files/ignored_1.ign\n+\t! ignored_files/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir ignored_files untracked_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_files/ignored_1.ign ignored_files/ignored_2.ign \\\n+\t\tuntracked_dir/untracked &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+# Test ignored matching behavior with --untracked=normal\n+test_expect_success 'Verify matching ignored files with --untracked-files=normal' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t? untracked_dir/\n+\t! ignored_dir/\n+\t! ignored_files/ignored_1.ign\n+\t! ignored_files/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir ignored_files untracked_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_files/ignored_1.ign ignored_files/ignored_2.ign \\\n+\t\tuntracked_dir/untracked &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+# Test status behavior on ignored folder with tracked file\n+test_expect_success 'Verify status behavior on ignored folder containing tracked file' '\n+\ttest_when_finished \"git clean -fdx && git reset HEAD~1 --hard\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! ignored_dir/ignored_1\n+\t! ignored_dir/ignored_1.ign\n+\t! ignored_dir/ignored_2\n+\t! ignored_dir/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \\\n+\t\tignored_dir/tracked &&\n+\tgit add -f ignored_dir/tracked &&\n+\ttest_tick &&\n+\tgit commit -m \"Force add file in ignored directory\" &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n test_done\n-- \n2.13.6\n\n"},{"id":"329805","messageId":"20171005205443.206900-7-jameson.miller81@gmail.com","threadId":"46918","inReplyTo":"20171005205443.206900-1-jameson.miller81@gmail.com","subject":"[PATCH 6/6] Handle unsupported combination status arguments","fromName":"","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-05T20:54:43Z","receivedAt":"2017-10-05T20:55:14Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"From: Jameson Miller <jamill@microsoft.com>\n\nIt is not clear what the correct behavior should be when you ask for\nspecific ignored behavior without reporting untracked files. For now,\nreport this as an unsupported combination of input arguments, so it\ncan be modified in the future without back compat concerns.\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n builtin/commit.c | 5 +++++\n 1 file changed, 5 insertions(+)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 34443b45d3..7812e106ad 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1400,6 +1400,11 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \thandle_untracked_files_arg(&s);\n \thandle_ignored_arg(&s);\n \n+\t/* Check for unsupported combination of args */\n+\tif (s.show_ignored_mode == SHOW_MATCHING_IGNORED &&\n+\t    s.show_untracked_files == SHOW_NO_UNTRACKED_FILES)\n+\t\tdie(_(\"Unsupported combination of ignored and untracked-files arguments\"));\n+\n \tparse_pathspec(&s.pathspec, 0,\n \t\t       PATHSPEC_PREFER_FULL,\n \t\t       prefix, argv);\n-- \n2.13.6\n\n"},{"id":"329806","messageId":"20171005205443.206900-5-jameson.miller81@gmail.com","threadId":"46918","inReplyTo":"20171005205443.206900-1-jameson.miller81@gmail.com","subject":"[PATCH 4/6] Expand support for ignored arguments on status","fromName":"","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-05T20:54:41Z","receivedAt":"2017-10-05T20:55:19Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"From: Jameson Miller <jamill@microsoft.com>\n\nTeach status command to handle matching ignored mode when showing\nuntracked files with the normal option.\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n dir.c | 20 ++++++++++++++++++--\n 1 file changed, 18 insertions(+), 2 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex b9af87eca9..8636d080b2 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1585,6 +1585,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,\n {\n \tint exclude;\n \tint has_path_in_index = !!index_file_exists(istate, path->buf, path->len, ignore_case);\n+\tenum path_treatment path_treatment;\n \n \tif (dtype == DT_UNKNOWN)\n \t\tdtype = get_dtype(de, istate, path->buf, path->len);\n@@ -1631,8 +1632,23 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,\n \t\treturn path_none;\n \tcase DT_DIR:\n \t\tstrbuf_addch(path, '/');\n-\t\treturn treat_directory(dir, istate, untracked, path->buf, path->len,\n-\t\t\t\t       baselen, exclude, pathspec);\n+\t\tpath_treatment = treat_directory(dir, istate, untracked,\n+\t\t\t\t\t\t path->buf, path->len,\n+\t\t\t\t\t\t baselen, exclude, pathspec);\n+\t\t/*\n+\t\t * If we are only want to return directories that\n+\t\t * match an exclude pattern, and this directories does\n+\t\t * not match an exclude pattern but all contents are\n+\t\t * excluded, then indicate that we should recurse into\n+\t\t * this directory (instead of marking the directory\n+\t\t * itself as an ignored path)\n+\t\t */\n+\t\tif (!exclude &&\n+\t\t    path_treatment == path_excluded &&\n+\t\t    (dir->flags & DIR_SHOW_IGNORED_TOO) &&\n+\t\t    (dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING))\n+\t\t\treturn path_recurse;\n+\t\treturn path_treatment;\n \tcase DT_REG:\n \tcase DT_LNK:\n \t\treturn exclude ? path_excluded : path_untracked;\n-- \n2.13.6\n\n"},{"id":"329807","messageId":"20171005205443.206900-2-jameson.miller81@gmail.com","threadId":"46918","inReplyTo":"20171005205443.206900-1-jameson.miller81@gmail.com","subject":"[PATCH 1/6] Teach status options around showing ignored files","fromName":"","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-05T20:54:38Z","receivedAt":"2017-10-05T20:55:23Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"From: Jameson Miller <jamill@microsoft.com>\n\nThis change teaches the status command more options to control which\nignored files are reported independently of the which untracked files\nare reported by allowing the `--ignored` option to take additional\narguments.  Currently, the shown ignored files are linked to how\nuntracked files are reported. Both ignored and untracked files are\ncontrolled by arguments to `--untracked-files` option. This makes it\nimpossible to show all untracked files individually, but show ignored\n\"files and directories\" (that is, ignored directories are shown as 1\nentry, instead of having all contents shown).\n\nOur application (Visual Studio) has a specific set of requirements\nabout how it wants untracked / ignored files reported by git status.\nIt requires all untracked files listed individually. It would like\nignored files and directories that explicity match an exclude pattern\nlisted. If an ignored directory matches an exclude pattern, then the\npath of the directory should be returned. If a directory does not\nmatch an exclude pattern, but all of its contents are ignored, then we\nwant the contained files reported instead of the directory.\n\nThe reason for controlling these behaviors separately is that there\ncan be a significant performance impact to scanning the contents of\nexcluded directories. Additionally, knowing whether a directory\nexplicitly matches an exclude pattern can help the application make\ndecisions about how to handle the directory. If an ignored directory\nitself matches an exclude pattern, then the application will know that\nany files underneath the directory must be ignored as well.\n\nAs a more concrete example, on Windows, Visual Studio creates a bin/\nand obj/ directory inside of the project where it writes all .obj and\nbinary build output files. Normal usage is to explicitly ignore these\n2 directory names in the .gitignore file (rather than or in addition\nto *.obj). We just want to see the \"bin/\" and \"obj/\" paths regardless\nof which \"--untracked-files\" flag is passed in. Additionally, if we\nknow the bin/ and obj/ directories are ignored, then we can ignore any\nfiles changes we notice underneath these paths, as we know they do not\naffect the output of stats.\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n builtin/commit.c | 27 +++++++++++++++++++++------\n dir.c            | 24 ++++++++++++++++++++++++\n dir.h            |  3 ++-\n wt-status.c      | 11 ++++++++---\n wt-status.h      |  8 +++++++-\n 5 files changed, 62 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex d75b3805ea..34443b45d3 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -118,7 +118,7 @@ static int edit_flag = -1; /* unspecified */\n static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;\n static int config_commit_verbose = -1; /* unspecified */\n static int no_post_rewrite, allow_empty_message;\n-static char *untracked_files_arg, *force_date, *ignore_submodule_arg;\n+static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;\n static char *sign_commit;\n \n /*\n@@ -139,7 +139,7 @@ static const char *cleanup_arg;\n static enum commit_whence whence;\n static int sequencer_in_use;\n static int use_editor = 1, include_status = 1;\n-static int show_ignored_in_status, have_option_m;\n+static int have_option_m;\n static struct strbuf message = STRBUF_INIT;\n \n static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;\n@@ -1075,6 +1075,19 @@ static const char *find_author_by_nickname(const char *name)\n \tdie(_(\"--author '%s' is not 'Name <email>' and matches no existing author\"), name);\n }\n \n+static void handle_ignored_arg(struct wt_status *s)\n+{\n+\tif (!ignored_arg)\n+\t\t; /* default already initialized */\n+\telse if (!strcmp(ignored_arg, \"traditional\"))\n+\t\ts->show_ignored_mode = SHOW_TRADITIONAL_IGNORED;\n+\telse if (!strcmp(ignored_arg, \"no\"))\n+\t\ts->show_ignored_mode = SHOW_NO_IGNORED;\n+\telse if (!strcmp(ignored_arg, \"matching\"))\n+\t\ts->show_ignored_mode = SHOW_MATCHING_IGNORED;\n+\telse\n+\t\tdie(_(\"Invalid ignored mode '%s'\"), ignored_arg);\n+}\n \n static void handle_untracked_files_arg(struct wt_status *s)\n {\n@@ -1363,8 +1376,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \t\t  N_(\"mode\"),\n \t\t  N_(\"show untracked files, optional modes: all, normal, no. (Default: all)\"),\n \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n-\t\tOPT_BOOL(0, \"ignored\", &show_ignored_in_status,\n-\t\t\t N_(\"show ignored files\")),\n+\t\t{ OPTION_STRING, 0, \"ignored\", &ignored_arg,\n+\t\t  N_(\"mode\"),\n+\t\t  N_(\"show ignored files, optional modes: traditional, matching, no. (Default: traditional)\"),\n+\t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"traditional\" },\n \t\t{ OPTION_STRING, 0, \"ignore-submodules\", &ignore_submodule_arg, N_(\"when\"),\n \t\t  N_(\"ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)\"),\n \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n@@ -1383,8 +1398,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \tfinalize_deferred_config(&s);\n \n \thandle_untracked_files_arg(&s);\n-\tif (show_ignored_in_status)\n-\t\ts.show_ignored_files = 1;\n+\thandle_ignored_arg(&s);\n+\n \tparse_pathspec(&s.pathspec, 0,\n \t\t       PATHSPEC_PREFER_FULL,\n \t\t       prefix, argv);\ndiff --git a/dir.c b/dir.c\nindex 1d17b800cf..b9af87eca9 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1389,6 +1389,30 @@ static enum path_treatment treat_directory(struct dir_struct *dir,\n \tcase index_nonexistent:\n \t\tif (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)\n \t\t\tbreak;\n+\t\tif (exclude &&\n+\t\t\t(dir->flags & DIR_SHOW_IGNORED_TOO) &&\n+\t\t\t(dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING)) {\n+\n+\t\t\t/*\n+\t\t\t * This is an excluded directory and we are\n+\t\t\t * showing ignored paths that match an exclude\n+\t\t\t * pattern.  (e.g. show directory as ignored\n+\t\t\t * only if it matches an exclude pattern).\n+\t\t\t * This path will either be 'path_excluded`\n+\t\t\t * (if we are showing empty directories or if\n+\t\t\t * the directory is not empty), or will be\n+\t\t\t * 'path_none' (empty directory, and we are\n+\t\t\t * not showing empty directories).\n+\t\t\t */\n+\t\t\tif (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))\n+\t\t\t\treturn path_excluded;\n+\n+\t\t\tif (read_directory_recursive(dir, istate, dirname, len,\n+\t\t\t\t\t\t     untracked, 1, 1, pathspec) == path_excluded)\n+\t\t\t\treturn path_excluded;\n+\n+\t\t\treturn path_none;\n+\t\t}\n \t\tif (!(dir->flags & DIR_NO_GITLINKS)) {\n \t\t\tunsigned char sha1[20];\n \t\t\tif (resolve_gitlink_ref(dirname, \"HEAD\", sha1) == 0)\ndiff --git a/dir.h b/dir.h\nindex e3717055d1..57b0943dae 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -152,7 +152,8 @@ struct dir_struct {\n \t\tDIR_COLLECT_IGNORED = 1<<4,\n \t\tDIR_SHOW_IGNORED_TOO = 1<<5,\n \t\tDIR_COLLECT_KILLED_ONLY = 1<<6,\n-\t\tDIR_KEEP_UNTRACKED_CONTENTS = 1<<7\n+\t\tDIR_KEEP_UNTRACKED_CONTENTS = 1<<7,\n+\t\tDIR_SHOW_IGNORED_TOO_MODE_MATCHING = 1<<8\n \t} flags;\n \tstruct dir_entry **entries;\n \tstruct dir_entry **ignored;\ndiff --git a/wt-status.c b/wt-status.c\nindex 6f730ee8f2..8301c84946 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -660,10 +660,15 @@ static void wt_status_collect_untracked(struct wt_status *s)\n \tif (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)\n \t\tdir.flags |=\n \t\t\tDIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;\n-\tif (s->show_ignored_files)\n+\tif (s->show_ignored_mode) {\n \t\tdir.flags |= DIR_SHOW_IGNORED_TOO;\n-\telse\n+\n+\t\tif (s->show_ignored_mode == SHOW_MATCHING_IGNORED)\n+\t\t\tdir.flags |= DIR_SHOW_IGNORED_TOO_MODE_MATCHING;\n+\t} else {\n \t\tdir.untracked = the_index.untracked;\n+\t}\n+\n \tsetup_standard_excludes(&dir);\n \n \tfill_directory(&dir, &the_index, &s->pathspec);\n@@ -1621,7 +1626,7 @@ static void wt_longstatus_print(struct wt_status *s)\n \t}\n \tif (s->show_untracked_files) {\n \t\twt_longstatus_print_other(s, &s->untracked, _(\"Untracked files\"), \"add\");\n-\t\tif (s->show_ignored_files)\n+\t\tif (s->show_ignored_mode)\n \t\t\twt_longstatus_print_other(s, &s->ignored, _(\"Ignored files\"), \"add -f\");\n \t\tif (advice_status_u_option && 2000 < s->untracked_in_ms) {\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL, \"%s\", \"\");\ndiff --git a/wt-status.h b/wt-status.h\nindex 64f4d33ea1..fe27b465e2 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -27,6 +27,12 @@ enum untracked_status_type {\n \tSHOW_ALL_UNTRACKED_FILES\n };\n \n+enum show_ignored_type {\n+\tSHOW_NO_IGNORED,\n+\tSHOW_TRADITIONAL_IGNORED,\n+\tSHOW_MATCHING_IGNORED,\n+};\n+\n /* from where does this commit originate */\n enum commit_whence {\n \tFROM_COMMIT,     /* normal */\n@@ -70,7 +76,7 @@ struct wt_status {\n \tint display_comment_prefix;\n \tint relative_paths;\n \tint submodule_summary;\n-\tint show_ignored_files;\n+\tenum show_ignored_type show_ignored_mode;\n \tenum untracked_status_type show_untracked_files;\n \tconst char *ignore_submodule_arg;\n \tchar color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];\n-- \n2.13.6\n\n"},{"id":"329808","messageId":"20171005205443.206900-3-jameson.miller81@gmail.com","threadId":"46918","inReplyTo":"20171005205443.206900-1-jameson.miller81@gmail.com","subject":"[PATCH 2/6] Update documentation for new directory and status logic","fromName":"","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-05T20:54:39Z","receivedAt":"2017-10-05T20:55:25Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"From: Jameson Miller <jamill@microsoft.com>\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n Documentation/git-status.txt                      | 22 +++++++++++++++++-\n Documentation/technical/api-directory-listing.txt | 28 +++++++++++++++++++----\n 2 files changed, 45 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-status.txt b/Documentation/git-status.txt\nindex 9f3a78a36c..7d1410ff3f 100644\n--- a/Documentation/git-status.txt\n+++ b/Documentation/git-status.txt\n@@ -97,8 +97,28 @@ configuration variable documented in linkgit:git-config[1].\n \t(and suppresses the output of submodule summaries when the config option\n \t`status.submoduleSummary` is set).\n \n---ignored::\n+--ignored[=<mode>]::\n \tShow ignored files as well.\n++\n+The mode parameter is used to specify the handling of ignored files.\n+It is optional: it defaults to 'default', and if specified, it must be\n+stuck to the option (e.g. '--ignored=default`, but not `--ignored default`).\n++\n+The possible options are:\n++\n+\t- 'traditional' - Shows ignored files and directories, unless\n+\t\t\t  --untracked-files=all is specifed, in which case\n+\t\t\t  individual files in ignored directories are\n+\t\t\t  displayed.\n+\t- 'no'\t        - Show no ignored files.\n+\t- 'matching'    - Shows ignored files and directories matching an\n+\t\t\t  ignore pattern.\n++\n+When 'matching' mode is specified, paths that explicity match an\n+ignored pattern are shown. If a directory matches an ignore pattern,\n+then it is shown, but not paths contained in the ignored directory. If\n+a directory does not match an ignore pattern, but all contents are\n+ignored, then the directory is not shown, but all contents are shown.\n \n -z::\n \tTerminate entries with NUL, instead of LF.  This implies\ndiff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt\nindex 6c77b4920c..86c981c29c 100644\n--- a/Documentation/technical/api-directory-listing.txt\n+++ b/Documentation/technical/api-directory-listing.txt\n@@ -22,16 +22,20 @@ The notable options are:\n \n `flags`::\n \n-\tA bit-field of options (the `*IGNORED*` flags are mutually exclusive):\n+\tA bit-field of options:\n \n `DIR_SHOW_IGNORED`:::\n \n-\tReturn just ignored files in `entries[]`, not untracked files.\n+\tReturn just ignored files in `entries[]`, not untracked\n+\tfiles. This is flag is mutually exclusive with\n+\t`DIR_SHOW_IGNORED_TOO`.\n \n `DIR_SHOW_IGNORED_TOO`:::\n \n-\tSimilar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]`\n-\tin addition to untracked files in `entries[]`.\n+\tSimilar to `DIR_SHOW_IGNORED`, but return ignored files in\n+\t`ignored[]` in addition to untracked files in\n+\t`entries[]`. This flag is mutually exclusive with\n+\t`DIR_SHOW_IGNORED`.\n \n `DIR_KEEP_UNTRACKED_CONTENTS`:::\n \n@@ -39,6 +43,22 @@ The notable options are:\n \tuntracked contents of untracked directories are also returned in\n \t`entries[]`.\n \n+`DIR_SHOW_IGNORED_TOO_MODE_MATCHING`:::\n+\n+\tOnly has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if\n+\tthis is set, returns ignored files and directories that match\n+\tan exclude pattern. If a directory matches an exclude pattern,\n+\tthen the directory is returned and the contained paths are\n+\tnot. A directory that does not match an exclude pattern will\n+\tnot be returned even if all of its contents are ignored. In\n+\tthis case, the contents are returned as individual entries.\n+\n+If this is set, files and directories\n+\tthat explicity match an ignore pattern are reported. Implicity\n+\tignored directories (directories that do not match an ignore\n+\tpattern, but whose contents are all ignored) are not reported,\n+\tinstead all of the contents are reported.\n+\n `DIR_COLLECT_IGNORED`:::\n \n \tSpecial mode for git-add. Return ignored files in `ignored[]` and\n-- \n2.13.6\n\n"},{"id":"329811","messageId":"20171005211643.GC88769@aiede.mtv.corp.google.com","threadId":"46918","inReplyTo":"20171005205443.206900-1-jameson.miller81@gmail.com","subject":"Re: [PATCH 0/6] Teach Status options around showing ignored files","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2017-10-05T21:16:43Z","receivedAt":"2017-10-05T21:16:52Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi,\n\njameson.miller81@gmail.com wrote:\n\n> This patch series is the second part of [1], which was split into 2\n> parts. The first part, added an optimization in the directory listing\n> logic to not scan the contents of ignored directories and was merged\n> to master with commit 5aaa7fd3. This patch series includes the second\n> part to expose additional arguments to the --ignored option on the\n> status command.\n\nThanks.\n\n> This patch series teaches the status command more options to control\n> which ignored files are reported independently of the which untracked\n[...]\n> Our application (Visual Studio) has a specific set of requirements\n> about how it wants untracked / ignored files reported by git status.\n[...]\n> The reason for controlling these behaviors separately is that there\n> can be a significant performance impact to scanning the contents of\n[....]\n> As a more concrete example, on Windows, Visual Studio creates a bin/\n> and obj/ directory inside of the project where it writes all .obj and\n[...]\n\nI see this information is also in patch 1/6.  That's a very good\nthing, since that makes performance numbers involved more concrete\nabout which patch brings them about and it becomes part of permanent\nhistory that way --- thanks.\n\nBut it took me a while to notice, and before then, I was trying to\nread through the cover letter to get an overview of which patches I am\nsupposed to look at.  For next time, could the cover letter say\nsomething like \"See patches 1 and 2 for more details about the\nmotivation\" instead of repeating the commit message content?  That\nwould save reviewers some time.\n\nThanks,\nJonathan\n"},{"id":"329813","messageId":"21c31e97-a550-1573-52c1-4c21584341bc@gmail.com","threadId":"46918","inReplyTo":"20171005211643.GC88769@aiede.mtv.corp.google.com","subject":"Re: [PATCH 0/6] Teach Status options around showing ignored files","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-05T21:22:02Z","receivedAt":"2017-10-05T21:22:11Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"\n\nOn 10/05/2017 05:16 PM, Jonathan Nieder wrote:\n> Hi,\n>\n> jameson.miller81@gmail.com wrote:\n>\n>> This patch series is the second part of [1], which was split into 2\n>> parts. The first part, added an optimization in the directory listing\n>> logic to not scan the contents of ignored directories and was merged\n>> to master with commit 5aaa7fd3. This patch series includes the second\n>> part to expose additional arguments to the --ignored option on the\n>> status command.\n> Thanks.\n>\n>> This patch series teaches the status command more options to control\n>> which ignored files are reported independently of the which untracked\n> [...]\n>> Our application (Visual Studio) has a specific set of requirements\n>> about how it wants untracked / ignored files reported by git status.\n> [...]\n>> The reason for controlling these behaviors separately is that there\n>> can be a significant performance impact to scanning the contents of\n> [....]\n>> As a more concrete example, on Windows, Visual Studio creates a bin/\n>> and obj/ directory inside of the project where it writes all .obj and\n> [...]\n>\n> I see this information is also in patch 1/6.  That's a very good\n> thing, since that makes performance numbers involved more concrete\n> about which patch brings them about and it becomes part of permanent\n> history that way --- thanks.\n>\n> But it took me a while to notice, and before then, I was trying to\n> read through the cover letter to get an overview of which patches I am\n> supposed to look at.  For next time, could the cover letter say\n> something like \"See patches 1 and 2 for more details about the\n> motivation\" instead of repeating the commit message content?  That\n> would save reviewers some time.\nWill do - thank you for the feedback!\n>\n> Thanks,\n> Jonathan\n\n"},{"id":"329818","messageId":"CAGZ79kauDYqkDcVOx3A4PcRttRUVpowAD51cV8pk4qOaU=a=3g@mail.gmail.com","threadId":"46918","inReplyTo":"20171005205443.206900-3-jameson.miller81@gmail.com","subject":"Re: [PATCH 2/6] Update documentation for new directory and status logic","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-10-05T21:51:38Z","receivedAt":"2017-10-05T21:51:46Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Thu, Oct 5, 2017 at 1:54 PM,  <jameson.miller81@gmail.com> wrote:\n> From: Jameson Miller <jamill@microsoft.com>\n>\n> Signed-off-by: Jameson Miller <jamill@microsoft.com>\n> ---\n>  Documentation/git-status.txt                      | 22 +++++++++++++++++-\n>  Documentation/technical/api-directory-listing.txt | 28 +++++++++++++++++++----\n>  2 files changed, 45 insertions(+), 5 deletions(-)\n>\n> diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt\n> index 9f3a78a36c..7d1410ff3f 100644\n> --- a/Documentation/git-status.txt\n> +++ b/Documentation/git-status.txt\n> @@ -97,8 +97,28 @@ configuration variable documented in linkgit:git-config[1].\n>         (and suppresses the output of submodule summaries when the config option\n>         `status.submoduleSummary` is set).\n>\n> ---ignored::\n> +--ignored[=<mode>]::\n>         Show ignored files as well.\n> ++\n> +The mode parameter is used to specify the handling of ignored files.\n> +It is optional: it defaults to 'default', and if specified, it must be\n> +stuck to the option (e.g. '--ignored=default`, but not `--ignored default`).\n\nIs there a rationale for not accepting `--ignored default`?\n(It's just the way OPTION_STRING inputs seem to work,\nbut in other cases [c.f. man git gc, search \"--prune=\" ] this is\njust implied). Or rather: no need to spell this out explicitly IMHO,\nas that draws the users attention to it, which might be confusing.\n\n> ++\n> +The possible options are:\n> ++\n> +       - 'traditional' - Shows ignored files and directories, unless\n> +                         --untracked-files=all is specifed, in which case\n> +                         individual files in ignored directories are\n> +                         displayed.\n> +       - 'no'          - Show no ignored files.\n> +       - 'matching'    - Shows ignored files and directories matching an\n> +                         ignore pattern.\n> ++\n> +When 'matching' mode is specified, paths that explicity match an\n> +ignored pattern are shown. If a directory matches an ignore pattern,\n> +then it is shown, but not paths contained in the ignored directory. If\n> +a directory does not match an ignore pattern, but all contents are\n> +ignored, then the directory is not shown, but all contents are shown.\n>\n>  -z::\n>         Terminate entries with NUL, instead of LF.  This implies\n> diff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt\n> index 6c77b4920c..86c981c29c 100644\n> --- a/Documentation/technical/api-directory-listing.txt\n> +++ b/Documentation/technical/api-directory-listing.txt\n> @@ -22,16 +22,20 @@ The notable options are:\n>\n>  `flags`::\n>\n> -       A bit-field of options (the `*IGNORED*` flags are mutually exclusive):\n> +       A bit-field of options:\n>\n>  `DIR_SHOW_IGNORED`:::\n>\n> -       Return just ignored files in `entries[]`, not untracked files.\n> +       Return just ignored files in `entries[]`, not untracked\n> +       files. This is flag is mutually exclusive with\n\n\"is flag is\" ?\n\n\n> +       `DIR_SHOW_IGNORED_TOO`.\n>\n>  `DIR_SHOW_IGNORED_TOO`:::\n>\n> -       Similar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]`\n> -       in addition to untracked files in `entries[]`.\n> +       Similar to `DIR_SHOW_IGNORED`, but return ignored files in\n> +       `ignored[]` in addition to untracked files in\n> +       `entries[]`. This flag is mutually exclusive with\n> +       `DIR_SHOW_IGNORED`.\n>\n>  `DIR_KEEP_UNTRACKED_CONTENTS`:::\n>\n> @@ -39,6 +43,22 @@ The notable options are:\n>         untracked contents of untracked directories are also returned in\n>         `entries[]`.\n>\n> +`DIR_SHOW_IGNORED_TOO_MODE_MATCHING`:::\n> +\n> +       Only has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if\n> +       this is set, returns ignored files and directories that match\n> +       an exclude pattern. If a directory matches an exclude pattern,\n> +       then the directory is returned and the contained paths are\n> +       not. A directory that does not match an exclude pattern will\n> +       not be returned even if all of its contents are ignored. In\n> +       this case, the contents are returned as individual entries.\n> +\n\nI think to make the asciidoc happy, you'd put a '+ LF' as paragraph\ndelimiter and the subsequent paragraphs are not indented.\nC.f. Documentation/git-gc.txt \"--auto\".\nOh, screw that. It turns out, this place is the only place in our docs\nwhere we use triple colons. So I don't know what I am talking about.\n\n> +If this is set, files and directories\n> +       that explicity match an ignore pattern are reported. Implicity\n> +       ignored directories (directories that do not match an ignore\n> +       pattern, but whose contents are all ignored) are not reported,\n> +       instead all of the contents are reported.\n> +\n>  `DIR_COLLECT_IGNORED`:::\n>\n>         Special mode for git-add. Return ignored files in `ignored[]` and\n> --\n> 2.13.6\n>\n"},{"id":"329819","messageId":"CAGZ79kaP7EthkcbAtuCSiFTU=nG8g-smxk5ZP=K=G5Ez2ZcsSQ@mail.gmail.com","threadId":"46918","inReplyTo":"20171005205443.206900-4-jameson.miller81@gmail.com","subject":"Re: [PATCH 3/6] Add tests for git status `--ignored=matching`","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-10-05T21:59:23Z","receivedAt":"2017-10-05T21:59:29Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Thu, Oct 5, 2017 at 1:54 PM,  <jameson.miller81@gmail.com> wrote:\n> From: Jameson Miller <jamill@microsoft.com>\n>\n> Add tests for new ignored mode (matching) when showing all untracked\n> files.\n>\n> Signed-off-by: Jameson Miller <jamill@microsoft.com>\n> ---\n>  t/t7519-ignored-mode.sh | 132 ++++++++++++++++++++++++++++++++++++++++++++++++\n>  1 file changed, 132 insertions(+)\n>  create mode 100755 t/t7519-ignored-mode.sh\n>\n> diff --git a/t/t7519-ignored-mode.sh b/t/t7519-ignored-mode.sh\n> new file mode 100755\n> index 0000000000..a8c35d1cbc\n> --- /dev/null\n> +++ b/t/t7519-ignored-mode.sh\n> @@ -0,0 +1,132 @@\n> +#!/bin/sh\n> +\n> +test_description='git status collapse ignored'\n> +\n> +. ./test-lib.sh\n> +\n> +# commit initial ignore file\n> +test_expect_success 'setup initial commit and ignore file' '\n\nHere and in the following tests, I have the impression that the\nleading comment says the same as the test title, so you could\nomit the comments, putting all effort into a well word smithed\ntest title.\n\n\n> +       cat >.gitignore <<-\\EOF &&\n> +       *.ign\n> +       ignored_dir/\n> +       !*.unignore\n> +       EOF\n> +       git add . &&\n> +       test_tick &&\n\nHow is timing relevant to these test?\n\nThanks,\nStefan\n"},{"id":"329821","messageId":"CAGZ79kbdObhu0j5yAo-KZk0H12oSzoP5sLd6S8kUbZMzEXBEQA@mail.gmail.com","threadId":"46918","inReplyTo":"20171005205443.206900-7-jameson.miller81@gmail.com","subject":"Re: [PATCH 6/6] Handle unsupported combination status arguments","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-10-05T22:08:08Z","receivedAt":"2017-10-05T22:08:14Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Thu, Oct 5, 2017 at 1:54 PM,  <jameson.miller81@gmail.com> wrote:\n> From: Jameson Miller <jamill@microsoft.com>\n>\n> It is not clear what the correct behavior should be when you ask for\n> specific ignored behavior without reporting untracked files. For now,\n> report this as an unsupported combination of input arguments, so it\n> can be modified in the future without back compat concerns.\n\nIs there a rationale to put this as an extra commit at the end,\nor could this be squashed into the first commit as well?\n\n>\n> Signed-off-by: Jameson Miller <jamill@microsoft.com>\n> ---\n>  builtin/commit.c | 5 +++++\n>  1 file changed, 5 insertions(+)\n>\n> diff --git a/builtin/commit.c b/builtin/commit.c\n> index 34443b45d3..7812e106ad 100644\n> --- a/builtin/commit.c\n> +++ b/builtin/commit.c\n> @@ -1400,6 +1400,11 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n>         handle_untracked_files_arg(&s);\n>         handle_ignored_arg(&s);\n>\n> +       /* Check for unsupported combination of args */\n\nOh, this is the first check that we add here for unsupported combinations.\nHow much value does this comment bring to the future reader?\n\n> +       if (s.show_ignored_mode == SHOW_MATCHING_IGNORED &&\n> +           s.show_untracked_files == SHOW_NO_UNTRACKED_FILES)\n> +               die(_(\"Unsupported combination of ignored and untracked-files arguments\"));\n\nThanks for taking care of the corner case!\nStefan\n"},{"id":"330171","messageId":"20171011133504.15049-1-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171005205443.206900-1-jameson.miller81@gmail.com","subject":"[PATCH v2 0/5] Teach Status options around showing ignored files","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-11T13:34:59Z","receivedAt":"2017-10-11T13:35:26Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"Changes in v2:\n\nAddressed review comments from the original series:\n\n* Made fixes / simplifications suggested for documentation\n\n* Removed test_tick from test scripts\n\n* Merged 2 commits (as suggested)\n\nJameson Miller (5):\n  Teach status options around showing ignored files\n  Update documentation for new directory and status logic\n  Add tests for git status `--ignored=matching`\n  Expand support for ignored arguments on status\n  Add tests around status handling of ignored arguments\n\n Documentation/git-status.txt                      |  21 ++-\n Documentation/technical/api-directory-listing.txt |  27 +++-\n builtin/commit.c                                  |  31 +++-\n dir.c                                             |  44 +++++-\n dir.h                                             |   3 +-\n t/t7519-ignored-mode.sh                           | 183 ++++++++++++++++++++++\n wt-status.c                                       |  11 +-\n wt-status.h                                       |   8 +-\n 8 files changed, 310 insertions(+), 18 deletions(-)\n create mode 100755 t/t7519-ignored-mode.sh\n\n-- \n2.13.6\n\n"},{"id":"330172","messageId":"20171011133504.15049-2-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171011133504.15049-1-jamill@microsoft.com","subject":"[PATCH v2 1/5] Teach status options around showing ignored files","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-11T13:35:00Z","receivedAt":"2017-10-11T13:35:28Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"This change teaches the status command more options to control which\nignored files are reported independently of the which untracked files\nare reported by allowing the `--ignored` option to take additional\narguments.  Currently, the shown ignored files are linked to how\nuntracked files are reported. Both ignored and untracked files are\ncontrolled by arguments to `--untracked-files` option. This makes it\nimpossible to show all untracked files individually, but show ignored\n\"files and directories\" (that is, ignored directories are shown as 1\nentry, instead of having all contents shown).\n\nOur application (Visual Studio) has a specific set of requirements\nabout how it wants untracked / ignored files reported by git status.\nIt requires all untracked files listed individually. It would like\nignored files and directories that explicity match an exclude pattern\nlisted. If an ignored directory matches an exclude pattern, then the\npath of the directory should be returned. If a directory does not\nmatch an exclude pattern, but all of its contents are ignored, then we\nwant the contained files reported instead of the directory.\n\nThe reason for controlling these behaviors separately is that there\ncan be a significant performance impact to scanning the contents of\nexcluded directories. Additionally, knowing whether a directory\nexplicitly matches an exclude pattern can help the application make\ndecisions about how to handle the directory. If an ignored directory\nitself matches an exclude pattern, then the application will know that\nany files underneath the directory must be ignored as well.\n\nAs a more concrete example, on Windows, Visual Studio creates a bin/\nand obj/ directory inside of the project where it writes all .obj and\nbinary build output files. Normal usage is to explicitly ignore these\n2 directory names in the .gitignore file (rather than or in addition\nto *.obj). We just want to see the \"bin/\" and \"obj/\" paths regardless\nof which \"--untracked-files\" flag is passed in. Additionally, if we\nknow the bin/ and obj/ directories are ignored, then we can ignore any\nfiles changes we notice underneath these paths, as we know they do not\naffect the output of stats.\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n builtin/commit.c | 31 +++++++++++++++++++++++++------\n dir.c            | 24 ++++++++++++++++++++++++\n dir.h            |  3 ++-\n wt-status.c      | 11 ++++++++---\n wt-status.h      |  8 +++++++-\n 5 files changed, 66 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex d75b3805ea..98d84d0277 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -118,7 +118,7 @@ static int edit_flag = -1; /* unspecified */\n static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;\n static int config_commit_verbose = -1; /* unspecified */\n static int no_post_rewrite, allow_empty_message;\n-static char *untracked_files_arg, *force_date, *ignore_submodule_arg;\n+static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;\n static char *sign_commit;\n \n /*\n@@ -139,7 +139,7 @@ static const char *cleanup_arg;\n static enum commit_whence whence;\n static int sequencer_in_use;\n static int use_editor = 1, include_status = 1;\n-static int show_ignored_in_status, have_option_m;\n+static int have_option_m;\n static struct strbuf message = STRBUF_INIT;\n \n static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;\n@@ -1075,6 +1075,19 @@ static const char *find_author_by_nickname(const char *name)\n \tdie(_(\"--author '%s' is not 'Name <email>' and matches no existing author\"), name);\n }\n \n+static void handle_ignored_arg(struct wt_status *s)\n+{\n+\tif (!ignored_arg)\n+\t\t; /* default already initialized */\n+\telse if (!strcmp(ignored_arg, \"traditional\"))\n+\t\ts->show_ignored_mode = SHOW_TRADITIONAL_IGNORED;\n+\telse if (!strcmp(ignored_arg, \"no\"))\n+\t\ts->show_ignored_mode = SHOW_NO_IGNORED;\n+\telse if (!strcmp(ignored_arg, \"matching\"))\n+\t\ts->show_ignored_mode = SHOW_MATCHING_IGNORED;\n+\telse\n+\t\tdie(_(\"Invalid ignored mode '%s'\"), ignored_arg);\n+}\n \n static void handle_untracked_files_arg(struct wt_status *s)\n {\n@@ -1363,8 +1376,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \t\t  N_(\"mode\"),\n \t\t  N_(\"show untracked files, optional modes: all, normal, no. (Default: all)\"),\n \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n-\t\tOPT_BOOL(0, \"ignored\", &show_ignored_in_status,\n-\t\t\t N_(\"show ignored files\")),\n+\t\t{ OPTION_STRING, 0, \"ignored\", &ignored_arg,\n+\t\t  N_(\"mode\"),\n+\t\t  N_(\"show ignored files, optional modes: traditional, matching, no. (Default: traditional)\"),\n+\t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"traditional\" },\n \t\t{ OPTION_STRING, 0, \"ignore-submodules\", &ignore_submodule_arg, N_(\"when\"),\n \t\t  N_(\"ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)\"),\n \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n@@ -1383,8 +1398,12 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \tfinalize_deferred_config(&s);\n \n \thandle_untracked_files_arg(&s);\n-\tif (show_ignored_in_status)\n-\t\ts.show_ignored_files = 1;\n+\thandle_ignored_arg(&s);\n+\n+\tif (s.show_ignored_mode == SHOW_MATCHING_IGNORED &&\n+\t    s.show_untracked_files == SHOW_NO_UNTRACKED_FILES)\n+\t\tdie(_(\"Unsupported combination of ignored and untracked-files arguments\"));\n+\n \tparse_pathspec(&s.pathspec, 0,\n \t\t       PATHSPEC_PREFER_FULL,\n \t\t       prefix, argv);\ndiff --git a/dir.c b/dir.c\nindex 1d17b800cf..b9af87eca9 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1389,6 +1389,30 @@ static enum path_treatment treat_directory(struct dir_struct *dir,\n \tcase index_nonexistent:\n \t\tif (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)\n \t\t\tbreak;\n+\t\tif (exclude &&\n+\t\t\t(dir->flags & DIR_SHOW_IGNORED_TOO) &&\n+\t\t\t(dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING)) {\n+\n+\t\t\t/*\n+\t\t\t * This is an excluded directory and we are\n+\t\t\t * showing ignored paths that match an exclude\n+\t\t\t * pattern.  (e.g. show directory as ignored\n+\t\t\t * only if it matches an exclude pattern).\n+\t\t\t * This path will either be 'path_excluded`\n+\t\t\t * (if we are showing empty directories or if\n+\t\t\t * the directory is not empty), or will be\n+\t\t\t * 'path_none' (empty directory, and we are\n+\t\t\t * not showing empty directories).\n+\t\t\t */\n+\t\t\tif (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))\n+\t\t\t\treturn path_excluded;\n+\n+\t\t\tif (read_directory_recursive(dir, istate, dirname, len,\n+\t\t\t\t\t\t     untracked, 1, 1, pathspec) == path_excluded)\n+\t\t\t\treturn path_excluded;\n+\n+\t\t\treturn path_none;\n+\t\t}\n \t\tif (!(dir->flags & DIR_NO_GITLINKS)) {\n \t\t\tunsigned char sha1[20];\n \t\t\tif (resolve_gitlink_ref(dirname, \"HEAD\", sha1) == 0)\ndiff --git a/dir.h b/dir.h\nindex e3717055d1..57b0943dae 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -152,7 +152,8 @@ struct dir_struct {\n \t\tDIR_COLLECT_IGNORED = 1<<4,\n \t\tDIR_SHOW_IGNORED_TOO = 1<<5,\n \t\tDIR_COLLECT_KILLED_ONLY = 1<<6,\n-\t\tDIR_KEEP_UNTRACKED_CONTENTS = 1<<7\n+\t\tDIR_KEEP_UNTRACKED_CONTENTS = 1<<7,\n+\t\tDIR_SHOW_IGNORED_TOO_MODE_MATCHING = 1<<8\n \t} flags;\n \tstruct dir_entry **entries;\n \tstruct dir_entry **ignored;\ndiff --git a/wt-status.c b/wt-status.c\nindex 6f730ee8f2..8301c84946 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -660,10 +660,15 @@ static void wt_status_collect_untracked(struct wt_status *s)\n \tif (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)\n \t\tdir.flags |=\n \t\t\tDIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;\n-\tif (s->show_ignored_files)\n+\tif (s->show_ignored_mode) {\n \t\tdir.flags |= DIR_SHOW_IGNORED_TOO;\n-\telse\n+\n+\t\tif (s->show_ignored_mode == SHOW_MATCHING_IGNORED)\n+\t\t\tdir.flags |= DIR_SHOW_IGNORED_TOO_MODE_MATCHING;\n+\t} else {\n \t\tdir.untracked = the_index.untracked;\n+\t}\n+\n \tsetup_standard_excludes(&dir);\n \n \tfill_directory(&dir, &the_index, &s->pathspec);\n@@ -1621,7 +1626,7 @@ static void wt_longstatus_print(struct wt_status *s)\n \t}\n \tif (s->show_untracked_files) {\n \t\twt_longstatus_print_other(s, &s->untracked, _(\"Untracked files\"), \"add\");\n-\t\tif (s->show_ignored_files)\n+\t\tif (s->show_ignored_mode)\n \t\t\twt_longstatus_print_other(s, &s->ignored, _(\"Ignored files\"), \"add -f\");\n \t\tif (advice_status_u_option && 2000 < s->untracked_in_ms) {\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL, \"%s\", \"\");\ndiff --git a/wt-status.h b/wt-status.h\nindex 64f4d33ea1..fe27b465e2 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -27,6 +27,12 @@ enum untracked_status_type {\n \tSHOW_ALL_UNTRACKED_FILES\n };\n \n+enum show_ignored_type {\n+\tSHOW_NO_IGNORED,\n+\tSHOW_TRADITIONAL_IGNORED,\n+\tSHOW_MATCHING_IGNORED,\n+};\n+\n /* from where does this commit originate */\n enum commit_whence {\n \tFROM_COMMIT,     /* normal */\n@@ -70,7 +76,7 @@ struct wt_status {\n \tint display_comment_prefix;\n \tint relative_paths;\n \tint submodule_summary;\n-\tint show_ignored_files;\n+\tenum show_ignored_type show_ignored_mode;\n \tenum untracked_status_type show_untracked_files;\n \tconst char *ignore_submodule_arg;\n \tchar color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];\n-- \n2.13.6\n\n"},{"id":"330173","messageId":"20171011133504.15049-3-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171011133504.15049-1-jamill@microsoft.com","subject":"[PATCH v2 2/5] Update documentation for new directory and status logic","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-11T13:35:01Z","receivedAt":"2017-10-11T13:35:30Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"Signed-off-by: Jameson Miller <jamill@microsoft.com>\n---\n Documentation/git-status.txt                      | 21 +++++++++++++++++-\n Documentation/technical/api-directory-listing.txt | 27 +++++++++++++++++++----\n 2 files changed, 43 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-status.txt b/Documentation/git-status.txt\nindex 9f3a78a36c..fc282e0a92 100644\n--- a/Documentation/git-status.txt\n+++ b/Documentation/git-status.txt\n@@ -97,8 +97,27 @@ configuration variable documented in linkgit:git-config[1].\n \t(and suppresses the output of submodule summaries when the config option\n \t`status.submoduleSummary` is set).\n \n---ignored::\n+--ignored[=<mode>]::\n \tShow ignored files as well.\n++\n+The mode parameter is used to specify the handling of ignored files.\n+It is optional: it defaults to 'traditional'.\n++\n+The possible options are:\n++\n+\t- 'traditional' - Shows ignored files and directories, unless\n+\t\t\t  --untracked-files=all is specifed, in which case\n+\t\t\t  individual files in ignored directories are\n+\t\t\t  displayed.\n+\t- 'no'\t        - Show no ignored files.\n+\t- 'matching'    - Shows ignored files and directories matching an\n+\t\t\t  ignore pattern.\n++\n+When 'matching' mode is specified, paths that explicity match an\n+ignored pattern are shown. If a directory matches an ignore pattern,\n+then it is shown, but not paths contained in the ignored directory. If\n+a directory does not match an ignore pattern, but all contents are\n+ignored, then the directory is not shown, but all contents are shown.\n \n -z::\n \tTerminate entries with NUL, instead of LF.  This implies\ndiff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt\nindex 6c77b4920c..7fae00f44f 100644\n--- a/Documentation/technical/api-directory-listing.txt\n+++ b/Documentation/technical/api-directory-listing.txt\n@@ -22,16 +22,20 @@ The notable options are:\n \n `flags`::\n \n-\tA bit-field of options (the `*IGNORED*` flags are mutually exclusive):\n+\tA bit-field of options:\n \n `DIR_SHOW_IGNORED`:::\n \n-\tReturn just ignored files in `entries[]`, not untracked files.\n+\tReturn just ignored files in `entries[]`, not untracked\n+\tfiles. This flag is mutually exclusive with\n+\t`DIR_SHOW_IGNORED_TOO`.\n \n `DIR_SHOW_IGNORED_TOO`:::\n \n-\tSimilar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]`\n-\tin addition to untracked files in `entries[]`.\n+\tSimilar to `DIR_SHOW_IGNORED`, but return ignored files in\n+\t`ignored[]` in addition to untracked files in\n+\t`entries[]`. This flag is mutually exclusive with\n+\t`DIR_SHOW_IGNORED`.\n \n `DIR_KEEP_UNTRACKED_CONTENTS`:::\n \n@@ -39,6 +43,21 @@ The notable options are:\n \tuntracked contents of untracked directories are also returned in\n \t`entries[]`.\n \n+`DIR_SHOW_IGNORED_TOO_MODE_MATCHING`:::\n+\n+\tOnly has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if\n+\tthis is set, returns ignored files and directories that match\n+\tan exclude pattern. If a directory matches an exclude pattern,\n+\tthen the directory is returned and the contained paths are\n+\tnot. A directory that does not match an exclude pattern will\n+\tnot be returned even if all of its contents are ignored. In\n+\tthis case, the contents are returned as individual entries.\n++\n+If this is set, files and directories that explicity match an ignore\n+pattern are reported. Implicity ignored directories (directories that\n+do not match an ignore pattern, but whose contents are all ignored)\n+are not reported, instead all of the contents are reported.\n+\n `DIR_COLLECT_IGNORED`:::\n \n \tSpecial mode for git-add. Return ignored files in `ignored[]` and\n-- \n2.13.6\n\n"},{"id":"330174","messageId":"20171011133504.15049-6-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171011133504.15049-1-jamill@microsoft.com","subject":"[PATCH v2 5/5] Add tests around status handling of ignored arguments","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-11T13:35:04Z","receivedAt":"2017-10-11T13:35:34Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"Add tests for status handling of '--ignored=matching` and\n`--untracked-files=normal`.\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n t/t7519-ignored-mode.sh | 60 ++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 59 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t7519-ignored-mode.sh b/t/t7519-ignored-mode.sh\nindex 76e91427b0..6be7701d79 100755\n--- a/t/t7519-ignored-mode.sh\n+++ b/t/t7519-ignored-mode.sh\n@@ -116,10 +116,68 @@ test_expect_success 'Verify status behavior on ignored folder containing tracked\n \t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \\\n \t\tignored_dir/tracked &&\n \tgit add -f ignored_dir/tracked &&\n-\ttest_tick &&\n \tgit commit -m \"Force add file in ignored directory\" &&\n \tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n \ttest_i18ncmp expect output\n '\n \n+test_expect_success 'Verify matching ignored files with --untracked-files=normal' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t? untracked_dir/\n+\t! ignored_dir/\n+\t! ignored_files/ignored_1.ign\n+\t! ignored_files/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir ignored_files untracked_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_files/ignored_1.ign ignored_files/ignored_2.ign \\\n+\t\tuntracked_dir/untracked &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify matching ignored files with --untracked-files=normal' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t? untracked_dir/\n+\t! ignored_dir/\n+\t! ignored_files/ignored_1.ign\n+\t! ignored_files/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir ignored_files untracked_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_files/ignored_1.ign ignored_files/ignored_2.ign \\\n+\t\tuntracked_dir/untracked &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status behavior on ignored folder containing tracked file' '\n+\ttest_when_finished \"git clean -fdx && git reset HEAD~1 --hard\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! ignored_dir/ignored_1\n+\t! ignored_dir/ignored_1.ign\n+\t! ignored_dir/ignored_2\n+\t! ignored_dir/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \\\n+\t\tignored_dir/tracked &&\n+\tgit add -f ignored_dir/tracked &&\n+\tgit commit -m \"Force add file in ignored directory\" &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n test_done\n-- \n2.13.6\n\n"},{"id":"330175","messageId":"20171011133504.15049-5-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171011133504.15049-1-jamill@microsoft.com","subject":"[PATCH v2 4/5] Expand support for ignored arguments on status","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-11T13:35:03Z","receivedAt":"2017-10-11T13:35:36Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"Teach status command to handle matching ignored mode when showing\nuntracked files with the normal option.\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n dir.c | 20 ++++++++++++++++++--\n 1 file changed, 18 insertions(+), 2 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex b9af87eca9..8636d080b2 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1585,6 +1585,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,\n {\n \tint exclude;\n \tint has_path_in_index = !!index_file_exists(istate, path->buf, path->len, ignore_case);\n+\tenum path_treatment path_treatment;\n \n \tif (dtype == DT_UNKNOWN)\n \t\tdtype = get_dtype(de, istate, path->buf, path->len);\n@@ -1631,8 +1632,23 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,\n \t\treturn path_none;\n \tcase DT_DIR:\n \t\tstrbuf_addch(path, '/');\n-\t\treturn treat_directory(dir, istate, untracked, path->buf, path->len,\n-\t\t\t\t       baselen, exclude, pathspec);\n+\t\tpath_treatment = treat_directory(dir, istate, untracked,\n+\t\t\t\t\t\t path->buf, path->len,\n+\t\t\t\t\t\t baselen, exclude, pathspec);\n+\t\t/*\n+\t\t * If we are only want to return directories that\n+\t\t * match an exclude pattern, and this directories does\n+\t\t * not match an exclude pattern but all contents are\n+\t\t * excluded, then indicate that we should recurse into\n+\t\t * this directory (instead of marking the directory\n+\t\t * itself as an ignored path)\n+\t\t */\n+\t\tif (!exclude &&\n+\t\t    path_treatment == path_excluded &&\n+\t\t    (dir->flags & DIR_SHOW_IGNORED_TOO) &&\n+\t\t    (dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING))\n+\t\t\treturn path_recurse;\n+\t\treturn path_treatment;\n \tcase DT_REG:\n \tcase DT_LNK:\n \t\treturn exclude ? path_excluded : path_untracked;\n-- \n2.13.6\n\n"},{"id":"330176","messageId":"20171011133504.15049-4-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171011133504.15049-1-jamill@microsoft.com","subject":"[PATCH v2 3/5] Add tests for git status `--ignored=matching`","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-11T13:35:02Z","receivedAt":"2017-10-11T13:35:37Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"Add tests for new ignored mode (matching) when showing all untracked\nfiles.\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n t/t7519-ignored-mode.sh | 125 ++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 125 insertions(+)\n create mode 100755 t/t7519-ignored-mode.sh\n\ndiff --git a/t/t7519-ignored-mode.sh b/t/t7519-ignored-mode.sh\nnew file mode 100755\nindex 0000000000..76e91427b0\n--- /dev/null\n+++ b/t/t7519-ignored-mode.sh\n@@ -0,0 +1,125 @@\n+#!/bin/sh\n+\n+test_description='git status collapse ignored'\n+\n+. ./test-lib.sh\n+\n+# commit initial ignore file\n+test_expect_success 'setup initial commit and ignore file' '\n+\tcat >.gitignore <<-\\EOF &&\n+\t*.ign\n+\tignored_dir/\n+\t!*.unignore\n+\tEOF\n+\tgit add . &&\n+\tgit commit -m \"Initial commit\"\n+'\n+\n+test_expect_success 'Verify behavior of status on folders with ignored files' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! dir/ignored/ignored_1.ign\n+\t! dir/ignored/ignored_2.ign\n+\t! ignored/ignored_1.ign\n+\t! ignored/ignored_2.ign\n+\tEOF\n+\n+\tmkdir -p ignored dir/ignored &&\n+\ttouch ignored/ignored_1.ign ignored/ignored_2.ign \\\n+\t\tdir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status behavior on folder with tracked & ignored files' '\n+\ttest_when_finished \"git clean -fdx && git reset HEAD~1 --hard\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! dir/tracked_ignored/ignored_1.ign\n+\t! dir/tracked_ignored/ignored_2.ign\n+\t! tracked_ignored/ignored_1.ign\n+\t! tracked_ignored/ignored_2.ign\n+\tEOF\n+\n+\tmkdir -p tracked_ignored dir/tracked_ignored &&\n+\ttouch tracked_ignored/tracked_1 tracked_ignored/tracked_2 \\\n+\t\ttracked_ignored/ignored_1.ign tracked_ignored/ignored_2.ign \\\n+\t\tdir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 \\\n+\t\tdir/tracked_ignored/ignored_1.ign dir/tracked_ignored/ignored_2.ign &&\n+\n+\tgit add tracked_ignored/tracked_1 tracked_ignored/tracked_2 \\\n+\t\tdir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 &&\n+\tgit commit -m \"commit tracked files\" &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status behavior on folder with untracked and ignored files' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? dir/untracked_ignored/untracked_1\n+\t? dir/untracked_ignored/untracked_2\n+\t? expect\n+\t? output\n+\t? untracked_ignored/untracked_1\n+\t? untracked_ignored/untracked_2\n+\t! dir/untracked_ignored/ignored_1.ign\n+\t! dir/untracked_ignored/ignored_2.ign\n+\t! untracked_ignored/ignored_1.ign\n+\t! untracked_ignored/ignored_2.ign\n+\tEOF\n+\n+\tmkdir -p untracked_ignored dir/untracked_ignored &&\n+\ttouch untracked_ignored/untracked_1 untracked_ignored/untracked_2 \\\n+\t\tuntracked_ignored/ignored_1.ign untracked_ignored/ignored_2.ign \\\n+\t\tdir/untracked_ignored/untracked_1 dir/untracked_ignored/untracked_2 \\\n+\t\tdir/untracked_ignored/ignored_1.ign dir/untracked_ignored/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status matching ignored files on ignored folder' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! ignored_dir/\n+\tEOF\n+\n+\tmkdir ignored_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status behavior on ignored folder containing tracked file' '\n+\ttest_when_finished \"git clean -fdx && git reset HEAD~1 --hard\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! ignored_dir/ignored_1\n+\t! ignored_dir/ignored_1.ign\n+\t! ignored_dir/ignored_2\n+\t! ignored_dir/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \\\n+\t\tignored_dir/tracked &&\n+\tgit add -f ignored_dir/tracked &&\n+\ttest_tick &&\n+\tgit commit -m \"Force add file in ignored directory\" &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_done\n-- \n2.13.6\n\n"},{"id":"330226","messageId":"xmqq60bl2iyp.fsf@gitster.mtv.corp.google.com","threadId":"46918","inReplyTo":"20171011133504.15049-1-jamill@microsoft.com","subject":"Re: [PATCH v2 0/5] Teach Status options around showing ignored files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-10-12T02:33:34Z","receivedAt":"2017-10-12T02:33:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jameson Miller <jameson.miller81@gmail.com> writes:\n\n> Changes in v2:\n>\n> Addressed review comments from the original series:\n>\n> * Made fixes / simplifications suggested for documentation\n>\n> * Removed test_tick from test scripts\n>\n> * Merged 2 commits (as suggested)\n>\n> Jameson Miller (5):\n>   Teach status options around showing ignored files\n>   Update documentation for new directory and status logic\n>   Add tests for git status `--ignored=matching`\n>   Expand support for ignored arguments on status\n>   Add tests around status handling of ignored arguments\n\nPlease make sure these titles mix well when they appear together\nwith changes from other people that are completely unrelated to\nthem.  Keep in mind that your \"git status\" is *not* the most\nimportant thing in the world (of course, it is no less important\nthan others, either).  Perhaps\n\n    status: add new options to show ignored files differently\n    status: document logic to show new directory\n    status: test --ignored=matching\n\netc.  Rules of thumb:\n\n (1) choose \"<area>: \" prefix appropriately\n (2) keep them short and to the point\n (3) word that follow \"<area>: \" prefix is not capitalized\n (4) no need for full-stop at the end of the title\n\n\n"},{"id":"330228","messageId":"xmqqy3oh13of.fsf@gitster.mtv.corp.google.com","threadId":"46918","inReplyTo":"20171011133504.15049-2-jamill@microsoft.com","subject":"Re: [PATCH v2 1/5] Teach status options around showing ignored files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-10-12T02:49:04Z","receivedAt":"2017-10-12T02:49:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jameson Miller <jameson.miller81@gmail.com> writes:\n\n> This change teaches the status command more options to control which\n> ignored files are reported independently of the which untracked files\n> are reported by allowing the `--ignored` option to take additional\n> arguments.  Currently, the shown ignored files are linked to how\n> untracked files are reported. Both ignored and untracked files are\n> controlled by arguments to `--untracked-files` option. This makes it\n> impossible to show all untracked files individually, but show ignored\n> \"files and directories\" (that is, ignored directories are shown as 1\n> entry, instead of having all contents shown).\n\nThe description makes sense.  And it makes sense to show a directory\nknown to contain only ignored paths as just one entry, instead of\nexploding that to individual files.\n\n> Our application (Visual Studio) has a specific set of requirements\n> about how it wants untracked / ignored files reported by git status.\n\nThis sentence does not read well.  VS has no obligation to read from\n\"git status\", so there is no \"specific set of requirements\" that\nmake us care.  If the output from \"status\" is insufficient you could\nbe reading from \"ls-files --ignored\", for example, if you want more\ndetails than \"git status\" gives you.\n\nThe sentence, and the paragraph that immediately follows it, need a\nserious attitude adjustment ;-), even though it is good as read as\nan explanation of what the proposed output wants to show.\n\n> The reason for controlling these behaviors separately is that there\n> can be a significant performance impact to scanning the contents of\n> excluded directories. Additionally, knowing whether a directory\n> explicitly matches an exclude pattern can help the application make\n> decisions about how to handle the directory. If an ignored directory\n> itself matches an exclude pattern, then the application will know that\n> any files underneath the directory must be ignored as well.\n\nWhile the above description taken standalone makes sense, doesn't\nthe \"we want all paths listed, without abbreviated to the directory\nand requiring the reader to infer from the fact that aidrectory is\nshown that everything in it are ignored\" the log message stated\nearlier contradict another change you earlier sent, that avoids\nscanning a directory that is known to be completely untracked\n(i.e. no path under it in the index) and return early once an\nuntracked file is found in it?\n\n> Signed-off-by: Jameson Miller <jamill@microsoft.com>\n> ---\n>  builtin/commit.c | 31 +++++++++++++++++++++++++------\n>  dir.c            | 24 ++++++++++++++++++++++++\n>  dir.h            |  3 ++-\n>  wt-status.c      | 11 ++++++++---\n>  wt-status.h      |  8 +++++++-\n>  5 files changed, 66 insertions(+), 11 deletions(-)\n>\n> diff --git a/builtin/commit.c b/builtin/commit.c\n> index d75b3805ea..98d84d0277 100644\n> --- a/builtin/commit.c\n> +++ b/builtin/commit.c\n> @@ -118,7 +118,7 @@ static int edit_flag = -1; /* unspecified */\n>  static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;\n>  static int config_commit_verbose = -1; /* unspecified */\n>  static int no_post_rewrite, allow_empty_message;\n> -static char *untracked_files_arg, *force_date, *ignore_submodule_arg;\n> +static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;\n>  static char *sign_commit;\n>  \n>  /*\n> @@ -139,7 +139,7 @@ static const char *cleanup_arg;\n>  static enum commit_whence whence;\n>  static int sequencer_in_use;\n>  static int use_editor = 1, include_status = 1;\n> -static int show_ignored_in_status, have_option_m;\n> +static int have_option_m;\n>  static struct strbuf message = STRBUF_INIT;\n>  \n>  static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;\n> @@ -1075,6 +1075,19 @@ static const char *find_author_by_nickname(const char *name)\n>  \tdie(_(\"--author '%s' is not 'Name <email>' and matches no existing author\"), name);\n>  }\n>  \n> +static void handle_ignored_arg(struct wt_status *s)\n> +{\n> +\tif (!ignored_arg)\n> +\t\t; /* default already initialized */\n> +\telse if (!strcmp(ignored_arg, \"traditional\"))\n> +\t\ts->show_ignored_mode = SHOW_TRADITIONAL_IGNORED;\n> +\telse if (!strcmp(ignored_arg, \"no\"))\n> +\t\ts->show_ignored_mode = SHOW_NO_IGNORED;\n> +\telse if (!strcmp(ignored_arg, \"matching\"))\n> +\t\ts->show_ignored_mode = SHOW_MATCHING_IGNORED;\n> +\telse\n> +\t\tdie(_(\"Invalid ignored mode '%s'\"), ignored_arg);\n> +}\n>  \n>  static void handle_untracked_files_arg(struct wt_status *s)\n>  {\n> @@ -1363,8 +1376,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n>  \t\t  N_(\"mode\"),\n>  \t\t  N_(\"show untracked files, optional modes: all, normal, no. (Default: all)\"),\n>  \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n> -\t\tOPT_BOOL(0, \"ignored\", &show_ignored_in_status,\n> -\t\t\t N_(\"show ignored files\")),\n> +\t\t{ OPTION_STRING, 0, \"ignored\", &ignored_arg,\n> +\t\t  N_(\"mode\"),\n> +\t\t  N_(\"show ignored files, optional modes: traditional, matching, no. (Default: traditional)\"),\n> +\t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"traditional\" },\n>  \t\t{ OPTION_STRING, 0, \"ignore-submodules\", &ignore_submodule_arg, N_(\"when\"),\n>  \t\t  N_(\"ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)\"),\n>  \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n> @@ -1383,8 +1398,12 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n>  \tfinalize_deferred_config(&s);\n>  \n>  \thandle_untracked_files_arg(&s);\n> -\tif (show_ignored_in_status)\n> -\t\ts.show_ignored_files = 1;\n> +\thandle_ignored_arg(&s);\n> +\n> +\tif (s.show_ignored_mode == SHOW_MATCHING_IGNORED &&\n> +\t    s.show_untracked_files == SHOW_NO_UNTRACKED_FILES)\n> +\t\tdie(_(\"Unsupported combination of ignored and untracked-files arguments\"));\n> +\n>  \tparse_pathspec(&s.pathspec, 0,\n>  \t\t       PATHSPEC_PREFER_FULL,\n>  \t\t       prefix, argv);\n> diff --git a/dir.c b/dir.c\n> index 1d17b800cf..b9af87eca9 100644\n> --- a/dir.c\n> +++ b/dir.c\n> @@ -1389,6 +1389,30 @@ static enum path_treatment treat_directory(struct dir_struct *dir,\n>  \tcase index_nonexistent:\n>  \t\tif (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)\n>  \t\t\tbreak;\n> +\t\tif (exclude &&\n> +\t\t\t(dir->flags & DIR_SHOW_IGNORED_TOO) &&\n> +\t\t\t(dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING)) {\n> +\n> +\t\t\t/*\n> +\t\t\t * This is an excluded directory and we are\n> +\t\t\t * showing ignored paths that match an exclude\n> +\t\t\t * pattern.  (e.g. show directory as ignored\n> +\t\t\t * only if it matches an exclude pattern).\n> +\t\t\t * This path will either be 'path_excluded`\n> +\t\t\t * (if we are showing empty directories or if\n> +\t\t\t * the directory is not empty), or will be\n> +\t\t\t * 'path_none' (empty directory, and we are\n> +\t\t\t * not showing empty directories).\n> +\t\t\t */\n> +\t\t\tif (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))\n> +\t\t\t\treturn path_excluded;\n> +\n> +\t\t\tif (read_directory_recursive(dir, istate, dirname, len,\n> +\t\t\t\t\t\t     untracked, 1, 1, pathspec) == path_excluded)\n> +\t\t\t\treturn path_excluded;\n> +\n> +\t\t\treturn path_none;\n> +\t\t}\n>  \t\tif (!(dir->flags & DIR_NO_GITLINKS)) {\n>  \t\t\tunsigned char sha1[20];\n>  \t\t\tif (resolve_gitlink_ref(dirname, \"HEAD\", sha1) == 0)\n> diff --git a/dir.h b/dir.h\n> index e3717055d1..57b0943dae 100644\n> --- a/dir.h\n> +++ b/dir.h\n> @@ -152,7 +152,8 @@ struct dir_struct {\n>  \t\tDIR_COLLECT_IGNORED = 1<<4,\n>  \t\tDIR_SHOW_IGNORED_TOO = 1<<5,\n>  \t\tDIR_COLLECT_KILLED_ONLY = 1<<6,\n> -\t\tDIR_KEEP_UNTRACKED_CONTENTS = 1<<7\n> +\t\tDIR_KEEP_UNTRACKED_CONTENTS = 1<<7,\n> +\t\tDIR_SHOW_IGNORED_TOO_MODE_MATCHING = 1<<8\n>  \t} flags;\n>  \tstruct dir_entry **entries;\n>  \tstruct dir_entry **ignored;\n> diff --git a/wt-status.c b/wt-status.c\n> index 6f730ee8f2..8301c84946 100644\n> --- a/wt-status.c\n> +++ b/wt-status.c\n> @@ -660,10 +660,15 @@ static void wt_status_collect_untracked(struct wt_status *s)\n>  \tif (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)\n>  \t\tdir.flags |=\n>  \t\t\tDIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;\n> -\tif (s->show_ignored_files)\n> +\tif (s->show_ignored_mode) {\n>  \t\tdir.flags |= DIR_SHOW_IGNORED_TOO;\n> -\telse\n> +\n> +\t\tif (s->show_ignored_mode == SHOW_MATCHING_IGNORED)\n> +\t\t\tdir.flags |= DIR_SHOW_IGNORED_TOO_MODE_MATCHING;\n> +\t} else {\n>  \t\tdir.untracked = the_index.untracked;\n> +\t}\n> +\n>  \tsetup_standard_excludes(&dir);\n>  \n>  \tfill_directory(&dir, &the_index, &s->pathspec);\n> @@ -1621,7 +1626,7 @@ static void wt_longstatus_print(struct wt_status *s)\n>  \t}\n>  \tif (s->show_untracked_files) {\n>  \t\twt_longstatus_print_other(s, &s->untracked, _(\"Untracked files\"), \"add\");\n> -\t\tif (s->show_ignored_files)\n> +\t\tif (s->show_ignored_mode)\n>  \t\t\twt_longstatus_print_other(s, &s->ignored, _(\"Ignored files\"), \"add -f\");\n>  \t\tif (advice_status_u_option && 2000 < s->untracked_in_ms) {\n>  \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL, \"%s\", \"\");\n> diff --git a/wt-status.h b/wt-status.h\n> index 64f4d33ea1..fe27b465e2 100644\n> --- a/wt-status.h\n> +++ b/wt-status.h\n> @@ -27,6 +27,12 @@ enum untracked_status_type {\n>  \tSHOW_ALL_UNTRACKED_FILES\n>  };\n>  \n> +enum show_ignored_type {\n> +\tSHOW_NO_IGNORED,\n> +\tSHOW_TRADITIONAL_IGNORED,\n> +\tSHOW_MATCHING_IGNORED,\n> +};\n> +\n>  /* from where does this commit originate */\n>  enum commit_whence {\n>  \tFROM_COMMIT,     /* normal */\n> @@ -70,7 +76,7 @@ struct wt_status {\n>  \tint display_comment_prefix;\n>  \tint relative_paths;\n>  \tint submodule_summary;\n> -\tint show_ignored_files;\n> +\tenum show_ignored_type show_ignored_mode;\n>  \tenum untracked_status_type show_untracked_files;\n>  \tconst char *ignore_submodule_arg;\n>  \tchar color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];\n"},{"id":"330229","messageId":"xmqqtvz513dm.fsf@gitster.mtv.corp.google.com","threadId":"46918","inReplyTo":"20171011133504.15049-3-jamill@microsoft.com","subject":"Re: [PATCH v2 2/5] Update documentation for new directory and status logic","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-10-12T02:55:33Z","receivedAt":"2017-10-12T02:55:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jameson Miller <jameson.miller81@gmail.com> writes:\n\n> Signed-off-by: Jameson Miller <jamill@microsoft.com>\n> ---\n>  Documentation/git-status.txt                      | 21 +++++++++++++++++-\n>  Documentation/technical/api-directory-listing.txt | 27 +++++++++++++++++++----\n>  2 files changed, 43 insertions(+), 5 deletions(-)\n>\n> diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt\n> index 9f3a78a36c..fc282e0a92 100644\n> --- a/Documentation/git-status.txt\n> +++ b/Documentation/git-status.txt\n> @@ -97,8 +97,27 @@ configuration variable documented in linkgit:git-config[1].\n>  \t(and suppresses the output of submodule summaries when the config option\n>  \t`status.submoduleSummary` is set).\n>  \n> ---ignored::\n> +--ignored[=<mode>]::\n>  \tShow ignored files as well.\n> ++\n> +The mode parameter is used to specify the handling of ignored files.\n> +It is optional: it defaults to 'traditional'.\n> ++\n> +The possible options are:\n> ++\n> +\t- 'traditional' - Shows ignored files and directories, unless\n> +\t\t\t  --untracked-files=all is specifed, in which case\n> +\t\t\t  individual files in ignored directories are\n> +\t\t\t  displayed.\n> +\t- 'no'\t        - Show no ignored files.\n> +\t- 'matching'    - Shows ignored files and directories matching an\n> +\t\t\t  ignore pattern.\n> ++\n> +When 'matching' mode is specified, paths that explicity match an\n> +ignored pattern are shown. If a directory matches an ignore pattern,\n> +then it is shown, but not paths contained in the ignored directory. If\n> +a directory does not match an ignore pattern, but all contents are\n> +ignored, then the directory is not shown, but all contents are shown.\n\nWell explained.\n\n> diff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt\n> index 6c77b4920c..7fae00f44f 100644\n> --- a/Documentation/technical/api-directory-listing.txt\n> +++ b/Documentation/technical/api-directory-listing.txt\n> @@ -22,16 +22,20 @@ The notable options are:\n>  \n>  `flags`::\n>  \n> -\tA bit-field of options (the `*IGNORED*` flags are mutually exclusive):\n> +\tA bit-field of options:\n>  \n>  `DIR_SHOW_IGNORED`:::\n>  \n> -\tReturn just ignored files in `entries[]`, not untracked files.\n> +\tReturn just ignored files in `entries[]`, not untracked\n> +\tfiles. This flag is mutually exclusive with\n> +\t`DIR_SHOW_IGNORED_TOO`.\n>  \n>  `DIR_SHOW_IGNORED_TOO`:::\n>  \n> -\tSimilar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]`\n> -\tin addition to untracked files in `entries[]`.\n> +\tSimilar to `DIR_SHOW_IGNORED`, but return ignored files in\n> +\t`ignored[]` in addition to untracked files in\n> +\t`entries[]`. This flag is mutually exclusive with\n> +\t`DIR_SHOW_IGNORED`.\n>  \n>  `DIR_KEEP_UNTRACKED_CONTENTS`:::\n>  \n> @@ -39,6 +43,21 @@ The notable options are:\n>  \tuntracked contents of untracked directories are also returned in\n>  \t`entries[]`.\n>  \n> +`DIR_SHOW_IGNORED_TOO_MODE_MATCHING`:::\n> +\n> +\tOnly has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if\n> +\tthis is set, returns ignored files and directories that match\n> +\tan exclude pattern. If a directory matches an exclude pattern,\n> +\tthen the directory is returned and the contained paths are\n> +\tnot. A directory that does not match an exclude pattern will\n> +\tnot be returned even if all of its contents are ignored. In\n> +\tthis case, the contents are returned as individual entries.\n> ++\n> +If this is set, files and directories that explicity match an ignore\n> +pattern are reported. Implicity ignored directories (directories that\n> +do not match an ignore pattern, but whose contents are all ignored)\n> +are not reported, instead all of the contents are reported.\n\nMakes me wonder if DIR_SHOW_IGNORED* should be splt out into a short\nenum.  We have:\n\n - Do not show ignored ones (0)\n\n - Collect ignored ones (DIR_SHOW_IGNORED)\n\n - Collect ignored and untracked ones separately (DIR_SHOW_IGNORED_TOO)\n\n - Collect ignored and duntracked ones separately, but limit them to\n   those mach exclude patterns explicitly (DIR_SHOW_IGNORED_TOO|...MODE_MATCHING)\n\nso we need two bits to fit a 4-possiblity enum.\n\nThen we do not have to worry about saying quirky things like A and B\nare incompatible, and C makes sense only when B is set, etc.\n\n>  `DIR_COLLECT_IGNORED`:::\n>  \n>  \tSpecial mode for git-add. Return ignored files in `ignored[]` and\n"},{"id":"330235","messageId":"xmqqinfl10gc.fsf@gitster.mtv.corp.google.com","threadId":"46918","inReplyTo":"20171011133504.15049-5-jamill@microsoft.com","subject":"Re: [PATCH v2 4/5] Expand support for ignored arguments on status","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-10-12T03:58:43Z","receivedAt":"2017-10-12T03:58:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jameson Miller <jameson.miller81@gmail.com> writes:\n\n> Teach status command to handle matching ignored mode when showing\n> untracked files with the normal option.\n>\n> Signed-off-by: Jameson Miller <jamill@microsoft.com>\n> ---\n>  dir.c | 20 ++++++++++++++++++--\n>  1 file changed, 18 insertions(+), 2 deletions(-)\n>\n> diff --git a/dir.c b/dir.c\n> index b9af87eca9..8636d080b2 100644\n> --- a/dir.c\n> +++ b/dir.c\n> @@ -1585,6 +1585,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,\n>  {\n>  \tint exclude;\n>  \tint has_path_in_index = !!index_file_exists(istate, path->buf, path->len, ignore_case);\n> +\tenum path_treatment path_treatment;\n>  \n>  \tif (dtype == DT_UNKNOWN)\n>  \t\tdtype = get_dtype(de, istate, path->buf, path->len);\n> @@ -1631,8 +1632,23 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,\n>  \t\treturn path_none;\n>  \tcase DT_DIR:\n>  \t\tstrbuf_addch(path, '/');\n> -\t\treturn treat_directory(dir, istate, untracked, path->buf, path->len,\n> -\t\t\t\t       baselen, exclude, pathspec);\n> +\t\tpath_treatment = treat_directory(dir, istate, untracked,\n> +\t\t\t\t\t\t path->buf, path->len,\n> +\t\t\t\t\t\t baselen, exclude, pathspec);\n> +\t\t/*\n> +\t\t * If we are only want to return directories that\n> +\t\t * match an exclude pattern, and this directories does\n\ns/are //; s/directories/directory/\n\n> +\t\t * not match an exclude pattern but all contents are\n> +\t\t * excluded, then indicate that we should recurse into\n> +\t\t * this directory (instead of marking the directory\n> +\t\t * itself as an ignored path)\n> +\t\t */\n> +\t\tif (!exclude &&\n> +\t\t    path_treatment == path_excluded &&\n> +\t\t    (dir->flags & DIR_SHOW_IGNORED_TOO) &&\n> +\t\t    (dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING))\n> +\t\t\treturn path_recurse;\n> +\t\treturn path_treatment;\n\nThe required change to the code is surprisingly small ;-) and it is\nwell explained in the comment.  Good job.\n\n\n>  \tcase DT_REG:\n>  \tcase DT_LNK:\n>  \t\treturn exclude ? path_excluded : path_untracked;\n"},{"id":"330236","messageId":"xmqqefq91041.fsf@gitster.mtv.corp.google.com","threadId":"46918","inReplyTo":"20171011133504.15049-6-jamill@microsoft.com","subject":"Re: [PATCH v2 5/5] Add tests around status handling of ignored arguments","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-10-12T04:06:06Z","receivedAt":"2017-10-12T04:06:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jameson Miller <jameson.miller81@gmail.com> writes:\n\n> Add tests for status handling of '--ignored=matching` and\n> `--untracked-files=normal`.\n>\n> Signed-off-by: Jameson Miller <jamill@microsoft.com>\n> ---\n\nHmph, having some tests in 3/5, changes in 4/5 and even more tests\nin 5/5 makes me as a reader a bit confused, as the description for\nthese two test patches does not make it clear how they are related\nand how they are different.  Is it that changes in 1/5 alone does\nnot fulfill the promise made by documentation added at 2/5 so 3/5\nonly has tests for behaviour that works with 1/5 alone but is broken\nwith respect to what 2/5 claims until 4/5 is applied, and these \"not\nworking with 1/5 alone, but works after 4/5\" are added in this step?\n\n>  t/t7519-ignored-mode.sh | 60 ++++++++++++++++++++++++++++++++++++++++++++++++-\n>  1 file changed, 59 insertions(+), 1 deletion(-)\n>\n> diff --git a/t/t7519-ignored-mode.sh b/t/t7519-ignored-mode.sh\n> index 76e91427b0..6be7701d79 100755\n> --- a/t/t7519-ignored-mode.sh\n> +++ b/t/t7519-ignored-mode.sh\n> @@ -116,10 +116,68 @@ test_expect_success 'Verify status behavior on ignored folder containing tracked\n>  \t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \\\n>  \t\tignored_dir/tracked &&\n>  \tgit add -f ignored_dir/tracked &&\n> -\ttest_tick &&\n>  \tgit commit -m \"Force add file in ignored directory\" &&\n>  \tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n>  \ttest_i18ncmp expect output\n>  '\n>  \n> +test_expect_success 'Verify matching ignored files with --untracked-files=normal' '\n> +\ttest_when_finished \"git clean -fdx\" &&\n> +\tcat >expect <<-\\EOF &&\n> +\t? expect\n> +\t? output\n> +\t? untracked_dir/\n> +\t! ignored_dir/\n> +\t! ignored_files/ignored_1.ign\n> +\t! ignored_files/ignored_2.ign\n> +\tEOF\n> +\n> +\tmkdir ignored_dir ignored_files untracked_dir &&\n> +\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n> +\t\tignored_files/ignored_1.ign ignored_files/ignored_2.ign \\\n> +\t\tuntracked_dir/untracked &&\n> +\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n> +\ttest_i18ncmp expect output\n> +'\n> +\n> +test_expect_success 'Verify matching ignored files with --untracked-files=normal' '\n> +\ttest_when_finished \"git clean -fdx\" &&\n> +\tcat >expect <<-\\EOF &&\n> +\t? expect\n> +\t? output\n> +\t? untracked_dir/\n> +\t! ignored_dir/\n> +\t! ignored_files/ignored_1.ign\n> +\t! ignored_files/ignored_2.ign\n> +\tEOF\n> +\n> +\tmkdir ignored_dir ignored_files untracked_dir &&\n> +\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n> +\t\tignored_files/ignored_1.ign ignored_files/ignored_2.ign \\\n> +\t\tuntracked_dir/untracked &&\n> +\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n> +\ttest_i18ncmp expect output\n> +'\n> +\n> +test_expect_success 'Verify status behavior on ignored folder containing tracked file' '\n> +\ttest_when_finished \"git clean -fdx && git reset HEAD~1 --hard\" &&\n> +\tcat >expect <<-\\EOF &&\n> +\t? expect\n> +\t? output\n> +\t! ignored_dir/ignored_1\n> +\t! ignored_dir/ignored_1.ign\n> +\t! ignored_dir/ignored_2\n> +\t! ignored_dir/ignored_2.ign\n> +\tEOF\n> +\n> +\tmkdir ignored_dir &&\n> +\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n> +\t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \\\n> +\t\tignored_dir/tracked &&\n> +\tgit add -f ignored_dir/tracked &&\n> +\tgit commit -m \"Force add file in ignored directory\" &&\n> +\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n> +\ttest_i18ncmp expect output\n> +'\n> +\n>  test_done\n"},{"id":"330303","messageId":"6a245436-dfce-fac2-3361-f9da07526ac0@gmail.com","threadId":"46918","inReplyTo":"xmqqy3oh13of.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 1/5] Teach status options around showing ignored files","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-12T20:15:00Z","receivedAt":"2017-10-12T20:15:08Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"\n\nOn 10/11/2017 10:49 PM, Junio C Hamano wrote:\n> Jameson Miller <jameson.miller81@gmail.com> writes:\n>\n>> This change teaches the status command more options to control which\n>> ignored files are reported independently of the which untracked files\n>> are reported by allowing the `--ignored` option to take additional\n>> arguments.  Currently, the shown ignored files are linked to how\n>> untracked files are reported. Both ignored and untracked files are\n>> controlled by arguments to `--untracked-files` option. This makes it\n>> impossible to show all untracked files individually, but show ignored\n>> \"files and directories\" (that is, ignored directories are shown as 1\n>> entry, instead of having all contents shown).\n> The description makes sense.  And it makes sense to show a directory\n> known to contain only ignored paths as just one entry, instead of\n> exploding that to individual files.\n>\n>> Our application (Visual Studio) has a specific set of requirements\n>> about how it wants untracked / ignored files reported by git status.\n> This sentence does not read well.  VS has no obligation to read from\n> \"git status\", so there is no \"specific set of requirements\" that\n> make us care.  If the output from \"status\" is insufficient you could\n> be reading from \"ls-files --ignored\", for example, if you want more\n> details than \"git status\" gives you.\n>\n> The sentence, and the paragraph that immediately follows it, need a\n> serious attitude adjustment ;-), even though it is good as read as\n> an explanation of what the proposed output wants to show.\n\nIt was not my intention to have this paragraph come across this\nway. I meant to express the ideal format of data that our\napplication would like (from whatever source) as motivation for\nwhy we are proposing these changes. I will reword this\nparagraph to remove any unintended implication otherwise.\n\n>> The reason for controlling these behaviors separately is that there\n>> can be a significant performance impact to scanning the contents of\n>> excluded directories. Additionally, knowing whether a directory\n>> explicitly matches an exclude pattern can help the application make\n>> decisions about how to handle the directory. If an ignored directory\n>> itself matches an exclude pattern, then the application will know that\n>> any files underneath the directory must be ignored as well.\n> While the above description taken standalone makes sense, doesn't\n> the \"we want all paths listed, without abbreviated to the directory\n> and requiring the reader to infer from the fact that aidrectory is\n> shown that everything in it are ignored\" the log message stated\n> earlier contradict another change you earlier sent, that avoids\n> scanning a directory that is known to be completely untracked\n> (i.e. no path under it in the index) and return early once an\n> untracked file is found in it?\n\nMy first set of changes introduced a perf optimization without\nany functional changes. The perf optimization was to avoid\nscanning a directory that is known to be ignored (i.e no path\nunder it in the index and the directory matches an exclude\npattern). It returns early once any file is found in it. Any file\nfound must be ignored, as it is contained in an ignored\ndirectory.\n\nThis second set of changes is to allow optional decoupling of how\nignored and untracked items are reported.\n\n>\n>> Signed-off-by: Jameson Miller <jamill@microsoft.com>\n>> ---\n>>   builtin/commit.c | 31 +++++++++++++++++++++++++------\n>>   dir.c            | 24 ++++++++++++++++++++++++\n>>   dir.h            |  3 ++-\n>>   wt-status.c      | 11 ++++++++---\n>>   wt-status.h      |  8 +++++++-\n>>   5 files changed, 66 insertions(+), 11 deletions(-)\n>>\n>> diff --git a/builtin/commit.c b/builtin/commit.c\n>> index d75b3805ea..98d84d0277 100644\n>> --- a/builtin/commit.c\n>> +++ b/builtin/commit.c\n>> @@ -118,7 +118,7 @@ static int edit_flag = -1; /* unspecified */\n>>   static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;\n>>   static int config_commit_verbose = -1; /* unspecified */\n>>   static int no_post_rewrite, allow_empty_message;\n>> -static char *untracked_files_arg, *force_date, *ignore_submodule_arg;\n>> +static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;\n>>   static char *sign_commit;\n>>   \n>>   /*\n>> @@ -139,7 +139,7 @@ static const char *cleanup_arg;\n>>   static enum commit_whence whence;\n>>   static int sequencer_in_use;\n>>   static int use_editor = 1, include_status = 1;\n>> -static int show_ignored_in_status, have_option_m;\n>> +static int have_option_m;\n>>   static struct strbuf message = STRBUF_INIT;\n>>   \n>>   static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;\n>> @@ -1075,6 +1075,19 @@ static const char *find_author_by_nickname(const char *name)\n>>   \tdie(_(\"--author '%s' is not 'Name <email>' and matches no existing author\"), name);\n>>   }\n>>   \n>> +static void handle_ignored_arg(struct wt_status *s)\n>> +{\n>> +\tif (!ignored_arg)\n>> +\t\t; /* default already initialized */\n>> +\telse if (!strcmp(ignored_arg, \"traditional\"))\n>> +\t\ts->show_ignored_mode = SHOW_TRADITIONAL_IGNORED;\n>> +\telse if (!strcmp(ignored_arg, \"no\"))\n>> +\t\ts->show_ignored_mode = SHOW_NO_IGNORED;\n>> +\telse if (!strcmp(ignored_arg, \"matching\"))\n>> +\t\ts->show_ignored_mode = SHOW_MATCHING_IGNORED;\n>> +\telse\n>> +\t\tdie(_(\"Invalid ignored mode '%s'\"), ignored_arg);\n>> +}\n>>   \n>>   static void handle_untracked_files_arg(struct wt_status *s)\n>>   {\n>> @@ -1363,8 +1376,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n>>   \t\t  N_(\"mode\"),\n>>   \t\t  N_(\"show untracked files, optional modes: all, normal, no. (Default: all)\"),\n>>   \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n>> -\t\tOPT_BOOL(0, \"ignored\", &show_ignored_in_status,\n>> -\t\t\t N_(\"show ignored files\")),\n>> +\t\t{ OPTION_STRING, 0, \"ignored\", &ignored_arg,\n>> +\t\t  N_(\"mode\"),\n>> +\t\t  N_(\"show ignored files, optional modes: traditional, matching, no. (Default: traditional)\"),\n>> +\t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"traditional\" },\n>>   \t\t{ OPTION_STRING, 0, \"ignore-submodules\", &ignore_submodule_arg, N_(\"when\"),\n>>   \t\t  N_(\"ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)\"),\n>>   \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n>> @@ -1383,8 +1398,12 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n>>   \tfinalize_deferred_config(&s);\n>>   \n>>   \thandle_untracked_files_arg(&s);\n>> -\tif (show_ignored_in_status)\n>> -\t\ts.show_ignored_files = 1;\n>> +\thandle_ignored_arg(&s);\n>> +\n>> +\tif (s.show_ignored_mode == SHOW_MATCHING_IGNORED &&\n>> +\t    s.show_untracked_files == SHOW_NO_UNTRACKED_FILES)\n>> +\t\tdie(_(\"Unsupported combination of ignored and untracked-files arguments\"));\n>> +\n>>   \tparse_pathspec(&s.pathspec, 0,\n>>   \t\t       PATHSPEC_PREFER_FULL,\n>>   \t\t       prefix, argv);\n>> diff --git a/dir.c b/dir.c\n>> index 1d17b800cf..b9af87eca9 100644\n>> --- a/dir.c\n>> +++ b/dir.c\n>> @@ -1389,6 +1389,30 @@ static enum path_treatment treat_directory(struct dir_struct *dir,\n>>   \tcase index_nonexistent:\n>>   \t\tif (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)\n>>   \t\t\tbreak;\n>> +\t\tif (exclude &&\n>> +\t\t\t(dir->flags & DIR_SHOW_IGNORED_TOO) &&\n>> +\t\t\t(dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING)) {\n>> +\n>> +\t\t\t/*\n>> +\t\t\t * This is an excluded directory and we are\n>> +\t\t\t * showing ignored paths that match an exclude\n>> +\t\t\t * pattern.  (e.g. show directory as ignored\n>> +\t\t\t * only if it matches an exclude pattern).\n>> +\t\t\t * This path will either be 'path_excluded`\n>> +\t\t\t * (if we are showing empty directories or if\n>> +\t\t\t * the directory is not empty), or will be\n>> +\t\t\t * 'path_none' (empty directory, and we are\n>> +\t\t\t * not showing empty directories).\n>> +\t\t\t */\n>> +\t\t\tif (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))\n>> +\t\t\t\treturn path_excluded;\n>> +\n>> +\t\t\tif (read_directory_recursive(dir, istate, dirname, len,\n>> +\t\t\t\t\t\t     untracked, 1, 1, pathspec) == path_excluded)\n>> +\t\t\t\treturn path_excluded;\n>> +\n>> +\t\t\treturn path_none;\n>> +\t\t}\n>>   \t\tif (!(dir->flags & DIR_NO_GITLINKS)) {\n>>   \t\t\tunsigned char sha1[20];\n>>   \t\t\tif (resolve_gitlink_ref(dirname, \"HEAD\", sha1) == 0)\n>> diff --git a/dir.h b/dir.h\n>> index e3717055d1..57b0943dae 100644\n>> --- a/dir.h\n>> +++ b/dir.h\n>> @@ -152,7 +152,8 @@ struct dir_struct {\n>>   \t\tDIR_COLLECT_IGNORED = 1<<4,\n>>   \t\tDIR_SHOW_IGNORED_TOO = 1<<5,\n>>   \t\tDIR_COLLECT_KILLED_ONLY = 1<<6,\n>> -\t\tDIR_KEEP_UNTRACKED_CONTENTS = 1<<7\n>> +\t\tDIR_KEEP_UNTRACKED_CONTENTS = 1<<7,\n>> +\t\tDIR_SHOW_IGNORED_TOO_MODE_MATCHING = 1<<8\n>>   \t} flags;\n>>   \tstruct dir_entry **entries;\n>>   \tstruct dir_entry **ignored;\n>> diff --git a/wt-status.c b/wt-status.c\n>> index 6f730ee8f2..8301c84946 100644\n>> --- a/wt-status.c\n>> +++ b/wt-status.c\n>> @@ -660,10 +660,15 @@ static void wt_status_collect_untracked(struct wt_status *s)\n>>   \tif (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)\n>>   \t\tdir.flags |=\n>>   \t\t\tDIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;\n>> -\tif (s->show_ignored_files)\n>> +\tif (s->show_ignored_mode) {\n>>   \t\tdir.flags |= DIR_SHOW_IGNORED_TOO;\n>> -\telse\n>> +\n>> +\t\tif (s->show_ignored_mode == SHOW_MATCHING_IGNORED)\n>> +\t\t\tdir.flags |= DIR_SHOW_IGNORED_TOO_MODE_MATCHING;\n>> +\t} else {\n>>   \t\tdir.untracked = the_index.untracked;\n>> +\t}\n>> +\n>>   \tsetup_standard_excludes(&dir);\n>>   \n>>   \tfill_directory(&dir, &the_index, &s->pathspec);\n>> @@ -1621,7 +1626,7 @@ static void wt_longstatus_print(struct wt_status *s)\n>>   \t}\n>>   \tif (s->show_untracked_files) {\n>>   \t\twt_longstatus_print_other(s, &s->untracked, _(\"Untracked files\"), \"add\");\n>> -\t\tif (s->show_ignored_files)\n>> +\t\tif (s->show_ignored_mode)\n>>   \t\t\twt_longstatus_print_other(s, &s->ignored, _(\"Ignored files\"), \"add -f\");\n>>   \t\tif (advice_status_u_option && 2000 < s->untracked_in_ms) {\n>>   \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL, \"%s\", \"\");\n>> diff --git a/wt-status.h b/wt-status.h\n>> index 64f4d33ea1..fe27b465e2 100644\n>> --- a/wt-status.h\n>> +++ b/wt-status.h\n>> @@ -27,6 +27,12 @@ enum untracked_status_type {\n>>   \tSHOW_ALL_UNTRACKED_FILES\n>>   };\n>>   \n>> +enum show_ignored_type {\n>> +\tSHOW_NO_IGNORED,\n>> +\tSHOW_TRADITIONAL_IGNORED,\n>> +\tSHOW_MATCHING_IGNORED,\n>> +};\n>> +\n>>   /* from where does this commit originate */\n>>   enum commit_whence {\n>>   \tFROM_COMMIT,     /* normal */\n>> @@ -70,7 +76,7 @@ struct wt_status {\n>>   \tint display_comment_prefix;\n>>   \tint relative_paths;\n>>   \tint submodule_summary;\n>> -\tint show_ignored_files;\n>> +\tenum show_ignored_type show_ignored_mode;\n>>   \tenum untracked_status_type show_untracked_files;\n>>   \tconst char *ignore_submodule_arg;\n>>   \tchar color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];\n\n"},{"id":"330304","messageId":"06a99e74-441e-ab22-fb4c-45302523d5cb@gmail.com","threadId":"46918","inReplyTo":"xmqqefq91041.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 5/5] Add tests around status handling of ignored arguments","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-12T20:16:38Z","receivedAt":"2017-10-12T20:16:48Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"\n\nOn 10/12/2017 12:06 AM, Junio C Hamano wrote:\n> Jameson Miller <jameson.miller81@gmail.com> writes:\n>\n>> Add tests for status handling of '--ignored=matching` and\n>> `--untracked-files=normal`.\n>>\n>> Signed-off-by: Jameson Miller <jamill@microsoft.com>\n>> ---\n> Hmph, having some tests in 3/5, changes in 4/5 and even more tests\n> in 5/5 makes me as a reader a bit confused, as the description for\n> these two test patches does not make it clear how they are related\n> and how they are different.  Is it that changes in 1/5 alone does\n> not fulfill the promise made by documentation added at 2/5 so 3/5\n> only has tests for behaviour that works with 1/5 alone but is broken\n> with respect to what 2/5 claims until 4/5 is applied, and these \"not\n> working with 1/5 alone, but works after 4/5\" are added in this step?\n\nCorrect. The changes in 1/5 are to implement \"--ignored=matching\"\nwith \"--untracked-files=all\" with corresponding tests in 3/5. The\nchanges in 4/5 are to implement \"--ignored=matching\"\nwith \"--untracked-files=normal\" and the corresponding tests are\nin 5/5.\n\nDo you have a preference on how I organized this work? I see\nseveral possible ways to split up this work. Maybe it would be\nless confusing to have the implementation in the first two\ncommits, then 1 commit with all the tests, and then a commit with\nthe documentation? I think it makes sense to have the logic for\nthe different flag combinations split into their own commits, but\nI am open to any suggestions.\n\n>\n>>   t/t7519-ignored-mode.sh | 60 ++++++++++++++++++++++++++++++++++++++++++++++++-\n>>   1 file changed, 59 insertions(+), 1 deletion(-)\n>>\n>> diff --git a/t/t7519-ignored-mode.sh b/t/t7519-ignored-mode.sh\n>> index 76e91427b0..6be7701d79 100755\n>> --- a/t/t7519-ignored-mode.sh\n>> +++ b/t/t7519-ignored-mode.sh\n>> @@ -116,10 +116,68 @@ test_expect_success 'Verify status behavior on ignored folder containing tracked\n>>   \t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \\\n>>   \t\tignored_dir/tracked &&\n>>   \tgit add -f ignored_dir/tracked &&\n>> -\ttest_tick &&\n>>   \tgit commit -m \"Force add file in ignored directory\" &&\n>>   \tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n>>   \ttest_i18ncmp expect output\n>>   '\n>>   \n>> +test_expect_success 'Verify matching ignored files with --untracked-files=normal' '\n>> +\ttest_when_finished \"git clean -fdx\" &&\n>> +\tcat >expect <<-\\EOF &&\n>> +\t? expect\n>> +\t? output\n>> +\t? untracked_dir/\n>> +\t! ignored_dir/\n>> +\t! ignored_files/ignored_1.ign\n>> +\t! ignored_files/ignored_2.ign\n>> +\tEOF\n>> +\n>> +\tmkdir ignored_dir ignored_files untracked_dir &&\n>> +\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n>> +\t\tignored_files/ignored_1.ign ignored_files/ignored_2.ign \\\n>> +\t\tuntracked_dir/untracked &&\n>> +\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n>> +\ttest_i18ncmp expect output\n>> +'\n>> +\n>> +test_expect_success 'Verify matching ignored files with --untracked-files=normal' '\n>> +\ttest_when_finished \"git clean -fdx\" &&\n>> +\tcat >expect <<-\\EOF &&\n>> +\t? expect\n>> +\t? output\n>> +\t? untracked_dir/\n>> +\t! ignored_dir/\n>> +\t! ignored_files/ignored_1.ign\n>> +\t! ignored_files/ignored_2.ign\n>> +\tEOF\n>> +\n>> +\tmkdir ignored_dir ignored_files untracked_dir &&\n>> +\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n>> +\t\tignored_files/ignored_1.ign ignored_files/ignored_2.ign \\\n>> +\t\tuntracked_dir/untracked &&\n>> +\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n>> +\ttest_i18ncmp expect output\n>> +'\n>> +\n>> +test_expect_success 'Verify status behavior on ignored folder containing tracked file' '\n>> +\ttest_when_finished \"git clean -fdx && git reset HEAD~1 --hard\" &&\n>> +\tcat >expect <<-\\EOF &&\n>> +\t? expect\n>> +\t? output\n>> +\t! ignored_dir/ignored_1\n>> +\t! ignored_dir/ignored_1.ign\n>> +\t! ignored_dir/ignored_2\n>> +\t! ignored_dir/ignored_2.ign\n>> +\tEOF\n>> +\n>> +\tmkdir ignored_dir &&\n>> +\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n>> +\t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \\\n>> +\t\tignored_dir/tracked &&\n>> +\tgit add -f ignored_dir/tracked &&\n>> +\tgit commit -m \"Force add file in ignored directory\" &&\n>> +\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n>> +\ttest_i18ncmp expect output\n>> +'\n>> +\n>>   test_done\n\n"},{"id":"330305","messageId":"cdf54fae-806e-c5b7-4420-da66bfcd196a@gmail.com","threadId":"46918","inReplyTo":"xmqq60bl2iyp.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 0/5] Teach Status options around showing ignored files","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-12T20:20:37Z","receivedAt":"2017-10-12T20:20:44Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"\n\nOn 10/11/2017 10:33 PM, Junio C Hamano wrote:\n> Jameson Miller <jameson.miller81@gmail.com> writes:\n>\n>> Changes in v2:\n>>\n>> Addressed review comments from the original series:\n>>\n>> * Made fixes / simplifications suggested for documentation\n>>\n>> * Removed test_tick from test scripts\n>>\n>> * Merged 2 commits (as suggested)\n>>\n>> Jameson Miller (5):\n>>    Teach status options around showing ignored files\n>>    Update documentation for new directory and status logic\n>>    Add tests for git status `--ignored=matching`\n>>    Expand support for ignored arguments on status\n>>    Add tests around status handling of ignored arguments\n> Please make sure these titles mix well when they appear together\n> with changes from other people that are completely unrelated to\n> them.  Keep in mind that your \"git status\" is *not* the most\n> important thing in the world (of course, it is no less important\n> than others, either).  Perhaps\n>\n>      status: add new options to show ignored files differently\n>      status: document logic to show new directory\n>      status: test --ignored=matching\nThank you for the suggestions - I will update the  commit titles in my \nnext round.\n>\n> etc.  Rules of thumb:\n>\n>   (1) choose \"<area>: \" prefix appropriately\n>   (2) keep them short and to the point\n>   (3) word that follow \"<area>: \" prefix is not capitalized\n>   (4) no need for full-stop at the end of the title\n>\n>\n\n"},{"id":"330309","messageId":"c71a2580-ade3-c8d0-d566-272bf3bb572b@gmail.com","threadId":"46918","inReplyTo":"xmqqtvz513dm.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 2/5] Update documentation for new directory and status logic","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-12T20:54:28Z","receivedAt":"2017-10-12T20:54:35Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"\n\nOn 10/11/2017 10:55 PM, Junio C Hamano wrote:\n> Jameson Miller <jameson.miller81@gmail.com> writes:\n>\n>> Signed-off-by: Jameson Miller <jamill@microsoft.com>\n>> ---\n>>   Documentation/git-status.txt                      | 21 +++++++++++++++++-\n>>   Documentation/technical/api-directory-listing.txt | 27 +++++++++++++++++++----\n>>   2 files changed, 43 insertions(+), 5 deletions(-)\n>>\n>> diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt\n>> index 9f3a78a36c..fc282e0a92 100644\n>> --- a/Documentation/git-status.txt\n>> +++ b/Documentation/git-status.txt\n>> @@ -97,8 +97,27 @@ configuration variable documented in linkgit:git-config[1].\n>>   \t(and suppresses the output of submodule summaries when the config option\n>>   \t`status.submoduleSummary` is set).\n>>   \n>> ---ignored::\n>> +--ignored[=<mode>]::\n>>   \tShow ignored files as well.\n>> ++\n>> +The mode parameter is used to specify the handling of ignored files.\n>> +It is optional: it defaults to 'traditional'.\n>> ++\n>> +The possible options are:\n>> ++\n>> +\t- 'traditional' - Shows ignored files and directories, unless\n>> +\t\t\t  --untracked-files=all is specifed, in which case\n>> +\t\t\t  individual files in ignored directories are\n>> +\t\t\t  displayed.\n>> +\t- 'no'\t        - Show no ignored files.\n>> +\t- 'matching'    - Shows ignored files and directories matching an\n>> +\t\t\t  ignore pattern.\n>> ++\n>> +When 'matching' mode is specified, paths that explicity match an\n>> +ignored pattern are shown. If a directory matches an ignore pattern,\n>> +then it is shown, but not paths contained in the ignored directory. If\n>> +a directory does not match an ignore pattern, but all contents are\n>> +ignored, then the directory is not shown, but all contents are shown.\n> Well explained.\n>\n>> diff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt\n>> index 6c77b4920c..7fae00f44f 100644\n>> --- a/Documentation/technical/api-directory-listing.txt\n>> +++ b/Documentation/technical/api-directory-listing.txt\n>> @@ -22,16 +22,20 @@ The notable options are:\n>>   \n>>   `flags`::\n>>   \n>> -\tA bit-field of options (the `*IGNORED*` flags are mutually exclusive):\n>> +\tA bit-field of options:\n>>   \n>>   `DIR_SHOW_IGNORED`:::\n>>   \n>> -\tReturn just ignored files in `entries[]`, not untracked files.\n>> +\tReturn just ignored files in `entries[]`, not untracked\n>> +\tfiles. This flag is mutually exclusive with\n>> +\t`DIR_SHOW_IGNORED_TOO`.\n>>   \n>>   `DIR_SHOW_IGNORED_TOO`:::\n>>   \n>> -\tSimilar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]`\n>> -\tin addition to untracked files in `entries[]`.\n>> +\tSimilar to `DIR_SHOW_IGNORED`, but return ignored files in\n>> +\t`ignored[]` in addition to untracked files in\n>> +\t`entries[]`. This flag is mutually exclusive with\n>> +\t`DIR_SHOW_IGNORED`.\n>>   \n>>   `DIR_KEEP_UNTRACKED_CONTENTS`:::\n>>   \n>> @@ -39,6 +43,21 @@ The notable options are:\n>>   \tuntracked contents of untracked directories are also returned in\n>>   \t`entries[]`.\n>>   \n>> +`DIR_SHOW_IGNORED_TOO_MODE_MATCHING`:::\n>> +\n>> +\tOnly has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if\n>> +\tthis is set, returns ignored files and directories that match\n>> +\tan exclude pattern. If a directory matches an exclude pattern,\n>> +\tthen the directory is returned and the contained paths are\n>> +\tnot. A directory that does not match an exclude pattern will\n>> +\tnot be returned even if all of its contents are ignored. In\n>> +\tthis case, the contents are returned as individual entries.\n>> ++\n>> +If this is set, files and directories that explicity match an ignore\n>> +pattern are reported. Implicity ignored directories (directories that\n>> +do not match an ignore pattern, but whose contents are all ignored)\n>> +are not reported, instead all of the contents are reported.\n> Makes me wonder if DIR_SHOW_IGNORED* should be splt out into a short\n> enum.  We have:\n>\n>   - Do not show ignored ones (0)\n>\n>   - Collect ignored ones (DIR_SHOW_IGNORED)\n>\n>   - Collect ignored and untracked ones separately (DIR_SHOW_IGNORED_TOO)\n>\n>   - Collect ignored and duntracked ones separately, but limit them to\n>     those mach exclude patterns explicitly (DIR_SHOW_IGNORED_TOO|...MODE_MATCHING)\n>\n> so we need two bits to fit a 4-possiblity enum.\n>\n> Then we do not have to worry about saying quirky things like A and B\n> are incompatible, and C makes sense only when B is set, etc.\nI could see a potential for other values for the \"show ignored\nmode\" flags - for example: \"NORMAL\", \"MATCHING\", \"ALL\"... Instead\nof making more change at this point in time, how would you feel\nabout waiting until the next change in this area.\n\nIf you would prefer for me to change these enums now, I can do\nthat.\n\n>\n>>   `DIR_COLLECT_IGNORED`:::\n>>   \n>>   \tSpecial mode for git-add. Return ignored files in `ignored[]` and\n\n"},{"id":"330326","messageId":"xmqqshenx4hv.fsf@gitster.mtv.corp.google.com","threadId":"46918","inReplyTo":"c71a2580-ade3-c8d0-d566-272bf3bb572b@gmail.com","subject":"Re: [PATCH v2 2/5] Update documentation for new directory and status logic","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-10-13T00:42:36Z","receivedAt":"2017-10-13T00:42:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jameson Miller <jameson.miller81@gmail.com> writes:\n\n>>> +If this is set, files and directories that explicity match an ignore\n>>> +pattern are reported. Implicity ignored directories (directories that\n>>> +do not match an ignore pattern, but whose contents are all ignored)\n>>> +are not reported, instead all of the contents are reported.\n>> Makes me wonder if DIR_SHOW_IGNORED* should be splt out into a short\n>> enum.  We have:\n>>\n>>   - Do not show ignored ones (0)\n>>\n>>   - Collect ignored ones (DIR_SHOW_IGNORED)\n>>\n>>   - Collect ignored and untracked ones separately (DIR_SHOW_IGNORED_TOO)\n>>\n>>   - Collect ignored and duntracked ones separately, but limit them to\n>>     those mach exclude patterns explicitly (DIR_SHOW_IGNORED_TOO|...MODE_MATCHING)\n>>\n>> so we need two bits to fit a 4-possiblity enum.\n>>\n>> Then we do not have to worry about saying quirky things like A and B\n>> are incompatible, and C makes sense only when B is set, etc.\n> I could see a potential for other values for the \"show ignored\n> mode\" flags - for example: \"NORMAL\", \"MATCHING\", \"ALL\"... Instead\n> of making more change at this point in time, how would you feel\n> about waiting until the next change in this area.\n>\n> If you would prefer for me to change these enums now, I can do\n> that.\n\n\"Makes me wonder\" was just that.  I was made to wonder.  I did not\nhave strong opinions either way.  You thought about the area of this\ncode longer than I did, so I do not mind you picking the course of\naction that is best for the project, and if you think it is better\nto wait until we know more about the vocabulary we want to support\nbefore we restructure these flags, that is fine by me.\n\nThanks.\n\n\n"},{"id":"330327","messageId":"xmqqo9pbx46y.fsf@gitster.mtv.corp.google.com","threadId":"46918","inReplyTo":"06a99e74-441e-ab22-fb4c-45302523d5cb@gmail.com","subject":"Re: [PATCH v2 5/5] Add tests around status handling of ignored arguments","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-10-13T00:49:09Z","receivedAt":"2017-10-13T00:49:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jameson Miller <jameson.miller81@gmail.com> writes:\n\n>> Hmph, having some tests in 3/5, changes in 4/5 and even more tests\n>> in 5/5 makes me as a reader a bit confused, as the description for\n>> these two test patches does not make it clear how they are related\n>> and how they are different.  Is it that changes in 1/5 alone does\n>> not fulfill the promise made by documentation added at 2/5 so 3/5\n>> only has tests for behaviour that works with 1/5 alone but is broken\n>> with respect to what 2/5 claims until 4/5 is applied, and these \"not\n>> working with 1/5 alone, but works after 4/5\" are added in this step?\n>\n> Correct. The changes in 1/5 are to implement \"--ignored=matching\"\n> with \"--untracked-files=all\" with corresponding tests in 3/5. The\n> changes in 4/5 are to implement \"--ignored=matching\"\n> with \"--untracked-files=normal\" and the corresponding tests are\n> in 5/5.\n>\n> Do you have a preference on how I organized this work? I see\n> several possible ways to split up this work. Maybe it would be\n> less confusing to have the implementation in the first two\n> commits, then 1 commit with all the tests, and then a commit with\n> the documentation? I think it makes sense to have the logic for\n> the different flag combinations split into their own commits, but\n> I am open to any suggestions.\n\nYeah, there are some alternatives, all valid.  \n\nSupport matching/all combination in 1/5, document that in 2/5, test\nthat in 3/5 and then do the same 3-patch series to support\nmatching/normal combination in 4/5, 5/5 and 6/5 would be one.  Doing\ncode, doc and test for matching/all in one patch and doing code, doc\nand test for matching/normal in another patch, resulting in a\ntwo-patch series may be another.  Or your \"code for matching/all in\n1/5, code for matching/normal in 2/5, doc and test for both in\nsubsequent steps\" is fine, too.  All of these would not leave things\nin inconsistent state when we interrupt the series in the middle,\nwhich is a desirable property for those who want to understand the\ntopic.\n\n"},{"id":"330642","messageId":"20171019160601.9382-1-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171005205443.206900-1-jameson.miller81@gmail.com","subject":"[PATCH v3 0/4] status: add option to show ignored files differently","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-19T16:05:57Z","receivedAt":"2017-10-19T16:06:21Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"The previous iteration can be found here:\n\nhttps://public-inbox.org/git/20171011133504.15049-1-jamill@microsoft.com/\n\nThe main difference is to address feedback around commit organization\nand wording.\n\nJameson Miller (4):\n  status: add option to show ignored files differently\n  status: report matching ignored and normal untracked\n  status: document options to show matching ignored files\n  status: test --ignored=matching\n\n Documentation/git-status.txt                      |  21 ++-\n Documentation/technical/api-directory-listing.txt |  27 +++-\n builtin/commit.c                                  |  31 +++-\n dir.c                                             |  44 +++++-\n dir.h                                             |   3 +-\n t/t7519-ignored-mode.sh                           | 183 ++++++++++++++++++++++\n wt-status.c                                       |  11 +-\n wt-status.h                                       |   8 +-\n 8 files changed, 310 insertions(+), 18 deletions(-)\n create mode 100755 t/t7519-ignored-mode.sh\n\n-- \n2.13.6\n\n"},{"id":"330643","messageId":"20171019160601.9382-2-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171019160601.9382-1-jamill@microsoft.com","subject":"[PATCH v3 1/4] status: add option to show ignored files differently","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-19T16:05:58Z","receivedAt":"2017-10-19T16:06:23Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"Teach the status command more flexibility in how ignored files are\nreported. Currently, the reporting of ignored files and untracked\nfiles are linked. You cannot control how ignored files are reported\nindependently of how untracked files are reported (i.e. `all` vs\n`normal`). This makes it impossible to show untracked files with the\n`all` option, but show ignored files with the `normal` option.\n\nThis work 1) adds the ability to control the reporting of ignored\nfiles independently of untracked files and 2) introduces the concept\nof status reporting ignored paths that explicitly match an ignored\npattern. There are 2 benefits to these changes: 1) if a consumer needs\nall untracked files but not all ignored files, there is a performance\nbenefit to not scanning all contents of an ignored directory and 2)\nreturning ignored files that explicitly match a path allow a consumer\nto make more informed decisions about when a status result might be\nstale.\n\nThis commit implements --ignored=matching with --untracked-files=all.\nThe following commit will implement --ignored=matching with\n--untracked=files=normal.\n\nAs an example of where this flexibility could be useful is that our\napplication (Visual Studio) runs the status command and presents the\noutput. It shows all untracked files individually (e.g. using the\n'--untracked-files==all' option), and would like to know about which\npaths are ignored. It uses information about ignored paths to make\ndecisions about when the status result might have changed.\nAdditionally, many projects place build output into directories inside\na repository's working directory (e.g. in \"bin/\" and \"obj/\"\ndirectories). Normal usage is to explicitly ignore these 2 directory\nnames in the .gitignore file (rather than or in addition to the *.obj\npattern).If an application could know that these directories are\nexplicitly ignored, it could infer that all contents are ignored as\nwell and make better informed decisions about files in these\ndirectories. It could infer that any changes under these paths would\nnot affect the output of status. Additionally, there can be a\nsignificant performance benefit by avoiding scanning through ignored\ndirectories.\n\nWhen status is set to report matching ignored files, it has the\nfollowing behavior. Ignored files and directories that explicitly\nmatch an exclude pattern are reported. If an ignored directory matches\nan exclude pattern, then the path of the directory is returned. If a\ndirectory does not match an exclude pattern, but all of its contents\nare ignored, then the contained files are reported instead of the\ndirectory.\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n builtin/commit.c | 31 +++++++++++++++++++++++++------\n dir.c            | 24 ++++++++++++++++++++++++\n dir.h            |  3 ++-\n wt-status.c      | 11 ++++++++---\n wt-status.h      |  8 +++++++-\n 5 files changed, 66 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex d75b3805ea..98d84d0277 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -118,7 +118,7 @@ static int edit_flag = -1; /* unspecified */\n static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;\n static int config_commit_verbose = -1; /* unspecified */\n static int no_post_rewrite, allow_empty_message;\n-static char *untracked_files_arg, *force_date, *ignore_submodule_arg;\n+static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;\n static char *sign_commit;\n \n /*\n@@ -139,7 +139,7 @@ static const char *cleanup_arg;\n static enum commit_whence whence;\n static int sequencer_in_use;\n static int use_editor = 1, include_status = 1;\n-static int show_ignored_in_status, have_option_m;\n+static int have_option_m;\n static struct strbuf message = STRBUF_INIT;\n \n static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;\n@@ -1075,6 +1075,19 @@ static const char *find_author_by_nickname(const char *name)\n \tdie(_(\"--author '%s' is not 'Name <email>' and matches no existing author\"), name);\n }\n \n+static void handle_ignored_arg(struct wt_status *s)\n+{\n+\tif (!ignored_arg)\n+\t\t; /* default already initialized */\n+\telse if (!strcmp(ignored_arg, \"traditional\"))\n+\t\ts->show_ignored_mode = SHOW_TRADITIONAL_IGNORED;\n+\telse if (!strcmp(ignored_arg, \"no\"))\n+\t\ts->show_ignored_mode = SHOW_NO_IGNORED;\n+\telse if (!strcmp(ignored_arg, \"matching\"))\n+\t\ts->show_ignored_mode = SHOW_MATCHING_IGNORED;\n+\telse\n+\t\tdie(_(\"Invalid ignored mode '%s'\"), ignored_arg);\n+}\n \n static void handle_untracked_files_arg(struct wt_status *s)\n {\n@@ -1363,8 +1376,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \t\t  N_(\"mode\"),\n \t\t  N_(\"show untracked files, optional modes: all, normal, no. (Default: all)\"),\n \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n-\t\tOPT_BOOL(0, \"ignored\", &show_ignored_in_status,\n-\t\t\t N_(\"show ignored files\")),\n+\t\t{ OPTION_STRING, 0, \"ignored\", &ignored_arg,\n+\t\t  N_(\"mode\"),\n+\t\t  N_(\"show ignored files, optional modes: traditional, matching, no. (Default: traditional)\"),\n+\t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"traditional\" },\n \t\t{ OPTION_STRING, 0, \"ignore-submodules\", &ignore_submodule_arg, N_(\"when\"),\n \t\t  N_(\"ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)\"),\n \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n@@ -1383,8 +1398,12 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \tfinalize_deferred_config(&s);\n \n \thandle_untracked_files_arg(&s);\n-\tif (show_ignored_in_status)\n-\t\ts.show_ignored_files = 1;\n+\thandle_ignored_arg(&s);\n+\n+\tif (s.show_ignored_mode == SHOW_MATCHING_IGNORED &&\n+\t    s.show_untracked_files == SHOW_NO_UNTRACKED_FILES)\n+\t\tdie(_(\"Unsupported combination of ignored and untracked-files arguments\"));\n+\n \tparse_pathspec(&s.pathspec, 0,\n \t\t       PATHSPEC_PREFER_FULL,\n \t\t       prefix, argv);\ndiff --git a/dir.c b/dir.c\nindex 1d17b800cf..b9af87eca9 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1389,6 +1389,30 @@ static enum path_treatment treat_directory(struct dir_struct *dir,\n \tcase index_nonexistent:\n \t\tif (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)\n \t\t\tbreak;\n+\t\tif (exclude &&\n+\t\t\t(dir->flags & DIR_SHOW_IGNORED_TOO) &&\n+\t\t\t(dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING)) {\n+\n+\t\t\t/*\n+\t\t\t * This is an excluded directory and we are\n+\t\t\t * showing ignored paths that match an exclude\n+\t\t\t * pattern.  (e.g. show directory as ignored\n+\t\t\t * only if it matches an exclude pattern).\n+\t\t\t * This path will either be 'path_excluded`\n+\t\t\t * (if we are showing empty directories or if\n+\t\t\t * the directory is not empty), or will be\n+\t\t\t * 'path_none' (empty directory, and we are\n+\t\t\t * not showing empty directories).\n+\t\t\t */\n+\t\t\tif (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))\n+\t\t\t\treturn path_excluded;\n+\n+\t\t\tif (read_directory_recursive(dir, istate, dirname, len,\n+\t\t\t\t\t\t     untracked, 1, 1, pathspec) == path_excluded)\n+\t\t\t\treturn path_excluded;\n+\n+\t\t\treturn path_none;\n+\t\t}\n \t\tif (!(dir->flags & DIR_NO_GITLINKS)) {\n \t\t\tunsigned char sha1[20];\n \t\t\tif (resolve_gitlink_ref(dirname, \"HEAD\", sha1) == 0)\ndiff --git a/dir.h b/dir.h\nindex e3717055d1..57b0943dae 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -152,7 +152,8 @@ struct dir_struct {\n \t\tDIR_COLLECT_IGNORED = 1<<4,\n \t\tDIR_SHOW_IGNORED_TOO = 1<<5,\n \t\tDIR_COLLECT_KILLED_ONLY = 1<<6,\n-\t\tDIR_KEEP_UNTRACKED_CONTENTS = 1<<7\n+\t\tDIR_KEEP_UNTRACKED_CONTENTS = 1<<7,\n+\t\tDIR_SHOW_IGNORED_TOO_MODE_MATCHING = 1<<8\n \t} flags;\n \tstruct dir_entry **entries;\n \tstruct dir_entry **ignored;\ndiff --git a/wt-status.c b/wt-status.c\nindex 6f730ee8f2..8301c84946 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -660,10 +660,15 @@ static void wt_status_collect_untracked(struct wt_status *s)\n \tif (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)\n \t\tdir.flags |=\n \t\t\tDIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;\n-\tif (s->show_ignored_files)\n+\tif (s->show_ignored_mode) {\n \t\tdir.flags |= DIR_SHOW_IGNORED_TOO;\n-\telse\n+\n+\t\tif (s->show_ignored_mode == SHOW_MATCHING_IGNORED)\n+\t\t\tdir.flags |= DIR_SHOW_IGNORED_TOO_MODE_MATCHING;\n+\t} else {\n \t\tdir.untracked = the_index.untracked;\n+\t}\n+\n \tsetup_standard_excludes(&dir);\n \n \tfill_directory(&dir, &the_index, &s->pathspec);\n@@ -1621,7 +1626,7 @@ static void wt_longstatus_print(struct wt_status *s)\n \t}\n \tif (s->show_untracked_files) {\n \t\twt_longstatus_print_other(s, &s->untracked, _(\"Untracked files\"), \"add\");\n-\t\tif (s->show_ignored_files)\n+\t\tif (s->show_ignored_mode)\n \t\t\twt_longstatus_print_other(s, &s->ignored, _(\"Ignored files\"), \"add -f\");\n \t\tif (advice_status_u_option && 2000 < s->untracked_in_ms) {\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL, \"%s\", \"\");\ndiff --git a/wt-status.h b/wt-status.h\nindex 64f4d33ea1..fe27b465e2 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -27,6 +27,12 @@ enum untracked_status_type {\n \tSHOW_ALL_UNTRACKED_FILES\n };\n \n+enum show_ignored_type {\n+\tSHOW_NO_IGNORED,\n+\tSHOW_TRADITIONAL_IGNORED,\n+\tSHOW_MATCHING_IGNORED,\n+};\n+\n /* from where does this commit originate */\n enum commit_whence {\n \tFROM_COMMIT,     /* normal */\n@@ -70,7 +76,7 @@ struct wt_status {\n \tint display_comment_prefix;\n \tint relative_paths;\n \tint submodule_summary;\n-\tint show_ignored_files;\n+\tenum show_ignored_type show_ignored_mode;\n \tenum untracked_status_type show_untracked_files;\n \tconst char *ignore_submodule_arg;\n \tchar color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];\n-- \n2.13.6\n\n"},{"id":"330644","messageId":"20171019160601.9382-3-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171019160601.9382-1-jamill@microsoft.com","subject":"[PATCH v3 2/4] status: report matching ignored and normal untracked","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-19T16:05:59Z","receivedAt":"2017-10-19T16:06:25Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"Teach status command to handle `--ignored=matching` with\n`--untracked-files=normal`\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n dir.c | 20 ++++++++++++++++++--\n 1 file changed, 18 insertions(+), 2 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex b9af87eca9..20457724c0 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1585,6 +1585,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,\n {\n \tint exclude;\n \tint has_path_in_index = !!index_file_exists(istate, path->buf, path->len, ignore_case);\n+\tenum path_treatment path_treatment;\n \n \tif (dtype == DT_UNKNOWN)\n \t\tdtype = get_dtype(de, istate, path->buf, path->len);\n@@ -1631,8 +1632,23 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,\n \t\treturn path_none;\n \tcase DT_DIR:\n \t\tstrbuf_addch(path, '/');\n-\t\treturn treat_directory(dir, istate, untracked, path->buf, path->len,\n-\t\t\t\t       baselen, exclude, pathspec);\n+\t\tpath_treatment = treat_directory(dir, istate, untracked,\n+\t\t\t\t\t\t path->buf, path->len,\n+\t\t\t\t\t\t baselen, exclude, pathspec);\n+\t\t/*\n+\t\t * If 1) we only want to return directories that\n+\t\t * match an exclude pattern and 2) this directory does\n+\t\t * not match an exclude pattern but all of its\n+\t\t * contents are excluded, then indicate that we should\n+\t\t * recurse into this directory (instead of marking the\n+\t\t * directory itself as an ignored path).\n+\t\t */\n+\t\tif (!exclude &&\n+\t\t    path_treatment == path_excluded &&\n+\t\t    (dir->flags & DIR_SHOW_IGNORED_TOO) &&\n+\t\t    (dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING))\n+\t\t\treturn path_recurse;\n+\t\treturn path_treatment;\n \tcase DT_REG:\n \tcase DT_LNK:\n \t\treturn exclude ? path_excluded : path_untracked;\n-- \n2.13.6\n\n"},{"id":"330645","messageId":"20171019160601.9382-4-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171019160601.9382-1-jamill@microsoft.com","subject":"[PATCH v3 3/4] status: document options to show matching ignored files","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-19T16:06:00Z","receivedAt":"2017-10-19T16:06:27Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"Signed-off-by: Jameson Miller <jamill@microsoft.com>\n---\n Documentation/git-status.txt                      | 21 +++++++++++++++++-\n Documentation/technical/api-directory-listing.txt | 27 +++++++++++++++++++----\n 2 files changed, 43 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-status.txt b/Documentation/git-status.txt\nindex 9f3a78a36c..fc282e0a92 100644\n--- a/Documentation/git-status.txt\n+++ b/Documentation/git-status.txt\n@@ -97,8 +97,27 @@ configuration variable documented in linkgit:git-config[1].\n \t(and suppresses the output of submodule summaries when the config option\n \t`status.submoduleSummary` is set).\n \n---ignored::\n+--ignored[=<mode>]::\n \tShow ignored files as well.\n++\n+The mode parameter is used to specify the handling of ignored files.\n+It is optional: it defaults to 'traditional'.\n++\n+The possible options are:\n++\n+\t- 'traditional' - Shows ignored files and directories, unless\n+\t\t\t  --untracked-files=all is specifed, in which case\n+\t\t\t  individual files in ignored directories are\n+\t\t\t  displayed.\n+\t- 'no'\t        - Show no ignored files.\n+\t- 'matching'    - Shows ignored files and directories matching an\n+\t\t\t  ignore pattern.\n++\n+When 'matching' mode is specified, paths that explicity match an\n+ignored pattern are shown. If a directory matches an ignore pattern,\n+then it is shown, but not paths contained in the ignored directory. If\n+a directory does not match an ignore pattern, but all contents are\n+ignored, then the directory is not shown, but all contents are shown.\n \n -z::\n \tTerminate entries with NUL, instead of LF.  This implies\ndiff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt\nindex 6c77b4920c..7fae00f44f 100644\n--- a/Documentation/technical/api-directory-listing.txt\n+++ b/Documentation/technical/api-directory-listing.txt\n@@ -22,16 +22,20 @@ The notable options are:\n \n `flags`::\n \n-\tA bit-field of options (the `*IGNORED*` flags are mutually exclusive):\n+\tA bit-field of options:\n \n `DIR_SHOW_IGNORED`:::\n \n-\tReturn just ignored files in `entries[]`, not untracked files.\n+\tReturn just ignored files in `entries[]`, not untracked\n+\tfiles. This flag is mutually exclusive with\n+\t`DIR_SHOW_IGNORED_TOO`.\n \n `DIR_SHOW_IGNORED_TOO`:::\n \n-\tSimilar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]`\n-\tin addition to untracked files in `entries[]`.\n+\tSimilar to `DIR_SHOW_IGNORED`, but return ignored files in\n+\t`ignored[]` in addition to untracked files in\n+\t`entries[]`. This flag is mutually exclusive with\n+\t`DIR_SHOW_IGNORED`.\n \n `DIR_KEEP_UNTRACKED_CONTENTS`:::\n \n@@ -39,6 +43,21 @@ The notable options are:\n \tuntracked contents of untracked directories are also returned in\n \t`entries[]`.\n \n+`DIR_SHOW_IGNORED_TOO_MODE_MATCHING`:::\n+\n+\tOnly has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if\n+\tthis is set, returns ignored files and directories that match\n+\tan exclude pattern. If a directory matches an exclude pattern,\n+\tthen the directory is returned and the contained paths are\n+\tnot. A directory that does not match an exclude pattern will\n+\tnot be returned even if all of its contents are ignored. In\n+\tthis case, the contents are returned as individual entries.\n++\n+If this is set, files and directories that explicity match an ignore\n+pattern are reported. Implicity ignored directories (directories that\n+do not match an ignore pattern, but whose contents are all ignored)\n+are not reported, instead all of the contents are reported.\n+\n `DIR_COLLECT_IGNORED`:::\n \n \tSpecial mode for git-add. Return ignored files in `ignored[]` and\n-- \n2.13.6\n\n"},{"id":"330646","messageId":"20171019160601.9382-5-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171019160601.9382-1-jamill@microsoft.com","subject":"[PATCH v3 4/4] status: test --ignored=matching","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-19T16:06:01Z","receivedAt":"2017-10-19T16:06:31Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"Add tests around status reporting ignord files that match an exclude\npattern for both --untracked-files=normal and --untracked-files=all.\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n t/t7519-ignored-mode.sh | 183 ++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 183 insertions(+)\n create mode 100755 t/t7519-ignored-mode.sh\n\ndiff --git a/t/t7519-ignored-mode.sh b/t/t7519-ignored-mode.sh\nnew file mode 100755\nindex 0000000000..6be7701d79\n--- /dev/null\n+++ b/t/t7519-ignored-mode.sh\n@@ -0,0 +1,183 @@\n+#!/bin/sh\n+\n+test_description='git status collapse ignored'\n+\n+. ./test-lib.sh\n+\n+# commit initial ignore file\n+test_expect_success 'setup initial commit and ignore file' '\n+\tcat >.gitignore <<-\\EOF &&\n+\t*.ign\n+\tignored_dir/\n+\t!*.unignore\n+\tEOF\n+\tgit add . &&\n+\tgit commit -m \"Initial commit\"\n+'\n+\n+test_expect_success 'Verify behavior of status on folders with ignored files' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! dir/ignored/ignored_1.ign\n+\t! dir/ignored/ignored_2.ign\n+\t! ignored/ignored_1.ign\n+\t! ignored/ignored_2.ign\n+\tEOF\n+\n+\tmkdir -p ignored dir/ignored &&\n+\ttouch ignored/ignored_1.ign ignored/ignored_2.ign \\\n+\t\tdir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status behavior on folder with tracked & ignored files' '\n+\ttest_when_finished \"git clean -fdx && git reset HEAD~1 --hard\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! dir/tracked_ignored/ignored_1.ign\n+\t! dir/tracked_ignored/ignored_2.ign\n+\t! tracked_ignored/ignored_1.ign\n+\t! tracked_ignored/ignored_2.ign\n+\tEOF\n+\n+\tmkdir -p tracked_ignored dir/tracked_ignored &&\n+\ttouch tracked_ignored/tracked_1 tracked_ignored/tracked_2 \\\n+\t\ttracked_ignored/ignored_1.ign tracked_ignored/ignored_2.ign \\\n+\t\tdir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 \\\n+\t\tdir/tracked_ignored/ignored_1.ign dir/tracked_ignored/ignored_2.ign &&\n+\n+\tgit add tracked_ignored/tracked_1 tracked_ignored/tracked_2 \\\n+\t\tdir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 &&\n+\tgit commit -m \"commit tracked files\" &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status behavior on folder with untracked and ignored files' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? dir/untracked_ignored/untracked_1\n+\t? dir/untracked_ignored/untracked_2\n+\t? expect\n+\t? output\n+\t? untracked_ignored/untracked_1\n+\t? untracked_ignored/untracked_2\n+\t! dir/untracked_ignored/ignored_1.ign\n+\t! dir/untracked_ignored/ignored_2.ign\n+\t! untracked_ignored/ignored_1.ign\n+\t! untracked_ignored/ignored_2.ign\n+\tEOF\n+\n+\tmkdir -p untracked_ignored dir/untracked_ignored &&\n+\ttouch untracked_ignored/untracked_1 untracked_ignored/untracked_2 \\\n+\t\tuntracked_ignored/ignored_1.ign untracked_ignored/ignored_2.ign \\\n+\t\tdir/untracked_ignored/untracked_1 dir/untracked_ignored/untracked_2 \\\n+\t\tdir/untracked_ignored/ignored_1.ign dir/untracked_ignored/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status matching ignored files on ignored folder' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! ignored_dir/\n+\tEOF\n+\n+\tmkdir ignored_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status behavior on ignored folder containing tracked file' '\n+\ttest_when_finished \"git clean -fdx && git reset HEAD~1 --hard\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! ignored_dir/ignored_1\n+\t! ignored_dir/ignored_1.ign\n+\t! ignored_dir/ignored_2\n+\t! ignored_dir/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \\\n+\t\tignored_dir/tracked &&\n+\tgit add -f ignored_dir/tracked &&\n+\tgit commit -m \"Force add file in ignored directory\" &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify matching ignored files with --untracked-files=normal' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t? untracked_dir/\n+\t! ignored_dir/\n+\t! ignored_files/ignored_1.ign\n+\t! ignored_files/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir ignored_files untracked_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_files/ignored_1.ign ignored_files/ignored_2.ign \\\n+\t\tuntracked_dir/untracked &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify matching ignored files with --untracked-files=normal' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t? untracked_dir/\n+\t! ignored_dir/\n+\t! ignored_files/ignored_1.ign\n+\t! ignored_files/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir ignored_files untracked_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_files/ignored_1.ign ignored_files/ignored_2.ign \\\n+\t\tuntracked_dir/untracked &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status behavior on ignored folder containing tracked file' '\n+\ttest_when_finished \"git clean -fdx && git reset HEAD~1 --hard\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! ignored_dir/ignored_1\n+\t! ignored_dir/ignored_1.ign\n+\t! ignored_dir/ignored_2\n+\t! ignored_dir/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \\\n+\t\tignored_dir/tracked &&\n+\tgit add -f ignored_dir/tracked &&\n+\tgit commit -m \"Force add file in ignored directory\" &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_done\n-- \n2.13.6\n\n"},{"id":"330664","messageId":"CAGZ79kacr868zQOXNJ889MY+cgjprU328syaQX4mY1qdm-c4rw@mail.gmail.com","threadId":"46918","inReplyTo":"20171019160601.9382-5-jamill@microsoft.com","subject":"Re: [PATCH v3 4/4] status: test --ignored=matching","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-10-19T19:33:58Z","receivedAt":"2017-10-19T19:34:05Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Thu, Oct 19, 2017 at 9:06 AM, Jameson Miller\n<jameson.miller81@gmail.com> wrote:\n\n> +test_expect_success 'Verify behavior of status on folders with ignored files' '\n\nhttps://stackoverflow.com/questions/5078676/what-is-the-difference-between-a-directory-and-a-folder\nWe test directories here?\n\nAll tests are testing --ignored=matching, do we want to have at least one test\nof \"no\" and \"traditional\" as well? (Just to see if the parsing works;\nwe can modify\nan existing test for that?)\n"},{"id":"330797","messageId":"xmqq4lqsdjai.fsf@gitster.mtv.corp.google.com","threadId":"46918","inReplyTo":"20171019160601.9382-5-jamill@microsoft.com","subject":"Re: [PATCH v3 4/4] status: test --ignored=matching","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-10-22T02:10:29Z","receivedAt":"2017-10-22T02:10:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jameson Miller <jameson.miller81@gmail.com> writes:\n\n> Add tests around status reporting ignord files that match an exclude\n> pattern for both --untracked-files=normal and --untracked-files=all.\n>\n> Signed-off-by: Jameson Miller <jamill@microsoft.com>\n> ---\n>  t/t7519-ignored-mode.sh | 183 ++++++++++++++++++++++++++++++++++++++++++++++++\n>  1 file changed, 183 insertions(+)\n>  create mode 100755 t/t7519-ignored-mode.sh\n\nBen's fsmonitor series already uses t7519, so please move this\nsomewhere else.  Perhaps\n\n    git diff --name-only --diff-filter=A master pu t/\n\nwould help you see what new tests that are missing from 'master'\nare added by topics in flight.\n"},{"id":"330817","messageId":"xmqqzi8ibkq6.fsf@gitster.mtv.corp.google.com","threadId":"46918","inReplyTo":"20171019160601.9382-2-jamill@microsoft.com","subject":"Re: [PATCH v3 1/4] status: add option to show ignored files differently","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-10-23T03:34:41Z","receivedAt":"2017-10-23T03:34:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jameson Miller <jameson.miller81@gmail.com> writes:\n\n> Teach the status command more flexibility in how ignored files are\n> reported. Currently, the reporting of ignored files and untracked\n> files are linked. You cannot control how ignored files are reported\n> independently of how untracked files are reported (i.e. `all` vs\n> `normal`). This makes it impossible to show untracked files with the\n> `all` option, but show ignored files with the `normal` option.\n> \n> This work 1) adds the ability to control the reporting of ignored\n> files independently of untracked files and 2) introduces the concept\n> of status reporting ignored paths that explicitly match an ignored\n> pattern. ...\n>\n> When status is set to report matching ignored files, it has the\n> following behavior. Ignored files and directories that explicitly\n> match an exclude pattern are reported. If an ignored directory matches\n> an exclude pattern, then the path of the directory is returned. If a\n> directory does not match an exclude pattern, but all of its contents\n> are ignored, then the contained files are reported instead of the\n> directory.\n\nThanks for an updated log message.  Very nicely explained.\n\n> +static void handle_ignored_arg(struct wt_status *s)\n> +{\n> +\tif (!ignored_arg)\n> +\t\t; /* default already initialized */\n> +\telse if (!strcmp(ignored_arg, \"traditional\"))\n> +\t\ts->show_ignored_mode = SHOW_TRADITIONAL_IGNORED;\n> +\telse if (!strcmp(ignored_arg, \"no\"))\n> +\t\ts->show_ignored_mode = SHOW_NO_IGNORED;\n> +\telse if (!strcmp(ignored_arg, \"matching\"))\n> +\t\ts->show_ignored_mode = SHOW_MATCHING_IGNORED;\n> +\telse\n> +\t\tdie(_(\"Invalid ignored mode '%s'\"), ignored_arg);\n> +}\n>  \n>  static void handle_untracked_files_arg(struct wt_status *s)\n>  {\n> @@ -1363,8 +1376,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n>  \t\t  N_(\"mode\"),\n>  \t\t  N_(\"show untracked files, optional modes: all, normal, no. (Default: all)\"),\n>  \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n> -\t\tOPT_BOOL(0, \"ignored\", &show_ignored_in_status,\n> -\t\t\t N_(\"show ignored files\")),\n> +\t\t{ OPTION_STRING, 0, \"ignored\", &ignored_arg,\n> +\t\t  N_(\"mode\"),\n> +\t\t  N_(\"show ignored files, optional modes: traditional, matching, no. (Default: traditional)\"),\n> +\t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"traditional\" },\n\nWe used to use \"show_ignored_in_status\" variable that can be either\n0 (when no --ignored is given, or an explicit --no-ignored is given,\non the command line) or 1 (when --ignored is given on the command\nline).\n\nWe still allow \"--ignored\" without value for backward compatibility,\nbut \n\n    $ git status -uall --ignored \\*.c\n\nmay trigger die() from handle_ignored_arg().  I wonder if this is\nsomething we care about.\n\n\t... goes and digs ...\n\nOK, because of PARSE_OPT_OPTARG, the above is safe; \"*.c\" is not\nmistaken as the value given to the --ignored option.  It also means\nthat\n\n    $ git status -uno --ignored matching\n\nwould not mean what a naïve reader may expect and does not error\nout, even though\n\n    $ git status -uno --ignored=matching\n\nwould.  Which is something we eventually might care about, but that\nis how parse-options PARSE_OPT_OPTARG works, and I consider \"fixing\"\nit is totally out of the scope of this series (e.g. the next option\n\"--ignore-submodules\" below shares exactly the same issue).\n\n>  \t\t{ OPTION_STRING, 0, \"ignore-submodules\", &ignore_submodule_arg, N_(\"when\"),\n>  \t\t  N_(\"ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)\"),\n>  \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n\n> diff --git a/wt-status.c b/wt-status.c\n> index 6f730ee8f2..8301c84946 100644\n> --- a/wt-status.c\n> +++ b/wt-status.c\n> @@ -660,10 +660,15 @@ static void wt_status_collect_untracked(struct wt_status *s)\n>  \tif (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)\n>  \t\tdir.flags |=\n>  \t\t\tDIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;\n> -\tif (s->show_ignored_files)\n> +\tif (s->show_ignored_mode) {\n\nNow we no longer use show_ignored_files that was a boolean yes/no,\nand instead use an enum show_ignored_mode, we'd better spell this\nout like so:\n\n\tif (s->show_ignored_mode == SHOW_NO_IGNORED) {\n\n>  \t\tdir.flags |= DIR_SHOW_IGNORED_TOO;\n> -\telse\n> +\n> +\t\tif (s->show_ignored_mode == SHOW_MATCHING_IGNORED)\n> +\t\t\tdir.flags |= DIR_SHOW_IGNORED_TOO_MODE_MATCHING;\n\nMental note: the old \"show_ignored_files is true\" case is now split\ninto two, i.e. traditional vs matching.  When matching is used,\ndir.flags gets a new bit, i.e. DIR_SHOW_IGNORED_TOO_MODE_MATCHING.\nLack of that bit means dir.c helpers should behave as before.\n\n> +\t} else {\n>  \t\tdir.untracked = the_index.untracked;\n> +\t}\n> +\n>  \tsetup_standard_excludes(&dir);\n>  \n>  \tfill_directory(&dir, &the_index, &s->pathspec);\n> @@ -1621,7 +1626,7 @@ static void wt_longstatus_print(struct wt_status *s)\n>  \t}\n>  \tif (s->show_untracked_files) {\n>  \t\twt_longstatus_print_other(s, &s->untracked, _(\"Untracked files\"), \"add\");\n> -\t\tif (s->show_ignored_files)\n> +\t\tif (s->show_ignored_mode)\n\nLikewise, i.e.\n\n\tif (s->show_ignored_mode == SHOW_NO_IGNORED)\n\n>  \t\t\twt_longstatus_print_other(s, &s->ignored, _(\"Ignored files\"), \"add -f\");\n>  \t\tif (advice_status_u_option && 2000 < s->untracked_in_ms) {\n>  \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL, \"%s\", \"\");\n\n> diff --git a/dir.c b/dir.c\n> index 1d17b800cf..b9af87eca9 100644\n> --- a/dir.c\n> +++ b/dir.c\n> @@ -1389,6 +1389,30 @@ static enum path_treatment treat_directory(struct dir_struct *dir,\n>  \tcase index_nonexistent:\n>  \t\tif (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)\n>  \t\t\tbreak;\n> +\t\tif (exclude &&\n> +\t\t\t(dir->flags & DIR_SHOW_IGNORED_TOO) &&\n> +\t\t\t(dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING)) {\n\nIt somehow feels superfluous to check both TOO and TOO_MODE_MATCHING\nhere (IOW, wouldn't the latter be superset of the former?  Is there\na valid case where the latter is set but not the former?), but it is\nperhaps OK as belt-and-suspenders sanity.\n\n> +\t\t\t/*\n> +\t\t\t * This is an excluded directory and we are\n> +\t\t\t * showing ignored paths that match an exclude\n> +\t\t\t * pattern.  (e.g. show directory as ignored\n> +\t\t\t * only if it matches an exclude pattern).\n> +\t\t\t * This path will either be 'path_excluded`\n> +\t\t\t * (if we are showing empty directories or if\n> +\t\t\t * the directory is not empty), or will be\n> +\t\t\t * 'path_none' (empty directory, and we are\n> +\t\t\t * not showing empty directories).\n> +\t\t\t */\n\nThe only caller of treat_directory() calls us when the path we are\nseeing (sans trailing '/' that was added immediately before we were\ncalled) passed \"is_excluded()\" check in the caller, so at this\npoint, the directory in question is being excluded because it\nmatched an exclude pattern.  IOW, if we are a/b/c, a pattern such as\n\"a/b/*\" may have said we are excluded and made the call to this\nfunction.  Do we need to ensure that a pattern like \"a/*\" is not\nwhat excluded us?  Or IGNORED_TOO_MODE_MATCHING also should show\n\"a/b/c\" when a pattern \"a/*\" matched it as \"ignored because it\nmatched an exclude pattern\"?\n\n> +\t\t\tif (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))\n> +\t\t\t\treturn path_excluded;\n> +\n> +\t\t\tif (read_directory_recursive(dir, istate, dirname, len,\n> +\t\t\t\t\t\t     untracked, 1, 1, pathspec) == path_excluded)\n> +\t\t\t\treturn path_excluded;\n> +\n> +\t\t\treturn path_none;\n> +\t\t}\n\n... I guess we won't get into such a situation because this codepath\nwill return only _excluded or _none when we are handing \"a/b\" which\nis already excluded \"a/*\", and does not cause the caller to recurse\ninto \"a/b/c\" in the first place.  So the updated code looks fine.\n\n>  \t\tif (!(dir->flags & DIR_NO_GITLINKS)) {\n>  \t\t\tunsigned char sha1[20];\n>  \t\t\tif (resolve_gitlink_ref(dirname, \"HEAD\", sha1) == 0)\n> diff --git a/dir.h b/dir.h\n> index e3717055d1..57b0943dae 100644\n> --- a/dir.h\n> +++ b/dir.h\n> @@ -152,7 +152,8 @@ struct dir_struct {\n>  \t\tDIR_COLLECT_IGNORED = 1<<4,\n>  \t\tDIR_SHOW_IGNORED_TOO = 1<<5,\n>  \t\tDIR_COLLECT_KILLED_ONLY = 1<<6,\n> -\t\tDIR_KEEP_UNTRACKED_CONTENTS = 1<<7\n> +\t\tDIR_KEEP_UNTRACKED_CONTENTS = 1<<7,\n> +\t\tDIR_SHOW_IGNORED_TOO_MODE_MATCHING = 1<<8\n>  \t} flags;\n>  \tstruct dir_entry **entries;\n>  \tstruct dir_entry **ignored;\n"},{"id":"330818","messageId":"xmqqvaj6bhf1.fsf@gitster.mtv.corp.google.com","threadId":"46918","inReplyTo":"20171019160601.9382-1-jamill@microsoft.com","subject":"Re: [PATCH v3 0/4] status: add option to show ignored files differently","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-10-23T04:46:10Z","receivedAt":"2017-10-23T04:46:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jameson Miller <jameson.miller81@gmail.com> writes:\n\n> Jameson Miller (4):\n>   status: add option to show ignored files differently\n>   status: report matching ignored and normal untracked\n>   status: document options to show matching ignored files\n>   status: test --ignored=matching\n\nThanks, queued.\n"},{"id":"330839","messageId":"20171023170534.157740-1-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171005205443.206900-1-jameson.miller81@gmail.com","subject":"[PATCH v4 0/4] status: add option to show ignored files differently","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-23T17:05:30Z","receivedAt":"2017-10-23T17:05:57Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"The previous iteration can be found here:\n\nhttps://public-inbox.org/git/20171019160601.9382-1-jamill@microsoft.com/\n\n\nChanges from V3 patch series:\n - Added extra test for --ignored=no an --ignored=traditional\n - Updated wording from \"folders\" -> \"directories\"\n - Renamed test to a name not taken in by topics in flight\n\nJameson Miller (4):\n  status: add option to show ignored files differently\n  status: report matching ignored and normal untracked\n  status: document options to show matching ignored files\n  status: test ignored modes\n\n Documentation/git-status.txt                      |  21 +-\n Documentation/technical/api-directory-listing.txt |  27 ++-\n builtin/commit.c                                  |  31 ++-\n dir.c                                             |  44 +++-\n dir.h                                             |   3 +-\n t/t7521-ignored-mode.sh                           | 233 ++++++++++++++++++++++\n wt-status.c                                       |  11 +-\n wt-status.h                                       |   8 +-\n 8 files changed, 360 insertions(+), 18 deletions(-)\n create mode 100755 t/t7521-ignored-mode.sh\n\n-- \n2.13.6\n\n"},{"id":"330840","messageId":"20171023170534.157740-2-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171023170534.157740-1-jamill@microsoft.com","subject":"[PATCH v4 1/4] status: add option to show ignored files differently","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-23T17:05:31Z","receivedAt":"2017-10-23T17:06:03Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"Teach the status command more flexibility in how ignored files are\nreported. Currently, the reporting of ignored files and untracked\nfiles are linked. You cannot control how ignored files are reported\nindependently of how untracked files are reported (i.e. `all` vs\n`normal`). This makes it impossible to show untracked files with the\n`all` option, but show ignored files with the `normal` option.\n\nThis work 1) adds the ability to control the reporting of ignored\nfiles independently of untracked files and 2) introduces the concept\nof status reporting ignored paths that explicitly match an ignored\npattern. There are 2 benefits to these changes: 1) if a consumer needs\nall untracked files but not all ignored files, there is a performance\nbenefit to not scanning all contents of an ignored directory and 2)\nreturning ignored files that explicitly match a path allow a consumer\nto make more informed decisions about when a status result might be\nstale.\n\nThis commit implements --ignored=matching with --untracked-files=all.\nThe following commit will implement --ignored=matching with\n--untracked=files=normal.\n\nAs an example of where this flexibility could be useful is that our\napplication (Visual Studio) runs the status command and presents the\noutput. It shows all untracked files individually (e.g. using the\n'--untracked-files==all' option), and would like to know about which\npaths are ignored. It uses information about ignored paths to make\ndecisions about when the status result might have changed.\nAdditionally, many projects place build output into directories inside\na repository's working directory (e.g. in \"bin/\" and \"obj/\"\ndirectories). Normal usage is to explicitly ignore these 2 directory\nnames in the .gitignore file (rather than or in addition to the *.obj\npattern).If an application could know that these directories are\nexplicitly ignored, it could infer that all contents are ignored as\nwell and make better informed decisions about files in these\ndirectories. It could infer that any changes under these paths would\nnot affect the output of status. Additionally, there can be a\nsignificant performance benefit by avoiding scanning through ignored\ndirectories.\n\nWhen status is set to report matching ignored files, it has the\nfollowing behavior. Ignored files and directories that explicitly\nmatch an exclude pattern are reported. If an ignored directory matches\nan exclude pattern, then the path of the directory is returned. If a\ndirectory does not match an exclude pattern, but all of its contents\nare ignored, then the contained files are reported instead of the\ndirectory.\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n builtin/commit.c | 31 +++++++++++++++++++++++++------\n dir.c            | 24 ++++++++++++++++++++++++\n dir.h            |  3 ++-\n wt-status.c      | 11 ++++++++---\n wt-status.h      |  8 +++++++-\n 5 files changed, 66 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex d75b3805ea..98d84d0277 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -118,7 +118,7 @@ static int edit_flag = -1; /* unspecified */\n static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;\n static int config_commit_verbose = -1; /* unspecified */\n static int no_post_rewrite, allow_empty_message;\n-static char *untracked_files_arg, *force_date, *ignore_submodule_arg;\n+static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;\n static char *sign_commit;\n \n /*\n@@ -139,7 +139,7 @@ static const char *cleanup_arg;\n static enum commit_whence whence;\n static int sequencer_in_use;\n static int use_editor = 1, include_status = 1;\n-static int show_ignored_in_status, have_option_m;\n+static int have_option_m;\n static struct strbuf message = STRBUF_INIT;\n \n static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;\n@@ -1075,6 +1075,19 @@ static const char *find_author_by_nickname(const char *name)\n \tdie(_(\"--author '%s' is not 'Name <email>' and matches no existing author\"), name);\n }\n \n+static void handle_ignored_arg(struct wt_status *s)\n+{\n+\tif (!ignored_arg)\n+\t\t; /* default already initialized */\n+\telse if (!strcmp(ignored_arg, \"traditional\"))\n+\t\ts->show_ignored_mode = SHOW_TRADITIONAL_IGNORED;\n+\telse if (!strcmp(ignored_arg, \"no\"))\n+\t\ts->show_ignored_mode = SHOW_NO_IGNORED;\n+\telse if (!strcmp(ignored_arg, \"matching\"))\n+\t\ts->show_ignored_mode = SHOW_MATCHING_IGNORED;\n+\telse\n+\t\tdie(_(\"Invalid ignored mode '%s'\"), ignored_arg);\n+}\n \n static void handle_untracked_files_arg(struct wt_status *s)\n {\n@@ -1363,8 +1376,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \t\t  N_(\"mode\"),\n \t\t  N_(\"show untracked files, optional modes: all, normal, no. (Default: all)\"),\n \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n-\t\tOPT_BOOL(0, \"ignored\", &show_ignored_in_status,\n-\t\t\t N_(\"show ignored files\")),\n+\t\t{ OPTION_STRING, 0, \"ignored\", &ignored_arg,\n+\t\t  N_(\"mode\"),\n+\t\t  N_(\"show ignored files, optional modes: traditional, matching, no. (Default: traditional)\"),\n+\t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"traditional\" },\n \t\t{ OPTION_STRING, 0, \"ignore-submodules\", &ignore_submodule_arg, N_(\"when\"),\n \t\t  N_(\"ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)\"),\n \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n@@ -1383,8 +1398,12 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \tfinalize_deferred_config(&s);\n \n \thandle_untracked_files_arg(&s);\n-\tif (show_ignored_in_status)\n-\t\ts.show_ignored_files = 1;\n+\thandle_ignored_arg(&s);\n+\n+\tif (s.show_ignored_mode == SHOW_MATCHING_IGNORED &&\n+\t    s.show_untracked_files == SHOW_NO_UNTRACKED_FILES)\n+\t\tdie(_(\"Unsupported combination of ignored and untracked-files arguments\"));\n+\n \tparse_pathspec(&s.pathspec, 0,\n \t\t       PATHSPEC_PREFER_FULL,\n \t\t       prefix, argv);\ndiff --git a/dir.c b/dir.c\nindex 1d17b800cf..b9af87eca9 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1389,6 +1389,30 @@ static enum path_treatment treat_directory(struct dir_struct *dir,\n \tcase index_nonexistent:\n \t\tif (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)\n \t\t\tbreak;\n+\t\tif (exclude &&\n+\t\t\t(dir->flags & DIR_SHOW_IGNORED_TOO) &&\n+\t\t\t(dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING)) {\n+\n+\t\t\t/*\n+\t\t\t * This is an excluded directory and we are\n+\t\t\t * showing ignored paths that match an exclude\n+\t\t\t * pattern.  (e.g. show directory as ignored\n+\t\t\t * only if it matches an exclude pattern).\n+\t\t\t * This path will either be 'path_excluded`\n+\t\t\t * (if we are showing empty directories or if\n+\t\t\t * the directory is not empty), or will be\n+\t\t\t * 'path_none' (empty directory, and we are\n+\t\t\t * not showing empty directories).\n+\t\t\t */\n+\t\t\tif (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))\n+\t\t\t\treturn path_excluded;\n+\n+\t\t\tif (read_directory_recursive(dir, istate, dirname, len,\n+\t\t\t\t\t\t     untracked, 1, 1, pathspec) == path_excluded)\n+\t\t\t\treturn path_excluded;\n+\n+\t\t\treturn path_none;\n+\t\t}\n \t\tif (!(dir->flags & DIR_NO_GITLINKS)) {\n \t\t\tunsigned char sha1[20];\n \t\t\tif (resolve_gitlink_ref(dirname, \"HEAD\", sha1) == 0)\ndiff --git a/dir.h b/dir.h\nindex e3717055d1..57b0943dae 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -152,7 +152,8 @@ struct dir_struct {\n \t\tDIR_COLLECT_IGNORED = 1<<4,\n \t\tDIR_SHOW_IGNORED_TOO = 1<<5,\n \t\tDIR_COLLECT_KILLED_ONLY = 1<<6,\n-\t\tDIR_KEEP_UNTRACKED_CONTENTS = 1<<7\n+\t\tDIR_KEEP_UNTRACKED_CONTENTS = 1<<7,\n+\t\tDIR_SHOW_IGNORED_TOO_MODE_MATCHING = 1<<8\n \t} flags;\n \tstruct dir_entry **entries;\n \tstruct dir_entry **ignored;\ndiff --git a/wt-status.c b/wt-status.c\nindex 29bc64cc02..6760df6176 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -658,10 +658,15 @@ static void wt_status_collect_untracked(struct wt_status *s)\n \tif (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)\n \t\tdir.flags |=\n \t\t\tDIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;\n-\tif (s->show_ignored_files)\n+\tif (s->show_ignored_mode) {\n \t\tdir.flags |= DIR_SHOW_IGNORED_TOO;\n-\telse\n+\n+\t\tif (s->show_ignored_mode == SHOW_MATCHING_IGNORED)\n+\t\t\tdir.flags |= DIR_SHOW_IGNORED_TOO_MODE_MATCHING;\n+\t} else {\n \t\tdir.untracked = the_index.untracked;\n+\t}\n+\n \tsetup_standard_excludes(&dir);\n \n \tfill_directory(&dir, &the_index, &s->pathspec);\n@@ -1619,7 +1624,7 @@ static void wt_longstatus_print(struct wt_status *s)\n \t}\n \tif (s->show_untracked_files) {\n \t\twt_longstatus_print_other(s, &s->untracked, _(\"Untracked files\"), \"add\");\n-\t\tif (s->show_ignored_files)\n+\t\tif (s->show_ignored_mode)\n \t\t\twt_longstatus_print_other(s, &s->ignored, _(\"Ignored files\"), \"add -f\");\n \t\tif (advice_status_u_option && 2000 < s->untracked_in_ms) {\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL, \"%s\", \"\");\ndiff --git a/wt-status.h b/wt-status.h\nindex 64f4d33ea1..fe27b465e2 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -27,6 +27,12 @@ enum untracked_status_type {\n \tSHOW_ALL_UNTRACKED_FILES\n };\n \n+enum show_ignored_type {\n+\tSHOW_NO_IGNORED,\n+\tSHOW_TRADITIONAL_IGNORED,\n+\tSHOW_MATCHING_IGNORED,\n+};\n+\n /* from where does this commit originate */\n enum commit_whence {\n \tFROM_COMMIT,     /* normal */\n@@ -70,7 +76,7 @@ struct wt_status {\n \tint display_comment_prefix;\n \tint relative_paths;\n \tint submodule_summary;\n-\tint show_ignored_files;\n+\tenum show_ignored_type show_ignored_mode;\n \tenum untracked_status_type show_untracked_files;\n \tconst char *ignore_submodule_arg;\n \tchar color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];\n-- \n2.13.6\n\n"},{"id":"330841","messageId":"20171023170534.157740-4-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171023170534.157740-1-jamill@microsoft.com","subject":"[PATCH v4 3/4] status: document options to show matching ignored files","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-23T17:05:33Z","receivedAt":"2017-10-23T17:06:05Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"Signed-off-by: Jameson Miller <jamill@microsoft.com>\n---\n Documentation/git-status.txt                      | 21 +++++++++++++++++-\n Documentation/technical/api-directory-listing.txt | 27 +++++++++++++++++++----\n 2 files changed, 43 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-status.txt b/Documentation/git-status.txt\nindex 9f3a78a36c..fc282e0a92 100644\n--- a/Documentation/git-status.txt\n+++ b/Documentation/git-status.txt\n@@ -97,8 +97,27 @@ configuration variable documented in linkgit:git-config[1].\n \t(and suppresses the output of submodule summaries when the config option\n \t`status.submoduleSummary` is set).\n \n---ignored::\n+--ignored[=<mode>]::\n \tShow ignored files as well.\n++\n+The mode parameter is used to specify the handling of ignored files.\n+It is optional: it defaults to 'traditional'.\n++\n+The possible options are:\n++\n+\t- 'traditional' - Shows ignored files and directories, unless\n+\t\t\t  --untracked-files=all is specifed, in which case\n+\t\t\t  individual files in ignored directories are\n+\t\t\t  displayed.\n+\t- 'no'\t        - Show no ignored files.\n+\t- 'matching'    - Shows ignored files and directories matching an\n+\t\t\t  ignore pattern.\n++\n+When 'matching' mode is specified, paths that explicity match an\n+ignored pattern are shown. If a directory matches an ignore pattern,\n+then it is shown, but not paths contained in the ignored directory. If\n+a directory does not match an ignore pattern, but all contents are\n+ignored, then the directory is not shown, but all contents are shown.\n \n -z::\n \tTerminate entries with NUL, instead of LF.  This implies\ndiff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt\nindex 6c77b4920c..7fae00f44f 100644\n--- a/Documentation/technical/api-directory-listing.txt\n+++ b/Documentation/technical/api-directory-listing.txt\n@@ -22,16 +22,20 @@ The notable options are:\n \n `flags`::\n \n-\tA bit-field of options (the `*IGNORED*` flags are mutually exclusive):\n+\tA bit-field of options:\n \n `DIR_SHOW_IGNORED`:::\n \n-\tReturn just ignored files in `entries[]`, not untracked files.\n+\tReturn just ignored files in `entries[]`, not untracked\n+\tfiles. This flag is mutually exclusive with\n+\t`DIR_SHOW_IGNORED_TOO`.\n \n `DIR_SHOW_IGNORED_TOO`:::\n \n-\tSimilar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]`\n-\tin addition to untracked files in `entries[]`.\n+\tSimilar to `DIR_SHOW_IGNORED`, but return ignored files in\n+\t`ignored[]` in addition to untracked files in\n+\t`entries[]`. This flag is mutually exclusive with\n+\t`DIR_SHOW_IGNORED`.\n \n `DIR_KEEP_UNTRACKED_CONTENTS`:::\n \n@@ -39,6 +43,21 @@ The notable options are:\n \tuntracked contents of untracked directories are also returned in\n \t`entries[]`.\n \n+`DIR_SHOW_IGNORED_TOO_MODE_MATCHING`:::\n+\n+\tOnly has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if\n+\tthis is set, returns ignored files and directories that match\n+\tan exclude pattern. If a directory matches an exclude pattern,\n+\tthen the directory is returned and the contained paths are\n+\tnot. A directory that does not match an exclude pattern will\n+\tnot be returned even if all of its contents are ignored. In\n+\tthis case, the contents are returned as individual entries.\n++\n+If this is set, files and directories that explicity match an ignore\n+pattern are reported. Implicity ignored directories (directories that\n+do not match an ignore pattern, but whose contents are all ignored)\n+are not reported, instead all of the contents are reported.\n+\n `DIR_COLLECT_IGNORED`:::\n \n \tSpecial mode for git-add. Return ignored files in `ignored[]` and\n-- \n2.13.6\n\n"},{"id":"330842","messageId":"20171023170534.157740-3-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171023170534.157740-1-jamill@microsoft.com","subject":"[PATCH v4 2/4] status: report matching ignored and normal untracked","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-23T17:05:32Z","receivedAt":"2017-10-23T17:06:08Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"Teach status command to handle `--ignored=matching` with\n`--untracked-files=normal`\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n dir.c | 20 ++++++++++++++++++--\n 1 file changed, 18 insertions(+), 2 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex b9af87eca9..20457724c0 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1585,6 +1585,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,\n {\n \tint exclude;\n \tint has_path_in_index = !!index_file_exists(istate, path->buf, path->len, ignore_case);\n+\tenum path_treatment path_treatment;\n \n \tif (dtype == DT_UNKNOWN)\n \t\tdtype = get_dtype(de, istate, path->buf, path->len);\n@@ -1631,8 +1632,23 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,\n \t\treturn path_none;\n \tcase DT_DIR:\n \t\tstrbuf_addch(path, '/');\n-\t\treturn treat_directory(dir, istate, untracked, path->buf, path->len,\n-\t\t\t\t       baselen, exclude, pathspec);\n+\t\tpath_treatment = treat_directory(dir, istate, untracked,\n+\t\t\t\t\t\t path->buf, path->len,\n+\t\t\t\t\t\t baselen, exclude, pathspec);\n+\t\t/*\n+\t\t * If 1) we only want to return directories that\n+\t\t * match an exclude pattern and 2) this directory does\n+\t\t * not match an exclude pattern but all of its\n+\t\t * contents are excluded, then indicate that we should\n+\t\t * recurse into this directory (instead of marking the\n+\t\t * directory itself as an ignored path).\n+\t\t */\n+\t\tif (!exclude &&\n+\t\t    path_treatment == path_excluded &&\n+\t\t    (dir->flags & DIR_SHOW_IGNORED_TOO) &&\n+\t\t    (dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING))\n+\t\t\treturn path_recurse;\n+\t\treturn path_treatment;\n \tcase DT_REG:\n \tcase DT_LNK:\n \t\treturn exclude ? path_excluded : path_untracked;\n-- \n2.13.6\n\n"},{"id":"330843","messageId":"20171023170534.157740-5-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171023170534.157740-1-jamill@microsoft.com","subject":"[PATCH v4 4/4] status: test ignored modes","fromName":"Jameson Miller","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-23T17:05:34Z","receivedAt":"2017-10-23T17:06:11Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"Add tests around status reporting ignord files that match an exclude\npattern for both --untracked-files=normal and --untracked-files=all.\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n t/t7521-ignored-mode.sh | 233 ++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 233 insertions(+)\n create mode 100755 t/t7521-ignored-mode.sh\n\ndiff --git a/t/t7521-ignored-mode.sh b/t/t7521-ignored-mode.sh\nnew file mode 100755\nindex 0000000000..91790943c3\n--- /dev/null\n+++ b/t/t7521-ignored-mode.sh\n@@ -0,0 +1,233 @@\n+#!/bin/sh\n+\n+test_description='git status ignored modes'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup initial commit and ignore file' '\n+\tcat >.gitignore <<-\\EOF &&\n+\t*.ign\n+\tignored_dir/\n+\t!*.unignore\n+\tEOF\n+\tgit add . &&\n+\tgit commit -m \"Initial commit\"\n+'\n+\n+test_expect_success 'Verify behavior of status on directories with ignored files' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! dir/ignored/ignored_1.ign\n+\t! dir/ignored/ignored_2.ign\n+\t! ignored/ignored_1.ign\n+\t! ignored/ignored_2.ign\n+\tEOF\n+\n+\tmkdir -p ignored dir/ignored &&\n+\ttouch ignored/ignored_1.ign ignored/ignored_2.ign \\\n+\t\tdir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status behavior on directory with tracked & ignored files' '\n+\ttest_when_finished \"git clean -fdx && git reset HEAD~1 --hard\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! dir/tracked_ignored/ignored_1.ign\n+\t! dir/tracked_ignored/ignored_2.ign\n+\t! tracked_ignored/ignored_1.ign\n+\t! tracked_ignored/ignored_2.ign\n+\tEOF\n+\n+\tmkdir -p tracked_ignored dir/tracked_ignored &&\n+\ttouch tracked_ignored/tracked_1 tracked_ignored/tracked_2 \\\n+\t\ttracked_ignored/ignored_1.ign tracked_ignored/ignored_2.ign \\\n+\t\tdir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 \\\n+\t\tdir/tracked_ignored/ignored_1.ign dir/tracked_ignored/ignored_2.ign &&\n+\n+\tgit add tracked_ignored/tracked_1 tracked_ignored/tracked_2 \\\n+\t\tdir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 &&\n+\tgit commit -m \"commit tracked files\" &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status behavior on directory with untracked and ignored files' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? dir/untracked_ignored/untracked_1\n+\t? dir/untracked_ignored/untracked_2\n+\t? expect\n+\t? output\n+\t? untracked_ignored/untracked_1\n+\t? untracked_ignored/untracked_2\n+\t! dir/untracked_ignored/ignored_1.ign\n+\t! dir/untracked_ignored/ignored_2.ign\n+\t! untracked_ignored/ignored_1.ign\n+\t! untracked_ignored/ignored_2.ign\n+\tEOF\n+\n+\tmkdir -p untracked_ignored dir/untracked_ignored &&\n+\ttouch untracked_ignored/untracked_1 untracked_ignored/untracked_2 \\\n+\t\tuntracked_ignored/ignored_1.ign untracked_ignored/ignored_2.ign \\\n+\t\tdir/untracked_ignored/untracked_1 dir/untracked_ignored/untracked_2 \\\n+\t\tdir/untracked_ignored/ignored_1.ign dir/untracked_ignored/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status matching ignored files on ignored directory' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! ignored_dir/\n+\tEOF\n+\n+\tmkdir ignored_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status behavior on ignored directory containing tracked file' '\n+\ttest_when_finished \"git clean -fdx && git reset HEAD~1 --hard\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! ignored_dir/ignored_1\n+\t! ignored_dir/ignored_1.ign\n+\t! ignored_dir/ignored_2\n+\t! ignored_dir/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \\\n+\t\tignored_dir/tracked &&\n+\tgit add -f ignored_dir/tracked &&\n+\tgit commit -m \"Force add file in ignored directory\" &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify matching ignored files with --untracked-files=normal' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t? untracked_dir/\n+\t! ignored_dir/\n+\t! ignored_files/ignored_1.ign\n+\t! ignored_files/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir ignored_files untracked_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_files/ignored_1.ign ignored_files/ignored_2.ign \\\n+\t\tuntracked_dir/untracked &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify matching ignored files with --untracked-files=normal' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t? untracked_dir/\n+\t! ignored_dir/\n+\t! ignored_files/ignored_1.ign\n+\t! ignored_files/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir ignored_files untracked_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_files/ignored_1.ign ignored_files/ignored_2.ign \\\n+\t\tuntracked_dir/untracked &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status behavior on ignored directory containing tracked file' '\n+\ttest_when_finished \"git clean -fdx && git reset HEAD~1 --hard\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! ignored_dir/ignored_1\n+\t! ignored_dir/ignored_1.ign\n+\t! ignored_dir/ignored_2\n+\t! ignored_dir/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \\\n+\t\tignored_dir/tracked &&\n+\tgit add -f ignored_dir/tracked &&\n+\tgit commit -m \"Force add file in ignored directory\" &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify behavior of status with --ignored=no' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\tEOF\n+\n+\tmkdir -p ignored dir/ignored &&\n+\ttouch ignored/ignored_1.ign ignored/ignored_2.ign \\\n+\t\tdir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=no --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify behavior of status with --ignored=traditional and --untracked-files=all' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! dir/ignored/ignored_1.ign\n+\t! dir/ignored/ignored_2.ign\n+\t! ignored/ignored_1.ign\n+\t! ignored/ignored_2.ign\n+\tEOF\n+\n+\tmkdir -p ignored dir/ignored &&\n+\ttouch ignored/ignored_1.ign ignored/ignored_2.ign \\\n+\t\tdir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=traditional --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify behavior of status with --ignored=traditional and --untracked-files=normal' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! dir/\n+\t! ignored/\n+\tEOF\n+\n+\tmkdir -p ignored dir/ignored &&\n+\ttouch ignored/ignored_1.ign ignored/ignored_2.ign \\\n+\t\tdir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=traditional --untracked-files=normal >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_done\n-- \n2.13.6\n\n"},{"id":"331377","messageId":"20171030172140.235797-1-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171005205443.206900-1-jameson.miller81@gmail.com","subject":"[PATCH v5 0/4] status: add option to show ignored files differently","fromName":"","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-30T17:21:36Z","receivedAt":"2017-10-30T17:21:50Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"From: Jameson Miller <jamill@microsoft.com>\n\nPrevious patch series can be found:\nhttps://public-inbox.org/git/20171023170534.157740-1-jamill@microsoft.com/\n\nOnly difference from previous version is to update the commit author\nemail to match corporate email address.\n\nJameson Miller (4):\n  status: add option to show ignored files differently\n  status: report matching ignored and normal untracked\n  status: document options to show matching ignored files\n  status: test ignored modes\n\n Documentation/git-status.txt                      |  21 +-\n Documentation/technical/api-directory-listing.txt |  27 ++-\n builtin/commit.c                                  |  31 ++-\n dir.c                                             |  44 +++-\n dir.h                                             |   3 +-\n t/t7521-ignored-mode.sh                           | 233 ++++++++++++++++++++++\n wt-status.c                                       |  11 +-\n wt-status.h                                       |   8 +-\n 8 files changed, 360 insertions(+), 18 deletions(-)\n create mode 100755 t/t7521-ignored-mode.sh\n\n-- \n2.13.6\n\n"},{"id":"331378","messageId":"20171030172140.235797-4-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171030172140.235797-1-jamill@microsoft.com","subject":"[PATCH v5 3/4] status: document options to show matching ignored files","fromName":"","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-30T17:21:39Z","receivedAt":"2017-10-30T17:22:04Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"From: Jameson Miller <jamill@microsoft.com>\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n Documentation/git-status.txt                      | 21 +++++++++++++++++-\n Documentation/technical/api-directory-listing.txt | 27 +++++++++++++++++++----\n 2 files changed, 43 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-status.txt b/Documentation/git-status.txt\nindex 9f3a78a36c..fc282e0a92 100644\n--- a/Documentation/git-status.txt\n+++ b/Documentation/git-status.txt\n@@ -97,8 +97,27 @@ configuration variable documented in linkgit:git-config[1].\n \t(and suppresses the output of submodule summaries when the config option\n \t`status.submoduleSummary` is set).\n \n---ignored::\n+--ignored[=<mode>]::\n \tShow ignored files as well.\n++\n+The mode parameter is used to specify the handling of ignored files.\n+It is optional: it defaults to 'traditional'.\n++\n+The possible options are:\n++\n+\t- 'traditional' - Shows ignored files and directories, unless\n+\t\t\t  --untracked-files=all is specifed, in which case\n+\t\t\t  individual files in ignored directories are\n+\t\t\t  displayed.\n+\t- 'no'\t        - Show no ignored files.\n+\t- 'matching'    - Shows ignored files and directories matching an\n+\t\t\t  ignore pattern.\n++\n+When 'matching' mode is specified, paths that explicity match an\n+ignored pattern are shown. If a directory matches an ignore pattern,\n+then it is shown, but not paths contained in the ignored directory. If\n+a directory does not match an ignore pattern, but all contents are\n+ignored, then the directory is not shown, but all contents are shown.\n \n -z::\n \tTerminate entries with NUL, instead of LF.  This implies\ndiff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt\nindex 6c77b4920c..7fae00f44f 100644\n--- a/Documentation/technical/api-directory-listing.txt\n+++ b/Documentation/technical/api-directory-listing.txt\n@@ -22,16 +22,20 @@ The notable options are:\n \n `flags`::\n \n-\tA bit-field of options (the `*IGNORED*` flags are mutually exclusive):\n+\tA bit-field of options:\n \n `DIR_SHOW_IGNORED`:::\n \n-\tReturn just ignored files in `entries[]`, not untracked files.\n+\tReturn just ignored files in `entries[]`, not untracked\n+\tfiles. This flag is mutually exclusive with\n+\t`DIR_SHOW_IGNORED_TOO`.\n \n `DIR_SHOW_IGNORED_TOO`:::\n \n-\tSimilar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]`\n-\tin addition to untracked files in `entries[]`.\n+\tSimilar to `DIR_SHOW_IGNORED`, but return ignored files in\n+\t`ignored[]` in addition to untracked files in\n+\t`entries[]`. This flag is mutually exclusive with\n+\t`DIR_SHOW_IGNORED`.\n \n `DIR_KEEP_UNTRACKED_CONTENTS`:::\n \n@@ -39,6 +43,21 @@ The notable options are:\n \tuntracked contents of untracked directories are also returned in\n \t`entries[]`.\n \n+`DIR_SHOW_IGNORED_TOO_MODE_MATCHING`:::\n+\n+\tOnly has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if\n+\tthis is set, returns ignored files and directories that match\n+\tan exclude pattern. If a directory matches an exclude pattern,\n+\tthen the directory is returned and the contained paths are\n+\tnot. A directory that does not match an exclude pattern will\n+\tnot be returned even if all of its contents are ignored. In\n+\tthis case, the contents are returned as individual entries.\n++\n+If this is set, files and directories that explicity match an ignore\n+pattern are reported. Implicity ignored directories (directories that\n+do not match an ignore pattern, but whose contents are all ignored)\n+are not reported, instead all of the contents are reported.\n+\n `DIR_COLLECT_IGNORED`:::\n \n \tSpecial mode for git-add. Return ignored files in `ignored[]` and\n-- \n2.13.6\n\n"},{"id":"331379","messageId":"20171030172140.235797-5-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171030172140.235797-1-jamill@microsoft.com","subject":"[PATCH v5 4/4] status: test ignored modes","fromName":"","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-30T17:21:40Z","receivedAt":"2017-10-30T17:22:10Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"From: Jameson Miller <jamill@microsoft.com>\n\nAdd tests around status reporting ignord files that match an exclude\npattern for both --untracked-files=normal and --untracked-files=all.\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n t/t7521-ignored-mode.sh | 233 ++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 233 insertions(+)\n create mode 100755 t/t7521-ignored-mode.sh\n\ndiff --git a/t/t7521-ignored-mode.sh b/t/t7521-ignored-mode.sh\nnew file mode 100755\nindex 0000000000..91790943c3\n--- /dev/null\n+++ b/t/t7521-ignored-mode.sh\n@@ -0,0 +1,233 @@\n+#!/bin/sh\n+\n+test_description='git status ignored modes'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup initial commit and ignore file' '\n+\tcat >.gitignore <<-\\EOF &&\n+\t*.ign\n+\tignored_dir/\n+\t!*.unignore\n+\tEOF\n+\tgit add . &&\n+\tgit commit -m \"Initial commit\"\n+'\n+\n+test_expect_success 'Verify behavior of status on directories with ignored files' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! dir/ignored/ignored_1.ign\n+\t! dir/ignored/ignored_2.ign\n+\t! ignored/ignored_1.ign\n+\t! ignored/ignored_2.ign\n+\tEOF\n+\n+\tmkdir -p ignored dir/ignored &&\n+\ttouch ignored/ignored_1.ign ignored/ignored_2.ign \\\n+\t\tdir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status behavior on directory with tracked & ignored files' '\n+\ttest_when_finished \"git clean -fdx && git reset HEAD~1 --hard\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! dir/tracked_ignored/ignored_1.ign\n+\t! dir/tracked_ignored/ignored_2.ign\n+\t! tracked_ignored/ignored_1.ign\n+\t! tracked_ignored/ignored_2.ign\n+\tEOF\n+\n+\tmkdir -p tracked_ignored dir/tracked_ignored &&\n+\ttouch tracked_ignored/tracked_1 tracked_ignored/tracked_2 \\\n+\t\ttracked_ignored/ignored_1.ign tracked_ignored/ignored_2.ign \\\n+\t\tdir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 \\\n+\t\tdir/tracked_ignored/ignored_1.ign dir/tracked_ignored/ignored_2.ign &&\n+\n+\tgit add tracked_ignored/tracked_1 tracked_ignored/tracked_2 \\\n+\t\tdir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 &&\n+\tgit commit -m \"commit tracked files\" &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status behavior on directory with untracked and ignored files' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? dir/untracked_ignored/untracked_1\n+\t? dir/untracked_ignored/untracked_2\n+\t? expect\n+\t? output\n+\t? untracked_ignored/untracked_1\n+\t? untracked_ignored/untracked_2\n+\t! dir/untracked_ignored/ignored_1.ign\n+\t! dir/untracked_ignored/ignored_2.ign\n+\t! untracked_ignored/ignored_1.ign\n+\t! untracked_ignored/ignored_2.ign\n+\tEOF\n+\n+\tmkdir -p untracked_ignored dir/untracked_ignored &&\n+\ttouch untracked_ignored/untracked_1 untracked_ignored/untracked_2 \\\n+\t\tuntracked_ignored/ignored_1.ign untracked_ignored/ignored_2.ign \\\n+\t\tdir/untracked_ignored/untracked_1 dir/untracked_ignored/untracked_2 \\\n+\t\tdir/untracked_ignored/ignored_1.ign dir/untracked_ignored/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status matching ignored files on ignored directory' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! ignored_dir/\n+\tEOF\n+\n+\tmkdir ignored_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status behavior on ignored directory containing tracked file' '\n+\ttest_when_finished \"git clean -fdx && git reset HEAD~1 --hard\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! ignored_dir/ignored_1\n+\t! ignored_dir/ignored_1.ign\n+\t! ignored_dir/ignored_2\n+\t! ignored_dir/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \\\n+\t\tignored_dir/tracked &&\n+\tgit add -f ignored_dir/tracked &&\n+\tgit commit -m \"Force add file in ignored directory\" &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify matching ignored files with --untracked-files=normal' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t? untracked_dir/\n+\t! ignored_dir/\n+\t! ignored_files/ignored_1.ign\n+\t! ignored_files/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir ignored_files untracked_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_files/ignored_1.ign ignored_files/ignored_2.ign \\\n+\t\tuntracked_dir/untracked &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify matching ignored files with --untracked-files=normal' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t? untracked_dir/\n+\t! ignored_dir/\n+\t! ignored_files/ignored_1.ign\n+\t! ignored_files/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir ignored_files untracked_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_files/ignored_1.ign ignored_files/ignored_2.ign \\\n+\t\tuntracked_dir/untracked &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify status behavior on ignored directory containing tracked file' '\n+\ttest_when_finished \"git clean -fdx && git reset HEAD~1 --hard\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! ignored_dir/ignored_1\n+\t! ignored_dir/ignored_1.ign\n+\t! ignored_dir/ignored_2\n+\t! ignored_dir/ignored_2.ign\n+\tEOF\n+\n+\tmkdir ignored_dir &&\n+\ttouch ignored_dir/ignored_1 ignored_dir/ignored_2 \\\n+\t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \\\n+\t\tignored_dir/tracked &&\n+\tgit add -f ignored_dir/tracked &&\n+\tgit commit -m \"Force add file in ignored directory\" &&\n+\tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify behavior of status with --ignored=no' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\tEOF\n+\n+\tmkdir -p ignored dir/ignored &&\n+\ttouch ignored/ignored_1.ign ignored/ignored_2.ign \\\n+\t\tdir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=no --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify behavior of status with --ignored=traditional and --untracked-files=all' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! dir/ignored/ignored_1.ign\n+\t! dir/ignored/ignored_2.ign\n+\t! ignored/ignored_1.ign\n+\t! ignored/ignored_2.ign\n+\tEOF\n+\n+\tmkdir -p ignored dir/ignored &&\n+\ttouch ignored/ignored_1.ign ignored/ignored_2.ign \\\n+\t\tdir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=traditional --untracked-files=all >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_expect_success 'Verify behavior of status with --ignored=traditional and --untracked-files=normal' '\n+\ttest_when_finished \"git clean -fdx\" &&\n+\tcat >expect <<-\\EOF &&\n+\t? expect\n+\t? output\n+\t! dir/\n+\t! ignored/\n+\tEOF\n+\n+\tmkdir -p ignored dir/ignored &&\n+\ttouch ignored/ignored_1.ign ignored/ignored_2.ign \\\n+\t\tdir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&\n+\n+\tgit status --porcelain=v2 --ignored=traditional --untracked-files=normal >output &&\n+\ttest_i18ncmp expect output\n+'\n+\n+test_done\n-- \n2.13.6\n\n"},{"id":"331380","messageId":"20171030172140.235797-3-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171030172140.235797-1-jamill@microsoft.com","subject":"[PATCH v5 2/4] status: report matching ignored and normal untracked","fromName":"","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-30T17:21:38Z","receivedAt":"2017-10-30T17:22:23Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"From: Jameson Miller <jamill@microsoft.com>\n\nTeach status command to handle `--ignored=matching` with\n`--untracked-files=normal`\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n dir.c | 20 ++++++++++++++++++--\n 1 file changed, 18 insertions(+), 2 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex b9af87eca9..20457724c0 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1585,6 +1585,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,\n {\n \tint exclude;\n \tint has_path_in_index = !!index_file_exists(istate, path->buf, path->len, ignore_case);\n+\tenum path_treatment path_treatment;\n \n \tif (dtype == DT_UNKNOWN)\n \t\tdtype = get_dtype(de, istate, path->buf, path->len);\n@@ -1631,8 +1632,23 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,\n \t\treturn path_none;\n \tcase DT_DIR:\n \t\tstrbuf_addch(path, '/');\n-\t\treturn treat_directory(dir, istate, untracked, path->buf, path->len,\n-\t\t\t\t       baselen, exclude, pathspec);\n+\t\tpath_treatment = treat_directory(dir, istate, untracked,\n+\t\t\t\t\t\t path->buf, path->len,\n+\t\t\t\t\t\t baselen, exclude, pathspec);\n+\t\t/*\n+\t\t * If 1) we only want to return directories that\n+\t\t * match an exclude pattern and 2) this directory does\n+\t\t * not match an exclude pattern but all of its\n+\t\t * contents are excluded, then indicate that we should\n+\t\t * recurse into this directory (instead of marking the\n+\t\t * directory itself as an ignored path).\n+\t\t */\n+\t\tif (!exclude &&\n+\t\t    path_treatment == path_excluded &&\n+\t\t    (dir->flags & DIR_SHOW_IGNORED_TOO) &&\n+\t\t    (dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING))\n+\t\t\treturn path_recurse;\n+\t\treturn path_treatment;\n \tcase DT_REG:\n \tcase DT_LNK:\n \t\treturn exclude ? path_excluded : path_untracked;\n-- \n2.13.6\n\n"},{"id":"331381","messageId":"20171030172140.235797-2-jamill@microsoft.com","threadId":"46918","inReplyTo":"20171030172140.235797-1-jamill@microsoft.com","subject":"[PATCH v5 1/4] status: add option to show ignored files differently","fromName":"","fromEmail":"jameson.miller81@gmail.com","sentAt":"2017-10-30T17:21:37Z","receivedAt":"2017-10-30T17:22:27Z","isPatch":true,"sender":{"key":"jameson.miller81@gmail.com","avatar":null},"body":"From: Jameson Miller <jamill@microsoft.com>\n\nTeach the status command more flexibility in how ignored files are\nreported. Currently, the reporting of ignored files and untracked\nfiles are linked. You cannot control how ignored files are reported\nindependently of how untracked files are reported (i.e. `all` vs\n`normal`). This makes it impossible to show untracked files with the\n`all` option, but show ignored files with the `normal` option.\n\nThis work 1) adds the ability to control the reporting of ignored\nfiles independently of untracked files and 2) introduces the concept\nof status reporting ignored paths that explicitly match an ignored\npattern. There are 2 benefits to these changes: 1) if a consumer needs\nall untracked files but not all ignored files, there is a performance\nbenefit to not scanning all contents of an ignored directory and 2)\nreturning ignored files that explicitly match a path allow a consumer\nto make more informed decisions about when a status result might be\nstale.\n\nThis commit implements --ignored=matching with --untracked-files=all.\nThe following commit will implement --ignored=matching with\n--untracked=files=normal.\n\nAs an example of where this flexibility could be useful is that our\napplication (Visual Studio) runs the status command and presents the\noutput. It shows all untracked files individually (e.g. using the\n'--untracked-files==all' option), and would like to know about which\npaths are ignored. It uses information about ignored paths to make\ndecisions about when the status result might have changed.\nAdditionally, many projects place build output into directories inside\na repository's working directory (e.g. in \"bin/\" and \"obj/\"\ndirectories). Normal usage is to explicitly ignore these 2 directory\nnames in the .gitignore file (rather than or in addition to the *.obj\npattern).If an application could know that these directories are\nexplicitly ignored, it could infer that all contents are ignored as\nwell and make better informed decisions about files in these\ndirectories. It could infer that any changes under these paths would\nnot affect the output of status. Additionally, there can be a\nsignificant performance benefit by avoiding scanning through ignored\ndirectories.\n\nWhen status is set to report matching ignored files, it has the\nfollowing behavior. Ignored files and directories that explicitly\nmatch an exclude pattern are reported. If an ignored directory matches\nan exclude pattern, then the path of the directory is returned. If a\ndirectory does not match an exclude pattern, but all of its contents\nare ignored, then the contained files are reported instead of the\ndirectory.\n\nSigned-off-by: Jameson Miller <jamill@microsoft.com>\n---\n builtin/commit.c | 31 +++++++++++++++++++++++++------\n dir.c            | 24 ++++++++++++++++++++++++\n dir.h            |  3 ++-\n wt-status.c      | 11 ++++++++---\n wt-status.h      |  8 +++++++-\n 5 files changed, 66 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex d75b3805ea..98d84d0277 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -118,7 +118,7 @@ static int edit_flag = -1; /* unspecified */\n static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;\n static int config_commit_verbose = -1; /* unspecified */\n static int no_post_rewrite, allow_empty_message;\n-static char *untracked_files_arg, *force_date, *ignore_submodule_arg;\n+static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;\n static char *sign_commit;\n \n /*\n@@ -139,7 +139,7 @@ static const char *cleanup_arg;\n static enum commit_whence whence;\n static int sequencer_in_use;\n static int use_editor = 1, include_status = 1;\n-static int show_ignored_in_status, have_option_m;\n+static int have_option_m;\n static struct strbuf message = STRBUF_INIT;\n \n static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;\n@@ -1075,6 +1075,19 @@ static const char *find_author_by_nickname(const char *name)\n \tdie(_(\"--author '%s' is not 'Name <email>' and matches no existing author\"), name);\n }\n \n+static void handle_ignored_arg(struct wt_status *s)\n+{\n+\tif (!ignored_arg)\n+\t\t; /* default already initialized */\n+\telse if (!strcmp(ignored_arg, \"traditional\"))\n+\t\ts->show_ignored_mode = SHOW_TRADITIONAL_IGNORED;\n+\telse if (!strcmp(ignored_arg, \"no\"))\n+\t\ts->show_ignored_mode = SHOW_NO_IGNORED;\n+\telse if (!strcmp(ignored_arg, \"matching\"))\n+\t\ts->show_ignored_mode = SHOW_MATCHING_IGNORED;\n+\telse\n+\t\tdie(_(\"Invalid ignored mode '%s'\"), ignored_arg);\n+}\n \n static void handle_untracked_files_arg(struct wt_status *s)\n {\n@@ -1363,8 +1376,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \t\t  N_(\"mode\"),\n \t\t  N_(\"show untracked files, optional modes: all, normal, no. (Default: all)\"),\n \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n-\t\tOPT_BOOL(0, \"ignored\", &show_ignored_in_status,\n-\t\t\t N_(\"show ignored files\")),\n+\t\t{ OPTION_STRING, 0, \"ignored\", &ignored_arg,\n+\t\t  N_(\"mode\"),\n+\t\t  N_(\"show ignored files, optional modes: traditional, matching, no. (Default: traditional)\"),\n+\t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"traditional\" },\n \t\t{ OPTION_STRING, 0, \"ignore-submodules\", &ignore_submodule_arg, N_(\"when\"),\n \t\t  N_(\"ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)\"),\n \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n@@ -1383,8 +1398,12 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \tfinalize_deferred_config(&s);\n \n \thandle_untracked_files_arg(&s);\n-\tif (show_ignored_in_status)\n-\t\ts.show_ignored_files = 1;\n+\thandle_ignored_arg(&s);\n+\n+\tif (s.show_ignored_mode == SHOW_MATCHING_IGNORED &&\n+\t    s.show_untracked_files == SHOW_NO_UNTRACKED_FILES)\n+\t\tdie(_(\"Unsupported combination of ignored and untracked-files arguments\"));\n+\n \tparse_pathspec(&s.pathspec, 0,\n \t\t       PATHSPEC_PREFER_FULL,\n \t\t       prefix, argv);\ndiff --git a/dir.c b/dir.c\nindex 1d17b800cf..b9af87eca9 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1389,6 +1389,30 @@ static enum path_treatment treat_directory(struct dir_struct *dir,\n \tcase index_nonexistent:\n \t\tif (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)\n \t\t\tbreak;\n+\t\tif (exclude &&\n+\t\t\t(dir->flags & DIR_SHOW_IGNORED_TOO) &&\n+\t\t\t(dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING)) {\n+\n+\t\t\t/*\n+\t\t\t * This is an excluded directory and we are\n+\t\t\t * showing ignored paths that match an exclude\n+\t\t\t * pattern.  (e.g. show directory as ignored\n+\t\t\t * only if it matches an exclude pattern).\n+\t\t\t * This path will either be 'path_excluded`\n+\t\t\t * (if we are showing empty directories or if\n+\t\t\t * the directory is not empty), or will be\n+\t\t\t * 'path_none' (empty directory, and we are\n+\t\t\t * not showing empty directories).\n+\t\t\t */\n+\t\t\tif (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))\n+\t\t\t\treturn path_excluded;\n+\n+\t\t\tif (read_directory_recursive(dir, istate, dirname, len,\n+\t\t\t\t\t\t     untracked, 1, 1, pathspec) == path_excluded)\n+\t\t\t\treturn path_excluded;\n+\n+\t\t\treturn path_none;\n+\t\t}\n \t\tif (!(dir->flags & DIR_NO_GITLINKS)) {\n \t\t\tunsigned char sha1[20];\n \t\t\tif (resolve_gitlink_ref(dirname, \"HEAD\", sha1) == 0)\ndiff --git a/dir.h b/dir.h\nindex e3717055d1..57b0943dae 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -152,7 +152,8 @@ struct dir_struct {\n \t\tDIR_COLLECT_IGNORED = 1<<4,\n \t\tDIR_SHOW_IGNORED_TOO = 1<<5,\n \t\tDIR_COLLECT_KILLED_ONLY = 1<<6,\n-\t\tDIR_KEEP_UNTRACKED_CONTENTS = 1<<7\n+\t\tDIR_KEEP_UNTRACKED_CONTENTS = 1<<7,\n+\t\tDIR_SHOW_IGNORED_TOO_MODE_MATCHING = 1<<8\n \t} flags;\n \tstruct dir_entry **entries;\n \tstruct dir_entry **ignored;\ndiff --git a/wt-status.c b/wt-status.c\nindex 29bc64cc02..6760df6176 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -658,10 +658,15 @@ static void wt_status_collect_untracked(struct wt_status *s)\n \tif (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)\n \t\tdir.flags |=\n \t\t\tDIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;\n-\tif (s->show_ignored_files)\n+\tif (s->show_ignored_mode) {\n \t\tdir.flags |= DIR_SHOW_IGNORED_TOO;\n-\telse\n+\n+\t\tif (s->show_ignored_mode == SHOW_MATCHING_IGNORED)\n+\t\t\tdir.flags |= DIR_SHOW_IGNORED_TOO_MODE_MATCHING;\n+\t} else {\n \t\tdir.untracked = the_index.untracked;\n+\t}\n+\n \tsetup_standard_excludes(&dir);\n \n \tfill_directory(&dir, &the_index, &s->pathspec);\n@@ -1619,7 +1624,7 @@ static void wt_longstatus_print(struct wt_status *s)\n \t}\n \tif (s->show_untracked_files) {\n \t\twt_longstatus_print_other(s, &s->untracked, _(\"Untracked files\"), \"add\");\n-\t\tif (s->show_ignored_files)\n+\t\tif (s->show_ignored_mode)\n \t\t\twt_longstatus_print_other(s, &s->ignored, _(\"Ignored files\"), \"add -f\");\n \t\tif (advice_status_u_option && 2000 < s->untracked_in_ms) {\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL, \"%s\", \"\");\ndiff --git a/wt-status.h b/wt-status.h\nindex 64f4d33ea1..fe27b465e2 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -27,6 +27,12 @@ enum untracked_status_type {\n \tSHOW_ALL_UNTRACKED_FILES\n };\n \n+enum show_ignored_type {\n+\tSHOW_NO_IGNORED,\n+\tSHOW_TRADITIONAL_IGNORED,\n+\tSHOW_MATCHING_IGNORED,\n+};\n+\n /* from where does this commit originate */\n enum commit_whence {\n \tFROM_COMMIT,     /* normal */\n@@ -70,7 +76,7 @@ struct wt_status {\n \tint display_comment_prefix;\n \tint relative_paths;\n \tint submodule_summary;\n-\tint show_ignored_files;\n+\tenum show_ignored_type show_ignored_mode;\n \tenum untracked_status_type show_untracked_files;\n \tconst char *ignore_submodule_arg;\n \tchar color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];\n-- \n2.13.6\n\n"},{"id":"331445","messageId":"xmqqh8ug2ej1.fsf@gitster.mtv.corp.google.com","threadId":"46918","inReplyTo":"20171030172140.235797-1-jamill@microsoft.com","subject":"Re: [PATCH v5 0/4] status: add option to show ignored files differently","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-10-31T03:16:02Z","receivedAt":"2017-10-31T03:16:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"jameson.miller81@gmail.com writes:\n\n> Only difference from previous version is to update the commit author\n> email to match corporate email address.\n\nWill replace; thanks.\n"}]}