Skip to content

Commit df52991

Browse files
mjcheethamdscho
authored andcommitted
worktree: conditionally allow worktree on VFS-enabled repos (#868)
Add GVFS_SUPPORTS_WORKTREES flag (1<<8) to core.gvfs bitmask. When set, allow git worktree commands to run on VFS-enabled repos instead of blocking them with BLOCK_ON_VFS_ENABLED. Force --no-checkout during worktree add when VFS is active so ProjFS can be mounted before files are projected. Support skip-clean-check marker file in worktree gitdir: if .git/worktrees/<name>/skip-clean-check exists, skip the cleanliness check during worktree remove. This allows VFSForGit's pre-command hook to unmount ProjFS after its own status check, then let git proceed without re-checking (which would fail without the virtual filesystem). The corresponding change in VFSForGit is microsoft/VFSForGit#1911 * [x] This change only applies to the virtualization hook and VFS for Git.
2 parents 48149e0 + 013cab2 commit df52991

4 files changed

Lines changed: 69 additions & 3 deletions

File tree

builtin/worktree.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,14 @@ static int add(int ac, const char **av, const char *prefix,
856856
if (ac < 1 || ac > 2)
857857
usage_with_options(git_worktree_add_usage, options);
858858

859+
/*
860+
* When the virtual file system is active, skip checkout during
861+
* worktree creation. The VFS layer will handle the checkout
862+
* after the worktree structure is set up.
863+
*/
864+
if (gvfs_config_is_set(the_repository, GVFS_USE_VIRTUAL_FILESYSTEM))
865+
opts.checkout = 0;
866+
859867
path = prefix_filename(prefix, av[0]);
860868
branch = ac < 2 ? "HEAD" : av[1];
861869
used_new_branch_options = new_branch || new_branch_force;
@@ -1376,6 +1384,21 @@ static int delete_git_work_tree(struct worktree *wt)
13761384
return ret;
13771385
}
13781386

1387+
/*
1388+
* Check if a pre-command hook has already verified worktree cleanliness
1389+
* and written a marker file to skip git's own check. VFSForGit uses this
1390+
* to unmount ProjFS after its own status check; without it, git's status
1391+
* call would fail because the virtual filesystem is no longer available.
1392+
*/
1393+
static int should_skip_clean_check(struct worktree *wt)
1394+
{
1395+
char *path = repo_common_path(the_repository,
1396+
"worktrees/%s/skip-clean-check", wt->id);
1397+
int skip = file_exists(path);
1398+
free(path);
1399+
return skip;
1400+
}
1401+
13791402
static int remove_worktree(int ac, const char **av, const char *prefix,
13801403
struct repository *repo UNUSED)
13811404
{
@@ -1415,7 +1438,7 @@ static int remove_worktree(int ac, const char **av, const char *prefix,
14151438
strbuf_release(&errmsg);
14161439

14171440
if (file_exists(wt->path)) {
1418-
if (!force)
1441+
if (!force && !(core_virtualfilesystem && should_skip_clean_check(wt)))
14191442
check_clean_worktree(wt, av[0]);
14201443

14211444
ret |= delete_git_work_tree(wt);
@@ -1487,6 +1510,14 @@ int cmd_worktree(int ac,
14871510

14881511
ac = parse_options(ac, av, prefix, options, git_worktree_usage, 0);
14891512

1513+
/*
1514+
* Block worktree commands when VFS is active unless the VFS layer
1515+
* has signaled worktree support via GVFS_SUPPORTS_WORKTREES.
1516+
*/
1517+
if (gvfs_config_is_set(the_repository, GVFS_USE_VIRTUAL_FILESYSTEM) &&
1518+
!gvfs_config_is_set(the_repository, GVFS_SUPPORTS_WORKTREES))
1519+
die("'git worktree' is not supported when using the virtual file system");
1520+
14901521
prepare_repo_settings(the_repository);
14911522
the_repository->settings.command_requires_full_index = 0;
14921523

git.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -762,7 +762,7 @@ static struct cmd_struct commands[] = {
762762
#ifndef WITH_BREAKING_CHANGES
763763
{ "whatchanged", cmd_whatchanged, RUN_SETUP | DEPRECATED },
764764
#endif
765-
{ "worktree", cmd_worktree, RUN_SETUP | BLOCK_ON_VFS_ENABLED },
765+
{ "worktree", cmd_worktree, RUN_SETUP },
766766
{ "write-tree", cmd_write_tree, RUN_SETUP },
767767
};
768768

gvfs.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,17 @@ struct repository;
2727
#define GVFS_USE_VIRTUAL_FILESYSTEM (1 << 3)
2828

2929
#define GVFS_FETCH_SKIP_REACHABILITY_AND_UPLOADPACK (1 << 4)
30+
/* Bit 5 was GVFS_LOWER_DEFAULT_SLOP, removed in 2018 (unused). */
3031
#define GVFS_BLOCK_FILTERS_AND_EOL_CONVERSIONS (1 << 6)
3132
#define GVFS_PREFETCH_DURING_FETCH (1 << 7)
3233

34+
/*
35+
* When set, this flag indicates that the VFS layer supports
36+
* git worktrees. This allows `git worktree add/remove` to
37+
* operate on VFS-enabled repositories.
38+
*/
39+
#define GVFS_SUPPORTS_WORKTREES (1 << 8)
40+
3341
#define GVFS_ANY_MASK 0xFFFFFFFF
3442

3543
int gvfs_config_is_set(struct repository *r, int mask);

t/t0402-block-command-on-gvfs.sh

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,34 @@ not_with_gvfs update-index --index-version 2
2727
not_with_gvfs update-index --skip-worktree
2828
not_with_gvfs update-index --no-skip-worktree
2929
not_with_gvfs update-index --split-index
30-
not_with_gvfs worktree list
30+
31+
# worktree is conditionally allowed: blocked when VFS enabled without
32+
# GVFS_SUPPORTS_WORKTREES.
33+
test_expect_success 'worktree blocked with VFS but without SUPPORTS_WORKTREES' '
34+
test_config core.gvfs $((0xffff & ~(1<<8))) && # all bits except GVFS_SUPPORTS_WORKTREES
35+
test_must_fail git worktree list 2>err &&
36+
test_grep "not supported when using the virtual file system" err
37+
'
38+
39+
test_expect_success 'worktree operations work when SUPPORTS_WORKTREES is set' '
40+
test_commit initial &&
41+
42+
# Use core.gvfs=true which sets all bits including SUPPORTS_WORKTREES.
43+
test_config core.gvfs true &&
44+
45+
# add: succeeds, forces --no-checkout (no initial.t on disk)
46+
git worktree add ../vfs-wt &&
47+
test_path_exists ../vfs-wt/.git &&
48+
! test_path_exists ../vfs-wt/initial.t &&
49+
50+
# list: shows the worktree
51+
git worktree list >out &&
52+
grep "vfs-wt" out &&
53+
54+
# remove: cleans up
55+
git worktree remove --force ../vfs-wt &&
56+
! test_path_exists ../vfs-wt
57+
'
3158

3259
test_expect_success 'test gc --auto succeeds when disabled via config' '
3360
test_config core.gvfs true &&

0 commit comments

Comments
 (0)