Skip to content

Commit b02d986

Browse files
committed
Merge virtualfilesystem hook
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 git#33 git#70 Signed-off-by: Ben Peart <Ben.Peart@microsoft.com> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
2 parents e222b1f + 7821224 commit b02d986

18 files changed

+939
-11
lines changed

Documentation/config/core.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,14 @@ Version 2 uses an opaque string so that the monitor can return
111111
something that can be used to determine what files have changed
112112
without race conditions.
113113

114+
core.virtualFilesystem::
115+
If set, the value of this variable is used as a command which
116+
will identify all files and directories that are present in
117+
the working directory. Git will only track and update files
118+
listed in the virtual file system. Using the virtual file system
119+
will supersede the sparse-checkout settings which will be ignored.
120+
See the "virtual file system" section of linkgit:githooks[5].
121+
114122
core.trustctime::
115123
If false, the ctime differences between the index and the
116124
working tree are ignored; useful when the inode change time

Documentation/githooks.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,26 @@ and "0" meaning they were not.
758758
Only one parameter should be set to "1" when the hook runs. The hook
759759
running passing "1", "1" should not be possible.
760760

761+
virtualFilesystem
762+
~~~~~~~~~~~~~~~~~~
763+
764+
"Virtual File System" allows populating the working directory sparsely.
765+
The projection data is typically automatically generated by an external
766+
process. Git will limit what files it checks for changes as well as which
767+
directories are checked for untracked files based on the path names given.
768+
Git will also only update those files listed in the projection.
769+
770+
The hook is invoked when the configuration option core.virtualFilesystem
771+
is set. It takes one argument, a version (currently 1).
772+
773+
The hook should output to stdout the list of all files in the working
774+
directory that git should track. The paths are relative to the root
775+
of the working directory and are separated by a single NUL. Full paths
776+
('dir1/a.txt') as well as directories are supported (ie 'dir1/').
777+
778+
The exit status determines whether git will use the data from the
779+
hook. On error, git will abort the command with an error message.
780+
761781
SEE ALSO
762782
--------
763783
linkgit:git-hook[1]

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,6 +1201,7 @@ LIB_OBJS += utf8.o
12011201
LIB_OBJS += varint.o
12021202
LIB_OBJS += version.o
12031203
LIB_OBJS += versioncmp.o
1204+
LIB_OBJS += virtualfilesystem.o
12041205
LIB_OBJS += walker.o
12051206
LIB_OBJS += wildmatch.o
12061207
LIB_OBJS += worktree.o

config.c

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1649,7 +1649,11 @@ int git_default_core_config(const char *var, const char *value,
16491649
}
16501650

16511651
if (!strcmp(var, "core.sparsecheckout")) {
1652-
core_apply_sparse_checkout = git_config_bool(var, value);
1652+
/* virtual file system relies on the sparse checkout logic so force it on */
1653+
if (core_virtualfilesystem)
1654+
core_apply_sparse_checkout = 1;
1655+
else
1656+
core_apply_sparse_checkout = git_config_bool(var, value);
16531657
return 0;
16541658
}
16551659

@@ -2740,6 +2744,44 @@ int repo_config_get_max_percent_split_change(struct repository *r)
27402744
return -1; /* default value */
27412745
}
27422746

