Merge pull request #536: Allow --no-src during clones and git worktree after clones

These are two highly-requested items from an internal team considering a
move to Scalar using Azure Repos.

1. Remove the requirement that we create a `src` directory at clone time.

2. Allow `git worktree` even when using the GVFS protocol.

These are not difficult to implement. The `--no-src` option could even
be submitted upstream (though the commit will need to drop one bit about
an interaction with the local cache path).
This commit is contained in:
Johannes Schindelin 2022-10-04 23:15:24 +02:00
Родитель 96d6fe392d 6508076132
Коммит ffd33cfd18
8 изменённых файлов: 60 добавлений и 10 удалений

Просмотреть файл

@ -9,7 +9,7 @@ SYNOPSIS
-------- --------
[verse] [verse]
scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]
[--local-cache-path <path>] [--cache-server-url <url>] [--local-cache-path <path>] [--cache-server-url <url>] [--[no-]src]
<url> [<enlistment>] <url> [<enlistment>]
scalar list scalar list
scalar register [<enlistment>] scalar register [<enlistment>]
@ -83,6 +83,9 @@ 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 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. `--single-branch` clone was made, no remote-tracking branch is created.
--no-src::
Skip adding a `src` directory within the target enlistment.
--[no-]full-clone:: --[no-]full-clone::
A sparse-checkout is initialized by default. This behavior can be A sparse-checkout is initialized by default. This behavior can be
turned off via `--full-clone`. turned off via `--full-clone`.

Просмотреть файл

