1616#include "help.h"
1717#include "json-parser.h"
1818
19+ static int is_unattended (void ) {
20+ return git_env_bool ("Scalar_UNATTENDED" , 0 );
21+ }
22+
1923static void setup_enlistment_directory (int argc , const char * * argv ,
2024 const char * const * usagestr ,
2125 const struct option * options ,
@@ -95,6 +99,19 @@ static int run_git(const char *arg, ...)
9599 return res ;
96100}
97101
102+ static const char * ensure_absolute_path (const char * path , char * * absolute )
103+ {
104+ struct strbuf buf = STRBUF_INIT ;
105+
106+ if (is_absolute_path (path ))
107+ return path ;
108+
109+ strbuf_realpath_forgiving (& buf , path , 1 );
110+ free (* absolute );
111+ * absolute = strbuf_detach (& buf , NULL );
112+ return * absolute ;
113+ }
114+
98115struct scalar_config {
99116 const char * key ;
100117 const char * value ;
@@ -397,6 +414,87 @@ static int supports_gvfs_protocol(const char *url, char **cache_server_url)
397414 return 0 ; /* error out quietly */
398415}
399416
417+ static char * default_cache_root (const char * root )
418+ {
419+ const char * env ;
420+
421+ if (is_unattended ())
422+ return xstrfmt ("%s/.scalarCache" , root );
423+
424+ #ifdef WIN32
425+ (void )env ;
426+ return xstrfmt ("%.*s.scalarCache" , offset_1st_component (root ), root );
427+ #elif defined(__APPLE__ )
428+ if ((env = getenv ("HOME" )) && * env )
429+ return xstrfmt ("%s/.scalarCache" , env );
430+ return NULL ;
431+ #else
432+ if ((env = getenv ("XDG_CACHE_HOME" )) && * env )
433+ return xstrfmt ("%s/scalar" , env );
434+ if ((env = getenv ("HOME" )) && * env )
435+ return xstrfmt ("%s/.cache/scalar" , env );
436+ return NULL ;
437+ #endif
438+ }
439+
440+ static int get_repository_id (struct json_iterator * it )
441+ {
442+ if (it -> type == JSON_STRING &&
443+ !strcasecmp (".repository.id" , it -> key .buf )) {
444+ * (char * * )it -> fn_data = strbuf_detach (& it -> string_value , NULL );
445+ return 1 ;
446+ }
447+
448+ return 0 ;
449+ }
450+
451+ /* Needs to run this in a worktree; gvfs-helper requires a Git repository */
452+ static char * get_cache_key (const char * url )
453+ {
454+ struct child_process cp = CHILD_PROCESS_INIT ;
455+ struct strbuf out = STRBUF_INIT ;
456+ char * cache_key = NULL ;
457+
458+ cp .git_cmd = 1 ;
459+ strvec_pushl (& cp .args , "gvfs-helper" , "--remote" , url ,
460+ "endpoint" , "vsts/info" , NULL );
461+ if (!pipe_command (& cp , NULL , 0 , & out , 512 , NULL , 0 )) {
462+ char * id = NULL ;
463+ struct json_iterator it =
464+ JSON_ITERATOR_INIT (out .buf , get_repository_id , & id );
465+
466+ if (iterate_json (& it ) < 0 )
467+ warning ("JSON parse error (%s)" , out .buf );
468+ else if (id )
469+ cache_key = xstrfmt ("id_%s" , id );
470+ free (id );
471+ }
472+
473+ if (!cache_key ) {
474+ struct strbuf downcased = STRBUF_INIT ;
475+ int hash_algo_index = hash_algo_by_name ("sha1" );
476+ const struct git_hash_algo * hash_algo = hash_algo_index < 0 ?
477+ the_hash_algo : & hash_algos [hash_algo_index ];
478+ git_hash_ctx ctx ;
479+ unsigned char hash [GIT_MAX_RAWSZ ];
480+
481+ strbuf_addstr (& downcased , url );
482+ strbuf_tolower (& downcased );
483+
484+ hash_algo -> init_fn (& ctx );
485+ hash_algo -> update_fn (& ctx , downcased .buf , downcased .len );
486+ hash_algo -> final_fn (hash , & ctx );
487+
488+ strbuf_release (& downcased );
489+
490+ cache_key = xstrfmt ("url_%s" ,
491+ hash_to_hex_algop (hash , hash_algo ));
492+ }
493+
494+ strbuf_release (& out );
495+ return cache_key ;
496+ }
497+
400498static char * remote_default_branch (const char * url )
401499{
402500 struct child_process cp = CHILD_PROCESS_INIT ;
@@ -494,12 +592,48 @@ void load_builtin_commands(const char *prefix, struct cmdnames *cmds)
494592 die ("not implemented" );
495593}
496594
595+ static int init_shared_object_cache (const char * url ,
596+ const char * local_cache_root )
597+ {
598+ struct strbuf buf = STRBUF_INIT ;
599+ int res = 0 ;
600+ char * cache_key = NULL , * shared_cache_path = NULL ;
601+
602+ if (!(cache_key = get_cache_key (url ))) {
603+ res = error (_ ("could not determine cache key for '%s'" ), url );
604+ goto cleanup ;
605+ }
606+
607+ shared_cache_path = xstrfmt ("%s/%s" , local_cache_root , cache_key );
608+ if (set_config ("gvfs.sharedCache=%s" , shared_cache_path )) {
609+ res = error (_ ("could not configure shared cache" ));
610+ goto cleanup ;
611+ }
612+
613+ strbuf_addf (& buf , "%s/pack" , shared_cache_path );
614+ switch (safe_create_leading_directories (buf .buf )) {
615+ case SCLD_OK : case SCLD_EXISTS :
616+ break ; /* okay */
617+ default :
618+ res = error_errno (_ ("could not initialize '%s'" ), buf .buf );
619+ goto cleanup ;
620+ }
621+
622+ write_file (git_path ("objects/info/alternates" ),"%s\n" , shared_cache_path );
623+
624+ cleanup :
625+ strbuf_release (& buf );
626+ free (shared_cache_path );
627+ free (cache_key );
628+ return res ;
629+ }
630+
497631static int cmd_clone (int argc , const char * * argv )
498632{
499633 const char * branch = NULL ;
500634 int full_clone = 0 , single_branch = 0 ;
501- const char * cache_server_url = NULL ;
502- char * default_cache_server_url = NULL ;
635+ const char * cache_server_url = NULL , * local_cache_root = NULL ;
636+ char * default_cache_server_url = NULL , * local_cache_root_abs = NULL ;
503637 struct option clone_options [] = {
504638 OPT_STRING ('b' , "branch" , & branch , N_ ("<branch>" ),
505639 N_ ("branch to checkout after clone" )),
@@ -511,6 +645,9 @@ static int cmd_clone(int argc, const char **argv)
511645 OPT_STRING (0 , "cache-server-url" , & cache_server_url ,
512646 N_ ("<url>" ),
513647 N_ ("the url or friendly name of the cache server" )),
648+ OPT_STRING (0 , "local-cache-path" , & local_cache_root ,
649+ N_ ("<path>" ),
650+ N_ ("override the path for the local Scalar cache" )),
514651 OPT_END (),
515652 };
516653 const char * const clone_usage [] = {
@@ -551,8 +688,20 @@ static int cmd_clone(int argc, const char **argv)
551688 if (is_directory (enlistment ))
552689 die (_ ("directory '%s' exists already" ), enlistment );
553690
691+ ensure_absolute_path (enlistment , & enlistment );
692+
554693 dir = xstrfmt ("%s/src" , enlistment );
555694
695+ if (!local_cache_root )
696+ local_cache_root = local_cache_root_abs =
697+ default_cache_root (enlistment );
698+ else
699+ local_cache_root = ensure_absolute_path (local_cache_root ,
700+ & local_cache_root_abs );
701+
702+ if (!local_cache_root )
703+ die (_ ("could not determine local cache root" ));
704+
556705 strbuf_reset (& buf );
557706 if (branch )
558707 strbuf_addf (& buf , "init.defaultBranch=%s" , branch );
@@ -572,8 +721,28 @@ static int cmd_clone(int argc, const char **argv)
572721
573722 setup_git_directory ();
574723
724+ git_config (git_default_config , NULL );
725+
726+ /*
727+ * This `dir_inside_of()` call relies on git_config() having parsed the
728+ * newly-initialized repository config's `core.ignoreCase` value.
729+ */
730+ if (dir_inside_of (local_cache_root , dir ) >= 0 ) {
731+ struct strbuf path = STRBUF_INIT ;
732+
733+ strbuf_addstr (& path , enlistment );
734+ if (chdir ("../.." ) < 0 ||
735+ remove_dir_recursively (& path , 0 ) < 0 )
736+ die (_ ("'--local-cache-path' cannot be inside the src "
737+ "folder;\nCould not remove '%s'" ), enlistment );
738+
739+ die (_ ("'--local-cache-path' cannot be inside the src folder" ));
740+ }
741+
575742 /* common-main already logs `argv` */
576743 trace2_def_repo (the_repository );
744+ trace2_data_intmax ("scalar" , the_repository , "unattended" ,
745+ is_unattended ());
577746
578747 if (!branch && !(branch = remote_default_branch (url ))) {
579748 res = error (_ ("failed to get default branch for '%s'" ), url );
@@ -598,6 +767,8 @@ static int cmd_clone(int argc, const char **argv)
598767 supports_gvfs_protocol (url , & default_cache_server_url );
599768
600769 if (gvfs_protocol ) {
770+ if ((res = init_shared_object_cache (url , local_cache_root )))
771+ goto cleanup ;
601772 if (!cache_server_url )
602773 cache_server_url = default_cache_server_url ;
603774 if (set_config ("core.useGVFSHelper=true" ) ||
@@ -668,6 +839,7 @@ static int cmd_clone(int argc, const char **argv)
668839 free (dir );
669840 strbuf_release (& buf );
670841 free (default_cache_server_url );
842+ free (local_cache_root_abs );
671843 return res ;
672844}
673845
@@ -1026,6 +1198,12 @@ int cmd_main(int argc, const char **argv)
10261198 struct strbuf scalar_usage = STRBUF_INIT ;
10271199 int i ;
10281200
1201+ if (is_unattended ()) {
1202+ setenv ("GIT_ASKPASS" , "" , 0 );
1203+ setenv ("GIT_TERMINAL_PROMPT" , "false" , 0 );
1204+ git_config_push_parameter ("credential.interactive=never" );
1205+ }
1206+
10291207 while (argc > 1 && * argv [1 ] == '-' ) {
10301208 if (!strcmp (argv [1 ], "-C" )) {
10311209 if (argc < 3 )
0 commit comments