2747+
int repo_config_get_virtualfilesystem(struct repository *r)
2748+
{
2749+
/* Run only once. */
2750+
static int virtual_filesystem_result = -1;
2751+
if (virtual_filesystem_result >= 0)
2752+
return virtual_filesystem_result;
2753+
2754+
if (repo_config_get_pathname(r, "core.virtualfilesystem", &core_virtualfilesystem))
2755+
core_virtualfilesystem = getenv("GIT_VIRTUALFILESYSTEM_TEST");
2756+
2757+
if (core_virtualfilesystem && !*core_virtualfilesystem)
2758+
core_virtualfilesystem = NULL;
2759+
2760+
if (core_virtualfilesystem) {
2761+
/*
2762+
* Some git commands spawn helpers and redirect the index to a different
2763+
* location. These include "difftool -d" and the sequencer
2764+
* (i.e. `git rebase -i`, `git cherry-pick` and `git revert`) and others.
2765+
* In those instances we don't want to update their temporary index with
2766+
* our virtualization data.
2767+
*/
2768+
char *default_index_file = xstrfmt("%s/%s", the_repository->gitdir, "index");
2769+
int should_run_hook = !strcmp(default_index_file, the_repository->index_file);
2770+
2771+
free(default_index_file);
2772+
if (should_run_hook) {
2773+
/* virtual file system relies on the sparse checkout logic so force it on */
2774+
core_apply_sparse_checkout = 1;
2775+
virtual_filesystem_result = 1;
2776+
return 1;
2777+
}
2778+
core_virtualfilesystem = NULL;
2779+
}
2780+
2781+
virtual_filesystem_result = 0;
2782+
return 0;
2783+
}
2784+
27432785
int repo_config_get_index_threads(struct repository *r, int *dest)
27442786
{
27452787
int is_bool, val;

config.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,8 @@ int repo_config_get_index_threads(struct repository *r, int *dest);
684684
int repo_config_get_split_index(struct repository *r);
685685
int repo_config_get_max_percent_split_change(struct repository *r);
686686

687+
int repo_config_get_virtualfilesystem(struct repository *r);
688+
687689
/* This dies if the configured or default date is in the future */
688690
int repo_config_get_expiry(struct repository *r, const char *key, char **output);
689691

dir.c

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include "git-compat-util.h"
1212
#include "abspath.h"
13+
#include "virtualfilesystem.h"
1314
#include "config.h"
1415
#include "convert.h"
1516
#include "dir.h"
@@ -1475,6 +1476,19 @@ enum pattern_match_result path_matches_pattern_list(
14751476
int result = NOT_MATCHED;
14761477
size_t slash_pos;
14771478

1479+
if (core_virtualfilesystem) {
1480+
/*
1481+
* The virtual file system data is used to prevent git from traversing
1482+
* any part of the tree that is not in the virtual file system. Return
1483+
* 1 to exclude the entry if it is not found in the virtual file system,
1484+
* else fall through to the regular excludes logic as it may further exclude.
1485+
*/
1486+
if (*dtype == DT_UNKNOWN)
1487+
*dtype = resolve_dtype(DT_UNKNOWN, istate, pathname, pathlen);
1488+
if (is_excluded_from_virtualfilesystem(pathname, pathlen, *dtype) > 0)
1489+
return 1;
1490+
}
1491+
14781492
if (!pl->use_cone_patterns) {
14791493
pattern = last_matching_pattern_from_list(pathname, pathlen, basename,
14801494
dtype, pl, istate);
@@ -1819,8 +1833,22 @@ struct path_pattern *last_matching_pattern(struct dir_struct *dir,
18191833
int is_excluded(struct dir_struct *dir, struct index_state *istate,
18201834
const char *pathname, int *dtype_p)
18211835
{
1822-
struct path_pattern *pattern =
1823-
last_matching_pattern(dir, istate, pathname, dtype_p);
1836+
struct path_pattern *pattern;
1837+
1838+
if (core_virtualfilesystem) {
1839+
/*
1840+
* The virtual file system data is used to prevent git from traversing
1841+
* any part of the tree that is not in the virtual file system. Return
1842+
* 1 to exclude the entry if it is not found in the virtual file system,
1843+
* else fall through to the regular excludes logic as it may further exclude.
1844+
*/
1845+
if (*dtype_p == DT_UNKNOWN)
1846+
*dtype_p = resolve_dtype(DT_UNKNOWN, istate, pathname, strlen(pathname));
1847+
if (is_excluded_from_virtualfilesystem(pathname, strlen(pathname), *dtype_p) > 0)
1848+
return 1;
1849+
}
1850+
1851+
pattern = last_matching_pattern(dir, istate, pathname, dtype_p);
18241852
if (pattern)
18251853
return pattern->flags & PATTERN_FLAG_NEGATIVE ? 0 : 1;
18261854
return 0;
@@ -2438,6 +2466,8 @@ static enum path_treatment treat_path(struct dir_struct *dir,
24382466
ignore_case);
24392467
if (dtype != DT_DIR && has_path_in_index)
24402468
return path_none;
2469+
if (is_excluded_from_virtualfilesystem(path->buf, path->len, dtype) > 0)
2470+
return path_excluded;
24412471

24422472
/*
24432473
* When we are looking at a directory P in the working tree,
@@ -2642,6 +2672,8 @@ static void add_path_to_appropriate_result_list(struct dir_struct *dir,
26422672
/* add the path to the appropriate result list */
26432673
switch (state) {
26442674
case path_excluded:
2675+
if (is_excluded_from_virtualfilesystem(path->buf, path->len, DT_DIR) > 0)
2676+
break;
26452677
if (dir->flags & DIR_SHOW_IGNORED)
26462678
dir_add_name(dir, istate, path->buf, path->len);
26472679
else if ((dir->flags & DIR_SHOW_IGNORED_TOO) ||

environment.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ int core_apply_sparse_checkout;
7272
int core_sparse_checkout_cone;
7373
int sparse_expect_files_outside_of_patterns;
7474
int core_gvfs;
75+
char *core_virtualfilesystem;
7576
int merge_log_config = -1;
7677
int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
7778
unsigned long pack_size_limit_cfg;

environment.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ extern unsigned long pack_size_limit_cfg;
171171
extern int max_allowed_tree_depth;
172172

173173
extern int core_preload_index;
174+
extern char *core_virtualfilesystem;
174175
extern int core_gvfs;
175176
extern int precomposed_unicode;
176177
extern int protect_hfs;

hook.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ int run_hooks_opt(struct repository *r, const char *hook_name,
196196
.hook_name = hook_name,
197197
.options = options,
198198
};
199-
const char *const hook_path = find_hook(r, hook_name);
199+
const char *hook_path = find_hook(r, hook_name);
200200
int ret = 0;
201201
const struct run_process_parallel_opts opts = {
202202
.tr2_category = "hook",
@@ -212,6 +212,18 @@ int run_hooks_opt(struct repository *r, const char *hook_name,
212212
.data = &cb_data,
213213
};
214214

215+
/*
216+
* Backwards compatibility hack in VFS for Git: when originally
217+
* introduced (and used!), it was called `post-indexchanged`, but this
218+
* name was changed during the review on the Git mailing list.
219+
*
220+
* Therefore, when the `post-index-change` hook is not found, let's
221+
* look for a hook with the old name (which would be found in case of
222+
* already-existing checkouts).
223+
*/
224+
if (!hook_path && !strcmp(hook_name, "post-index-change"))
225+
hook_path = find_hook(r, "post-indexchanged");
226+
215227
if (!options)
216228
BUG("a struct run_hooks_opt must be provided to run_hooks");
217229

read-cache.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "git-compat-util.h"
1010
#include "bulk-checkin.h"
11+
#include "virtualfilesystem.h"
1112
#include "config.h"
1213
#include "date.h"
1314
#include "diff.h"
@@ -1979,6 +1980,7 @@ static void post_read_index_from(struct index_state *istate)
19791980
tweak_untracked_cache(istate);
19801981
tweak_split_index(istate);
19811982
tweak_fsmonitor(istate);
1983+
apply_virtualfilesystem(istate);
19821984
}
19831985

19841986
static size_t estimate_cache_size_from_compressed(unsigned int entries)

sparse-index.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ static int add_path_to_index(const struct object_id *oid,
267267
size_t len = base->len;
268268

269269
if (S_ISDIR(mode)) {
270-
int dtype;
270+
int dtype = DT_DIR;
271271
size_t baselen = base->len;
272272
if (!ctx->pl)
273273
return READ_TREE_RECURSIVE;
@@ -391,7 +391,7 @@ void expand_index(struct index_state *istate, struct pattern_list *pl)
391391
struct cache_entry *ce = istate->cache[i];
392392
struct tree *tree;
393393
struct pathspec ps;
394-
int dtype;
394+
int dtype = DT_UNKNOWN;
395395

396396
if (!S_ISSPARSEDIR(ce->ce_mode)) {
397397
set_index_entry(full, full->cache_nr++, ce);
@@ -666,6 +666,7 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista
666666
void clear_skip_worktree_from_present_files(struct index_state *istate)
667667
{
668668
if (!core_apply_sparse_checkout ||
669+
core_virtualfilesystem ||
669670
sparse_expect_files_outside_of_patterns)
670671
return;
671672

t/t1090-sparse-checkout-scope.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ test_expect_success 'in partial clone, sparse checkout only fetches needed blobs
108108
'
109109

110110
test_expect_success 'checkout does not delete items outside the sparse checkout file' '
111-
# The "sparse.expectfilesoutsideofpatterns" config will prevent the
111+
# The "core.virtualfilesystem" config will prevent the
112112
# SKIP_WORKTREE flag from being dropped on files present on-disk.
113-
test_config sparse.expectfilesoutsideofpatterns true &&
113+
test_config core.virtualfilesystem true &&
114114
115115
test_config core.gvfs 8 &&
116116
git checkout -b outside &&

0 commit comments

Comments
 (0)