зеркало из 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. Update 2022-04-05: disable the "present-despite-SKIP_WORKTREE" file removal behavior when 'core.virtualfilesystem' is enabled. Signed-off-by: Ben Peart <benpeart@microsoft.com>
This commit is contained in:
Родитель
0a56d43054
Коммит
7c8003fc03
|
@ -111,6 +111,14 @@ Version 2 uses an opaque string so that the monitor can return
|
||||||
something that can be used to determine what files have changed
|
something that can be used to determine what files have changed
|
||||||
without race conditions.
|
without race conditions.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
|
@ -698,6 +698,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.
|
||||||
|
|
||||||
SEE ALSO
|
SEE ALSO
|
||||||
--------
|
--------
|
||||||
linkgit:git-hook[1]
|
linkgit:git-hook[1]
|
||||||
|
|
1
Makefile
1
Makefile
|
@ -1121,6 +1121,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
|
@ -1074,6 +1074,7 @@ enum fsync_method {
|
||||||
|
|
||||||
extern enum fsync_method fsync_method;
|
extern enum fsync_method fsync_method;
|
||||||
extern int core_preload_index;
|
extern int core_preload_index;
|
||||||
|
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;
|
||||||
|
|
30
config.c
30
config.c
|
@ -1736,7 +1736,11 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2800,6 +2804,30 @@ int git_config_get_max_percent_split_change(void)
|
||||||
return -1; /* default value */
|
return -1; /* default value */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int git_config_get_virtualfilesystem(void)
|
||||||
|
{
|
||||||
|
/* Run only once. */
|
||||||
|
static int virtual_filesystem_result = -1;
|
||||||
|
if (virtual_filesystem_result >= 0)
|
||||||
|
return virtual_filesystem_result;
|
||||||
|
|
||||||
|
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;
|
||||||
|
virtual_filesystem_result = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual_filesystem_result = 0;
|
||||||
|
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
|
@ -614,6 +614,7 @@ int git_config_get_pathname(const char *key, const char **dest);
|
||||||
int git_config_get_index_threads(int *dest);
|
int git_config_get_index_threads(int *dest);
|
||||||
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_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);
|
||||||
|
|
32
dir.c
32
dir.c
|
@ -6,6 +6,7 @@
|
||||||
* Junio Hamano, 2005-2006
|
* Junio Hamano, 2005-2006
|
||||||
*/
|
*/
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
|
#include "virtualfilesystem.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "dir.h"
|
#include "dir.h"
|
||||||
#include "object-store.h"
|
#include "object-store.h"
|
||||||
|
@ -1414,6 +1415,17 @@ enum pattern_match_result path_matches_pattern_list(
|
||||||
int result = NOT_MATCHED;
|
int result = NOT_MATCHED;
|
||||||
size_t slash_pos;
|
size_t slash_pos;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 = resolve_dtype(DT_UNKNOWN, istate, pathname, pathlen);
|
||||||
|
if (is_excluded_from_virtualfilesystem(pathname, pathlen, *dtype) > 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (!pl->use_cone_patterns) {
|
if (!pl->use_cone_patterns) {
|
||||||
pattern = last_matching_pattern_from_list(pathname, pathlen, basename,
|
pattern = last_matching_pattern_from_list(pathname, pathlen, basename,
|
||||||
dtype, pl, istate);
|
dtype, pl, istate);
|
||||||
|
@ -1757,8 +1769,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 = resolve_dtype(DT_UNKNOWN, 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;
|
||||||
|
@ -2344,6 +2368,8 @@ static enum path_treatment treat_path(struct dir_struct *dir,
|
||||||
ignore_case);
|
ignore_case);
|
||||||
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,
|
||||||
|
@ -2548,6 +2574,8 @@ static void add_path_to_appropriate_result_list(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) ||
|
||||||
|
|
|
@ -73,6 +73,7 @@ int core_apply_sparse_checkout;
|
||||||
int core_sparse_checkout_cone;
|
int core_sparse_checkout_cone;
|
||||||
int sparse_expect_files_outside_of_patterns;
|
int sparse_expect_files_outside_of_patterns;
|
||||||
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;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "gvfs.h"
|
#include "gvfs.h"
|
||||||
|
#include "virtualfilesystem.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "diff.h"
|
#include "diff.h"
|
||||||
#include "diffcore.h"
|
#include "diffcore.h"
|
||||||
|
@ -2047,6 +2048,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)
|
||||||
|
|
|
@ -495,6 +495,7 @@ void clear_skip_worktree_from_present_files(struct index_state *istate)
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (!core_apply_sparse_checkout ||
|
if (!core_apply_sparse_checkout ||
|
||||||
|
core_virtualfilesystem ||
|
||||||
sparse_expect_files_outside_of_patterns)
|
sparse_expect_files_outside_of_patterns)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -107,9 +107,9 @@ test_expect_success 'in partial clone, sparse checkout only fetches needed blobs
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'checkout does not delete items outside the sparse checkout file' '
|
test_expect_success 'checkout does not delete items outside the sparse checkout file' '
|
||||||
# The "sparse.expectfilesoutsideofpatterns" config will prevent the
|
# The "core.virtualfilesystem" config will prevent the
|
||||||
# SKIP_WORKTREE flag from being dropped on files present on-disk.
|
# SKIP_WORKTREE flag from being dropped on files present on-disk.
|
||||||
test_config sparse.expectfilesoutsideofpatterns true &&
|
test_config core.virtualfilesystem true &&
|
||||||
|
|
||||||
test_config core.gvfs 8 &&
|
test_config core.gvfs 8 &&
|
||||||
git checkout -b outside &&
|
git checkout -b outside &&
|
||||||
|
|
|
@ -0,0 +1,350 @@
|
||||||
|
#!/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' '
|
||||||
|
git branch -M main &&
|
||||||
|
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 main
|
||||||
|
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
|
|
@ -1,5 +1,6 @@
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "gvfs.h"
|
#include "gvfs.h"
|
||||||
|
#include "virtualfilesystem.h"
|
||||||
#include "strvec.h"
|
#include "strvec.h"
|
||||||
#include "repository.h"
|
#include "repository.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
@ -1682,6 +1683,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;
|
||||||
|
|
||||||
|
@ -1903,7 +1912,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
|
||||||
if (!o->skip_sparse_checkout && !o->pl) {
|
if (!o->skip_sparse_checkout && !o->pl) {
|
||||||
memset(&pl, 0, sizeof(pl));
|
memset(&pl, 0, sizeof(pl));
|
||||||
free_pattern_list = 1;
|
free_pattern_list = 1;
|
||||||
populate_from_existing_patterns(o, &pl);
|
if (core_virtualfilesystem)
|
||||||
|
o->pl = &pl;
|
||||||
|
else
|
||||||
|
populate_from_existing_patterns(o, &pl);
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&o->result, 0, sizeof(o->result));
|
memset(&o->result, 0, sizeof(o->result));
|
||||||
|
|
|
@ -0,0 +1,308 @@
|
||||||
|
#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;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
strbuf_init(vfs_data, 0);
|
||||||
|
|
||||||
|
strvec_push(&cp.args, core_virtualfilesystem);
|
||||||
|
strvec_pushf(&cp.args, "%d", HOOK_INTERFACE_VERSION);
|
||||||
|
cp.use_shell = 1;
|
||||||
|
cp.dir = get_git_work_tree();
|
||||||
|
|
||||||
|
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_clear_and_free(&virtual_filesystem_hashmap, struct virtualfilesystem, ent);
|
||||||
|
hashmap_clear_and_free(&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
|
|
@ -1572,6 +1572,8 @@ static void show_sparse_checkout_in_use(struct wt_status *s,
|
||||||
{
|
{
|
||||||
if (s->state.sparse_checkout_percentage == SPARSE_CHECKOUT_DISABLED)
|
if (s->state.sparse_checkout_percentage == SPARSE_CHECKOUT_DISABLED)
|
||||||
return;
|
return;
|
||||||
|
if (core_virtualfilesystem)
|
||||||
|
return;
|
||||||
|
|
||||||
if (s->state.sparse_checkout_percentage == SPARSE_CHECKOUT_SPARSE_INDEX)
|
if (s->state.sparse_checkout_percentage == SPARSE_CHECKOUT_SPARSE_INDEX)
|
||||||
status_printf_ln(s, color, _("You are in a sparse checkout."));
|
status_printf_ln(s, color, _("You are in a sparse checkout."));
|
||||||
|
|
Загрузка…
Ссылка в новой задаче