diff --git a/Documentation/scalar.txt b/Documentation/scalar.txt index 31d7018cdd7817..14291e5effe399 100644 --- a/Documentation/scalar.txt +++ b/Documentation/scalar.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] scalar clone [--single-branch] [--branch ] [--full-clone] - [--local-cache-path ] [--cache-server-url ] + [--local-cache-path ] [--cache-server-url ] [--[no-]src] [] scalar list scalar register [] @@ -84,6 +84,11 @@ remote-tracking branch for the branch this option was used for the initial cloning. If the HEAD at the remote did not point at any branch when `--single-branch` clone was made, no remote-tracking branch is created. +--[no-]src:: + Specify if the repository should be created within a `src` directory + within ``. This is the default behavior, so use + `--no-src` to opt-out of the creation of the `src` directory. + --[no-]full-clone:: A sparse-checkout is initialized by default. This behavior can be turned off via `--full-clone`. diff --git a/abspath.c b/abspath.c index 7a5f114382f182..058e638e986e98 100644 --- a/abspath.c +++ b/abspath.c @@ -12,7 +12,7 @@ int is_directory(const char *path) } /* removes the last path component from 'path' except if 'path' is root */ -static void strip_last_component(struct strbuf *path) +void strip_last_path_component(struct strbuf *path) { size_t offset = offset_1st_component(path->buf); size_t len = path->len; @@ -117,7 +117,7 @@ static char *strbuf_realpath_1(struct strbuf *resolved, const char *path, continue; /* '.' component */ } else if (next.len == 2 && !strcmp(next.buf, "..")) { /* '..' component; strip the last path component */ - strip_last_component(resolved); + strip_last_path_component(resolved); continue; } @@ -169,7 +169,7 @@ static char *strbuf_realpath_1(struct strbuf *resolved, const char *path, * strip off the last component since it will * be replaced with the contents of the symlink */ - strip_last_component(resolved); + strip_last_path_component(resolved); } /* diff --git a/builtin/worktree.c b/builtin/worktree.c index cd62eef240ee40..aa595c78a1f913 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "gvfs.h" #include "checkout.h" #include "config.h" #include "builtin.h" @@ -1118,6 +1119,13 @@ int cmd_worktree(int ac, const char **av, const char *prefix) git_config(git_worktree_config, NULL); + /* + * git-worktree is special-cased to work in Scalar repositories + * even when they use the GVFS Protocol. + */ + if (core_gvfs & GVFS_USE_VIRTUAL_FILESYSTEM) + die("'git %s' is not supported on a GVFS repo", "worktree"); + if (ac < 2) usage_with_options(worktree_usage, options); if (!prefix) diff --git a/git.c b/git.c index 546f3a1a3d6727..7c83c5c6db47cb 100644 --- a/git.c +++ b/git.c @@ -702,7 +702,7 @@ static struct cmd_struct commands[] = { { "verify-tag", cmd_verify_tag, RUN_SETUP }, { "version", cmd_version }, { "whatchanged", cmd_whatchanged, RUN_SETUP }, - { "worktree", cmd_worktree, RUN_SETUP | NO_PARSEOPT | BLOCK_ON_GVFS_REPO }, + { "worktree", cmd_worktree, RUN_SETUP | NO_PARSEOPT }, { "write-tree", cmd_write_tree, RUN_SETUP }, }; diff --git a/gvfs.h b/gvfs.h index 7d999f3e8d234f..99c5205aa043d7 100644 --- a/gvfs.h +++ b/gvfs.h @@ -14,7 +14,18 @@ #define GVFS_SKIP_SHA_ON_INDEX (1 << 0) #define GVFS_BLOCK_COMMANDS (1 << 1) #define GVFS_MISSING_OK (1 << 2) + +/* + * This behavior of not deleting outside of the sparse-checkout + * is specific to the virtual filesystem support. It is only + * enabled by VFS for Git, and so can be used as an indicator + * that we are in a virtualized filesystem environment and not + * in a Scalar environment. This bit has two names to reflect + * that. + */ #define GVFS_NO_DELETE_OUTSIDE_SPARSECHECKOUT (1 << 3) +#define GVFS_USE_VIRTUAL_FILESYSTEM (1 << 3) + #define GVFS_FETCH_SKIP_REACHABILITY_AND_UPLOADPACK (1 << 4) #define GVFS_BLOCK_FILTERS_AND_EOL_CONVERSIONS (1 << 6) #define GVFS_PREFETCH_DURING_FETCH (1 << 7) diff --git a/path.h b/path.h index 0a59c85a62eda0..db417a43bd859d 100644 --- a/path.h +++ b/path.h @@ -179,6 +179,10 @@ const char *git_path_auto_merge(struct repository *r); const char *git_path_fetch_head(struct repository *r); const char *git_path_shallow(struct repository *r); +/** + * Remove the last path component from 'path' except if 'path' is root. + */ +void strip_last_path_component(struct strbuf *path); int ends_with_path_components(const char *path, const char *components); diff --git a/scalar.c b/scalar.c index 17b3f94576b0d9..ba83d4ac9d16d7 100644 --- a/scalar.c +++ b/scalar.c @@ -17,6 +17,7 @@ #include "fsmonitor-ipc.h" #include "json-parser.h" #include "remote.h" +#include "path.h" static int is_unattended(void) { return git_env_bool("Scalar_UNATTENDED", 0); @@ -597,8 +598,13 @@ static char *default_cache_root(const char *root) { const char *env; - if (is_unattended()) - return xstrfmt("%s/.scalarCache", root); + if (is_unattended()) { + struct strbuf path = STRBUF_INIT; + strbuf_addstr(&path, root); + strip_last_path_component(&path); + strbuf_addstr(&path, "/.scalarCache"); + return strbuf_detach(&path, NULL); + } #ifdef WIN32 (void)env; @@ -811,6 +817,8 @@ static int cmd_clone(int argc, const char **argv) int full_clone = 0, single_branch = 0, dummy = 0; const char *cache_server_url = NULL, *local_cache_root = NULL; char *default_cache_server_url = NULL, *local_cache_root_abs = NULL; + const char *enlistment_parent; + int src = 1; struct option clone_options[] = { OPT_STRING('b', "branch", &branch, N_(""), N_("branch to checkout after clone")), @@ -819,6 +827,8 @@ static int cmd_clone(int argc, const char **argv) OPT_BOOL(0, "single-branch", &single_branch, N_("only download metadata for the branch that will " "be checked out")), + OPT_BOOL(0, "src", &src, + N_("create repository within 'src' directory")), OPT_STRING(0, "cache-server-url", &cache_server_url, N_(""), N_("the url or friendly name of the cache server")), @@ -869,7 +879,13 @@ static int cmd_clone(int argc, const char **argv) ensure_absolute_path(enlistment, &enlistment); - dir = xstrfmt("%s/src", enlistment); + if (src) { + dir = xstrfmt("%s/src", enlistment); + enlistment_parent = "../.."; + } else { + dir = xstrdup(enlistment); + enlistment_parent = ".."; + } if (!local_cache_root) local_cache_root = local_cache_root_abs = @@ -910,7 +926,7 @@ static int cmd_clone(int argc, const char **argv) struct strbuf path = STRBUF_INIT; strbuf_addstr(&path, enlistment); - if (chdir("../..") < 0 || + if (chdir(enlistment_parent) < 0 || remove_dir_recursively(&path, 0) < 0) die(_("'--local-cache-path' cannot be inside the src " "folder;\nCould not remove '%s'"), enlistment); diff --git a/t/t9099-scalar.sh b/t/t9099-scalar.sh index 7d06df99e75c1f..8bc081b0ebe05f 100755 --- a/t/t9099-scalar.sh +++ b/t/t9099-scalar.sh @@ -200,7 +200,7 @@ test_expect_success '`scalar clone` with GVFS-enabled server' ' cache_key="url_$(printf "%s" http://$HOST_PORT/ | tr A-Z a-z | test-tool sha1)" && - echo "$(pwd)/using-gvfs/.scalarCache/$cache_key" >expect && + echo "$(pwd)/.scalarCache/$cache_key" >expect && git -C using-gvfs/src config gvfs.sharedCache >actual && test_cmp expect actual && @@ -269,4 +269,12 @@ test_expect_success '`scalar delete` with existing repo' ' test_path_is_missing existing ' +test_expect_success '`scalar clone --no-src`' ' + scalar clone --src "file://$(pwd)" with-src && + scalar clone --no-src "file://$(pwd)" without-src && + + test_path_is_dir with-src/src && + test_path_is_missing without-src/src +' + test_done