Skip to content

Commit

Permalink
Merge virtualfilesystem hook
Browse files Browse the repository at this point in the history
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.

The hook was first contributed in private, but was extended via the
following pull requests:

	#15
	#27
	#33
	msysgit#70

Signed-off-by: Ben Peart <Ben.Peart@microsoft.com>
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
  • Loading branch information
dscho committed Nov 30, 2022
2 parents a937df9 + c3ac52a commit 9c7a72b
Show file tree
Hide file tree
Showing 18 changed files with 887 additions and 7 deletions.
8 changes: 8 additions & 0 deletions Documentation/config/core.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
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::
If false, the ctime differences between the index and the
working tree are ignored; useful when the inode change time
Expand Down
20 changes: 20 additions & 0 deletions Documentation/githooks.txt
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,26 @@ and "0" meaning they were not.
Only one parameter should be set to "1" when the hook runs. The hook
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
--------
linkgit:git-hook[1]
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1190,6 +1190,7 @@ LIB_OBJS += utf8.o
LIB_OBJS += varint.o
LIB_OBJS += version.o
LIB_OBJS += versioncmp.o
LIB_OBJS += virtualfilesystem.o
LIB_OBJS += walker.o
LIB_OBJS += wildmatch.o
LIB_OBJS += worktree.o
Expand Down
1 change: 1 addition & 0 deletions cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -1050,6 +1050,7 @@ enum fsync_method {

extern enum fsync_method fsync_method;
extern int core_preload_index;
extern const char *core_virtualfilesystem;
extern int core_gvfs;
extern int precomposed_unicode;
extern int protect_hfs;
Expand Down
44 changes: 43 additions & 1 deletion config.c
Original file line number Diff line number Diff line change
Expand Up @@ -1750,7 +1750,11 @@ int git_default_core_config(const char *var, const char *value, void *cb)
}

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;
}

Expand Down Expand Up @@ -2800,6 +2804,44 @@ int git_config_get_max_percent_split_change(void)
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;

if (core_virtualfilesystem) {
/*
* Some git commands spawn helpers and redirect the index to a different
* location. These include "difftool -d" and the sequencer
* (i.e. `git rebase -i`, `git cherry-pick` and `git revert`) and others.
* In those instances we don't want to update their temporary index with
* our virtualization data.
*/
char *default_index_file = xstrfmt("%s/%s", the_repository->gitdir, "index");
int should_run_hook = !strcmp(default_index_file, the_repository->index_file);

free(default_index_file);
if (should_run_hook) {
/* virtual file system relies on the sparse checkout logic so force it on */
core_apply_sparse_checkout = 1;
virtual_filesystem_result = 1;
return 1;
}
core_virtualfilesystem = NULL;
}

virtual_filesystem_result = 0;
return 0;
}

int git_config_get_index_threads(int *dest)
{
int is_bool, val;
Expand Down
1 change: 1 addition & 0 deletions config.h
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ int git_config_get_pathname(const char *key, const char **dest);
int git_config_get_index_threads(int *dest);
int git_config_get_split_index(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 */
int git_config_get_expiry(const char *key, const char **output);
Expand Down
32 changes: 30 additions & 2 deletions dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Junio Hamano, 2005-2006
*/
#include "cache.h"
#include "virtualfilesystem.h"
#include "config.h"
#include "dir.h"
#include "object-store.h"
Expand Down Expand Up @@ -1412,6 +1413,17 @@ enum pattern_match_result path_matches_pattern_list(
int result = NOT_MATCHED;
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) {
pattern = last_matching_pattern_from_list(pathname, pathlen, basename,
dtype, pl, istate);
Expand Down Expand Up @@ -1755,8 +1767,20 @@ struct path_pattern *last_matching_pattern(struct dir_struct *dir,
int is_excluded(struct dir_struct *dir, struct index_state *istate,
const char *pathname, int *dtype_p)
{
struct path_pattern *pattern =
last_matching_pattern(dir, istate, pathname, dtype_p);
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_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)
return pattern->flags & PATTERN_FLAG_NEGATIVE ? 0 : 1;
return 0;
Expand Down Expand Up @@ -2342,6 +2366,8 @@ static enum path_treatment treat_path(struct dir_struct *dir,
ignore_case);
if (dtype != DT_DIR && has_path_in_index)
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,
Expand Down Expand Up @@ -2546,6 +2572,8 @@ static void add_path_to_appropriate_result_list(struct dir_struct *dir,
/* add the path to the appropriate result list */
switch (state) {
case path_excluded:
if (is_excluded_from_virtualfilesystem(path->buf, path->len, DT_DIR) > 0)
break;
if (dir->flags & DIR_SHOW_IGNORED)
dir_add_name(dir, istate, path->buf, path->len);
else if ((dir->flags & DIR_SHOW_IGNORED_TOO) ||
Expand Down
1 change: 1 addition & 0 deletions environment.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ int core_apply_sparse_checkout;
int core_sparse_checkout_cone;
int sparse_expect_files_outside_of_patterns;
int core_gvfs;
const char *core_virtualfilesystem;
int merge_log_config = -1;
int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
unsigned long pack_size_limit_cfg;
Expand Down
14 changes: 13 additions & 1 deletion hook.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ int run_hooks_opt(const char *hook_name, struct run_hooks_opt *options)
.hook_name = hook_name,
.options = options,
};
const char *const hook_path = find_hook(hook_name);
const char *hook_path = find_hook(hook_name);
int ret = 0;
const struct run_process_parallel_opts opts = {
.tr2_category = "hook",
Expand All @@ -189,6 +189,18 @@ int run_hooks_opt(const char *hook_name, struct run_hooks_opt *options)
.data = &cb_data,
};

/*
* Backwards compatibility hack in VFS for Git: when originally
* introduced (and used!), it was called `post-indexchanged`, but this
* name was changed during the review on the Git mailing list.
*
* Therefore, when the `post-index-change` hook is not found, let's
* look for a hook with the old name (which would be found in case of
* already-existing checkouts).
*/
if (!hook_path && !strcmp(hook_name, "post-index-change"))
hook_path = find_hook("post-indexchanged");

if (!options)
BUG("a struct run_hooks_opt must be provided to run_hooks");

Expand Down
2 changes: 2 additions & 0 deletions read-cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
#include "cache.h"
#include "gvfs.h"
#include "virtualfilesystem.h"
#include "config.h"
#include "diff.h"
#include "diffcore.h"
Expand Down Expand Up @@ -2070,6 +2071,7 @@ static void post_read_index_from(struct index_state *istate)
tweak_untracked_cache(istate);
tweak_split_index(istate);
tweak_fsmonitor(istate);
apply_virtualfilesystem(istate);
}

static size_t estimate_cache_size_from_compressed(unsigned int entries)
Expand Down
1 change: 1 addition & 0 deletions sparse-index.c
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@ void clear_skip_worktree_from_present_files(struct index_state *istate)
int restarted = 0;

if (!core_apply_sparse_checkout ||
core_virtualfilesystem ||
sparse_expect_files_outside_of_patterns)
return;

Expand Down
4 changes: 2 additions & 2 deletions t/t1090-sparse-checkout-scope.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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' '
# 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.
test_config sparse.expectfilesoutsideofpatterns true &&
test_config core.virtualfilesystem true &&
test_config core.gvfs 8 &&
git checkout -b outside &&
Expand Down
Loading

0 comments on commit 9c7a72b

Please sign in to comment.