Skip to content

Commit a5c97d8

Browse files
dschovdye
authored andcommitted
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 df45a16 commit a5c97d8

File tree

3 files changed

+203
-6
lines changed

3 files changed

+203
-6
lines changed

Documentation/scalar.txt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ scalar - A tool for managing large Git repositories
88
SYNOPSIS
99
--------
1010
[verse]
11-
scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<enlistment>]
11+
scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]
12+
[--local-cache-path <path>] [--cache-server-url <url>]
13+
<url> [<enlistment>]
1214
scalar list
1315
scalar register [<enlistment>]
1416
scalar unregister [<enlistment>]
@@ -84,6 +86,17 @@ cloning. If the HEAD at the remote did not point at any branch when
8486
A sparse-checkout is initialized by default. This behavior can be
8587
turned off via `--full-clone`.
8688

89+
--local-cache-path <path>::
90+
Override the path to the local cache root directory; Pre-fetched objects
91+
are stored into a repository-dependent subdirectory of that path.
92+
+
93+
The default is `<drive>:\.scalarCache` on Windows (on the same drive as the
94+
clone), and `~/.scalarCache` on macOS.
95+
96+
--cache-server-url <url>::
97+
Retrieve missing objects from the specified remote, which is expected to
98+
understand the GVFS protocol.
99+
87100
List
88101
~~~~
89102

diagnose.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ int create_diagnostics_archive(struct strbuf *zip_path, enum diagnose_mode mode)
220220
struct strvec archiver_args = STRVEC_INIT;
221221
char **argv_copy = NULL;
222222
int stdout_fd = -1, archiver_fd = -1;
223-
char *cache_server_url = NULL;
223+
char *cache_server_url = NULL, *shared_cache = NULL;
224224
struct strbuf buf = STRBUF_INIT;
225225
int res, i;
226226
struct archive_dir archive_dirs[] = {
@@ -258,8 +258,10 @@ int create_diagnostics_archive(struct strbuf *zip_path, enum diagnose_mode mode)
258258
strbuf_addf(&buf, "Repository root: %s\n", the_repository->worktree);
259259

260260
git_config_get_string("gvfs.cache-server", &cache_server_url);
261-
strbuf_addf(&buf, "Cache Server: %s\n\n",
262-
cache_server_url ? cache_server_url : "None");
261+
git_config_get_string("gvfs.sharedCache", &shared_cache);
262+
strbuf_addf(&buf, "Cache Server: %s\nLocal Cache: %s\n\n",
263+
cache_server_url ? cache_server_url : "None",
264+
shared_cache ? shared_cache : "None");
263265

264266
get_disk_info(&buf);
265267
write_or_die(stdout_fd, buf.buf, buf.len);
@@ -319,6 +321,7 @@ int create_diagnostics_archive(struct strbuf *zip_path, enum diagnose_mode mode)
319321
strvec_clear(&archiver_args);
320322
strbuf_release(&buf);
321323
free(cache_server_url);
324+
free(shared_cache);
322325

323326
return res;
324327
}

scalar.c

Lines changed: 183 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "git-compat-util.h"
66
#include "abspath.h"
77
#include "gettext.h"
8+
#include "hex.h"
89
#include "parse-options.h"
910
#include "config.h"
1011
#include "run-command.h"
@@ -13,12 +14,18 @@
1314
#include "fsmonitor-settings.h"
1415
#include "refs.h"
1516
#include "dir.h"
17+
#include "object-file.h"
1618
#include "packfile.h"
1719
#include "help.h"
1820
#include "setup.h"
21+
#include "wrapper.h"
1922
#include "trace2.h"
2023
#include "json-parser.h"
2124

25+
static int is_unattended(void) {
26+
return git_env_bool("Scalar_UNATTENDED", 0);
27+
}
28+
2229
static void setup_enlistment_directory(int argc, const char **argv,
2330
const char * const *usagestr,
2431
const struct option *options,
@@ -103,6 +110,19 @@ static int run_git(const char *arg, ...)
103110
return res;
104111
}
105112

113+
static const char *ensure_absolute_path(const char *path, char **absolute)
114+
{
115+
struct strbuf buf = STRBUF_INIT;
116+
117+
if (is_absolute_path(path))
118+
return path;
119+
120+
strbuf_realpath_forgiving(&buf, path, 1);
121+
free(*absolute);
122+
*absolute = strbuf_detach(&buf, NULL);
123+
return *absolute;
124+
}
125+
106126
struct scalar_config {
107127
const char *key;
108128
const char *value;
@@ -409,6 +429,87 @@ static int supports_gvfs_protocol(const char *url, char **cache_server_url)
409429
return 0; /* error out quietly */
410430
}
411431

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

