Skip to content

Commit 8e5b8ca

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 #33 #70 Signed-off-by: Ben Peart <Ben.Peart@microsoft.com> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
2 parents 1fed6cc + eea3547 commit 8e5b8ca

20 files changed

+939
-9
lines changed

Documentation/config/core.adoc

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.adoc

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
@@ -1208,6 +1208,7 @@ LIB_OBJS += utf8.o
12081208
LIB_OBJS += varint.o
12091209
LIB_OBJS += version.o
12101210
LIB_OBJS += versioncmp.o
1211+
LIB_OBJS += virtualfilesystem.o
12111212
LIB_OBJS += walker.o
12121213
LIB_OBJS += wildmatch.o
12131214
LIB_OBJS += worktree.o

config.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2186,6 +2186,46 @@ int repo_config_get_max_percent_split_change(struct repository *r)
21862186
return -1; /* default value */
21872187
}
21882188

2189+
int repo_config_get_virtualfilesystem(struct repository *r)
2190+
{
2191+
/* Run only once. */
2192+
static int virtual_filesystem_result = -1;
2193+
extern char *core_virtualfilesystem;
2194+
extern int core_apply_sparse_checkout;
2195+
if (virtual_filesystem_result >= 0)
2196+
return virtual_filesystem_result;
2197+
2198+
if (repo_config_get_pathname(r, "core.virtualfilesystem", &core_virtualfilesystem))
2199+
core_virtualfilesystem = xstrdup_or_null(getenv("GIT_VIRTUALFILESYSTEM_TEST"));
2200+
2201+
if (core_virtualfilesystem && !*core_virtualfilesystem)
2202+
FREE_AND_NULL(core_virtualfilesystem);
2203+
2204+
if (core_virtualfilesystem) {
2205+
/*
2206+
* Some git commands spawn helpers and redirect the index to a different
2207+
* location. These include "difftool -d" and the sequencer
2208+
* (i.e. `git rebase -i`, `git cherry-pick` and `git revert`) and others.
2209+
* In those instances we don't want to update their temporary index with
2210+
* our virtualization data.
2211+
*/
2212+
char *default_index_file = xstrfmt("%s/%s", r->gitdir, "index");
2213+
int should_run_hook = !strcmp(default_index_file, r->index_file);
2214+
2215+
free(default_index_file);
2216+
if (should_run_hook) {
2217+
/* virtual file system relies on the sparse checkout logic so force it on */
2218+
core_apply_sparse_checkout = 1;
2219+
virtual_filesystem_result = 1;
2220+
return 1;
2221+
}
2222+
FREE_AND_NULL(core_virtualfilesystem);
2223+
}
2224+
2225+
virtual_filesystem_result = 0;
2226+
return 0;
2227+
}
2228+
21892229
int repo_config_get_index_threads(struct repository *r, int *dest)
21902230
{
21912231
int is_bool, val;

config.h

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

689+
int repo_config_get_virtualfilesystem(struct repository *r);
690+
689691
/* This dies if the configured or default date is in the future */
690692
int repo_config_get_expiry(struct repository *r, const char *key, char **output);
691693

dir.c

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "git-compat-util.h"
1313
#include "abspath.h"
14+
#include "virtualfilesystem.h"
1415
#include "config.h"
1516
#include "convert.h"
1617
#include "dir.h"
@@ -1526,6 +1527,19 @@ enum pattern_match_result path_matches_pattern_list(
15261527
int result = NOT_MATCHED;
15271528
size_t slash_pos;
15281529

1530+
if (core_virtualfilesystem) {
1531+
/*
1532+
* The virtual file system data is used to prevent git from traversing
1533+
* any part of the tree that is not in the virtual file system. Return
1534+
* 1 to exclude the entry if it is not found in the virtual file system,
1535+
* else fall through to the regular excludes logic as it may further exclude.
1536+
*/
1537+
if (*dtype == DT_UNKNOWN)
1538+
*dtype = resolve_dtype(DT_UNKNOWN, istate, pathname, pathlen);
1539+
if (is_excluded_from_virtualfilesystem(pathname, pathlen, *dtype) > 0)
1540+
return 1;
1541+
}
1542+
15291543
if (!pl->use_cone_patterns) {
15301544
pattern = last_matching_pattern_from_list(pathname, pathlen, basename,
15311545
dtype, pl, istate);
@@ -1615,6 +1629,13 @@ static int path_in_sparse_checkout_1(const char *path,
16151629
enum pattern_match_result match = UNDECIDED;
16161630
const char *end, *slash;
16171631

1632+
/*
1633+
* When using a virtual filesystem, there aren't really patterns
1634+
* to follow, but be extra careful to skip this check.
1635+
*/
1636+
if (core_virtualfilesystem)
1637+
return 1;
1638+
16181639
/*
16191640
* We default to accepting a path if the path is empty, there are no
16201641
* patterns, or the patterns are of the wrong type.
@@ -1870,8 +1891,22 @@ struct path_pattern *last_matching_pattern(struct dir_struct *dir,
18701891
int is_excluded(struct dir_struct *dir, struct index_state *istate,
18711892
const char *pathname, int *dtype_p)
18721893
{
1873-
struct path_pattern *pattern =
1874-
last_matching_pattern(dir, istate, pathname, dtype_p);
1894+
struct path_pattern *pattern;
1895+
1896+
if (core_virtualfilesystem) {
1897+
/*
1898+
* The virtual file system data is used to prevent git from traversing
1899+
* any part of the tree that is not in the virtual file system. Return
1900+
* 1 to exclude the entry if it is not found in the virtual file system,
1901+
* else fall through to the regular excludes logic as it may further exclude.
1902+
*/
1903+
if (*dtype_p == DT_UNKNOWN)
1904+
*dtype_p = resolve_dtype(DT_UNKNOWN, istate, pathname, strlen(pathname));
1905+
if (is_excluded_from_virtualfilesystem(pathname, strlen(pathname), *dtype_p) > 0)
1906+
return 1;
1907+
}
1908+
1909+
pattern = last_matching_pattern(dir, istate, pathname, dtype_p);
18751910
if (pattern)
18761911
return pattern->flags & PATTERN_FLAG_NEGATIVE ? 0 : 1;
18771912
return 0;
@@ -2489,6 +2524,8 @@ static enum path_treatment treat_path(struct dir_struct *dir,
24892524
ignore_case);
24902525
if (dtype != DT_DIR && has_path_in_index)
24912526
return path_none;
2527+
if (is_excluded_from_virtualfilesystem(path->buf, path->len, dtype) > 0)
2528+
return path_excluded;
24922529

24932530
/*
24942531
* When we are looking at a directory P in the working tree,
@@ -2693,6 +2730,8 @@ static void add_path_to_appropriate_result_list(struct dir_struct *dir,
26932730
/* add the path to the appropriate result list */
26942731
switch (state) {
26952732
case path_excluded:
2733+
if (is_excluded_from_virtualfilesystem(path->buf, path->len, DT_DIR) > 0)
2734+
break;
26962735
if (dir->flags & DIR_SHOW_IGNORED)
26972736
dir_add_name(dir, istate, path->buf, path->len);
26982737
else if ((dir->flags & DIR_SHOW_IGNORED_TOO) ||

environment.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ int grafts_keep_true_parents;
7878
int core_apply_sparse_checkout;
7979
int core_sparse_checkout_cone;
8080
int sparse_expect_files_outside_of_patterns;
81+
char *core_virtualfilesystem;
8182
int merge_log_config = -1;
8283
int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
8384
unsigned long pack_size_limit_cfg;
@@ -545,7 +546,11 @@ int git_default_core_config(const char *var, const char *value,
545546
}
546547

547548
if (!strcmp(var, "core.sparsecheckout")) {
548-
core_apply_sparse_checkout = git_config_bool(var, value);
549+
/* virtual file system relies on the sparse checkout logic so force it on */
550+
if (core_virtualfilesystem)
551+
core_apply_sparse_checkout = 1;
552+
else
553+
core_apply_sparse_checkout = git_config_bool(var, value);
549554
return 0;
550555
}
551556

environment.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ extern int pack_compression_level;
160160
extern unsigned long pack_size_limit_cfg;
161161
extern int max_allowed_tree_depth;
162162

163+
extern char *core_virtualfilesystem;
163164
extern int precomposed_unicode;
164165
extern int protect_hfs;
165166
extern int protect_ntfs;

hook.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ int run_hooks_opt(struct repository *r, const char *hook_name,
185185
.hook_name = hook_name,
186186
.options = options,
187187
};
188-
const char *const hook_path = find_hook(r, hook_name);
188+
const char *hook_path = find_hook(r, hook_name);
189189
int ret = 0;
190190
const struct run_process_parallel_opts opts = {
191191
.tr2_category = "hook",
@@ -201,6 +201,18 @@ int run_hooks_opt(struct repository *r, const char *hook_name,
201201
.data = &cb_data,
202202
};
203203

204+
/*
205+
* Backwards compatibility hack in VFS for Git: when originally
206+
* introduced (and used!), it was called `post-indexchanged`, but this
207+
* name was changed during the review on the Git mailing list.
208+
*
209+
* Therefore, when the `post-index-change` hook is not found, let's
210+
* look for a hook with the old name (which would be found in case of
211+
* already-existing checkouts).
212+
*/
213+
if (!hook_path && !strcmp(hook_name, "post-index-change"))
214+
hook_path = find_hook(r, "post-indexchanged");
215+
204216
if (!options)
205217
BUG("a struct run_hooks_opt must be provided to run_hooks");
206218

meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,7 @@ libgit_sources = [
526526
'varint.c',
527527
'version.c',
528528
'versioncmp.c',
529+
'virtualfilesystem.c',
529530
'walker.c',
530531
'wildmatch.c',
531532
'worktree.c',

0 commit comments

Comments
 (0)