зеркало из https://github.com/microsoft/git.git
Add virtual file system settings and hook proc
On index load, clear/set the skip worktree bits based on the virtual file system data. Use virtual file system data to update skip-worktree bit in unpack-trees. Use virtual file system data to exclude files and folders not explicitly requested. Signed-off-by: Ben Peart <benpeart@microsoft.com>
This commit is contained in:
Родитель
ce3086896d
Коммит
e711ae3e12
|
@ -68,6 +68,14 @@ core.fsmonitor::
|
||||||
avoiding unnecessary processing of files that have not changed.
|
avoiding unnecessary processing of files that have not changed.
|
||||||
See the "fsmonitor-watchman" section of linkgit:githooks[5].
|
See the "fsmonitor-watchman" section of linkgit:githooks[5].
|
||||||
|
|
||||||
|
core.virtualFilesystem::
|
||||||
|
If set, the value of this variable is used as a command which
|
||||||
|
will identify all files and directories that are present in
|
||||||
|
the working directory. Git will only track and update files
|
||||||
|
listed in the virtual file system. Using the virtual file system
|
||||||
|
will supersede the sparse-checkout settings which will be ignored.
|
||||||
|
See the "virtual file system" section of linkgit:githooks[5].
|
||||||
|
|
||||||
core.trustctime::
|
core.trustctime::
|
||||||
If false, the ctime differences between the index and the
|
If false, the ctime differences between the index and the
|
||||||
working tree are ignored; useful when the inode change time
|
working tree are ignored; useful when the inode change time
|
||||||
|
|
|
@ -540,6 +540,26 @@ and "0" meaning they were not.
|
||||||
Only one parameter should be set to "1" when the hook runs. The hook
|
Only one parameter should be set to "1" when the hook runs. The hook
|
||||||
running passing "1", "1" should not be possible.
|
running passing "1", "1" should not be possible.
|
||||||
|
|
||||||
|
virtualFilesystem
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
"Virtual File System" allows populating the working directory sparsely.
|
||||||
|
The projection data is typically automatically generated by an external
|
||||||
|
process. Git will limit what files it checks for changes as well as which
|
||||||
|
directories are checked for untracked files based on the path names given.
|
||||||
|
Git will also only update those files listed in the projection.
|
||||||
|
|
||||||
|
The hook is invoked when the configuration option core.virtualFilesystem
|
||||||
|
is set. It takes one argument, a version (currently 1).
|
||||||
|
|
||||||
|
The hook should output to stdout the list of all files in the working
|
||||||
|
directory that git should track. The paths are relative to the root
|
||||||
|
of the working directory and are separated by a single NUL. Full paths
|
||||||
|
('dir1/a.txt') as well as directories are supported (ie 'dir1/').
|
||||||
|
|
||||||
|
The exit status determines whether git will use the data from the
|
||||||
|
hook. On error, git will abort the command with an error message.
|
||||||
|
|
||||||
GIT
|
GIT
|
||||||
---
|
---
|
||||||
Part of the linkgit:git[1] suite
|
Part of the linkgit:git[1] suite
|
||||||
|
|
1
Makefile
1
Makefile
|
@ -1026,6 +1026,7 @@ LIB_OBJS += utf8.o
|
||||||
LIB_OBJS += varint.o
|
LIB_OBJS += varint.o
|
||||||
LIB_OBJS += version.o
|
LIB_OBJS += version.o
|
||||||
LIB_OBJS += versioncmp.o
|
LIB_OBJS += versioncmp.o
|
||||||
|
LIB_OBJS += virtualfilesystem.o
|
||||||
LIB_OBJS += walker.o
|
LIB_OBJS += walker.o
|
||||||
LIB_OBJS += wildmatch.o
|
LIB_OBJS += wildmatch.o
|
||||||
LIB_OBJS += worktree.o
|
LIB_OBJS += worktree.o
|
||||||
|
|
1
cache.h
1
cache.h
|
@ -919,6 +919,7 @@ extern char *git_replace_ref_base;
|
||||||
extern int fsync_object_files;
|
extern int fsync_object_files;
|
||||||
extern int core_preload_index;
|
extern int core_preload_index;
|
||||||
extern int core_apply_sparse_checkout;
|
extern int core_apply_sparse_checkout;
|
||||||
|
extern const char *core_virtualfilesystem;
|
||||||
extern int core_gvfs;
|
extern int core_gvfs;
|
||||||
extern int precomposed_unicode;
|
extern int precomposed_unicode;
|
||||||
extern int protect_hfs;
|
extern int protect_hfs;
|
||||||
|
|
23
config.c
23
config.c
|
@ -1366,7 +1366,11 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp(var, "core.sparsecheckout")) {
|
if (!strcmp(var, "core.sparsecheckout")) {
|
||||||
core_apply_sparse_checkout = git_config_bool(var, value);
|
/* virtual file system relies on the sparse checkout logic so force it on */
|
||||||
|
if (core_virtualfilesystem)
|
||||||
|
core_apply_sparse_checkout = 1;
|
||||||
|
else
|
||||||
|
core_apply_sparse_checkout = git_config_bool(var, value);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2337,6 +2341,23 @@ int git_config_get_fsmonitor(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int git_config_get_virtualfilesystem(void)
|
||||||
|
{
|
||||||
|
if (git_config_get_pathname("core.virtualfilesystem", &core_virtualfilesystem))
|
||||||
|
core_virtualfilesystem = getenv("GIT_VIRTUALFILESYSTEM_TEST");
|
||||||
|
|
||||||
|
if (core_virtualfilesystem && !*core_virtualfilesystem)
|
||||||
|
core_virtualfilesystem = NULL;
|
||||||
|
|
||||||
|
/* virtual file system relies on the sparse checkout logic so force it on */
|
||||||
|
if (core_virtualfilesystem) {
|
||||||
|
core_apply_sparse_checkout = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int git_config_get_index_threads(int *dest)
|
int git_config_get_index_threads(int *dest)
|
||||||
{
|
{
|
||||||
int is_bool, val;
|
int is_bool, val;
|
||||||
|
|
1
config.h
1
config.h
|
@ -256,6 +256,7 @@ int git_config_get_untracked_cache(void);
|
||||||
int git_config_get_split_index(void);
|
int git_config_get_split_index(void);
|
||||||
int git_config_get_max_percent_split_change(void);
|
int git_config_get_max_percent_split_change(void);
|
||||||
int git_config_get_fsmonitor(void);
|
int git_config_get_fsmonitor(void);
|
||||||
|
int git_config_get_virtualfilesystem(void);
|
||||||
|
|
||||||
/* This dies if the configured or default date is in the future */
|
/* This dies if the configured or default date is in the future */
|
||||||
int git_config_get_expiry(const char *key, const char **output);
|
int git_config_get_expiry(const char *key, const char **output);
|
||||||
|
|
34
dir.c
34
dir.c
|
@ -20,6 +20,7 @@
|
||||||
#include "ewah/ewok.h"
|
#include "ewah/ewok.h"
|
||||||
#include "fsmonitor.h"
|
#include "fsmonitor.h"
|
||||||
#include "submodule-config.h"
|
#include "submodule-config.h"
|
||||||
|
#include "virtualfilesystem.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tells read_directory_recursive how a file or directory should be treated.
|
* Tells read_directory_recursive how a file or directory should be treated.
|
||||||
|
@ -1139,6 +1140,18 @@ enum pattern_match_result path_matches_pattern_list(
|
||||||
struct index_state *istate)
|
struct index_state *istate)
|
||||||
{
|
{
|
||||||
struct path_pattern *pattern;
|
struct path_pattern *pattern;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The virtual file system data is used to prevent git from traversing
|
||||||
|
* any part of the tree that is not in the virtual file system. Return
|
||||||
|
* 1 to exclude the entry if it is not found in the virtual file system,
|
||||||
|
* else fall through to the regular excludes logic as it may further exclude.
|
||||||
|
*/
|
||||||
|
if (*dtype == DT_UNKNOWN)
|
||||||
|
*dtype = get_dtype(NULL, istate, pathname, pathlen);
|
||||||
|
if (is_excluded_from_virtualfilesystem(pathname, pathlen, *dtype) > 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
pattern = last_matching_pattern_from_list(pathname, pathlen, basename,
|
pattern = last_matching_pattern_from_list(pathname, pathlen, basename,
|
||||||
dtype, pl, istate);
|
dtype, pl, istate);
|
||||||
if (pattern) {
|
if (pattern) {
|
||||||
|
@ -1359,8 +1372,20 @@ struct path_pattern *last_matching_pattern(struct dir_struct *dir,
|
||||||
int is_excluded(struct dir_struct *dir, struct index_state *istate,
|
int is_excluded(struct dir_struct *dir, struct index_state *istate,
|
||||||
const char *pathname, int *dtype_p)
|
const char *pathname, int *dtype_p)
|
||||||
{
|
{
|
||||||
struct path_pattern *pattern =
|
struct path_pattern *pattern;
|
||||||
last_matching_pattern(dir, istate, pathname, dtype_p);
|
|
||||||
|
/*
|
||||||
|
* The virtual file system data is used to prevent git from traversing
|
||||||
|
* any part of the tree that is not in the virtual file system. Return
|
||||||
|
* 1 to exclude the entry if it is not found in the virtual file system,
|
||||||
|
* else fall through to the regular excludes logic as it may further exclude.
|
||||||
|
*/
|
||||||
|
if (*dtype_p == DT_UNKNOWN)
|
||||||
|
*dtype_p = get_dtype(NULL, istate, pathname, strlen(pathname));
|
||||||
|
if (is_excluded_from_virtualfilesystem(pathname, strlen(pathname), *dtype_p) > 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
pattern = last_matching_pattern(dir, istate, pathname, dtype_p);
|
||||||
if (pattern)
|
if (pattern)
|
||||||
return pattern->flags & PATTERN_FLAG_NEGATIVE ? 0 : 1;
|
return pattern->flags & PATTERN_FLAG_NEGATIVE ? 0 : 1;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1725,6 +1750,9 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
|
||||||
if (dtype != DT_DIR && has_path_in_index)
|
if (dtype != DT_DIR && has_path_in_index)
|
||||||
return path_none;
|
return path_none;
|
||||||
|
|
||||||
|
if (is_excluded_from_virtualfilesystem(path->buf, path->len, dtype) > 0)
|
||||||
|
return path_excluded;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When we are looking at a directory P in the working tree,
|
* When we are looking at a directory P in the working tree,
|
||||||
* there are three cases:
|
* there are three cases:
|
||||||
|
@ -2074,6 +2102,8 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
|
||||||
/* add the path to the appropriate result list */
|
/* add the path to the appropriate result list */
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case path_excluded:
|
case path_excluded:
|
||||||
|
if (is_excluded_from_virtualfilesystem(path.buf, path.len, DT_DIR) > 0)
|
||||||
|
break;
|
||||||
if (dir->flags & DIR_SHOW_IGNORED)
|
if (dir->flags & DIR_SHOW_IGNORED)
|
||||||
dir_add_name(dir, istate, path.buf, path.len);
|
dir_add_name(dir, istate, path.buf, path.len);
|
||||||
else if ((dir->flags & DIR_SHOW_IGNORED_TOO) ||
|
else if ((dir->flags & DIR_SHOW_IGNORED_TOO) ||
|
||||||
|
|
|
@ -68,6 +68,7 @@ char *notes_ref_name;
|
||||||
int grafts_replace_parents = 1;
|
int grafts_replace_parents = 1;
|
||||||
int core_apply_sparse_checkout;
|
int core_apply_sparse_checkout;
|
||||||
int core_gvfs;
|
int core_gvfs;
|
||||||
|
const char *core_virtualfilesystem;
|
||||||
int merge_log_config = -1;
|
int merge_log_config = -1;
|
||||||
int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
|
int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
|
||||||
unsigned long pack_size_limit_cfg;
|
unsigned long pack_size_limit_cfg;
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "fsmonitor.h"
|
#include "fsmonitor.h"
|
||||||
#include "thread-utils.h"
|
#include "thread-utils.h"
|
||||||
#include "progress.h"
|
#include "progress.h"
|
||||||
|
#include "virtualfilesystem.h"
|
||||||
#include "gvfs.h"
|
#include "gvfs.h"
|
||||||
|
|
||||||
/* Mask for the name length in ce_flags in the on-disk index */
|
/* Mask for the name length in ce_flags in the on-disk index */
|
||||||
|
@ -1904,6 +1905,7 @@ static void post_read_index_from(struct index_state *istate)
|
||||||
tweak_untracked_cache(istate);
|
tweak_untracked_cache(istate);
|
||||||
tweak_split_index(istate);
|
tweak_split_index(istate);
|
||||||
tweak_fsmonitor(istate);
|
tweak_fsmonitor(istate);
|
||||||
|
apply_virtualfilesystem(istate);
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t estimate_cache_size_from_compressed(unsigned int entries)
|
static size_t estimate_cache_size_from_compressed(unsigned int entries)
|
||||||
|
|
|
@ -0,0 +1,349 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test_description='virtual file system tests'
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
clean_repo () {
|
||||||
|
rm .git/index &&
|
||||||
|
git -c core.virtualfilesystem= reset --hard HEAD &&
|
||||||
|
git -c core.virtualfilesystem= clean -fd &&
|
||||||
|
touch untracked.txt &&
|
||||||
|
touch dir1/untracked.txt &&
|
||||||
|
touch dir2/untracked.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
test_expect_success 'setup' '
|
||||||
|
mkdir -p .git/hooks/ &&
|
||||||
|
cat > .gitignore <<-\EOF &&
|
||||||
|
.gitignore
|
||||||
|
expect*
|
||||||
|
actual*
|
||||||
|
EOF
|
||||||
|
mkdir -p dir1 &&
|
||||||
|
touch dir1/file1.txt &&
|
||||||
|
touch dir1/file2.txt &&
|
||||||
|
mkdir -p dir2 &&
|
||||||
|
touch dir2/file1.txt &&
|
||||||
|
touch dir2/file2.txt &&
|
||||||
|
git add . &&
|
||||||
|
git commit -m "initial" &&
|
||||||
|
git config --local core.virtualfilesystem .git/hooks/virtualfilesystem
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'test hook parameters and version' '
|
||||||
|
clean_repo &&
|
||||||
|
write_script .git/hooks/virtualfilesystem <<-\EOF &&
|
||||||
|
if test "$#" -ne 1
|
||||||
|
then
|
||||||
|
echo "$0: Exactly 1 argument expected" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$1" != 1
|
||||||
|
then
|
||||||
|
echo "$0: Unsupported hook version." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
EOF
|
||||||
|
git status &&
|
||||||
|
write_script .git/hooks/virtualfilesystem <<-\EOF &&
|
||||||
|
exit 3
|
||||||
|
EOF
|
||||||
|
test_must_fail git status
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'verify status is clean' '
|
||||||
|
clean_repo &&
|
||||||
|
write_script .git/hooks/virtualfilesystem <<-\EOF &&
|
||||||
|
printf "dir2/file1.txt\0"
|
||||||
|
EOF
|
||||||
|
rm -f .git/index &&
|
||||||
|
git checkout -f &&
|
||||||
|
write_script .git/hooks/virtualfilesystem <<-\EOF &&
|
||||||
|
printf "dir2/file1.txt\0"
|
||||||
|
printf "dir1/file1.txt\0"
|
||||||
|
printf "dir1/file2.txt\0"
|
||||||
|
EOF
|
||||||
|
git status > actual &&
|
||||||
|
cat > expected <<-\EOF &&
|
||||||
|
On branch master
|
||||||
|
nothing to commit, working tree clean
|
||||||
|
EOF
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'verify skip-worktree bit is set for absolute path' '
|
||||||
|
clean_repo &&
|
||||||
|
write_script .git/hooks/virtualfilesystem <<-\EOF &&
|
||||||
|
printf "dir1/file1.txt\0"
|
||||||
|
EOF
|
||||||
|
git ls-files -v > actual &&
|
||||||
|
cat > expected <<-\EOF &&
|
||||||
|
H dir1/file1.txt
|
||||||
|
S dir1/file2.txt
|
||||||
|
S dir2/file1.txt
|
||||||
|
S dir2/file2.txt
|
||||||
|
EOF
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'verify skip-worktree bit is cleared for absolute path' '
|
||||||
|
clean_repo &&
|
||||||
|
write_script .git/hooks/virtualfilesystem <<-\EOF &&
|
||||||
|
printf "dir1/file2.txt\0"
|
||||||
|
EOF
|
||||||
|
git ls-files -v > actual &&
|
||||||
|
cat > expected <<-\EOF &&
|
||||||
|
S dir1/file1.txt
|
||||||
|
H dir1/file2.txt
|
||||||
|
S dir2/file1.txt
|
||||||
|
S dir2/file2.txt
|
||||||
|
EOF
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'verify folder wild cards' '
|
||||||
|
clean_repo &&
|
||||||
|
write_script .git/hooks/virtualfilesystem <<-\EOF &&
|
||||||
|
printf "dir1/\0"
|
||||||
|
EOF
|
||||||
|
git ls-files -v > actual &&
|
||||||
|
cat > expected <<-\EOF &&
|
||||||
|
H dir1/file1.txt
|
||||||
|
H dir1/file2.txt
|
||||||
|
S dir2/file1.txt
|
||||||
|
S dir2/file2.txt
|
||||||
|
EOF
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'verify folders not included are ignored' '
|
||||||
|
clean_repo &&
|
||||||
|
write_script .git/hooks/virtualfilesystem <<-\EOF &&
|
||||||
|
printf "dir1/file1.txt\0"
|
||||||
|
printf "dir1/file2.txt\0"
|
||||||
|
EOF
|
||||||
|
mkdir -p dir1/dir2 &&
|
||||||
|
touch dir1/a &&
|
||||||
|
touch dir1/b &&
|
||||||
|
touch dir1/dir2/a &&
|
||||||
|
touch dir1/dir2/b &&
|
||||||
|
git add . &&
|
||||||
|
git ls-files -v > actual &&
|
||||||
|
cat > expected <<-\EOF &&
|
||||||
|
H dir1/file1.txt
|
||||||
|
H dir1/file2.txt
|
||||||
|
S dir2/file1.txt
|
||||||
|
S dir2/file2.txt
|
||||||
|
EOF
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'verify including one file doesnt include the rest' '
|
||||||
|
clean_repo &&
|
||||||
|
write_script .git/hooks/virtualfilesystem <<-\EOF &&
|
||||||
|
printf "dir1/file1.txt\0"
|
||||||
|
printf "dir1/file2.txt\0"
|
||||||
|
printf "dir1/dir2/a\0"
|
||||||
|
EOF
|
||||||
|
mkdir -p dir1/dir2 &&
|
||||||
|
touch dir1/a &&
|
||||||
|
touch dir1/b &&
|
||||||
|
touch dir1/dir2/a &&
|
||||||
|
touch dir1/dir2/b &&
|
||||||
|
git add . &&
|
||||||
|
git ls-files -v > actual &&
|
||||||
|
cat > expected <<-\EOF &&
|
||||||
|
H dir1/dir2/a
|
||||||
|
H dir1/file1.txt
|
||||||
|
H dir1/file2.txt
|
||||||
|
S dir2/file1.txt
|
||||||
|
S dir2/file2.txt
|
||||||
|
EOF
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'verify files not listed are ignored by git clean -f -x' '
|
||||||
|
clean_repo &&
|
||||||
|
write_script .git/hooks/virtualfilesystem <<-\EOF &&
|
||||||
|
printf "untracked.txt\0"
|
||||||
|
printf "dir1/\0"
|
||||||
|
EOF
|
||||||
|
mkdir -p dir3 &&
|
||||||
|
touch dir3/untracked.txt &&
|
||||||
|
git clean -f -x &&
|
||||||
|
test ! -f untracked.txt &&
|
||||||
|
test -d dir1 &&
|
||||||
|
test -f dir1/file1.txt &&
|
||||||
|
test -f dir1/file2.txt &&
|
||||||
|
test ! -f dir1/untracked.txt &&
|
||||||
|
test -f dir2/file1.txt &&
|
||||||
|
test -f dir2/file2.txt &&
|
||||||
|
test -f dir2/untracked.txt &&
|
||||||
|
test -d dir3 &&
|
||||||
|
test -f dir3/untracked.txt
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'verify files not listed are ignored by git clean -f -d -x' '
|
||||||
|
clean_repo &&
|
||||||
|
write_script .git/hooks/virtualfilesystem <<-\EOF &&
|
||||||
|
printf "untracked.txt\0"
|
||||||
|
printf "dir1/\0"
|
||||||
|
printf "dir3/\0"
|
||||||
|
EOF
|
||||||
|
mkdir -p dir3 &&
|
||||||
|
touch dir3/untracked.txt &&
|
||||||
|
git clean -f -d -x &&
|
||||||
|
test ! -f untracked.txt &&
|
||||||
|
test -d dir1 &&
|
||||||
|
test -f dir1/file1.txt &&
|
||||||
|
test -f dir1/file2.txt &&
|
||||||
|
test ! -f dir1/untracked.txt &&
|
||||||
|
test -f dir2/file1.txt &&
|
||||||
|
test -f dir2/file2.txt &&
|
||||||
|
test -f dir2/untracked.txt &&
|
||||||
|
test ! -d dir3 &&
|
||||||
|
test ! -f dir3/untracked.txt
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'verify folder entries include all files' '
|
||||||
|
clean_repo &&
|
||||||
|
write_script .git/hooks/virtualfilesystem <<-\EOF &&
|
||||||
|
printf "dir1/\0"
|
||||||
|
EOF
|
||||||
|
mkdir -p dir1/dir2 &&
|
||||||
|
touch dir1/a &&
|
||||||
|
touch dir1/b &&
|
||||||
|
touch dir1/dir2/a &&
|
||||||
|
touch dir1/dir2/b &&
|
||||||
|
git status -su > actual &&
|
||||||
|
cat > expected <<-\EOF &&
|
||||||
|
?? dir1/a
|
||||||
|
?? dir1/b
|
||||||
|
?? dir1/untracked.txt
|
||||||
|
EOF
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'verify case insensitivity of virtual file system entries' '
|
||||||
|
clean_repo &&
|
||||||
|
write_script .git/hooks/virtualfilesystem <<-\EOF &&
|
||||||
|
printf "dir1/a\0"
|
||||||
|
printf "Dir1/Dir2/a\0"
|
||||||
|
printf "DIR2/\0"
|
||||||
|
EOF
|
||||||
|
mkdir -p dir1/dir2 &&
|
||||||
|
touch dir1/a &&
|
||||||
|
touch dir1/b &&
|
||||||
|
touch dir1/dir2/a &&
|
||||||
|
touch dir1/dir2/b &&
|
||||||
|
git -c core.ignorecase=false status -su > actual &&
|
||||||
|
cat > expected <<-\EOF &&
|
||||||
|
?? dir1/a
|
||||||
|
EOF
|
||||||
|
test_cmp expected actual &&
|
||||||
|
git -c core.ignorecase=true status -su > actual &&
|
||||||
|
cat > expected <<-\EOF &&
|
||||||
|
?? dir1/a
|
||||||
|
?? dir1/dir2/a
|
||||||
|
?? dir2/untracked.txt
|
||||||
|
EOF
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'on file created' '
|
||||||
|
clean_repo &&
|
||||||
|
write_script .git/hooks/virtualfilesystem <<-\EOF &&
|
||||||
|
printf "dir1/file3.txt\0"
|
||||||
|
EOF
|
||||||
|
touch dir1/file3.txt &&
|
||||||
|
git add . &&
|
||||||
|
git ls-files -v > actual &&
|
||||||
|
cat > expected <<-\EOF &&
|
||||||
|
S dir1/file1.txt
|
||||||
|
S dir1/file2.txt
|
||||||
|
H dir1/file3.txt
|
||||||
|
S dir2/file1.txt
|
||||||
|
S dir2/file2.txt
|
||||||
|
EOF
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'on file renamed' '
|
||||||
|
clean_repo &&
|
||||||
|
write_script .git/hooks/virtualfilesystem <<-\EOF &&
|
||||||
|
printf "dir1/file1.txt\0"
|
||||||
|
printf "dir1/file3.txt\0"
|
||||||
|
EOF
|
||||||
|
mv dir1/file1.txt dir1/file3.txt &&
|
||||||
|
git status -su > actual &&
|
||||||
|
cat > expected <<-\EOF &&
|
||||||
|
D dir1/file1.txt
|
||||||
|
?? dir1/file3.txt
|
||||||
|
EOF
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'on file deleted' '
|
||||||
|
clean_repo &&
|
||||||
|
write_script .git/hooks/virtualfilesystem <<-\EOF &&
|
||||||
|
printf "dir1/file1.txt\0"
|
||||||
|
EOF
|
||||||
|
rm dir1/file1.txt &&
|
||||||
|
git status -su > actual &&
|
||||||
|
cat > expected <<-\EOF &&
|
||||||
|
D dir1/file1.txt
|
||||||
|
EOF
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'on file overwritten' '
|
||||||
|
clean_repo &&
|
||||||
|
write_script .git/hooks/virtualfilesystem <<-\EOF &&
|
||||||
|
printf "dir1/file1.txt\0"
|
||||||
|
EOF
|
||||||
|
echo "overwritten" > dir1/file1.txt &&
|
||||||
|
git status -su > actual &&
|
||||||
|
cat > expected <<-\EOF &&
|
||||||
|
M dir1/file1.txt
|
||||||
|
EOF
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'on folder created' '
|
||||||
|
clean_repo &&
|
||||||
|
write_script .git/hooks/virtualfilesystem <<-\EOF &&
|
||||||
|
printf "dir1/dir1/\0"
|
||||||
|
EOF
|
||||||
|
mkdir -p dir1/dir1 &&
|
||||||
|
git status -su > actual &&
|
||||||
|
cat > expected <<-\EOF &&
|
||||||
|
EOF
|
||||||
|
test_cmp expected actual &&
|
||||||
|
git clean -fd &&
|
||||||
|
test ! -d "/dir1/dir1"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'on folder renamed' '
|
||||||
|
clean_repo &&
|
||||||
|
write_script .git/hooks/virtualfilesystem <<-\EOF &&
|
||||||
|
printf "dir3/\0"
|
||||||
|
printf "dir1/file1.txt\0"
|
||||||
|
printf "dir1/file2.txt\0"
|
||||||
|
printf "dir3/file1.txt\0"
|
||||||
|
printf "dir3/file2.txt\0"
|
||||||
|
EOF
|
||||||
|
mv dir1 dir3 &&
|
||||||
|
git status -su > actual &&
|
||||||
|
cat > expected <<-\EOF &&
|
||||||
|
D dir1/file1.txt
|
||||||
|
D dir1/file2.txt
|
||||||
|
?? dir3/file1.txt
|
||||||
|
?? dir3/file2.txt
|
||||||
|
?? dir3/untracked.txt
|
||||||
|
EOF
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_done
|
|
@ -17,6 +17,7 @@
|
||||||
#include "object-store.h"
|
#include "object-store.h"
|
||||||
#include "promisor-remote.h"
|
#include "promisor-remote.h"
|
||||||
#include "gvfs.h"
|
#include "gvfs.h"
|
||||||
|
#include "virtualfilesystem.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Error messages expected by scripts out of plumbing commands such as
|
* Error messages expected by scripts out of plumbing commands such as
|
||||||
|
@ -1356,6 +1357,14 @@ static int clear_ce_flags_1(struct index_state *istate,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if it's not in the virtual file system, exit early */
|
||||||
|
if (core_virtualfilesystem) {
|
||||||
|
if (is_included_in_virtualfilesystem(ce->name, ce->ce_namelen) > 0)
|
||||||
|
ce->ce_flags &= ~clear_mask;
|
||||||
|
cache++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (prefix->len && strncmp(ce->name, prefix->buf, prefix->len))
|
if (prefix->len && strncmp(ce->name, prefix->buf, prefix->len))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1478,12 +1487,16 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
|
||||||
if (!core_apply_sparse_checkout || !o->update)
|
if (!core_apply_sparse_checkout || !o->update)
|
||||||
o->skip_sparse_checkout = 1;
|
o->skip_sparse_checkout = 1;
|
||||||
if (!o->skip_sparse_checkout) {
|
if (!o->skip_sparse_checkout) {
|
||||||
char *sparse = git_pathdup("info/sparse-checkout");
|
if (core_virtualfilesystem) {
|
||||||
if (add_patterns_from_file_to_list(sparse, "", 0, &pl, NULL) < 0)
|
|
||||||
o->skip_sparse_checkout = 1;
|
|
||||||
else
|
|
||||||
o->pl = &pl;
|
o->pl = &pl;
|
||||||
free(sparse);
|
} else {
|
||||||
|
char *sparse = git_pathdup("info/sparse-checkout");
|
||||||
|
if (add_patterns_from_file_to_list(sparse, "", 0, &pl, NULL) < 0)
|
||||||
|
o->skip_sparse_checkout = 1;
|
||||||
|
else
|
||||||
|
o->pl = &pl;
|
||||||
|
free(sparse);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&o->result, 0, sizeof(o->result));
|
memset(&o->result, 0, sizeof(o->result));
|
||||||
|
@ -1575,7 +1588,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sparse checkout loop #2: set NEW_SKIP_WORKTREE on entries not in loop #1
|
* Sparse checkout loop #2: set NEW_SKIP_WORKTREE on entries not in loop #1
|
||||||
* If the will have NEW_SKIP_WORKTREE, also set CE_SKIP_WORKTREE
|
* If they will have NEW_SKIP_WORKTREE, also set CE_SKIP_WORKTREE
|
||||||
* so apply_sparse_checkout() won't attempt to remove it from worktree
|
* so apply_sparse_checkout() won't attempt to remove it from worktree
|
||||||
*/
|
*/
|
||||||
mark_new_skip_worktree(o->pl, &o->result, CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
|
mark_new_skip_worktree(o->pl, &o->result, CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
|
||||||
|
|
|
@ -0,0 +1,312 @@
|
||||||
|
#include "cache.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "dir.h"
|
||||||
|
#include "hashmap.h"
|
||||||
|
#include "run-command.h"
|
||||||
|
#include "virtualfilesystem.h"
|
||||||
|
|
||||||
|
#define HOOK_INTERFACE_VERSION (1)
|
||||||
|
|
||||||
|
static struct strbuf virtual_filesystem_data = STRBUF_INIT;
|
||||||
|
static struct hashmap virtual_filesystem_hashmap;
|
||||||
|
static struct hashmap parent_directory_hashmap;
|
||||||
|
|
||||||
|
struct virtualfilesystem {
|
||||||
|
struct hashmap_entry ent; /* must be the first member! */
|
||||||
|
const char *pattern;
|
||||||
|
int patternlen;
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned int(*vfshash)(const void *buf, size_t len);
|
||||||
|
static int(*vfscmp)(const char *a, const char *b, size_t len);
|
||||||
|
|
||||||
|
static int vfs_hashmap_cmp(const void *unused_cmp_data,
|
||||||
|
const struct hashmap_entry *he1,
|
||||||
|
const struct hashmap_entry *he2,
|
||||||
|
const void *key)
|
||||||
|
{
|
||||||
|
const struct virtualfilesystem *vfs1 =
|
||||||
|
container_of(he1, const struct virtualfilesystem, ent);
|
||||||
|
const struct virtualfilesystem *vfs2 =
|
||||||
|
container_of(he2, const struct virtualfilesystem, ent);
|
||||||
|
|
||||||
|
return vfscmp(vfs1->pattern, vfs2->pattern, vfs1->patternlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_virtual_filesystem_data(struct strbuf *vfs_data)
|
||||||
|
{
|
||||||
|
struct child_process cp = CHILD_PROCESS_INIT;
|
||||||
|
char ver[64];
|
||||||
|
const char *argv[3];
|
||||||
|
int err;
|
||||||
|
|
||||||
|
strbuf_init(vfs_data, 0);
|
||||||
|
|
||||||
|
snprintf(ver, sizeof(ver), "%d", HOOK_INTERFACE_VERSION);
|
||||||
|
argv[0] = core_virtualfilesystem;
|
||||||
|
argv[1] = ver;
|
||||||
|
argv[2] = NULL;
|
||||||
|
cp.argv = argv;
|
||||||
|
cp.use_shell = 1;
|
||||||
|
|
||||||
|
err = capture_command(&cp, vfs_data, 1024);
|
||||||
|
if (err)
|
||||||
|
die("unable to load virtual file system");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_includes_hashmap(struct hashmap *map, const char *pattern, int patternlen)
|
||||||
|
{
|
||||||
|
struct strbuf sb = STRBUF_INIT;
|
||||||
|
struct virtualfilesystem vfs;
|
||||||
|
char *slash;
|
||||||
|
|
||||||
|
/* Check straight mapping */
|
||||||
|
strbuf_reset(&sb);
|
||||||
|
strbuf_add(&sb, pattern, patternlen);
|
||||||
|
vfs.pattern = sb.buf;
|
||||||
|
vfs.patternlen = sb.len;
|
||||||
|
hashmap_entry_init(&vfs.ent, vfshash(vfs.pattern, vfs.patternlen));
|
||||||
|
if (hashmap_get_entry(map, &vfs, ent, NULL)) {
|
||||||
|
strbuf_release(&sb);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check to see if it matches a directory or any path
|
||||||
|
* underneath it. In other words, 'a/b/foo.txt' will match
|
||||||
|
* '/', 'a/', and 'a/b/'.
|
||||||
|
*/
|
||||||
|
slash = strchr(sb.buf, '/');
|
||||||
|
while (slash) {
|
||||||
|
vfs.pattern = sb.buf;
|
||||||
|
vfs.patternlen = slash - sb.buf + 1;
|
||||||
|
hashmap_entry_init(&vfs.ent, vfshash(vfs.pattern, vfs.patternlen));
|
||||||
|
if (hashmap_get_entry(map, &vfs, ent, NULL)) {
|
||||||
|
strbuf_release(&sb);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
slash = strchr(slash + 1, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
strbuf_release(&sb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void includes_hashmap_add(struct hashmap *map, const char *pattern, const int patternlen)
|
||||||
|
{
|
||||||
|
struct virtualfilesystem *vfs;
|
||||||
|
|
||||||
|
vfs = xmalloc(sizeof(struct virtualfilesystem));
|
||||||
|
vfs->pattern = pattern;
|
||||||
|
vfs->patternlen = patternlen;
|
||||||
|
hashmap_entry_init(&vfs->ent, vfshash(vfs->pattern, vfs->patternlen));
|
||||||
|
hashmap_add(map, &vfs->ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initialize_includes_hashmap(struct hashmap *map, struct strbuf *vfs_data)
|
||||||
|
{
|
||||||
|
char *buf, *entry;
|
||||||
|
size_t len;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build a hashmap of the virtual file system data we can use to look
|
||||||
|
* for cache entry matches quickly
|
||||||
|
*/
|
||||||
|
vfshash = ignore_case ? memihash : memhash;
|
||||||
|
vfscmp = ignore_case ? strncasecmp : strncmp;
|
||||||
|
hashmap_init(map, vfs_hashmap_cmp, NULL, 0);
|
||||||
|
|
||||||
|
entry = buf = vfs_data->buf;
|
||||||
|
len = vfs_data->len;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (buf[i] == '\0') {
|
||||||
|
includes_hashmap_add(map, entry, buf + i - entry);
|
||||||
|
entry = buf + i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return 1 if the requested item is found in the virtual file system,
|
||||||
|
* 0 for not found and -1 for undecided.
|
||||||
|
*/
|
||||||
|
int is_included_in_virtualfilesystem(const char *pathname, int pathlen)
|
||||||
|
{
|
||||||
|
if (!core_virtualfilesystem)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!virtual_filesystem_hashmap.tablesize && virtual_filesystem_data.len)
|
||||||
|
initialize_includes_hashmap(&virtual_filesystem_hashmap, &virtual_filesystem_data);
|
||||||
|
if (!virtual_filesystem_hashmap.tablesize)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return check_includes_hashmap(&virtual_filesystem_hashmap, pathname, pathlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parent_directory_hashmap_add(struct hashmap *map, const char *pattern, const int patternlen)
|
||||||
|
{
|
||||||
|
char *slash;
|
||||||
|
struct virtualfilesystem *vfs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add any directories leading up to the file as the excludes logic
|
||||||
|
* needs to match directories leading up to the files as well. Detect
|
||||||
|
* and prevent unnecessary duplicate entries which will be common.
|
||||||
|
*/
|
||||||
|
if (patternlen > 1) {
|
||||||
|
slash = strchr(pattern + 1, '/');
|
||||||
|
while (slash) {
|
||||||
|
vfs = xmalloc(sizeof(struct virtualfilesystem));
|
||||||
|
vfs->pattern = pattern;
|
||||||
|
vfs->patternlen = slash - pattern + 1;
|
||||||
|
hashmap_entry_init(&vfs->ent, vfshash(vfs->pattern, vfs->patternlen));
|
||||||
|
if (hashmap_get_entry(map, vfs, ent, NULL))
|
||||||
|
free(vfs);
|
||||||
|
else
|
||||||
|
hashmap_add(map, &vfs->ent);
|
||||||
|
slash = strchr(slash + 1, '/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initialize_parent_directory_hashmap(struct hashmap *map, struct strbuf *vfs_data)
|
||||||
|
{
|
||||||
|
char *buf, *entry;
|
||||||
|
size_t len;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build a hashmap of the parent directories contained in the virtual
|
||||||
|
* file system data we can use to look for matches quickly
|
||||||
|
*/
|
||||||
|
vfshash = ignore_case ? memihash : memhash;
|
||||||
|
vfscmp = ignore_case ? strncasecmp : strncmp;
|
||||||
|
hashmap_init(map, vfs_hashmap_cmp, NULL, 0);
|
||||||
|
|
||||||
|
entry = buf = vfs_data->buf;
|
||||||
|
len = vfs_data->len;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (buf[i] == '\0') {
|
||||||
|
parent_directory_hashmap_add(map, entry, buf + i - entry);
|
||||||
|
entry = buf + i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_directory_hashmap(struct hashmap *map, const char *pathname, int pathlen)
|
||||||
|
{
|
||||||
|
struct strbuf sb = STRBUF_INIT;
|
||||||
|
struct virtualfilesystem vfs;
|
||||||
|
|
||||||
|
/* Check for directory */
|
||||||
|
strbuf_reset(&sb);
|
||||||
|
strbuf_add(&sb, pathname, pathlen);
|
||||||
|
strbuf_addch(&sb, '/');
|
||||||
|
vfs.pattern = sb.buf;
|
||||||
|
vfs.patternlen = sb.len;
|
||||||
|
hashmap_entry_init(&vfs.ent, vfshash(vfs.pattern, vfs.patternlen));
|
||||||
|
if (hashmap_get_entry(map, &vfs, ent, NULL)) {
|
||||||
|
strbuf_release(&sb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
strbuf_release(&sb);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return 1 for exclude, 0 for include and -1 for undecided.
|
||||||
|
*/
|
||||||
|
int is_excluded_from_virtualfilesystem(const char *pathname, int pathlen, int dtype)
|
||||||
|
{
|
||||||
|
if (!core_virtualfilesystem)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (dtype != DT_REG && dtype != DT_DIR && dtype != DT_LNK)
|
||||||
|
die(_("is_excluded_from_virtualfilesystem passed unhandled dtype"));
|
||||||
|
|
||||||
|
if (dtype == DT_REG) {
|
||||||
|
int ret = is_included_in_virtualfilesystem(pathname, pathlen);
|
||||||
|
if (ret > 0)
|
||||||
|
return 0;
|
||||||
|
if (ret == 0)
|
||||||
|
return 1;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dtype == DT_DIR || dtype == DT_LNK) {
|
||||||
|
if (!parent_directory_hashmap.tablesize && virtual_filesystem_data.len)
|
||||||
|
initialize_parent_directory_hashmap(&parent_directory_hashmap, &virtual_filesystem_data);
|
||||||
|
if (!parent_directory_hashmap.tablesize)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return check_directory_hashmap(&parent_directory_hashmap, pathname, pathlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the CE_SKIP_WORKTREE bits based on the virtual file system.
|
||||||
|
*/
|
||||||
|
void apply_virtualfilesystem(struct index_state *istate)
|
||||||
|
{
|
||||||
|
char *buf, *entry;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!git_config_get_virtualfilesystem())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!virtual_filesystem_data.len)
|
||||||
|
get_virtual_filesystem_data(&virtual_filesystem_data);
|
||||||
|
|
||||||
|
/* set CE_SKIP_WORKTREE bit on all entries */
|
||||||
|
for (i = 0; i < istate->cache_nr; i++)
|
||||||
|
istate->cache[i]->ce_flags |= CE_SKIP_WORKTREE;
|
||||||
|
|
||||||
|
/* clear CE_SKIP_WORKTREE bit for everything in the virtual file system */
|
||||||
|
entry = buf = virtual_filesystem_data.buf;
|
||||||
|
for (i = 0; i < virtual_filesystem_data.len; i++) {
|
||||||
|
if (buf[i] == '\0') {
|
||||||
|
int pos, len;
|
||||||
|
|
||||||
|
len = buf + i - entry;
|
||||||
|
|
||||||
|
/* look for a directory wild card (ie "dir1/") */
|
||||||
|
if (buf[i - 1] == '/') {
|
||||||
|
if (ignore_case)
|
||||||
|
adjust_dirname_case(istate, entry);
|
||||||
|
pos = index_name_pos(istate, entry, len - 1);
|
||||||
|
if (pos < 0) {
|
||||||
|
pos = -pos - 1;
|
||||||
|
while (pos < istate->cache_nr && !fspathncmp(istate->cache[pos]->name, entry, len)) {
|
||||||
|
istate->cache[pos]->ce_flags &= ~CE_SKIP_WORKTREE;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ignore_case) {
|
||||||
|
struct cache_entry *ce = index_file_exists(istate, entry, len, ignore_case);
|
||||||
|
if (ce)
|
||||||
|
ce->ce_flags &= ~CE_SKIP_WORKTREE;
|
||||||
|
} else {
|
||||||
|
int pos = index_name_pos(istate, entry, len);
|
||||||
|
if (pos >= 0)
|
||||||
|
istate->cache[pos]->ce_flags &= ~CE_SKIP_WORKTREE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry += len + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free the virtual file system data structures.
|
||||||
|
*/
|
||||||
|
void free_virtualfilesystem(void) {
|
||||||
|
hashmap_free_entries(&virtual_filesystem_hashmap, struct virtualfilesystem, ent);
|
||||||
|
hashmap_free_entries(&parent_directory_hashmap, struct virtualfilesystem, ent);
|
||||||
|
strbuf_release(&virtual_filesystem_data);
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef VIRTUALFILESYSTEM_H
|
||||||
|
#define VIRTUALFILESYSTEM_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the CE_SKIP_WORKTREE bits based on the virtual file system.
|
||||||
|
*/
|
||||||
|
void apply_virtualfilesystem(struct index_state *istate);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return 1 if the requested item is found in the virtual file system,
|
||||||
|
* 0 for not found and -1 for undecided.
|
||||||
|
*/
|
||||||
|
int is_included_in_virtualfilesystem(const char *pathname, int pathlen);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return 1 for exclude, 0 for include and -1 for undecided.
|
||||||
|
*/
|
||||||
|
int is_excluded_from_virtualfilesystem(const char *pathname, int pathlen, int dtype);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free the virtual file system data structures.
|
||||||
|
*/
|
||||||
|
void free_virtualfilesystem(void);
|
||||||
|
|
||||||
|
#endif
|
Загрузка…
Ссылка в новой задаче