610+
static int init_shared_object_cache(const char *url,
611+
const char *local_cache_root)
612+
{
613+
struct strbuf buf = STRBUF_INIT;
614+
int res = 0;
615+
char *cache_key = NULL, *shared_cache_path = NULL;
616+
617+
if (!(cache_key = get_cache_key(url))) {
618+
res = error(_("could not determine cache key for '%s'"), url);
619+
goto cleanup;
620+
}
621+
622+
shared_cache_path = xstrfmt("%s/%s", local_cache_root, cache_key);
623+
if (set_config("gvfs.sharedCache=%s", shared_cache_path)) {
624+
res = error(_("could not configure shared cache"));
625+
goto cleanup;
626+
}
627+
628+
strbuf_addf(&buf, "%s/pack", shared_cache_path);
629+
switch (safe_create_leading_directories(buf.buf)) {
630+
case SCLD_OK: case SCLD_EXISTS:
631+
break; /* okay */
632+
default:
633+
res = error_errno(_("could not initialize '%s'"), buf.buf);
634+
goto cleanup;
635+
}
636+
637+
write_file(git_path("objects/info/alternates"),"%s\n", shared_cache_path);
638+
639+
cleanup:
640+
strbuf_release(&buf);
641+
free(shared_cache_path);
642+
free(cache_key);
643+
return res;
644+
}
645+
509646
static int cmd_clone(int argc, const char **argv)
510647
{
511648
const char *branch = NULL;
512649
int full_clone = 0, single_branch = 0, show_progress = isatty(2);
513-
const char *cache_server_url = NULL;
514-
char *default_cache_server_url = NULL;
650+
const char *cache_server_url = NULL, *local_cache_root = NULL;
651+
char *default_cache_server_url = NULL, *local_cache_root_abs = NULL;
515652
struct option clone_options[] = {
516653
OPT_STRING('b', "branch", &branch, N_("<branch>"),
517654
N_("branch to checkout after clone")),
@@ -523,6 +660,9 @@ static int cmd_clone(int argc, const char **argv)
523660
OPT_STRING(0, "cache-server-url", &cache_server_url,
524661
N_("<url>"),
525662
N_("the url or friendly name of the cache server")),
663+
OPT_STRING(0, "local-cache-path", &local_cache_root,
664+
N_("<path>"),
665+
N_("override the path for the local Scalar cache")),
526666
OPT_END(),
527667
};
528668
const char * const clone_usage[] = {
@@ -563,8 +703,20 @@ static int cmd_clone(int argc, const char **argv)
563703
if (is_directory(enlistment))
564704
die(_("directory '%s' exists already"), enlistment);
565705

706+
ensure_absolute_path(enlistment, &enlistment);
707+
566708
dir = xstrfmt("%s/src", enlistment);
567709

710+
if (!local_cache_root)
711+
local_cache_root = local_cache_root_abs =
712+
default_cache_root(enlistment);
713+
else
714+
local_cache_root = ensure_absolute_path(local_cache_root,
715+
&local_cache_root_abs);
716+
717+
if (!local_cache_root)
718+
die(_("could not determine local cache root"));
719+
568720
strbuf_reset(&buf);
569721
if (branch)
570722
strbuf_addf(&buf, "init.defaultBranch=%s", branch);
@@ -584,8 +736,28 @@ static int cmd_clone(int argc, const char **argv)
584736

585737
setup_git_directory();
586738

739+
git_config(git_default_config, NULL);
740+
741+
/*
742+
* This `dir_inside_of()` call relies on git_config() having parsed the
743+
* newly-initialized repository config's `core.ignoreCase` value.
744+
*/
745+
if (dir_inside_of(local_cache_root, dir) >= 0) {
746+
struct strbuf path = STRBUF_INIT;
747+
748+
strbuf_addstr(&path, enlistment);
749+
if (chdir("../..") < 0 ||
750+
remove_dir_recursively(&path, 0) < 0)
751+
die(_("'--local-cache-path' cannot be inside the src "
752+
"folder;\nCould not remove '%s'"), enlistment);
753+
754+
die(_("'--local-cache-path' cannot be inside the src folder"));
755+
}
756+
587757
/* common-main already logs `argv` */
588758
trace2_def_repo(the_repository);
759+
trace2_data_intmax("scalar", the_repository, "unattended",
760+
is_unattended());
589761

590762
if (!branch && !(branch = remote_default_branch(url))) {
591763
res = error(_("failed to get default branch for '%s'"), url);
@@ -610,6 +782,8 @@ static int cmd_clone(int argc, const char **argv)
610782
supports_gvfs_protocol(url, &default_cache_server_url);
611783

612784
if (gvfs_protocol) {
785+
if ((res = init_shared_object_cache(url, local_cache_root)))
786+
goto cleanup;
613787
if (!cache_server_url)
614788
cache_server_url = default_cache_server_url;
615789
if (set_config("core.useGVFSHelper=true") ||
@@ -684,6 +858,7 @@ static int cmd_clone(int argc, const char **argv)
684858
free(dir);
685859
strbuf_release(&buf);
686860
free(default_cache_server_url);
861+
free(local_cache_root_abs);
687862
return res;
688863
}
689864

@@ -1058,6 +1233,12 @@ int cmd_main(int argc, const char **argv)
10581233
struct strbuf scalar_usage = STRBUF_INIT;
10591234
int i;
10601235

1236+
if (is_unattended()) {
1237+
setenv("GIT_ASKPASS", "", 0);
1238+
setenv("GIT_TERMINAL_PROMPT", "false", 0);
1239+
git_config_push_parameter("credential.interactive=never");
1240+
}
1241+
10611242
while (argc > 1 && *argv[1] == '-') {
10621243
if (!strcmp(argv[1], "-C")) {
10631244
if (argc < 3)

0 commit comments

Comments
 (0)