diff --git a/builtin/worktree.c b/builtin/worktree.c index 0ec68b6e090db8..5d2c458b6b8cf1 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -838,6 +838,14 @@ static int add(int ac, const char **av, const char *prefix, if (ac < 1 || ac > 2) usage_with_options(git_worktree_add_usage, options); + /* + * When the virtual file system is active, skip checkout during + * worktree creation. The VFS layer will handle the checkout + * after the worktree structure is set up. + */ + if (gvfs_config_is_set(the_repository, GVFS_USE_VIRTUAL_FILESYSTEM)) + opts.checkout = 0; + path = prefix_filename(prefix, av[0]); branch = ac < 2 ? "HEAD" : av[1]; used_new_branch_options = new_branch || new_branch_force; @@ -1358,6 +1366,21 @@ static int delete_git_work_tree(struct worktree *wt) return ret; } +/* + * Check if a pre-command hook has already verified worktree cleanliness + * and written a marker file to skip git's own check. VFSForGit uses this + * to unmount ProjFS after its own status check; without it, git's status + * call would fail because the virtual filesystem is no longer available. + */ +static int should_skip_clean_check(struct worktree *wt) +{ + char *path = repo_common_path(the_repository, + "worktrees/%s/skip-clean-check", wt->id); + int skip = file_exists(path); + free(path); + return skip; +} + static int remove_worktree(int ac, const char **av, const char *prefix, struct repository *repo UNUSED) { @@ -1397,7 +1420,7 @@ static int remove_worktree(int ac, const char **av, const char *prefix, strbuf_release(&errmsg); if (file_exists(wt->path)) { - if (!force) + if (!force && !(core_virtualfilesystem && should_skip_clean_check(wt))) check_clean_worktree(wt, av[0]); ret |= delete_git_work_tree(wt); @@ -1469,6 +1492,14 @@ int cmd_worktree(int ac, ac = parse_options(ac, av, prefix, options, git_worktree_usage, 0); + /* + * Block worktree commands when VFS is active unless the VFS layer + * has signaled worktree support via GVFS_SUPPORTS_WORKTREES. + */ + if (gvfs_config_is_set(the_repository, GVFS_USE_VIRTUAL_FILESYSTEM) && + !gvfs_config_is_set(the_repository, GVFS_SUPPORTS_WORKTREES)) + die("'git worktree' is not supported when using the virtual file system"); + prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; diff --git a/git.c b/git.c index e36d3d6d7f1b10..08661187ecb0e5 100644 --- a/git.c +++ b/git.c @@ -761,7 +761,7 @@ static struct cmd_struct commands[] = { #ifndef WITH_BREAKING_CHANGES { "whatchanged", cmd_whatchanged, RUN_SETUP | DEPRECATED }, #endif - { "worktree", cmd_worktree, RUN_SETUP | BLOCK_ON_VFS_ENABLED }, + { "worktree", cmd_worktree, RUN_SETUP }, { "write-tree", cmd_write_tree, RUN_SETUP }, }; diff --git a/gvfs.h b/gvfs.h index 26a0617fce661a..f905ec93bf8013 100644 --- a/gvfs.h +++ b/gvfs.h @@ -30,6 +30,13 @@ struct repository; #define GVFS_BLOCK_FILTERS_AND_EOL_CONVERSIONS (1 << 6) #define GVFS_PREFETCH_DURING_FETCH (1 << 7) +/* + * When set, this flag indicates that the VFS layer supports + * git worktrees. This allows `git worktree add/remove` to + * operate on VFS-enabled repositories. + */ +#define GVFS_SUPPORTS_WORKTREES (1 << 8) + #define GVFS_ANY_MASK 0xFFFFFFFF int gvfs_config_is_set(struct repository *r, int mask); diff --git a/t/t0402-block-command-on-gvfs.sh b/t/t0402-block-command-on-gvfs.sh index a8f7fb07a603e2..d2ef75b9f62c71 100755 --- a/t/t0402-block-command-on-gvfs.sh +++ b/t/t0402-block-command-on-gvfs.sh @@ -27,7 +27,34 @@ not_with_gvfs update-index --index-version 2 not_with_gvfs update-index --skip-worktree not_with_gvfs update-index --no-skip-worktree not_with_gvfs update-index --split-index -not_with_gvfs worktree list + +# worktree is conditionally allowed: blocked when VFS enabled without +# GVFS_SUPPORTS_WORKTREES. +test_expect_success 'worktree blocked with VFS but without SUPPORTS_WORKTREES' ' + test_config core.gvfs 95 && + test_must_fail git worktree list 2>err && + test_grep "not supported when using the virtual file system" err +' + +test_expect_success 'worktree operations work when SUPPORTS_WORKTREES is set' ' + test_commit initial && + + # Use core.gvfs=true which sets all bits including SUPPORTS_WORKTREES. + test_config core.gvfs true && + + # add: succeeds, forces --no-checkout (no initial.t on disk) + git worktree add ../vfs-wt && + test_path_exists ../vfs-wt/.git && + ! test_path_exists ../vfs-wt/initial.t && + + # list: shows the worktree + git worktree list >out && + grep "vfs-wt" out && + + # remove: cleans up + git worktree remove --force ../vfs-wt && + ! test_path_exists ../vfs-wt +' test_expect_success 'test gc --auto succeeds when disabled via config' ' test_config core.gvfs true &&