2006-05-17 10:02:14 +08:00
|
|
|
#ifndef DIR_H
|
|
|
|
#define DIR_H
|
|
|
|
|
2012-12-27 10:32:21 +08:00
|
|
|
/* See Documentation/technical/api-directory-listing.txt */
|
|
|
|
|
2012-06-02 02:28:00 +08:00
|
|
|
#include "strbuf.h"
|
|
|
|
|
2006-05-17 10:02:14 +08:00
|
|
|
struct dir_entry {
|
builtin-add: simplify (and increase accuracy of) exclude handling
Previously, the code would always set up the excludes, and then manually
pick through the pathspec we were given, assuming that non-added but
existing paths were just ignored. This was mostly correct, but would
erroneously mark a totally empty directory as 'ignored'.
Instead, we now use the collect_ignored option of dir_struct, which
unambiguously tells us whether a path was ignored. This simplifies the
code, and means empty directories are now just not mentioned at all.
Furthermore, we now conditionally ask dir_struct to respect excludes,
depending on whether the '-f' flag has been set. This means we don't have
to pick through the result, checking for an 'ignored' flag; ignored entries
were either added or not in the first place.
We can safely get rid of the special 'ignored' flags to dir_entry, which
were not used anywhere else.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Jonas Fonseca <fonseca@diku.dk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-06-13 05:42:14 +08:00
|
|
|
unsigned int len;
|
2006-05-17 10:02:14 +08:00
|
|
|
char name[FLEX_ARRAY]; /* more */
|
|
|
|
};
|
|
|
|
|
2007-10-29 04:27:13 +08:00
|
|
|
#define EXC_FLAG_NODIR 1
|
|
|
|
#define EXC_FLAG_ENDSWITH 4
|
2008-01-31 17:17:48 +08:00
|
|
|
#define EXC_FLAG_MUSTBEDIR 8
|
2012-10-15 14:24:38 +08:00
|
|
|
#define EXC_FLAG_NEGATIVE 16
|
2007-10-29 04:27:13 +08:00
|
|
|
|
2014-07-14 17:48:03 +08:00
|
|
|
struct exclude {
|
|
|
|
/*
|
|
|
|
* This allows callers of last_exclude_matching() etc.
|
|
|
|
* to determine the origin of the matching pattern.
|
|
|
|
*/
|
|
|
|
struct exclude_list *el;
|
|
|
|
|
|
|
|
const char *pattern;
|
|
|
|
int patternlen;
|
|
|
|
int nowildcardlen;
|
|
|
|
const char *base;
|
|
|
|
int baselen;
|
2016-03-02 01:02:59 +08:00
|
|
|
unsigned flags; /* EXC_FLAG_* */
|
2014-07-14 17:48:03 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Counting starts from 1 for line numbers in ignore files,
|
|
|
|
* and from -1 decrementing for patterns from CLI args.
|
|
|
|
*/
|
|
|
|
int srcpos;
|
|
|
|
};
|
|
|
|
|
2012-12-27 10:32:21 +08:00
|
|
|
/*
|
2013-01-07 00:58:03 +08:00
|
|
|
* Each excludes file will be parsed into a fresh exclude_list which
|
|
|
|
* is appended to the relevant exclude_list_group (either EXC_DIRS or
|
|
|
|
* EXC_FILE). An exclude_list within the EXC_CMDL exclude_list_group
|
|
|
|
* can also be used to represent the list of --exclude values passed
|
|
|
|
* via CLI args.
|
2012-12-27 10:32:21 +08:00
|
|
|
*/
|
2006-05-17 10:02:14 +08:00
|
|
|
struct exclude_list {
|
|
|
|
int nr;
|
|
|
|
int alloc;
|
2013-01-07 00:58:04 +08:00
|
|
|
|
2013-01-07 00:58:03 +08:00
|
|
|
/* remember pointer to exclude file contents so we can free() */
|
|
|
|
char *filebuf;
|
|
|
|
|
2013-01-07 00:58:04 +08:00
|
|
|
/* origin of list, e.g. path to filename, or descriptive string */
|
|
|
|
const char *src;
|
|
|
|
|
2014-07-14 17:48:03 +08:00
|
|
|
struct exclude **excludes;
|
2006-05-17 10:02:14 +08:00
|
|
|
};
|
|
|
|
|
2012-12-27 10:32:21 +08:00
|
|
|
/*
|
|
|
|
* The contents of the per-directory exclude files are lazily read on
|
|
|
|
* demand and then cached in memory, one per exclude_stack struct, in
|
|
|
|
* order to avoid opening and parsing each one every time that
|
|
|
|
* directory is traversed.
|
|
|
|
*/
|
2007-11-29 18:17:44 +08:00
|
|
|
struct exclude_stack {
|
2012-12-27 10:32:21 +08:00
|
|
|
struct exclude_stack *prev; /* the struct exclude_stack for the parent directory */
|
2007-11-29 18:17:44 +08:00
|
|
|
int baselen;
|
2013-01-07 00:58:03 +08:00
|
|
|
int exclude_ix; /* index of exclude_list within EXC_DIRS exclude_list_group */
|
untracked cache: record .gitignore information and dir hierarchy
The idea is if we can capture all input and (non-rescursive) output of
read_directory_recursive(), and can verify later that all the input is
the same, then the second r_d_r() should produce the same output as in
the first run.
The requirement for this to work is stat info of a directory MUST
change if an entry is added to or removed from that directory (and
should not change often otherwise). If your OS and filesystem do not
meet this requirement, untracked cache is not for you. Most file
systems on *nix should be fine. On Windows, NTFS is fine while FAT may
not be [1] even though FAT on Linux seems to be fine.
The list of input of r_d_r() is in the big comment block in dir.h. In
short, the output of a directory (not counting subdirs) mainly depends
on stat info of the directory in question, all .gitignore leading to
it and the check_only flag when r_d_r() is called recursively. This
patch records all this info (and the output) as r_d_r() runs.
Two hash_sha1_file() are required for $GIT_DIR/info/exclude and
core.excludesfile unless their stat data matches. hash_sha1_file() is
only needed when .gitignore files in the worktree are modified,
otherwise their SHA-1 in index is used (see the previous patch).
We could store stat data for .gitignore files so we don't have to
rehash them if their content is different from index, but I think
.gitignore files are rarely modified, so not worth extra cache data
(and hashing penalty read-cache.c:verify_hdr(), as we will be storing
this as an index extension).
The implication is, if you change .gitignore, you better add it to the
index soon or you lose all the benefit of untracked cache because a
modified .gitignore invalidates all subdirs recursively. This is
especially bad for .gitignore at root.
This cached output is about untracked files only, not ignored files
because the number of tracked files is usually small, so small cache
overhead, while the number of ignored files could go really high
(e.g. *.o files mixing with source code).
[1] "Description of NTFS date and time stamps for files and folders"
http://support.microsoft.com/kb/299648
Helped-by: Torsten Bögershausen <tboegi@web.de>
Helped-by: David Turner <dturner@twopensource.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-03-08 18:12:25 +08:00
|
|
|
struct untracked_cache_dir *ucd;
|
2013-01-07 00:58:03 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct exclude_list_group {
|
|
|
|
int nr, alloc;
|
|
|
|
struct exclude_list *el;
|
2007-11-29 18:17:44 +08:00
|
|
|
};
|
|
|
|
|
2015-03-08 18:12:24 +08:00
|
|
|
struct sha1_stat {
|
|
|
|
struct stat_data stat;
|
|
|
|
unsigned char sha1[20];
|
|
|
|
int valid;
|
|
|
|
};
|
|
|
|
|
untracked cache: record .gitignore information and dir hierarchy
The idea is if we can capture all input and (non-rescursive) output of
read_directory_recursive(), and can verify later that all the input is
the same, then the second r_d_r() should produce the same output as in
the first run.
The requirement for this to work is stat info of a directory MUST
change if an entry is added to or removed from that directory (and
should not change often otherwise). If your OS and filesystem do not
meet this requirement, untracked cache is not for you. Most file
systems on *nix should be fine. On Windows, NTFS is fine while FAT may
not be [1] even though FAT on Linux seems to be fine.
The list of input of r_d_r() is in the big comment block in dir.h. In
short, the output of a directory (not counting subdirs) mainly depends
on stat info of the directory in question, all .gitignore leading to
it and the check_only flag when r_d_r() is called recursively. This
patch records all this info (and the output) as r_d_r() runs.
Two hash_sha1_file() are required for $GIT_DIR/info/exclude and
core.excludesfile unless their stat data matches. hash_sha1_file() is
only needed when .gitignore files in the worktree are modified,
otherwise their SHA-1 in index is used (see the previous patch).
We could store stat data for .gitignore files so we don't have to
rehash them if their content is different from index, but I think
.gitignore files are rarely modified, so not worth extra cache data
(and hashing penalty read-cache.c:verify_hdr(), as we will be storing
this as an index extension).
The implication is, if you change .gitignore, you better add it to the
index soon or you lose all the benefit of untracked cache because a
modified .gitignore invalidates all subdirs recursively. This is
especially bad for .gitignore at root.
This cached output is about untracked files only, not ignored files
because the number of tracked files is usually small, so small cache
overhead, while the number of ignored files could go really high
(e.g. *.o files mixing with source code).
[1] "Description of NTFS date and time stamps for files and folders"
http://support.microsoft.com/kb/299648
Helped-by: Torsten Bögershausen <tboegi@web.de>
Helped-by: David Turner <dturner@twopensource.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-03-08 18:12:25 +08:00
|
|
|
/*
|
|
|
|
* Untracked cache
|
|
|
|
*
|
|
|
|
* The following inputs are sufficient to determine what files in a
|
|
|
|
* directory are excluded:
|
|
|
|
*
|
|
|
|
* - The list of files and directories of the directory in question
|
|
|
|
* - The $GIT_DIR/index
|
|
|
|
* - dir_struct flags
|
|
|
|
* - The content of $GIT_DIR/info/exclude
|
|
|
|
* - The content of core.excludesfile
|
|
|
|
* - The content (or the lack) of .gitignore of all parent directories
|
|
|
|
* from $GIT_WORK_TREE
|
|
|
|
* - The check_only flag in read_directory_recursive (for
|
|
|
|
* DIR_HIDE_EMPTY_DIRECTORIES)
|
|
|
|
*
|
|
|
|
* The first input can be checked using directory mtime. In many
|
|
|
|
* filesystems, directory mtime (stat_data field) is updated when its
|
|
|
|
* files or direct subdirs are added or removed.
|
|
|
|
*
|
|
|
|
* The second one can be hooked from cache_tree_invalidate_path().
|
|
|
|
* Whenever a file (or a submodule) is added or removed from a
|
|
|
|
* directory, we invalidate that directory.
|
|
|
|
*
|
|
|
|
* The remaining inputs are easy, their SHA-1 could be used to verify
|
|
|
|
* their contents (exclude_sha1[], info_exclude_sha1[] and
|
|
|
|
* excludes_file_sha1[])
|
|
|
|
*/
|
|
|
|
struct untracked_cache_dir {
|
|
|
|
struct untracked_cache_dir **dirs;
|
|
|
|
char **untracked;
|
|
|
|
struct stat_data stat_data;
|
|
|
|
unsigned int untracked_alloc, dirs_nr, dirs_alloc;
|
|
|
|
unsigned int untracked_nr;
|
|
|
|
unsigned int check_only : 1;
|
2015-03-08 18:12:30 +08:00
|
|
|
/* all data except 'dirs' in this struct are good */
|
2015-03-08 18:12:26 +08:00
|
|
|
unsigned int valid : 1;
|
2015-03-08 18:12:30 +08:00
|
|
|
unsigned int recurse : 1;
|
untracked cache: record .gitignore information and dir hierarchy
The idea is if we can capture all input and (non-rescursive) output of
read_directory_recursive(), and can verify later that all the input is
the same, then the second r_d_r() should produce the same output as in
the first run.
The requirement for this to work is stat info of a directory MUST
change if an entry is added to or removed from that directory (and
should not change often otherwise). If your OS and filesystem do not
meet this requirement, untracked cache is not for you. Most file
systems on *nix should be fine. On Windows, NTFS is fine while FAT may
not be [1] even though FAT on Linux seems to be fine.
The list of input of r_d_r() is in the big comment block in dir.h. In
short, the output of a directory (not counting subdirs) mainly depends
on stat info of the directory in question, all .gitignore leading to
it and the check_only flag when r_d_r() is called recursively. This
patch records all this info (and the output) as r_d_r() runs.
Two hash_sha1_file() are required for $GIT_DIR/info/exclude and
core.excludesfile unless their stat data matches. hash_sha1_file() is
only needed when .gitignore files in the worktree are modified,
otherwise their SHA-1 in index is used (see the previous patch).
We could store stat data for .gitignore files so we don't have to
rehash them if their content is different from index, but I think
.gitignore files are rarely modified, so not worth extra cache data
(and hashing penalty read-cache.c:verify_hdr(), as we will be storing
this as an index extension).
The implication is, if you change .gitignore, you better add it to the
index soon or you lose all the benefit of untracked cache because a
modified .gitignore invalidates all subdirs recursively. This is
especially bad for .gitignore at root.
This cached output is about untracked files only, not ignored files
because the number of tracked files is usually small, so small cache
overhead, while the number of ignored files could go really high
(e.g. *.o files mixing with source code).
[1] "Description of NTFS date and time stamps for files and folders"
http://support.microsoft.com/kb/299648
Helped-by: Torsten Bögershausen <tboegi@web.de>
Helped-by: David Turner <dturner@twopensource.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-03-08 18:12:25 +08:00
|
|
|
/* null SHA-1 means this directory does not have .gitignore */
|
|
|
|
unsigned char exclude_sha1[20];
|
|
|
|
char name[FLEX_ARRAY];
|
|
|
|
};
|
|
|
|
|
|
|
|
struct untracked_cache {
|
|
|
|
struct sha1_stat ss_info_exclude;
|
|
|
|
struct sha1_stat ss_excludes_file;
|
|
|
|
const char *exclude_per_dir;
|
2015-03-08 18:12:46 +08:00
|
|
|
struct strbuf ident;
|
untracked cache: record .gitignore information and dir hierarchy
The idea is if we can capture all input and (non-rescursive) output of
read_directory_recursive(), and can verify later that all the input is
the same, then the second r_d_r() should produce the same output as in
the first run.
The requirement for this to work is stat info of a directory MUST
change if an entry is added to or removed from that directory (and
should not change often otherwise). If your OS and filesystem do not
meet this requirement, untracked cache is not for you. Most file
systems on *nix should be fine. On Windows, NTFS is fine while FAT may
not be [1] even though FAT on Linux seems to be fine.
The list of input of r_d_r() is in the big comment block in dir.h. In
short, the output of a directory (not counting subdirs) mainly depends
on stat info of the directory in question, all .gitignore leading to
it and the check_only flag when r_d_r() is called recursively. This
patch records all this info (and the output) as r_d_r() runs.
Two hash_sha1_file() are required for $GIT_DIR/info/exclude and
core.excludesfile unless their stat data matches. hash_sha1_file() is
only needed when .gitignore files in the worktree are modified,
otherwise their SHA-1 in index is used (see the previous patch).
We could store stat data for .gitignore files so we don't have to
rehash them if their content is different from index, but I think
.gitignore files are rarely modified, so not worth extra cache data
(and hashing penalty read-cache.c:verify_hdr(), as we will be storing
this as an index extension).
The implication is, if you change .gitignore, you better add it to the
index soon or you lose all the benefit of untracked cache because a
modified .gitignore invalidates all subdirs recursively. This is
especially bad for .gitignore at root.
This cached output is about untracked files only, not ignored files
because the number of tracked files is usually small, so small cache
overhead, while the number of ignored files could go really high
(e.g. *.o files mixing with source code).
[1] "Description of NTFS date and time stamps for files and folders"
http://support.microsoft.com/kb/299648
Helped-by: Torsten Bögershausen <tboegi@web.de>
Helped-by: David Turner <dturner@twopensource.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-03-08 18:12:25 +08:00
|
|
|
/*
|
|
|
|
* dir_struct#flags must match dir_flags or the untracked
|
|
|
|
* cache is ignored.
|
|
|
|
*/
|
|
|
|
unsigned dir_flags;
|
|
|
|
struct untracked_cache_dir *root;
|
|
|
|
/* Statistics */
|
|
|
|
int dir_created;
|
2015-03-08 18:12:26 +08:00
|
|
|
int gitignore_invalidated;
|
untracked cache: record/validate dir mtime and reuse cached output
The main readdir loop in read_directory_recursive() is replaced with a
new one that checks if cached results of a directory is still valid.
If a file is added or removed from the index, the containing directory
is invalidated (but not its subdirs). If directory's mtime is changed,
the same happens. If a .gitignore is updated, the containing directory
and all subdirs are invalidated recursively. If dir_struct#flags or
other conditions change, the cache is ignored.
If a directory is invalidated, we opendir/readdir/closedir and run the
exclude machinery on that directory listing as usual. If untracked
cache is also enabled, we'll update the cache along the way. If a
directory is validated, we simply pull the untracked listing out from
the cache. The cache also records the list of direct subdirs that we
have to recurse in. Fully excluded directories are seen as "untracked
files".
In the best case when no dirs are invalidated, read_directory()
becomes a series of
stat(dir), open(.gitignore), fstat(), read(), close() and optionally
hash_sha1_file()
For comparison, standard read_directory() is a sequence of
opendir(), readdir(), open(.gitignore), fstat(), read(), close(), the
expensive last_exclude_matching() and closedir().
We already try not to open(.gitignore) if we know it does not exist,
so open/fstat/read/close sequence does not apply to every
directory. The sequence could be reduced further, as noted in
prep_exclude() in another patch. So in theory, the entire best-case
read_directory sequence could be reduced to a series of stat() and
nothing else.
This is not a silver bullet approach. When you compile a C file, for
example, the old .o file is removed and a new one with the same name
created, effectively invalidating the containing directory's cache
(but not its subdirectories). If your build process touches every
directory, this cache adds extra overhead for nothing, so it's a good
idea to separate generated files from tracked files.. Editors may use
the same strategy for saving files. And of course you're out of luck
running your repo on an unsupported filesystem and/or operating system.
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-03-08 18:12:29 +08:00
|
|
|
int dir_invalidated;
|
|
|
|
int dir_opened;
|
untracked cache: record .gitignore information and dir hierarchy
The idea is if we can capture all input and (non-rescursive) output of
read_directory_recursive(), and can verify later that all the input is
the same, then the second r_d_r() should produce the same output as in
the first run.
The requirement for this to work is stat info of a directory MUST
change if an entry is added to or removed from that directory (and
should not change often otherwise). If your OS and filesystem do not
meet this requirement, untracked cache is not for you. Most file
systems on *nix should be fine. On Windows, NTFS is fine while FAT may
not be [1] even though FAT on Linux seems to be fine.
The list of input of r_d_r() is in the big comment block in dir.h. In
short, the output of a directory (not counting subdirs) mainly depends
on stat info of the directory in question, all .gitignore leading to
it and the check_only flag when r_d_r() is called recursively. This
patch records all this info (and the output) as r_d_r() runs.
Two hash_sha1_file() are required for $GIT_DIR/info/exclude and
core.excludesfile unless their stat data matches. hash_sha1_file() is
only needed when .gitignore files in the worktree are modified,
otherwise their SHA-1 in index is used (see the previous patch).
We could store stat data for .gitignore files so we don't have to
rehash them if their content is different from index, but I think
.gitignore files are rarely modified, so not worth extra cache data
(and hashing penalty read-cache.c:verify_hdr(), as we will be storing
this as an index extension).
The implication is, if you change .gitignore, you better add it to the
index soon or you lose all the benefit of untracked cache because a
modified .gitignore invalidates all subdirs recursively. This is
especially bad for .gitignore at root.
This cached output is about untracked files only, not ignored files
because the number of tracked files is usually small, so small cache
overhead, while the number of ignored files could go really high
(e.g. *.o files mixing with source code).
[1] "Description of NTFS date and time stamps for files and folders"
http://support.microsoft.com/kb/299648
Helped-by: Torsten Bögershausen <tboegi@web.de>
Helped-by: David Turner <dturner@twopensource.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-03-08 18:12:25 +08:00
|
|
|
};
|
|
|
|
|
2006-05-17 10:02:14 +08:00
|
|
|
struct dir_struct {
|
|
|
|
int nr, alloc;
|
2007-06-11 21:39:50 +08:00
|
|
|
int ignored_nr, ignored_alloc;
|
2009-02-16 20:20:25 +08:00
|
|
|
enum {
|
|
|
|
DIR_SHOW_IGNORED = 1<<0,
|
|
|
|
DIR_SHOW_OTHER_DIRECTORIES = 1<<1,
|
|
|
|
DIR_HIDE_EMPTY_DIRECTORIES = 1<<2,
|
|
|
|
DIR_NO_GITLINKS = 1<<3,
|
2013-04-16 03:15:03 +08:00
|
|
|
DIR_COLLECT_IGNORED = 1<<4,
|
ls-files -k: a directory only can be killed if the index has a non-directory
"ls-files -o" and "ls-files -k" both traverse the working tree down
to find either all untracked paths or those that will be "killed"
(removed from the working tree to make room) when the paths recorded
in the index are checked out. It is necessary to traverse the
working tree fully when enumerating all the "other" paths, but when
we are only interested in "killed" paths, we can take advantage of
the fact that paths that do not overlap with entries in the index
can never be killed.
The treat_one_path() helper function, which is called during the
recursive traversal, is the ideal place to implement an
optimization.
When we are looking at a directory P in the working tree, there are
three cases:
(1) P exists in the index. Everything inside the directory P in
the working tree needs to go when P is checked out from the
index.
(2) P does not exist in the index, but there is P/Q in the index.
We know P will stay a directory when we check out the contents
of the index, but we do not know yet if there is a directory
P/Q in the working tree to be killed, so we need to recurse.
(3) P does not exist in the index, and there is no P/Q in the index
to require P to be a directory, either. Only in this case, we
know that everything inside P will not be killed without
recursing.
Note that this helper is called by treat_leading_path() that decides
if we need to traverse only subdirectories of a single common
leading directory, which is essential for this optimization to be
correct. This caller checks each level of the leading path
component from shallower directory to deeper ones, and that is what
allows us to only check if the path appears in the index. If the
call to treat_one_path() weren't there, given a path P/Q/R, the real
traversal may start from directory P/Q/R, even when the index
records P as a regular file, and we would end up having to check if
any leading subpath in P/Q/R, e.g. P, appears in the index.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-16 03:13:46 +08:00
|
|
|
DIR_SHOW_IGNORED_TOO = 1<<5,
|
2017-05-18 16:21:52 +08:00
|
|
|
DIR_COLLECT_KILLED_ONLY = 1<<6,
|
|
|
|
DIR_KEEP_UNTRACKED_CONTENTS = 1<<7
|
2009-02-16 20:20:25 +08:00
|
|
|
} flags;
|
2006-05-17 10:02:14 +08:00
|
|
|
struct dir_entry **entries;
|
2007-06-11 21:39:50 +08:00
|
|
|
struct dir_entry **ignored;
|
2006-05-17 10:02:14 +08:00
|
|
|
|
|
|
|
/* Exclude info */
|
|
|
|
const char *exclude_per_dir;
|
2013-01-07 00:58:03 +08:00
|
|
|
|
2007-11-29 18:17:44 +08:00
|
|
|
/*
|
2013-01-07 00:58:03 +08:00
|
|
|
* We maintain three groups of exclude pattern lists:
|
|
|
|
*
|
2007-11-29 18:17:44 +08:00
|
|
|
* EXC_CMDL lists patterns explicitly given on the command line.
|
|
|
|
* EXC_DIRS lists patterns obtained from per-directory ignore files.
|
2013-01-07 00:58:03 +08:00
|
|
|
* EXC_FILE lists patterns from fallback ignore files, e.g.
|
|
|
|
* - .git/info/exclude
|
|
|
|
* - core.excludesfile
|
|
|
|
*
|
|
|
|
* Each group contains multiple exclude lists, a single list
|
|
|
|
* per source.
|
2007-11-29 18:17:44 +08:00
|
|
|
*/
|
|
|
|
#define EXC_CMDL 0
|
|
|
|
#define EXC_DIRS 1
|
|
|
|
#define EXC_FILE 2
|
2013-01-07 00:58:03 +08:00
|
|
|
struct exclude_list_group exclude_list_group[3];
|
2007-11-29 18:17:44 +08:00
|
|
|
|
2012-12-27 10:32:21 +08:00
|
|
|
/*
|
|
|
|
* Temporary variables which are used during loading of the
|
|
|
|
* per-directory exclude lists.
|
|
|
|
*
|
|
|
|
* exclude_stack points to the top of the exclude_stack, and
|
|
|
|
* basebuf contains the full path to the current
|
dir.c: unify is_excluded and is_path_excluded APIs
The is_excluded and is_path_excluded APIs are very similar, except for a
few noteworthy differences:
is_excluded doesn't handle ignored directories, results for paths within
ignored directories are incorrect. This is probably based on the premise
that recursive directory scans should stop at ignored directories, which
is no longer true (in certain cases, read_directory_recursive currently
calls is_excluded *and* is_path_excluded to get correct ignored state).
is_excluded caches parsed .gitignore files of the last directory in struct
dir_struct. If the directory changes, it finds a common parent directory
and is very careful to drop only as much state as necessary. On the other
hand, is_excluded will also read and parse .gitignore files in already
ignored directories, which are completely irrelevant.
is_path_excluded correctly handles ignored directories by checking if any
component in the path is excluded. As it uses is_excluded internally, this
unfortunately forces is_excluded to drop and re-read all .gitignore files,
as there is no common parent directory for the root dir.
is_path_excluded tracks state in a separate struct path_exclude_check,
which is essentially a wrapper of dir_struct with two more fields. However,
as is_path_excluded also modifies dir_struct, it is not possible to e.g.
use multiple path_exclude_check structures with the same dir_struct in
parallel. The additional structure just unnecessarily complicates the API.
Teach is_excluded / prep_exclude about ignored directories: whenever
entering a new directory, first check if the entire directory is excluded.
Remember the excluded state in dir_struct. Don't traverse into already
ignored directories (i.e. don't read irrelevant .gitignore files).
Directories could also be excluded by exclude patterns specified on the
command line or .git/info/exclude, so we cannot simply skip prep_exclude
entirely if there's no .gitignore file name (dir_struct.exclude_per_dir).
Move this check to just before actually reading the file.
is_path_excluded is now equivalent to is_excluded, so we can simply
redirect to it (the public API is cleaned up in the next patch).
The performance impact of the additional ignored check per directory is
hardly noticeable when reading directories recursively (e.g. 'git status').
However, performance of git commands using the is_path_excluded API (e.g.
'git ls-files --cached --ignored --exclude-standard') is greatly improved
as this no longer re-reads .gitignore files on each call.
Here's some performance data from the linux and WebKit repos (best of 10
runs on a Debian Linux on SSD, core.preloadIndex=true):
| ls-files -ci | status | status --ignored
| linux | WebKit | linux | WebKit | linux | WebKit
-------+-------+--------+-------+--------+-------+---------
before | 0.506 | 6.539 | 0.212 | 1.555 | 0.323 | 2.541
after | 0.080 | 1.191 | 0.218 | 1.583 | 0.321 | 2.579
gain | 6.325 | 5.490 | 0.972 | 0.982 | 1.006 | 0.985
Signed-off-by: Karsten Blees <blees@dcon.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-16 03:12:14 +08:00
|
|
|
* (sub)directory in the traversal. Exclude points to the
|
|
|
|
* matching exclude struct if the directory is excluded.
|
2012-12-27 10:32:21 +08:00
|
|
|
*/
|
2007-11-29 18:17:44 +08:00
|
|
|
struct exclude_stack *exclude_stack;
|
dir.c: unify is_excluded and is_path_excluded APIs
The is_excluded and is_path_excluded APIs are very similar, except for a
few noteworthy differences:
is_excluded doesn't handle ignored directories, results for paths within
ignored directories are incorrect. This is probably based on the premise
that recursive directory scans should stop at ignored directories, which
is no longer true (in certain cases, read_directory_recursive currently
calls is_excluded *and* is_path_excluded to get correct ignored state).
is_excluded caches parsed .gitignore files of the last directory in struct
dir_struct. If the directory changes, it finds a common parent directory
and is very careful to drop only as much state as necessary. On the other
hand, is_excluded will also read and parse .gitignore files in already
ignored directories, which are completely irrelevant.
is_path_excluded correctly handles ignored directories by checking if any
component in the path is excluded. As it uses is_excluded internally, this
unfortunately forces is_excluded to drop and re-read all .gitignore files,
as there is no common parent directory for the root dir.
is_path_excluded tracks state in a separate struct path_exclude_check,
which is essentially a wrapper of dir_struct with two more fields. However,
as is_path_excluded also modifies dir_struct, it is not possible to e.g.
use multiple path_exclude_check structures with the same dir_struct in
parallel. The additional structure just unnecessarily complicates the API.
Teach is_excluded / prep_exclude about ignored directories: whenever
entering a new directory, first check if the entire directory is excluded.
Remember the excluded state in dir_struct. Don't traverse into already
ignored directories (i.e. don't read irrelevant .gitignore files).
Directories could also be excluded by exclude patterns specified on the
command line or .git/info/exclude, so we cannot simply skip prep_exclude
entirely if there's no .gitignore file name (dir_struct.exclude_per_dir).
Move this check to just before actually reading the file.
is_path_excluded is now equivalent to is_excluded, so we can simply
redirect to it (the public API is cleaned up in the next patch).
The performance impact of the additional ignored check per directory is
hardly noticeable when reading directories recursively (e.g. 'git status').
However, performance of git commands using the is_path_excluded API (e.g.
'git ls-files --cached --ignored --exclude-standard') is greatly improved
as this no longer re-reads .gitignore files on each call.
Here's some performance data from the linux and WebKit repos (best of 10
runs on a Debian Linux on SSD, core.preloadIndex=true):
| ls-files -ci | status | status --ignored
| linux | WebKit | linux | WebKit | linux | WebKit
-------+-------+--------+-------+--------+-------+---------
before | 0.506 | 6.539 | 0.212 | 1.555 | 0.323 | 2.541
after | 0.080 | 1.191 | 0.218 | 1.583 | 0.321 | 2.579
gain | 6.325 | 5.490 | 0.972 | 0.982 | 1.006 | 0.985
Signed-off-by: Karsten Blees <blees@dcon.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-16 03:12:14 +08:00
|
|
|
struct exclude *exclude;
|
2014-07-14 17:50:22 +08:00
|
|
|
struct strbuf basebuf;
|
untracked cache: record .gitignore information and dir hierarchy
The idea is if we can capture all input and (non-rescursive) output of
read_directory_recursive(), and can verify later that all the input is
the same, then the second r_d_r() should produce the same output as in
the first run.
The requirement for this to work is stat info of a directory MUST
change if an entry is added to or removed from that directory (and
should not change often otherwise). If your OS and filesystem do not
meet this requirement, untracked cache is not for you. Most file
systems on *nix should be fine. On Windows, NTFS is fine while FAT may
not be [1] even though FAT on Linux seems to be fine.
The list of input of r_d_r() is in the big comment block in dir.h. In
short, the output of a directory (not counting subdirs) mainly depends
on stat info of the directory in question, all .gitignore leading to
it and the check_only flag when r_d_r() is called recursively. This
patch records all this info (and the output) as r_d_r() runs.
Two hash_sha1_file() are required for $GIT_DIR/info/exclude and
core.excludesfile unless their stat data matches. hash_sha1_file() is
only needed when .gitignore files in the worktree are modified,
otherwise their SHA-1 in index is used (see the previous patch).
We could store stat data for .gitignore files so we don't have to
rehash them if their content is different from index, but I think
.gitignore files are rarely modified, so not worth extra cache data
(and hashing penalty read-cache.c:verify_hdr(), as we will be storing
this as an index extension).
The implication is, if you change .gitignore, you better add it to the
index soon or you lose all the benefit of untracked cache because a
modified .gitignore invalidates all subdirs recursively. This is
especially bad for .gitignore at root.
This cached output is about untracked files only, not ignored files
because the number of tracked files is usually small, so small cache
overhead, while the number of ignored files could go really high
(e.g. *.o files mixing with source code).
[1] "Description of NTFS date and time stamps for files and folders"
http://support.microsoft.com/kb/299648
Helped-by: Torsten Bögershausen <tboegi@web.de>
Helped-by: David Turner <dturner@twopensource.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-03-08 18:12:25 +08:00
|
|
|
|
|
|
|
/* Enable untracked file cache if set */
|
|
|
|
struct untracked_cache *untracked;
|
|
|
|
struct sha1_stat ss_info_exclude;
|
|
|
|
struct sha1_stat ss_excludes_file;
|
2015-03-08 18:12:26 +08:00
|
|
|
unsigned unmanaged_exclude_files;
|
2006-05-17 10:02:14 +08:00
|
|
|
};
|
|
|
|
|
2017-06-09 02:08:12 +08:00
|
|
|
/*Count the number of slashes for string s*/
|
|
|
|
extern int count_slashes(const char *s);
|
|
|
|
|
2013-01-07 00:58:06 +08:00
|
|
|
/*
|
|
|
|
* The ordering of these constants is significant, with
|
|
|
|
* higher-numbered match types signifying "closer" (i.e. more
|
|
|
|
* specific) matches which will override lower-numbered match types
|
|
|
|
* when populating the seen[] array.
|
|
|
|
*/
|
2006-12-25 19:09:52 +08:00
|
|
|
#define MATCHED_RECURSIVELY 1
|
|
|
|
#define MATCHED_FNMATCH 2
|
|
|
|
#define MATCHED_EXACTLY 3
|
2013-07-14 16:35:28 +08:00
|
|
|
extern int simple_length(const char *match);
|
|
|
|
extern int no_wildcard(const char *string);
|
2013-07-14 16:35:57 +08:00
|
|
|
extern char *common_prefix(const struct pathspec *pathspec);
|
2014-01-24 21:40:30 +08:00
|
|
|
extern int match_pathspec(const struct pathspec *pathspec,
|
|
|
|
const char *name, int namelen,
|
2014-01-24 21:40:33 +08:00
|
|
|
int prefix, char *seen, int is_dir);
|
2015-03-25 05:12:10 +08:00
|
|
|
extern int report_path_error(const char *ps_matched, const struct pathspec *pathspec, const char *prefix);
|
2010-12-15 23:02:44 +08:00
|
|
|
extern int within_depth(const char *name, int namelen, int depth, int max_depth);
|
2006-05-20 07:07:51 +08:00
|
|
|
|
2017-05-06 03:53:34 +08:00
|
|
|
extern int fill_directory(struct dir_struct *dir,
|
|
|
|
struct index_state *istate,
|
|
|
|
const struct pathspec *pathspec);
|
2017-05-06 03:53:33 +08:00
|
|
|
extern int read_directory(struct dir_struct *, struct index_state *istate,
|
|
|
|
const char *path, int len,
|
|
|
|
const struct pathspec *pathspec);
|
2006-12-05 08:00:46 +08:00
|
|
|
|
2017-05-06 03:53:27 +08:00
|
|
|
extern int is_excluded_from_list(const char *pathname, int pathlen,
|
|
|
|
const char *basename, int *dtype,
|
|
|
|
struct exclude_list *el,
|
|
|
|
struct index_state *istate);
|
2017-05-06 03:53:25 +08:00
|
|
|
struct dir_entry *dir_add_ignored(struct dir_struct *dir,
|
|
|
|
struct index_state *istate,
|
|
|
|
const char *pathname, int len);
|
2012-06-02 02:28:00 +08:00
|
|
|
|
2012-10-15 14:24:39 +08:00
|
|
|
/*
|
|
|
|
* these implement the matching logic for dir.c:excluded_from_list and
|
|
|
|
* attr.c:path_matches()
|
|
|
|
*/
|
|
|
|
extern int match_basename(const char *, int,
|
2016-03-02 01:02:59 +08:00
|
|
|
const char *, int, int, unsigned);
|
2012-10-15 14:24:39 +08:00
|
|
|
extern int match_pathname(const char *, int,
|
|
|
|
const char *, int,
|
2016-03-02 01:02:59 +08:00
|
|
|
const char *, int, int, unsigned);
|
2012-10-15 14:24:39 +08:00
|
|
|
|
2013-04-16 03:12:57 +08:00
|
|
|
extern struct exclude *last_exclude_matching(struct dir_struct *dir,
|
2017-05-06 03:53:30 +08:00
|
|
|
struct index_state *istate,
|
2013-04-16 03:12:57 +08:00
|
|
|
const char *name, int *dtype);
|
2012-06-06 12:17:52 +08:00
|
|
|
|
2017-05-06 03:53:30 +08:00
|
|
|
extern int is_excluded(struct dir_struct *dir,
|
|
|
|
struct index_state *istate,
|
|
|
|
const char *name, int *dtype);
|
2012-06-02 02:28:00 +08:00
|
|
|
|
2013-01-07 00:58:04 +08:00
|
|
|
extern struct exclude_list *add_exclude_list(struct dir_struct *dir,
|
|
|
|
int group_type, const char *src);
|
2009-08-20 21:47:04 +08:00
|
|
|
extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,
|
2017-05-06 03:53:28 +08:00
|
|
|
struct exclude_list *el, struct index_state *istate);
|
2006-05-17 10:02:14 +08:00
|
|
|
extern void add_excludes_from_file(struct dir_struct *, const char *fname);
|
2016-03-02 01:02:59 +08:00
|
|
|
extern void parse_exclude_pattern(const char **string, int *patternlen, unsigned *flags, int *nowildcardlen);
|
2006-05-17 10:02:14 +08:00
|
|
|
extern void add_exclude(const char *string, const char *base,
|
2013-01-07 00:58:04 +08:00
|
|
|
int baselen, struct exclude_list *el, int srcpos);
|
2012-12-27 10:32:29 +08:00
|
|
|
extern void clear_exclude_list(struct exclude_list *el);
|
2013-01-07 00:58:05 +08:00
|
|
|
extern void clear_directory(struct dir_struct *dir);
|
2006-09-08 16:05:34 +08:00
|
|
|
extern int file_exists(const char *);
|
2006-05-17 10:02:14 +08:00
|
|
|
|
2007-08-01 08:29:17 +08:00
|
|
|
extern int is_inside_dir(const char *dir);
|
2011-03-26 17:04:24 +08:00
|
|
|
extern int dir_inside_of(const char *subdir, const char *dir);
|
2007-08-01 08:29:17 +08:00
|
|
|
|
2009-01-10 20:07:50 +08:00
|
|
|
static inline int is_dot_or_dotdot(const char *name)
|
|
|
|
{
|
|
|
|
return (name[0] == '.' &&
|
|
|
|
(name[1] == '\0' ||
|
|
|
|
(name[1] == '.' && name[2] == '\0')));
|
|
|
|
}
|
|
|
|
|
2009-01-11 20:19:12 +08:00
|
|
|
extern int is_empty_dir(const char *dir);
|
|
|
|
|
core.excludesfile clean-up
There are inconsistencies in the way commands currently handle
the core.excludesfile configuration variable. The problem is
the variable is too new to be noticed by anything other than
git-add and git-status.
* git-ls-files does not notice any of the "ignore" files by
default, as it predates the standardized set of ignore files.
The calling scripts established the convention to use
.git/info/exclude, .gitignore, and later core.excludesfile.
* git-add and git-status know about it because they call
add_excludes_from_file() directly with their own notion of
which standard set of ignore files to use. This is just a
stupid duplication of code that need to be updated every time
the definition of the standard set of ignore files is
changed.
* git-read-tree takes --exclude-per-directory=<gitignore>,
not because the flexibility was needed. Again, this was
because the option predates the standardization of the ignore
files.
* git-merge-recursive uses hardcoded per-directory .gitignore
and nothing else. git-clean (scripted version) does not
honor core.* because its call to underlying ls-files does not
know about it. git-clean in C (parked in 'pu') doesn't either.
We probably could change git-ls-files to use the standard set
when no excludes are specified on the command line and ignore
processing was asked, or something like that, but that will be a
change in semantics and might break people's scripts in a subtle
way. I am somewhat reluctant to make such a change.
On the other hand, I think it makes perfect sense to fix
git-read-tree, git-merge-recursive and git-clean to follow the
same rule as other commands. I do not think of a valid use case
to give an exclude-per-directory that is nonstandard to
read-tree command, outside a "negative" test in the t1004 test
script.
This patch is the first step to untangle this mess.
The next step would be to teach read-tree, merge-recursive and
clean (in C) to use setup_standard_excludes().
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-11-14 16:05:00 +08:00
|
|
|
extern void setup_standard_excludes(struct dir_struct *dir);
|
2009-07-01 06:33:45 +08:00
|
|
|
|
2016-04-24 13:07:59 +08:00
|
|
|
|
|
|
|
/* Constants for remove_dir_recursively: */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If a non-directory is found within path, stop and return an error.
|
|
|
|
* (In this case some empty directories might already have been
|
|
|
|
* removed.)
|
|
|
|
*/
|
2009-07-01 06:33:45 +08:00
|
|
|
#define REMOVE_DIR_EMPTY_ONLY 01
|
2016-04-24 13:07:59 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If any Git work trees are found within path, skip them without
|
|
|
|
* considering it an error.
|
|
|
|
*/
|
2009-07-01 06:33:45 +08:00
|
|
|
#define REMOVE_DIR_KEEP_NESTED_GIT 02
|
2016-04-24 13:07:59 +08:00
|
|
|
|
|
|
|
/* Remove the contents of path, but leave path itself. */
|
2012-03-15 22:58:54 +08:00
|
|
|
#define REMOVE_DIR_KEEP_TOPLEVEL 04
|
2016-04-24 13:07:59 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove path and its contents, recursively. flags is a combination
|
|
|
|
* of the above REMOVE_DIR_* constants. Return 0 on success.
|
|
|
|
*
|
|
|
|
* This function uses path as temporary scratch space, but restores it
|
|
|
|
* before returning.
|
|
|
|
*/
|
2009-07-01 06:33:45 +08:00
|
|
|
extern int remove_dir_recursively(struct strbuf *path, int flag);
|
2007-09-28 23:28:54 +08:00
|
|
|
|
2008-09-27 06:56:46 +08:00
|
|
|
/* tries to remove the path with empty directories along it, ignores ENOENT */
|
|
|
|
extern int remove_path(const char *path);
|
|
|
|
|
2016-04-22 21:01:24 +08:00
|
|
|
extern int fspathcmp(const char *a, const char *b);
|
|
|
|
extern int fspathncmp(const char *a, const char *b, size_t count);
|
2010-10-03 17:56:41 +08:00
|
|
|
|
2012-11-24 12:33:49 +08:00
|
|
|
/*
|
|
|
|
* The prefix part of pattern must not contains wildcards.
|
|
|
|
*/
|
2013-07-14 16:36:08 +08:00
|
|
|
struct pathspec_item;
|
|
|
|
extern int git_fnmatch(const struct pathspec_item *item,
|
|
|
|
const char *pattern, const char *string,
|
|
|
|
int prefix);
|
2012-11-24 12:33:49 +08:00
|
|
|
|
2016-10-08 02:18:51 +08:00
|
|
|
extern int submodule_path_match(const struct pathspec *ps,
|
|
|
|
const char *submodule_name,
|
|
|
|
char *seen);
|
|
|
|
|
2014-01-24 21:40:28 +08:00
|
|
|
static inline int ce_path_match(const struct cache_entry *ce,
|
|
|
|
const struct pathspec *pathspec,
|
|
|
|
char *seen)
|
|
|
|
{
|
2014-01-24 21:40:33 +08:00
|
|
|
return match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen,
|
|
|
|
S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode));
|
2014-01-24 21:40:28 +08:00
|
|
|
}
|
|
|
|
|
2014-01-24 21:40:29 +08:00
|
|
|
static inline int dir_path_match(const struct dir_entry *ent,
|
|
|
|
const struct pathspec *pathspec,
|
|
|
|
int prefix, char *seen)
|
|
|
|
{
|
2014-01-24 21:40:33 +08:00
|
|
|
int has_trailing_dir = ent->len && ent->name[ent->len - 1] == '/';
|
|
|
|
int len = has_trailing_dir ? ent->len - 1 : ent->len;
|
|
|
|
return match_pathspec(pathspec, ent->name, len, prefix, seen,
|
|
|
|
has_trailing_dir);
|
2014-01-24 21:40:29 +08:00
|
|
|
}
|
|
|
|
|
2017-05-18 16:21:53 +08:00
|
|
|
int cmp_dir_entry(const void *p1, const void *p2);
|
|
|
|
int check_dir_entry_contains(const struct dir_entry *out, const struct dir_entry *in);
|
|
|
|
|
2015-03-08 18:12:35 +08:00
|
|
|
void untracked_cache_invalidate_path(struct index_state *, const char *);
|
|
|
|
void untracked_cache_remove_from_index(struct index_state *, const char *);
|
|
|
|
void untracked_cache_add_to_index(struct index_state *, const char *);
|
|
|
|
|
2015-03-08 18:12:34 +08:00
|
|
|
void free_untracked_cache(struct untracked_cache *);
|
|
|
|
struct untracked_cache *read_untracked_extension(const void *data, unsigned long sz);
|
2015-03-08 18:12:33 +08:00
|
|
|
void write_untracked_extension(struct strbuf *out, struct untracked_cache *untracked);
|
2016-01-24 23:28:19 +08:00
|
|
|
void add_untracked_cache(struct index_state *istate);
|
2016-01-24 23:28:20 +08:00
|
|
|
void remove_untracked_cache(struct index_state *istate);
|
2016-12-13 03:04:34 +08:00
|
|
|
extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
|
2016-12-13 03:04:35 +08:00
|
|
|
extern void relocate_gitdir(const char *path,
|
|
|
|
const char *old_git_dir,
|
|
|
|
const char *new_git_dir);
|
2006-05-17 10:02:14 +08:00
|
|
|
#endif
|