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>
This commit is contained in:

committed by
Junio C Hamano

parent
df5bcdf83a
commit
fb89888849
@ -33,6 +33,12 @@ The notable options are:
|
|||||||
Similar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]`
|
Similar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]`
|
||||||
in addition to untracked files in `entries[]`.
|
in addition to untracked files in `entries[]`.
|
||||||
|
|
||||||
|
`DIR_KEEP_UNTRACKED_CONTENTS`:::
|
||||||
|
|
||||||
|
Only has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if this is set, the
|
||||||
|
untracked contents of untracked directories are also returned in
|
||||||
|
`entries[]`.
|
||||||
|
|
||||||
`DIR_COLLECT_IGNORED`:::
|
`DIR_COLLECT_IGNORED`:::
|
||||||
|
|
||||||
Special mode for git-add. Return ignored files in `ignored[]` and
|
Special mode for git-add. Return ignored files in `ignored[]` and
|
||||||
|
31
dir.c
31
dir.c
@ -1813,6 +1813,14 @@ static int cmp_name(const void *p1, const void *p2)
|
|||||||
return name_compare(e1->name, e1->len, e2->name, e2->len);
|
return name_compare(e1->name, e1->len, e2->name, e2->len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* check if *out lexically strictly contains *in */
|
||||||
|
static int check_contains(const struct dir_entry *out, const struct dir_entry *in)
|
||||||
|
{
|
||||||
|
return (out->len < in->len) &&
|
||||||
|
(out->name[out->len - 1] == '/') &&
|
||||||
|
!memcmp(out->name, in->name, out->len);
|
||||||
|
}
|
||||||
|
|
||||||
static int treat_leading_path(struct dir_struct *dir,
|
static int treat_leading_path(struct dir_struct *dir,
|
||||||
const char *path, int len,
|
const char *path, int len,
|
||||||
const struct pathspec *pathspec)
|
const struct pathspec *pathspec)
|
||||||
@ -2028,6 +2036,29 @@ int read_directory(struct dir_struct *dir, const char *path,
|
|||||||
read_directory_recursive(dir, path, len, untracked, 0, pathspec);
|
read_directory_recursive(dir, path, len, untracked, 0, pathspec);
|
||||||
QSORT(dir->entries, dir->nr, cmp_name);
|
QSORT(dir->entries, dir->nr, cmp_name);
|
||||||
QSORT(dir->ignored, dir->ignored_nr, cmp_name);
|
QSORT(dir->ignored, dir->ignored_nr, cmp_name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If DIR_SHOW_IGNORED_TOO is set, read_directory_recursive() will
|
||||||
|
* also pick up untracked contents of untracked dirs; by default
|
||||||
|
* we discard these, but given DIR_KEEP_UNTRACKED_CONTENTS we do not.
|
||||||
|
*/
|
||||||
|
if ((dir->flags & DIR_SHOW_IGNORED_TOO) &&
|
||||||
|
!(dir->flags & DIR_KEEP_UNTRACKED_CONTENTS)) {
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
/* remove from dir->entries untracked contents of untracked dirs */
|
||||||
|
for (i = j = 0; j < dir->nr; j++) {
|
||||||
|
if (i && check_contains(dir->entries[i - 1], dir->entries[j])) {
|
||||||
|
free(dir->entries[j]);
|
||||||
|
dir->entries[j] = NULL;
|
||||||
|
} else {
|
||||||
|
dir->entries[i++] = dir->entries[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dir->nr = i;
|
||||||
|
}
|
||||||
|
|
||||||
if (dir->untracked) {
|
if (dir->untracked) {
|
||||||
static struct trace_key trace_untracked_stats = TRACE_KEY_INIT(UNTRACKED_STATS);
|
static struct trace_key trace_untracked_stats = TRACE_KEY_INIT(UNTRACKED_STATS);
|
||||||
trace_printf_key(&trace_untracked_stats,
|
trace_printf_key(&trace_untracked_stats,
|
||||||
|
3
dir.h
3
dir.h
@ -151,7 +151,8 @@ struct dir_struct {
|
|||||||
DIR_NO_GITLINKS = 1<<3,
|
DIR_NO_GITLINKS = 1<<3,
|
||||||
DIR_COLLECT_IGNORED = 1<<4,
|
DIR_COLLECT_IGNORED = 1<<4,
|
||||||
DIR_SHOW_IGNORED_TOO = 1<<5,
|
DIR_SHOW_IGNORED_TOO = 1<<5,
|
||||||
DIR_COLLECT_KILLED_ONLY = 1<<6
|
DIR_COLLECT_KILLED_ONLY = 1<<6,
|
||||||
|
DIR_KEEP_UNTRACKED_CONTENTS = 1<<7
|
||||||
} flags;
|
} flags;
|
||||||
struct dir_entry **entries;
|
struct dir_entry **entries;
|
||||||
struct dir_entry **ignored;
|
struct dir_entry **ignored;
|
||||||
|
@ -12,7 +12,7 @@ cat >expected <<\EOF
|
|||||||
!! untracked/ignored
|
!! untracked/ignored
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
test_expect_failure 'status untracked directory with --ignored' '
|
test_expect_success 'status untracked directory with --ignored' '
|
||||||
echo "ignored" >.gitignore &&
|
echo "ignored" >.gitignore &&
|
||||||
mkdir untracked &&
|
mkdir untracked &&
|
||||||
: >untracked/ignored &&
|
: >untracked/ignored &&
|
||||||
@ -21,7 +21,7 @@ test_expect_failure 'status untracked directory with --ignored' '
|
|||||||
test_cmp expected actual
|
test_cmp expected actual
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_failure 'same with gitignore starting with BOM' '
|
test_expect_success 'same with gitignore starting with BOM' '
|
||||||
printf "\357\273\277ignored\n" >.gitignore &&
|
printf "\357\273\277ignored\n" >.gitignore &&
|
||||||
mkdir -p untracked &&
|
mkdir -p untracked &&
|
||||||
: >untracked/ignored &&
|
: >untracked/ignored &&
|
||||||
|
Reference in New Issue
Block a user