5
5
#include "git-compat-util.h"
6
6
#include "abspath.h"
7
7
#include "gettext.h"
8
+ #include "hex.h"
8
9
#include "parse-options.h"
9
10
#include "config.h"
10
11
#include "run-command.h"
13
14
#include "fsmonitor-settings.h"
14
15
#include "refs.h"
15
16
#include "dir.h"
17
+ #include "object-file.h"
16
18
#include "packfile.h"
17
19
#include "help.h"
18
20
#include "setup.h"
21
+ #include "wrapper.h"
19
22
#include "trace2.h"
20
23
#include "json-parser.h"
21
24
25
+ static int is_unattended (void ) {
26
+ return git_env_bool ("Scalar_UNATTENDED" , 0 );
27
+ }
28
+
22
29
static void setup_enlistment_directory (int argc , const char * * argv ,
23
30
const char * const * usagestr ,
24
31
const struct option * options ,
@@ -103,6 +110,19 @@ static int run_git(const char *arg, ...)
103
110
return res ;
104
111
}
105
112
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
+
106
126
struct scalar_config {
107
127
const char * key ;
108
128
const char * value ;
@@ -409,6 +429,87 @@ static int supports_gvfs_protocol(const char *url, char **cache_server_url)
409
429
return 0 ; /* error out quietly */
410
430
}
411
431
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
+
412
513
static char * remote_default_branch (const char * url )
413
514
{
414
515
struct child_process cp = CHILD_PROCESS_INIT ;
@@ -506,12 +607,48 @@ void load_builtin_commands(const char *prefix, struct cmdnames *cmds)
506
607
die ("not implemented" );
507
608
}
508
609
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
+
509
646
static int cmd_clone (int argc , const char * * argv )
510
647
{
511
648
const char * branch = NULL ;
512
649
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 ;
515
652
struct option clone_options [] = {
516
653
OPT_STRING ('b' , "branch" , & branch , N_ ("<branch>" ),
517
654
N_ ("branch to checkout after clone" )),
@@ -523,6 +660,9 @@ static int cmd_clone(int argc, const char **argv)
523
660
OPT_STRING (0 , "cache-server-url" , & cache_server_url ,
524
661
N_ ("<url>" ),
525
662
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" )),
526
666
OPT_END (),
527
667
};
528
668
const char * const clone_usage [] = {
@@ -563,8 +703,20 @@ static int cmd_clone(int argc, const char **argv)
563
703
if (is_directory (enlistment ))
564
704
die (_ ("directory '%s' exists already" ), enlistment );
565
705
706
+ ensure_absolute_path (enlistment , & enlistment );
707
+
566
708
dir = xstrfmt ("%s/src" , enlistment );
567
709
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
+
568
720
strbuf_reset (& buf );
569
721
if (branch )
570
722
strbuf_addf (& buf , "init.defaultBranch=%s" , branch );
@@ -584,8 +736,28 @@ static int cmd_clone(int argc, const char **argv)
584
736
585
737
setup_git_directory ();
586
738
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
+
587
757
/* common-main already logs `argv` */
588
758
trace2_def_repo (the_repository );
759
+ trace2_data_intmax ("scalar" , the_repository , "unattended" ,
760
+ is_unattended ());
589
761
590
762
if (!branch && !(branch = remote_default_branch (url ))) {
591
763
res = error (_ ("failed to get default branch for '%s'" ), url );
@@ -610,6 +782,8 @@ static int cmd_clone(int argc, const char **argv)
610
782
supports_gvfs_protocol (url , & default_cache_server_url );
611
783
612
784
if (gvfs_protocol ) {
785
+ if ((res = init_shared_object_cache (url , local_cache_root )))
786
+ goto cleanup ;
613
787
if (!cache_server_url )
614
788
cache_server_url = default_cache_server_url ;
615
789
if (set_config ("core.useGVFSHelper=true" ) ||
@@ -684,6 +858,7 @@ static int cmd_clone(int argc, const char **argv)
684
858
free (dir );
685
859
strbuf_release (& buf );
686
860
free (default_cache_server_url );
861
+ free (local_cache_root_abs );
687
862
return res ;
688
863
}
689
864
@@ -1058,6 +1233,12 @@ int cmd_main(int argc, const char **argv)
1058
1233
struct strbuf scalar_usage = STRBUF_INIT ;
1059
1234
int i ;
1060
1235
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
+
1061
1242
while (argc > 1 && * argv [1 ] == '-' ) {
1062
1243
if (!strcmp (argv [1 ], "-C" )) {
1063
1244
if (argc < 3 )
0 commit comments