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"
13
+ #include "environment.h"
12
14
#include "run-command.h"
13
15
#include "simple-ipc.h"
14
16
#include "fsmonitor-ipc.h"
15
17
#include "fsmonitor-settings.h"
16
18
#include "refs.h"
17
19
#include "dir.h"
20
+ #include "object-file.h"
18
21
#include "packfile.h"
19
22
#include "help.h"
20
23
#include "setup.h"
24
+ #include "wrapper.h"
21
25
#include "trace2.h"
22
26
#include "json-parser.h"
27
+ #include "path.h"
28
+
29
+ static int is_unattended (void ) {
30
+ return git_env_bool ("Scalar_UNATTENDED" , 0 );
31
+ }
23
32
24
33
static void setup_enlistment_directory (int argc , const char * * argv ,
25
34
const char * const * usagestr ,
@@ -106,6 +115,19 @@ static int run_git(const char *arg, ...)
106
115
return res ;
107
116
}
108
117
118
+ static const char * ensure_absolute_path (const char * path , char * * absolute )
119
+ {
120
+ struct strbuf buf = STRBUF_INIT ;
121
+
122
+ if (is_absolute_path (path ))
123
+ return path ;
124
+
125
+ strbuf_realpath_forgiving (& buf , path , 1 );
126
+ free (* absolute );
127
+ * absolute = strbuf_detach (& buf , NULL );
128
+ return * absolute ;
129
+ }
130
+
109
131
struct scalar_config {
110
132
const char * key ;
111
133
const char * value ;
@@ -434,6 +456,87 @@ static int supports_gvfs_protocol(const char *url, char **cache_server_url)
434
456
return 0 ; /* error out quietly */
435
457
}
436
458
459
+ static char * default_cache_root (const char * root )
460
+ {
461
+ const char * env ;
462
+
463
+ if (is_unattended ())
464
+ return xstrfmt ("%s/.scalarCache" , root );
465
+
466
+ #ifdef WIN32
467
+ (void )env ;
468
+ return xstrfmt ("%.*s.scalarCache" , offset_1st_component (root ), root );
469
+ #elif defined(__APPLE__ )
470
+ if ((env = getenv ("HOME" )) && * env )
471
+ return xstrfmt ("%s/.scalarCache" , env );
472
+ return NULL ;
473
+ #else
474
+ if ((env = getenv ("XDG_CACHE_HOME" )) && * env )
475
+ return xstrfmt ("%s/scalar" , env );
476
+ if ((env = getenv ("HOME" )) && * env )
477
+ return xstrfmt ("%s/.cache/scalar" , env );
478
+ return NULL ;
479
+ #endif
480
+ }
481
+
482
+ static int get_repository_id (struct json_iterator * it )
483
+ {
484
+ if (it -> type == JSON_STRING &&
485
+ !strcasecmp (".repository.id" , it -> key .buf )) {
486
+ * (char * * )it -> fn_data = strbuf_detach (& it -> string_value , NULL );
487
+ return 1 ;
488
+ }
489
+
490
+ return 0 ;
491
+ }
492
+
493
+ /* Needs to run this in a worktree; gvfs-helper requires a Git repository */
494
+ static char * get_cache_key (const char * url )
495
+ {
496
+ struct child_process cp = CHILD_PROCESS_INIT ;
497
+ struct strbuf out = STRBUF_INIT ;
498
+ char * cache_key = NULL ;
499
+
500
+ cp .git_cmd = 1 ;
501
+ strvec_pushl (& cp .args , "gvfs-helper" , "--remote" , url ,
502
+ "endpoint" , "vsts/info" , NULL );
503
+ if (!pipe_command (& cp , NULL , 0 , & out , 512 , NULL , 0 )) {
504
+ char * id = NULL ;
505
+ struct json_iterator it =
506
+ JSON_ITERATOR_INIT (out .buf , get_repository_id , & id );
507
+
508
+ if (iterate_json (& it ) < 0 )
509
+ warning ("JSON parse error (%s)" , out .buf );
510
+ else if (id )
511
+ cache_key = xstrfmt ("id_%s" , id );
512
+ free (id );
513
+ }
514
+
515
+ if (!cache_key ) {
516
+ struct strbuf downcased = STRBUF_INIT ;
517
+ int hash_algo_index = hash_algo_by_name ("sha1" );
518
+ const struct git_hash_algo * hash_algo = hash_algo_index < 0 ?
519
+ the_hash_algo : & hash_algos [hash_algo_index ];
520
+ struct git_hash_ctx ctx ;
521
+ unsigned char hash [GIT_MAX_RAWSZ ];
522
+
523
+ strbuf_addstr (& downcased , url );
524
+ strbuf_tolower (& downcased );
525
+
526
+ hash_algo -> init_fn (& ctx );
527
+ hash_algo -> update_fn (& ctx , downcased .buf , downcased .len );
528
+ hash_algo -> final_fn (hash , & ctx );
529
+
530
+ strbuf_release (& downcased );
531
+
532
+ cache_key = xstrfmt ("url_%s" ,
533
+ hash_to_hex_algop (hash , hash_algo ));
534
+ }
535
+
536
+ strbuf_release (& out );
537
+ return cache_key ;
538
+ }
539
+
437
540
static char * remote_default_branch (const char * url )
438
541
{
439
542
struct child_process cp = CHILD_PROCESS_INIT ;
@@ -528,14 +631,52 @@ void load_builtin_commands(const char *prefix UNUSED,
528
631
die ("not implemented" );
529
632
}
530
633
634
+ static int init_shared_object_cache (const char * url ,
635
+ const char * local_cache_root )
636
+ {
637
+ struct strbuf buf = STRBUF_INIT ;
638
+ int res = 0 ;
639
+ char * cache_key = NULL , * shared_cache_path = NULL , * alternates = NULL ;
640
+
641
+ if (!(cache_key = get_cache_key (url ))) {
642
+ res = error (_ ("could not determine cache key for '%s'" ), url );
643
+ goto cleanup ;
644
+ }
645
+
646
+ shared_cache_path = xstrfmt ("%s/%s" , local_cache_root , cache_key );
647
+ if (set_config ("gvfs.sharedCache=%s" , shared_cache_path )) {
648
+ res = error (_ ("could not configure shared cache" ));
649
+ goto cleanup ;
650
+ }
651
+
652
+ strbuf_addf (& buf , "%s/pack" , shared_cache_path );
653
+ switch (safe_create_leading_directories (the_repository , buf .buf )) {
654
+ case SCLD_OK : case SCLD_EXISTS :
655
+ break ; /* okay */
656
+ default :
657
+ res = error_errno (_ ("could not initialize '%s'" ), buf .buf );
658
+ goto cleanup ;
659
+ }
660
+
661
+ alternates = repo_git_path (the_repository , "objects/info/alternates" );
662
+ write_file (alternates , "%s\n" , shared_cache_path );
663
+
664
+ cleanup :
665
+ strbuf_release (& buf );
666
+ free (shared_cache_path );
667
+ free (cache_key );
668
+ free (alternates );
669
+ return res ;
670
+ }
671
+
531
672
static int cmd_clone (int argc , const char * * argv )
532
673
{
533
674
const char * branch = NULL ;
534
675
char * branch_to_free = NULL ;
535
676
int full_clone = 0 , single_branch = 0 , show_progress = isatty (2 );
536
677
int src = 1 , tags = 1 , maintenance = 1 ;
537
- const char * cache_server_url = NULL ;
538
- char * default_cache_server_url = NULL ;
678
+ const char * cache_server_url = NULL , * local_cache_root = NULL ;
679
+ char * default_cache_server_url = NULL , * local_cache_root_abs = NULL ;
539
680
struct option clone_options [] = {
540
681
OPT_STRING ('b' , "branch" , & branch , N_ ("<branch>" ),
541
682
N_ ("branch to checkout after clone" )),
@@ -553,6 +694,9 @@ static int cmd_clone(int argc, const char **argv)
553
694
OPT_STRING (0 , "cache-server-url" , & cache_server_url ,
554
695
N_ ("<url>" ),
555
696
N_ ("the url or friendly name of the cache server" )),
697
+ OPT_STRING (0 , "local-cache-path" , & local_cache_root ,
698
+ N_ ("<path>" ),
699
+ N_ ("override the path for the local Scalar cache" )),
556
700
OPT_END (),
557
701
};
558
702
const char * const clone_usage [] = {
@@ -594,11 +738,23 @@ static int cmd_clone(int argc, const char **argv)
594
738
if (is_directory (enlistment ))
595
739
die (_ ("directory '%s' exists already" ), enlistment );
596
740
741
+ ensure_absolute_path (enlistment , & enlistment );
742
+
597
743
if (src )
598
744
dir = xstrfmt ("%s/src" , enlistment );
599
745
else
600
746
dir = xstrdup (enlistment );
601
747
748
+ if (!local_cache_root )
749
+ local_cache_root = local_cache_root_abs =
750
+ default_cache_root (enlistment );
751
+ else
752
+ local_cache_root = ensure_absolute_path (local_cache_root ,
753
+ & local_cache_root_abs );
754
+
755
+ if (!local_cache_root )
756
+ die (_ ("could not determine local cache root" ));
757
+
602
758
strbuf_reset (& buf );
603
759
if (branch )
604
760
strbuf_addf (& buf , "init.defaultBranch=%s" , branch );
@@ -618,8 +774,28 @@ static int cmd_clone(int argc, const char **argv)
618
774
619
775
setup_git_directory ();
620
776
777
+ repo_config (the_repository , git_default_config , NULL );
778
+
779
+ /*
780
+ * This `dir_inside_of()` call relies on git_config() having parsed the
781
+ * newly-initialized repository config's `core.ignoreCase` value.
782
+ */
783
+ if (dir_inside_of (local_cache_root , dir ) >= 0 ) {
784
+ struct strbuf path = STRBUF_INIT ;
785
+
786
+ strbuf_addstr (& path , enlistment );
787
+ if (chdir ("../.." ) < 0 ||
788
+ remove_dir_recursively (& path , 0 ) < 0 )
789
+ die (_ ("'--local-cache-path' cannot be inside the src "
790
+ "folder;\nCould not remove '%s'" ), enlistment );
791
+
792
+ die (_ ("'--local-cache-path' cannot be inside the src folder" ));
793
+ }
794
+
621
795
/* common-main already logs `argv` */
622
796
trace2_def_repo (the_repository );
797
+ trace2_data_intmax ("scalar" , the_repository , "unattended" ,
798
+ is_unattended ());
623
799
624
800
if (!branch && !(branch = branch_to_free = remote_default_branch (url ))) {
625
801
res = error (_ ("failed to get default branch for '%s'" ), url );
@@ -649,6 +825,8 @@ static int cmd_clone(int argc, const char **argv)
649
825
supports_gvfs_protocol (url , & default_cache_server_url );
650
826
651
827
if (gvfs_protocol ) {
828
+ if ((res = init_shared_object_cache (url , local_cache_root )))
829
+ goto cleanup ;
652
830
if (!cache_server_url )
653
831
cache_server_url = default_cache_server_url ;
654
832
if (set_config ("core.useGVFSHelper=true" ) ||
@@ -727,6 +905,7 @@ static int cmd_clone(int argc, const char **argv)
727
905
free (dir );
728
906
strbuf_release (& buf );
729
907
free (default_cache_server_url );
908
+ free (local_cache_root_abs );
730
909
return res ;
731
910
}
732
911
@@ -1161,6 +1340,12 @@ int cmd_main(int argc, const char **argv)
1161
1340
struct strbuf scalar_usage = STRBUF_INIT ;
1162
1341
int i ;
1163
1342
1343
+ if (is_unattended ()) {
1344
+ setenv ("GIT_ASKPASS" , "" , 0 );
1345
+ setenv ("GIT_TERMINAL_PROMPT" , "false" , 0 );
1346
+ git_config_push_parameter ("credential.interactive=false" );
1347
+ }
1348
+
1164
1349
while (argc > 1 && * argv [1 ] == '-' ) {
1165
1350
if (!strcmp (argv [1 ], "-C" )) {
1166
1351
if (argc < 3 )
0 commit comments