Skip to content

Commit c270115

Browse files
committed
scalar: do initialize gvfs.sharedCache
This finalizes the port of the `QueryVstsInfo()` function: we already taught `gvfs-helper` to access the `vsts/info` endpoint on demand, we implemented proper JSON parsing, and now it is time to hook it all up. To that end, we also provide a default local cache root directory. It works the same way as the .NET version of Scalar: it uses C:\scalarCache on Windows, ~/.scalarCache/ on macOS and ~/.cache/scalar on Linux Modified to include call to is_unattended() that was removed from a previous commit. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
1 parent 0f4bc44 commit c270115

File tree

3 files changed

+203
-6
lines changed

3 files changed

+203
-6
lines changed

Diff for: Documentation/scalar.txt

+13-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ SYNOPSIS
99
--------
1010
[verse]
1111
scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]
12-
[--[no-]src] <url> [<enlistment>]
12+
[--[no-]src] [--local-cache-path <path>] [--cache-server-url <url>]
13+
<url> [<enlistment>]
1314
scalar list
1415
scalar register [<enlistment>]
1516
scalar unregister [<enlistment>]
@@ -97,6 +98,17 @@ cloning. If the HEAD at the remote did not point at any branch when
9798
A sparse-checkout is initialized by default. This behavior can be
9899
turned off via `--full-clone`.
99100

101+
--local-cache-path <path>::
102+
Override the path to the local cache root directory; Pre-fetched objects
103+
are stored into a repository-dependent subdirectory of that path.
104+
+
105+
The default is `<drive>:\.scalarCache` on Windows (on the same drive as the
106+
clone), and `~/.scalarCache` on macOS.
107+
108+
--cache-server-url <url>::
109+
Retrieve missing objects from the specified remote, which is expected to
110+
understand the GVFS protocol.
111+
100112
List
101113
~~~~
102114

Diff for: diagnose.c

+6-3
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ int create_diagnostics_archive(struct strbuf *zip_path, enum diagnose_mode mode)
185185
struct strvec archiver_args = STRVEC_INIT;
186186
char **argv_copy = NULL;
187187
int stdout_fd = -1, archiver_fd = -1;
188-
char *cache_server_url = NULL;
188+
char *cache_server_url = NULL, *shared_cache = NULL;
189189
struct strbuf buf = STRBUF_INIT;
190190
int res;
191191
struct archive_dir archive_dirs[] = {
@@ -223,8 +223,10 @@ int create_diagnostics_archive(struct strbuf *zip_path, enum diagnose_mode mode)
223223
strbuf_addf(&buf, "Repository root: %s\n", the_repository->worktree);
224224

225225
git_config_get_string("gvfs.cache-server", &cache_server_url);
226-
strbuf_addf(&buf, "Cache Server: %s\n\n",
227-
cache_server_url ? cache_server_url : "None");
226+
git_config_get_string("gvfs.sharedCache", &shared_cache);
227+
strbuf_addf(&buf, "Cache Server: %s\nLocal Cache: %s\n\n",
228+
cache_server_url ? cache_server_url : "None",
229+
shared_cache ? shared_cache : "None");
228230

229231
get_disk_info(&buf);
230232
write_or_die(stdout_fd, buf.buf, buf.len);
@@ -284,6 +286,7 @@ int create_diagnostics_archive(struct strbuf *zip_path, enum diagnose_mode mode)
284286
strvec_clear(&archiver_args);
285287
strbuf_release(&buf);
286288
free(cache_server_url);
289+
free(shared_cache);
287290

288291
return res;
289292
}

Diff for: scalar.c

+184-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "git-compat-util.h"
88
#include "abspath.h"
99
#include "gettext.h"
10+
#include "hex.h"
1011
#include "parse-options.h"
1112
#include "config.h"
1213
#include "run-command.h"
@@ -15,11 +16,18 @@
1516
#include "fsmonitor-settings.h"
1617
#include "refs.h"
1718
#include "dir.h"
19+
#include "object-file.h"
1820
#include "packfile.h"
1921
#include "help.h"
2022
#include "setup.h"
23+
#include "wrapper.h"
2124
#include "trace2.h"
2225
#include "json-parser.h"
26+
#include "path.h"
27+
28+
static int is_unattended(void) {
29+
return git_env_bool("Scalar_UNATTENDED", 0);
30+
}
2331

2432
static void setup_enlistment_directory(int argc, const char **argv,
2533
const char * const *usagestr,
@@ -106,6 +114,19 @@ static int run_git(const char *arg, ...)
106114
return res;
107115
}
108116

