dir: hide untracked contents of untracked dirs

When we taught read_directory_recursive() to recurse into untracked directories in search of ignored files given DIR_SHOW_IGNORED_TOO, that had the side effect of teaching it to collect the untracked contents of untracked directories. It doesn't always make sense to return these, though (we do need them for `clean -d`), so we introduce a flag (DIR_KEEP_UNTRACKED_CONTENTS) to control whether or not read_directory() strips dir->entries of the untracked contents of untracked dirs. We also introduce check_contains() to check if one dir_entry corresponds to a path which contains the path corresponding to another dir_entry. This also fixes known breakages in t7061, since status --ignored now searches untracked directories for ignored files. Signed-off-by: Samuel Lijin <sxlijin@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Samuel Lijin committed May 18, 2017 at 04:21 UTC fb898888491b83c9a3396fb559032ca78807a0c0
4 files changed +41 -3
Documentation/technical/api-directory-listing.txt
+6
@@ -33,6 +33,12 @@ The notable options are:
33 Similar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]`
34 in addition to untracked files in `entries[]`.
35
36 +`DIR_KEEP_UNTRACKED_CONTENTS`:::
37 +
38 + Only has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if this is set, the
39 + untracked contents of untracked directories are also returned in
40 + `entries[]`.
41 +
42 `DIR_COLLECT_IGNORED`:::
43
44 Special mode for git-add. Return ignored files in `ignored[]` and
dir.c
+31
@@ -1813,6 +1813,14 @@ static int cmp_name(const void *p1, const void *p2)
1813 return name_compare(e1->name, e1->len, e2->name, e2->len);
1814 }
1815
1816 +/* check if *out lexically strictly contains *in */
1817 +static int check_contains(const struct dir_entry *out, const struct dir_entry *in)
1818 +{
1819 + return (out->len < in->len) &&
1820 + (out->name[out->len - 1] == '/') &&
1821 + !memcmp(out->name, in->name, out->len);
1822 +}
1823 +
1824 static int treat_leading_path(struct dir_struct *dir,
1825 const char *path, int len,
1826 const struct pathspec *pathspec)
@@ -2028,6 +2036,29 @@ int read_directory(struct dir_struct *dir, const char *path,
2036 read_directory_recursive(dir, path, len, untracked, 0, pathspec);
2037 QSORT(dir->entries, dir->nr, cmp_name);
2038 QSORT(dir->ignored, dir->ignored_nr, cmp_name);
2039 +
2040 + /*
2041 + * If DIR_SHOW_IGNORED_TOO is set, read_directory_recursive() will
2042 + * also pick up untracked contents of untracked dirs; by default
2043 + * we discard these, but given DIR_KEEP_UNTRACKED_CONTENTS we do not.
2044 + */
2045 + if ((dir->flags & DIR_SHOW_IGNORED_TOO) &&
2046 + !(dir->flags & DIR_KEEP_UNTRACKED_CONTENTS)) {
2047 + int i, j;
2048 +
2049 + /* remove from dir->entries untracked contents of untracked dirs */
2050 + for (i = j = 0; j < dir->nr; j++) {
2051 + if (i && check_contains(dir->entries[i - 1], dir->entries[j])) {
2052 + free(dir->entries[j]);
2053 + dir->entries[j] = NULL;
2054 + } else {
2055 + dir->entries[i++] = dir->entries[j];
2056 + }
2057 + }
2058 +
2059 + dir->nr = i;
2060 + }
2061 +
2062 if (dir->untracked) {
2063 static struct trace_key trace_untracked_stats = TRACE_KEY_INIT(UNTRACKED_STATS);
2064 trace_printf_key(&trace_untracked_stats,
dir.h
+2 -1
@@ -151,7 +151,8 @@ struct dir_struct {
151 DIR_NO_GITLINKS = 1<<3,
152 DIR_COLLECT_IGNORED = 1<<4,
153 DIR_SHOW_IGNORED_TOO = 1<<5,
154 - DIR_COLLECT_KILLED_ONLY = 1<<6
154 + DIR_COLLECT_KILLED_ONLY = 1<<6,
155 + DIR_KEEP_UNTRACKED_CONTENTS = 1<<7
156 } flags;
157 struct dir_entry **entries;
158 struct dir_entry **ignored;
t/t7061-wtstatus-ignore.sh
+2 -2
@@ -12,7 +12,7 @@ cat >expected <<\EOF
12 !! untracked/ignored
13 EOF
14
15 -test_expect_failure 'status untracked directory with --ignored' '
15 +test_expect_success 'status untracked directory with --ignored' '
16 echo "ignored" >.gitignore &&
17 mkdir untracked &&
18 : >untracked/ignored &&
@@ -21,7 +21,7 @@ test_expect_failure 'status untracked directory with --ignored' '
21 test_cmp expected actual
22 '
23
24 -test_expect_failure 'same with gitignore starting with BOM' '
24 +test_expect_success 'same with gitignore starting with BOM' '
25 printf "\357\273\277ignored\n" >.gitignore &&
26 mkdir -p untracked &&
27 : >untracked/ignored &&