@ -12,7 +12,7 @@ int is_directory(const char *path)
} }
/* removes the last path component from 'path' except if 'path' is root */ /* 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 offset = offset_1st_component(path->buf);
size_t len = path->len; size_t len = path->len;
@ -117,7 +117,7 @@ static char *strbuf_realpath_1(struct strbuf *resolved, const char *path,
continue; /* '.' component */ continue; /* '.' component */
} else if (next.len == 2 && !strcmp(next.buf, "..")) { } else if (next.len == 2 && !strcmp(next.buf, "..")) {
/* '..' component; strip the last path component */ /* '..' component; strip the last path component */
strip_last_component(resolved); strip_last_path_component(resolved);
continue; continue;
} }
@ -169,7 +169,7 @@ static char *strbuf_realpath_1(struct strbuf *resolved, const char *path,
* strip off the last component since it will * strip off the last component since it will
* be replaced with the contents of the symlink * be replaced with the contents of the symlink
*/ */
strip_last_component(resolved); strip_last_path_component(resolved);
} }
/* /*

Просмотреть файл

@ -1,4 +1,5 @@
#include "cache.h" #include "cache.h"
#include "gvfs.h"
#include "checkout.h" #include "checkout.h"
#include "config.h" #include "config.h"
#include "builtin.h" #include "builtin.h"
@ -1127,6 +1128,13 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
git_config(git_worktree_config, NULL); 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 (!prefix) if (!prefix)
prefix = ""; prefix = "";

2
git.c
Просмотреть файл

@ -703,7 +703,7 @@ static struct cmd_struct commands[] = {
{ "verify-tag", cmd_verify_tag, RUN_SETUP }, { "verify-tag", cmd_verify_tag, RUN_SETUP },
{ "version", cmd_version }, { "version", cmd_version },
{ "whatchanged", cmd_whatchanged, RUN_SETUP }, { "whatchanged", cmd_whatchanged, RUN_SETUP },
{ "worktree", cmd_worktree, RUN_SETUP | BLOCK_ON_GVFS_REPO }, { "worktree", cmd_worktree, RUN_SETUP },
{ "write-tree", cmd_write_tree, RUN_SETUP }, { "write-tree", cmd_write_tree, RUN_SETUP },
}; };

11
gvfs.h
Просмотреть файл

@ -14,7 +14,18 @@
#define GVFS_SKIP_SHA_ON_INDEX (1 << 0) #define GVFS_SKIP_SHA_ON_INDEX (1 << 0)
#define GVFS_BLOCK_COMMANDS (1 << 1) #define GVFS_BLOCK_COMMANDS (1 << 1)
#define GVFS_MISSING_OK (1 << 2) #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_NO_DELETE_OUTSIDE_SPARSECHECKOUT (1 << 3)
#define GVFS_USE_VIRTUAL_FILESYSTEM (1 << 3)
#define GVFS_FETCH_SKIP_REACHABILITY_AND_UPLOADPACK (1 << 4) #define GVFS_FETCH_SKIP_REACHABILITY_AND_UPLOADPACK (1 << 4)
#define GVFS_BLOCK_FILTERS_AND_EOL_CONVERSIONS (1 << 6) #define GVFS_BLOCK_FILTERS_AND_EOL_CONVERSIONS (1 << 6)
#define GVFS_PREFETCH_DURING_FETCH (1 << 7) #define GVFS_PREFETCH_DURING_FETCH (1 << 7)

4
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_fetch_head(struct repository *r);
const char *git_path_shallow(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); int ends_with_path_components(const char *path, const char *components);

Просмотреть файл

@ -16,6 +16,7 @@
#include "help.h" #include "help.h"
#include "json-parser.h" #include "json-parser.h"
#include "remote.h" #include "remote.h"
#include "path.h"
static int is_unattended(void) { static int is_unattended(void) {
return git_env_bool("Scalar_UNATTENDED", 0); return git_env_bool("Scalar_UNATTENDED", 0);
@ -461,8 +462,13 @@ static char *default_cache_root(const char *root)
{ {
const char *env; const char *env;
if (is_unattended()) if (is_unattended()) {
return xstrfmt("%s/.scalarCache", root); 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 #ifdef WIN32
(void)env; (void)env;
@ -685,6 +691,8 @@ static int cmd_clone(int argc, const char **argv)
int full_clone = 0, single_branch = 0, dummy = 0; int full_clone = 0, single_branch = 0, dummy = 0;
const char *cache_server_url = NULL, *local_cache_root = NULL; const char *cache_server_url = NULL, *local_cache_root = NULL;
char *default_cache_server_url = NULL, *local_cache_root_abs = NULL; char *default_cache_server_url = NULL, *local_cache_root_abs = NULL;
const char *enlistment_parent;
int no_src = 0;
struct option clone_options[] = { struct option clone_options[] = {
OPT_STRING('b', "branch", &branch, N_("<branch>"), OPT_STRING('b', "branch", &branch, N_("<branch>"),
N_("branch to checkout after clone")), N_("branch to checkout after clone")),
@ -693,6 +701,8 @@ static int cmd_clone(int argc, const char **argv)
OPT_BOOL(0, "single-branch", &single_branch, OPT_BOOL(0, "single-branch", &single_branch,
N_("only download metadata for the branch that will " N_("only download metadata for the branch that will "
"be checked out")), "be checked out")),
OPT_BOOL(0, "no-src", &no_src,
N_("skip creating a 'src' directory")),
OPT_STRING(0, "cache-server-url", &cache_server_url, OPT_STRING(0, "cache-server-url", &cache_server_url,
N_("<url>"), N_("<url>"),
N_("the url or friendly name of the cache server")), N_("the url or friendly name of the cache server")),
@ -743,7 +753,13 @@ static int cmd_clone(int argc, const char **argv)
ensure_absolute_path(enlistment, &enlistment); ensure_absolute_path(enlistment, &enlistment);
dir = xstrfmt("%s/src", enlistment); if (!no_src) {
dir = xstrfmt("%s/src", enlistment);
enlistment_parent = "../..";
} else {
dir = xstrdup(enlistment);
enlistment_parent = "..";
}
if (!local_cache_root) if (!local_cache_root)
local_cache_root = local_cache_root_abs = local_cache_root = local_cache_root_abs =
@ -784,7 +800,7 @@ static int cmd_clone(int argc, const char **argv)
struct strbuf path = STRBUF_INIT; struct strbuf path = STRBUF_INIT;
strbuf_addstr(&path, enlistment); strbuf_addstr(&path, enlistment);
if (chdir("../..") < 0 || if (chdir(enlistment_parent) < 0 ||
remove_dir_recursively(&path, 0) < 0) remove_dir_recursively(&path, 0) < 0)
die(_("'--local-cache-path' cannot be inside the src " die(_("'--local-cache-path' cannot be inside the src "
"folder;\nCould not remove '%s'"), enlistment); "folder;\nCould not remove '%s'"), enlistment);

Просмотреть файл

@ -282,7 +282,7 @@ test_expect_success '`scalar clone` with GVFS-enabled server' '
cache_key="url_$(printf "%s" http://$HOST_PORT/ | cache_key="url_$(printf "%s" http://$HOST_PORT/ |
tr A-Z a-z | tr A-Z a-z |
test-tool sha1)" && 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 && git -C using-gvfs/src config gvfs.sharedCache >actual &&
test_cmp expect actual && test_cmp expect actual &&
@ -367,4 +367,12 @@ test_expect_success '`scalar delete` with existing repo' '
test_path_is_missing existing 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 test_done