117+
static const char *ensure_absolute_path(const char *path, char **absolute)
118+
{
119+
struct strbuf buf = STRBUF_INIT;
120+
121+
if (is_absolute_path(path))
122+
return path;
123+
124+
strbuf_realpath_forgiving(&buf, path, 1);
125+
free(*absolute);
126+
*absolute = strbuf_detach(&buf, NULL);
127+
return *absolute;
128+
}
129+
109130
struct scalar_config {
110131
const char *key;
111132
const char *value;
@@ -418,6 +439,87 @@ static int supports_gvfs_protocol(const char *url, char **cache_server_url)
418439
return 0; /* error out quietly */
419440
}
420441

442+
static char *default_cache_root(const char *root)
443+
{
444+
const char *env;
445+
446+
if (is_unattended())
447+
return xstrfmt("%s/.scalarCache", root);
448+
449+
#ifdef WIN32
450+
(void)env;
451+
return xstrfmt("%.*s.scalarCache", offset_1st_component(root), root);
452+
#elif defined(__APPLE__)
453+
if ((env = getenv("HOME")) && *env)
454+
return xstrfmt("%s/.scalarCache", env);
455+
return NULL;
456+
#else
457+
if ((env = getenv("XDG_CACHE_HOME")) && *env)
458+
return xstrfmt("%s/scalar", env);
459+
if ((env = getenv("HOME")) && *env)
460+
return xstrfmt("%s/.cache/scalar", env);
461+
return NULL;
462+
#endif
463+
}
464+
465+
static int get_repository_id(struct json_iterator *it)
466+
{
467+
if (it->type == JSON_STRING &&
468+
!strcasecmp(".repository.id", it->key.buf)) {
469+
*(char **)it->fn_data = strbuf_detach(&it->string_value, NULL);
470+
return 1;
471+
}
472+
473+
return 0;
474+
}
475+
476+
/* Needs to run this in a worktree; gvfs-helper requires a Git repository */
477+
static char *get_cache_key(const char *url)
478+
{
479+
struct child_process cp = CHILD_PROCESS_INIT;
480+
struct strbuf out = STRBUF_INIT;
481+
char *cache_key = NULL;
482+
483+
cp.git_cmd = 1;
484+
strvec_pushl(&cp.args, "gvfs-helper", "--remote", url,
485+
"endpoint", "vsts/info", NULL);
486+
if (!pipe_command(&cp, NULL, 0, &out, 512, NULL, 0)) {
487+
char *id = NULL;
488+
struct json_iterator it =
489+
JSON_ITERATOR_INIT(out.buf, get_repository_id, &id);
490+
491+
if (iterate_json(&it) < 0)
492+
warning("JSON parse error (%s)", out.buf);
493+
else if (id)
494+
cache_key = xstrfmt("id_%s", id);
495+
free(id);
496+
}
497+
498+
if (!cache_key) {
499+
struct strbuf downcased = STRBUF_INIT;
500+
int hash_algo_index = hash_algo_by_name("sha1");
501+
const struct git_hash_algo *hash_algo = hash_algo_index < 0 ?
502+
the_hash_algo : &hash_algos[hash_algo_index];
503+
git_hash_ctx ctx;
504+
unsigned char hash[GIT_MAX_RAWSZ];
505+
506+
strbuf_addstr(&downcased, url);
507+
strbuf_tolower(&downcased);
508+
509+
hash_algo->init_fn(&ctx);
510+
hash_algo->update_fn(&ctx, downcased.buf, downcased.len);
511+
hash_algo->final_fn(hash, &ctx);
512+
513+
strbuf_release(&downcased);
514+
515+
cache_key = xstrfmt("url_%s",
516+
hash_to_hex_algop(hash, hash_algo));
517+
}
518+
519+
strbuf_release(&out);
520+
return cache_key;
521+
}
522+
421523
static char *remote_default_branch(const char *url)
422524
{
423525
struct child_process cp = CHILD_PROCESS_INIT;
@@ -512,13 +614,49 @@ void load_builtin_commands(const char *prefix UNUSED,
512614
die("not implemented");
513615
}
514616

617+
static int init_shared_object_cache(const char *url,
618+
const char *local_cache_root)
619+
{
620+
struct strbuf buf = STRBUF_INIT;
621+
int res = 0;
622+
char *cache_key = NULL, *shared_cache_path = NULL;
623+
624+
if (!(cache_key = get_cache_key(url))) {
625+
res = error(_("could not determine cache key for '%s'"), url);
626+
goto cleanup;
627+
}
628+
629+
shared_cache_path = xstrfmt("%s/%s", local_cache_root, cache_key);
630+
if (set_config("gvfs.sharedCache=%s", shared_cache_path)) {
631+
res = error(_("could not configure shared cache"));
632+
goto cleanup;
633+
}
634+
635+
strbuf_addf(&buf, "%s/pack", shared_cache_path);
636+
switch (safe_create_leading_directories(buf.buf)) {
637+
case SCLD_OK: case SCLD_EXISTS:
638+
break; /* okay */
639+
default:
640+
res = error_errno(_("could not initialize '%s'"), buf.buf);
641+
goto cleanup;
642+
}
643+
644+
write_file(git_path("objects/info/alternates"),"%s\n", shared_cache_path);
645+
646+
cleanup:
647+
strbuf_release(&buf);
648+
free(shared_cache_path);
649+
free(cache_key);
650+
return res;
651+
}
652+
515653
static int cmd_clone(int argc, const char **argv)
516654
{
517655
const char *branch = NULL;
518656
int full_clone = 0, single_branch = 0, show_progress = isatty(2);
519657
int src = 1, tags = 1;
520-
const char *cache_server_url = NULL;
521-
char *default_cache_server_url = NULL;
658+
const char *cache_server_url = NULL, *local_cache_root = NULL;
659+
char *default_cache_server_url = NULL, *local_cache_root_abs = NULL;
522660
struct option clone_options[] = {
523661
OPT_STRING('b', "branch", &branch, N_("<branch>"),
524662
N_("branch to checkout after clone")),
@@ -534,6 +672,9 @@ static int cmd_clone(int argc, const char **argv)
534672
OPT_STRING(0, "cache-server-url", &cache_server_url,
535673
N_("<url>"),
536674
N_("the url or friendly name of the cache server")),
675+
OPT_STRING(0, "local-cache-path", &local_cache_root,
676+
N_("<path>"),
677+
N_("override the path for the local Scalar cache")),
537678
OPT_END(),
538679
};
539680
const char * const clone_usage[] = {
@@ -575,11 +716,23 @@ static int cmd_clone(int argc, const char **argv)
575716
if (is_directory(enlistment))
576717
die(_("directory '%s' exists already"), enlistment);
577718

719+
ensure_absolute_path(enlistment, &enlistment);
720+
578721
if (src)
579722
dir = xstrfmt("%s/src", enlistment);
580723
else
581724
dir = xstrdup(enlistment);
582725

726+
if (!local_cache_root)
727+
local_cache_root = local_cache_root_abs =
728+
default_cache_root(enlistment);
729+
else
730+
local_cache_root = ensure_absolute_path(local_cache_root,
731+
&local_cache_root_abs);
732+
733+
if (!local_cache_root)
734+
die(_("could not determine local cache root"));
735+
583736
strbuf_reset(&buf);
584737
if (branch)
585738
strbuf_addf(&buf, "init.defaultBranch=%s", branch);
@@ -599,8 +752,28 @@ static int cmd_clone(int argc, const char **argv)
599752

600753
setup_git_directory();
601754

755+
git_config(git_default_config, NULL);
756+
757+
/*
758+
* This `dir_inside_of()` call relies on git_config() having parsed the
759+
* newly-initialized repository config's `core.ignoreCase` value.
760+
*/
761+
if (dir_inside_of(local_cache_root, dir) >= 0) {
762+
struct strbuf path = STRBUF_INIT;
763+
764+
strbuf_addstr(&path, enlistment);
765+
if (chdir("../..") < 0 ||
766+
remove_dir_recursively(&path, 0) < 0)
767+
die(_("'--local-cache-path' cannot be inside the src "
768+
"folder;\nCould not remove '%s'"), enlistment);
769+
770+
die(_("'--local-cache-path' cannot be inside the src folder"));
771+
}
772+
602773
/* common-main already logs `argv` */
603774
trace2_def_repo(the_repository);
775+
trace2_data_intmax("scalar", the_repository, "unattended",
776+
is_unattended());
604777

605778
if (!branch && !(branch = remote_default_branch(url))) {
606779
res = error(_("failed to get default branch for '%s'"), url);
@@ -630,6 +803,8 @@ static int cmd_clone(int argc, const char **argv)
630803
supports_gvfs_protocol(url, &default_cache_server_url);
631804

632805
if (gvfs_protocol) {
806+
if ((res = init_shared_object_cache(url, local_cache_root)))
807+
goto cleanup;
633808
if (!cache_server_url)
634809
cache_server_url = default_cache_server_url;
635810
if (set_config("core.useGVFSHelper=true") ||
@@ -706,6 +881,7 @@ static int cmd_clone(int argc, const char **argv)
706881
free(dir);
707882
strbuf_release(&buf);
708883
free(default_cache_server_url);
884+
free(local_cache_root_abs);
709885
return res;
710886
}
711887

@@ -1117,6 +1293,12 @@ int cmd_main(int argc, const char **argv)
11171293
struct strbuf scalar_usage = STRBUF_INIT;
11181294
int i;
11191295

1296+
if (is_unattended()) {
1297+
setenv("GIT_ASKPASS", "", 0);
1298+
setenv("GIT_TERMINAL_PROMPT", "false", 0);
1299+
git_config_push_parameter("credential.interactive=false");
1300+
}
1301+
11201302
while (argc > 1 && *argv[1] == '-') {
11211303
if (!strcmp(argv[1], "-C")) {
11221304
if (argc < 3)

0 commit comments

Comments
 (0)