7
7
#include "git-compat-util.h"
8
8
#include "abspath.h"
9
9
#include "gettext.h"
10
+ #include "hex.h"
10
11
#include "parse-options.h"
11
12
#include "config.h"
12
13
#include "run-command.h"
15
16
#include "fsmonitor-settings.h"
16
17
#include "refs.h"
17
18
#include "dir.h"
19
+ #include "object-file.h"
18
20
#include "packfile.h"
19
21
#include "help.h"
20
22
#include "setup.h"
23
+ #include "wrapper.h"
21
24
#include "trace2.h"
22
25
#include "json-parser.h"
26
+ #include "path.h"
27
+
28
+ static int is_unattended (void ) {
29
+ return git_env_bool ("Scalar_UNATTENDED" , 0 );
30
+ }
23
31
24
32
static void setup_enlistment_directory (int argc , const char * * argv ,
25
33
const char * const * usagestr ,
@@ -106,6 +114,19 @@ static int run_git(const char *arg, ...)
106
114
return res ;
107
115
}
108
116
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
+
109
130
struct scalar_config {
110
131
const char * key ;
111
132
const char * value ;
@@ -419,6 +440,87 @@ static int supports_gvfs_protocol(const char *url, char **cache_server_url)
419
440
return 0 ; /* error out quietly */
420
441
}
421
442
443
+ static char * default_cache_root (const char * root )
444
+ {
445
+ const char * env ;
446
+
447
+ if (is_unattended ())
448
+ return xstrfmt ("%s/.scalarCache" , root );
449
+
450
+ #ifdef WIN32
451
+ (void )env ;
452
+ return xstrfmt ("%.*s.scalarCache" , offset_1st_component (root ), root );
453
+ #elif defined(__APPLE__ )
454
+ if ((env = getenv ("HOME" )) && * env )
455
+ return xstrfmt ("%s/.scalarCache" , env );
456
+ return NULL ;
457
+ #else
458
+ if ((env = getenv ("XDG_CACHE_HOME" )) && * env )
459
+ return xstrfmt ("%s/scalar" , env );
460
+ if ((env = getenv ("HOME" )) && * env )
461
+ return xstrfmt ("%s/.cache/scalar" , env );
462
+ return NULL ;
463
+ #endif
464
+ }
465
+
466
+ static int get_repository_id (struct json_iterator * it )
467
+ {
468
+ if (it -> type == JSON_STRING &&
469
+ !strcasecmp (".repository.id" , it -> key .buf )) {
470
+ * (char * * )it -> fn_data = strbuf_detach (& it -> string_value , NULL );
471
+ return 1 ;
472
+ }
473
+
474
+ return 0 ;
475
+ }
476
+
477
+ /* Needs to run this in a worktree; gvfs-helper requires a Git repository */
478
+ static char * get_cache_key (const char * url )
479
+ {
480
+ struct child_process cp = CHILD_PROCESS_INIT ;
481
+ struct strbuf out = STRBUF_INIT ;
482
+ char * cache_key = NULL ;
483
+
484
+ cp .git_cmd = 1 ;
485
+ strvec_pushl (& cp .args , "gvfs-helper" , "--remote" , url ,
486
+ "endpoint" , "vsts/info" , NULL );
487
+ if (!pipe_command (& cp , NULL , 0 , & out , 512 , NULL , 0 )) {
488
+ char * id = NULL ;
489
+ struct json_iterator it =
490
+ JSON_ITERATOR_INIT (out .buf , get_repository_id , & id );
491
+
492
+ if (iterate_json (& it ) < 0 )
493
+ warning ("JSON parse error (%s)" , out .buf );
494
+ else if (id )
495
+ cache_key = xstrfmt ("id_%s" , id );
496
+ free (id );
497
+ }
498
+
499
+ if (!cache_key ) {
500
+ struct strbuf downcased = STRBUF_INIT ;
501
+ int hash_algo_index = hash_algo_by_name ("sha1" );
502
+ const struct git_hash_algo * hash_algo = hash_algo_index < 0 ?
503
+ the_hash_algo : & hash_algos [hash_algo_index ];
504
+ struct git_hash_ctx ctx ;
505
+ unsigned char hash [GIT_MAX_RAWSZ ];
506
+
507
+ strbuf_addstr (& downcased , url );
508
+ strbuf_tolower (& downcased );
509
+
510
+ hash_algo -> init_fn (& ctx );
511
+ hash_algo -> update_fn (& ctx , downcased .buf , downcased .len );
512
+ hash_algo -> final_fn (hash , & ctx );
513
+
514
+ strbuf_release (& downcased );
515
+
516
+ cache_key = xstrfmt ("url_%s" ,
517
+ hash_to_hex_algop (hash , hash_algo ));
518
+ }
519
+
520
+ strbuf_release (& out );
521
+ return cache_key ;
522
+ }
523
+
422
524
static char * remote_default_branch (const char * url )
423
525
{
424
526
struct child_process cp = CHILD_PROCESS_INIT ;
@@ -513,14 +615,52 @@ void load_builtin_commands(const char *prefix UNUSED,
513
615
die ("not implemented" );
514
616
}
515
617
618
+ static int init_shared_object_cache (const char * url ,
619
+ const char * local_cache_root )
620
+ {
621
+ struct strbuf buf = STRBUF_INIT ;
622
+ int res = 0 ;
623
+ char * cache_key = NULL , * shared_cache_path = NULL , * alternates = NULL ;
624
+
625
+ if (!(cache_key = get_cache_key (url ))) {
626
+ res = error (_ ("could not determine cache key for '%s'" ), url );
627
+ goto cleanup ;
628
+ }
629
+
630
+ shared_cache_path = xstrfmt ("%s/%s" , local_cache_root , cache_key );
631
+ if (set_config ("gvfs.sharedCache=%s" , shared_cache_path )) {
632
+ res = error (_ ("could not configure shared cache" ));
633
+ goto cleanup ;
634
+ }
635
+
636
+ strbuf_addf (& buf , "%s/pack" , shared_cache_path );
637
+ switch (safe_create_leading_directories (buf .buf )) {
638
+ case SCLD_OK : case SCLD_EXISTS :
639
+ break ; /* okay */
640
+ default :
641
+ res = error_errno (_ ("could not initialize '%s'" ), buf .buf );
642
+ goto cleanup ;
643
+ }
644
+
645
+ alternates = repo_git_path (the_repository , "objects/info/alternates" );
646
+ write_file (alternates , "%s\n" , shared_cache_path );
647
+
648
+ cleanup :
649
+ strbuf_release (& buf );
650
+ free (shared_cache_path );
651
+ free (cache_key );
652
+ free (alternates );
653
+ return res ;
654
+ }
655
+
516
656
static int cmd_clone (int argc , const char * * argv )
517
657
{
518
658
const char * branch = NULL ;
519
659
char * branch_to_free = NULL ;
520
660
int full_clone = 0 , single_branch = 0 , show_progress = isatty (2 );
521
661
int src = 1 , tags = 1 ;
522
- const char * cache_server_url = NULL ;
523
- char * default_cache_server_url = NULL ;
662
+ const char * cache_server_url = NULL , * local_cache_root = NULL ;
663
+ char * default_cache_server_url = NULL , * local_cache_root_abs = NULL ;
524
664
struct option clone_options [] = {
525
665
OPT_STRING ('b' , "branch" , & branch , N_ ("<branch>" ),
526
666
N_ ("branch to checkout after clone" )),
@@ -536,6 +676,9 @@ static int cmd_clone(int argc, const char **argv)
536
676
OPT_STRING (0 , "cache-server-url" , & cache_server_url ,
537
677
N_ ("<url>" ),
538
678
N_ ("the url or friendly name of the cache server" )),
679
+ OPT_STRING (0 , "local-cache-path" , & local_cache_root ,
680
+ N_ ("<path>" ),
681
+ N_ ("override the path for the local Scalar cache" )),
539
682
OPT_END (),
540
683
};
541
684
const char * const clone_usage [] = {
@@ -577,11 +720,23 @@ static int cmd_clone(int argc, const char **argv)
577
720
if (is_directory (enlistment ))
578
721
die (_ ("directory '%s' exists already" ), enlistment );
579
722
723
+ ensure_absolute_path (enlistment , & enlistment );
724
+
580
725
if (src )
581
726
dir = xstrfmt ("%s/src" , enlistment );
582
727
else
583
728
dir = xstrdup (enlistment );
584
729
730
+ if (!local_cache_root )
731
+ local_cache_root = local_cache_root_abs =
732
+ default_cache_root (enlistment );
733
+ else
734
+ local_cache_root = ensure_absolute_path (local_cache_root ,
735
+ & local_cache_root_abs );
736
+
737
+ if (!local_cache_root )
738
+ die (_ ("could not determine local cache root" ));
739
+
585
740
strbuf_reset (& buf );
586
741
if (branch )
587
742
strbuf_addf (& buf , "init.defaultBranch=%s" , branch );
@@ -601,8 +756,28 @@ static int cmd_clone(int argc, const char **argv)
601
756
602
757
setup_git_directory ();
603
758
759
+ git_config (git_default_config , NULL );
760
+
761
+ /*
762
+ * This `dir_inside_of()` call relies on git_config() having parsed the
763
+ * newly-initialized repository config's `core.ignoreCase` value.
764
+ */
765
+ if (dir_inside_of (local_cache_root , dir ) >= 0 ) {
766
+ struct strbuf path = STRBUF_INIT ;
767
+
768
+ strbuf_addstr (& path , enlistment );
769
+ if (chdir ("../.." ) < 0 ||
770
+ remove_dir_recursively (& path , 0 ) < 0 )
771
+ die (_ ("'--local-cache-path' cannot be inside the src "
772
+ "folder;\nCould not remove '%s'" ), enlistment );
773
+
774
+ die (_ ("'--local-cache-path' cannot be inside the src folder" ));
775
+ }
776
+
604
777
/* common-main already logs `argv` */
605
778
trace2_def_repo (the_repository );
779
+ trace2_data_intmax ("scalar" , the_repository , "unattended" ,
780
+ is_unattended ());
606
781
607
782
if (!branch && !(branch = branch_to_free = remote_default_branch (url ))) {
608
783
res = error (_ ("failed to get default branch for '%s'" ), url );
@@ -632,6 +807,8 @@ static int cmd_clone(int argc, const char **argv)
632
807
supports_gvfs_protocol (url , & default_cache_server_url );
633
808
634
809
if (gvfs_protocol ) {
810
+ if ((res = init_shared_object_cache (url , local_cache_root )))
811
+ goto cleanup ;
635
812
if (!cache_server_url )
636
813
cache_server_url = default_cache_server_url ;
637
814
if (set_config ("core.useGVFSHelper=true" ) ||
@@ -709,6 +886,7 @@ static int cmd_clone(int argc, const char **argv)
709
886
free (dir );
710
887
strbuf_release (& buf );
711
888
free (default_cache_server_url );
889
+ free (local_cache_root_abs );
712
890
return res ;
713
891
}
714
892
@@ -1120,6 +1298,12 @@ int cmd_main(int argc, const char **argv)
1120
1298
struct strbuf scalar_usage = STRBUF_INIT ;
1121
1299
int i ;
1122
1300
1301
+ if (is_unattended ()) {
1302
+ setenv ("GIT_ASKPASS" , "" , 0 );
1303
+ setenv ("GIT_TERMINAL_PROMPT" , "false" , 0 );
1304
+ git_config_push_parameter ("credential.interactive=false" );
1305
+ }
1306
+
1123
1307
while (argc > 1 && * argv [1 ] == '-' ) {
1124
1308
if (!strcmp (argv [1 ], "-C" )) {
1125
1309
if (argc < 3 )
0 commit comments