@@ -11,6 +11,7 @@ import (
1111 "path/filepath"
1212 "regexp"
1313 "runtime"
14+ "sort"
1415 "strings"
1516 "sync"
1617 "sync/atomic"
@@ -40,7 +41,7 @@ type CommandRunner interface {
4041
4142// GitCloner abstracts git cloning operations for better testability
4243type GitCloner interface {
43- Clone (ctx context.Context , repository , targetDir string , quiet bool ) error
44+ Clone (ctx context.Context , repository , targetDir string , quiet , shallow bool ) error
4445}
4546
4647// DirectoryChecker abstracts directory existence checking for better testability
@@ -108,8 +109,8 @@ func (cr *DefaultCommandRunner) RunWithOutput(ctx context.Context, name string,
108109// DefaultGitCloner provides real git cloning functionality
109110type DefaultGitCloner struct {}
110111
111- func (gc * DefaultGitCloner ) Clone (_ context.Context , repository , targetDir string , quiet bool ) error {
112- return secureGitClone (repository , targetDir , quiet )
112+ func (gc * DefaultGitCloner ) Clone (_ context.Context , repository , targetDir string , quiet , shallow bool ) error {
113+ return secureGitClone (repository , targetDir , quiet , shallow )
113114}
114115
115116// DefaultDirectoryChecker provides real directory checking functionality
@@ -179,6 +180,7 @@ type Config struct {
179180 ShowCommandHelp bool
180181 ShowVersionInfo bool
181182 Quiet bool
183+ ShallowClone bool
182184 Workers int
183185 RepositoryArgs []string
184186 Dependencies * Dependencies
@@ -291,7 +293,7 @@ func (wp *WorkerPool) processJob(job RepositoryJob, _ int) {
291293 ctx , cancel := context .WithTimeout (context .Background (), 10 * time .Minute )
292294 defer cancel ()
293295
294- if err := wp .config .Dependencies .GitClone .Clone (ctx , job .Repository , filepath .Dir (projectDir ), wp .config .Quiet ); err != nil {
296+ if err := wp .config .Dependencies .GitClone .Clone (ctx , job .Repository , filepath .Dir (projectDir ), wp .config .Quiet , wp . config . ShallowClone ); err != nil {
295297 result .Error = fmt .Errorf ("failed clone repository '%s': %w" , job .Repository , err )
296298 wp .results <- result
297299 return
@@ -724,7 +726,7 @@ func validatePath(path string) error {
724726}
725727
726728// secureGitClone performs git clone with additional security measures including timeout
727- func secureGitClone (repository , targetDir string , quiet bool ) error {
729+ func secureGitClone (repository , targetDir string , quiet , shallow bool ) error {
728730 // Double-check validation (defense in depth)
729731 if err := validateRepositoryURL (repository ); err != nil {
730732 return fmt .Errorf ("security validation failed: %w" , err )
@@ -741,7 +743,12 @@ func secureGitClone(repository, targetDir string, quiet bool) error {
741743 defer cancel ()
742744
743745 // Create command with explicit arguments and timeout context (no shell interpretation)
744- cmd := exec .CommandContext (ctx , "git" , "clone" , "--" , repository )
746+ args := []string {"clone" }
747+ if shallow {
748+ args = append (args , "--depth=1" )
749+ }
750+ args = append (args , "--" , repository )
751+ cmd := exec .CommandContext (ctx , "git" , args ... )
745752
746753 // Set working directory
747754 cmd .Dir = cleanTargetDir
@@ -775,6 +782,7 @@ func parseArgs() (*Config, error) {
775782 pflag .BoolVarP (& config .ShowCommandHelp , "help" , "h" , false , "Show this help message and exit" )
776783 pflag .BoolVarP (& config .ShowVersionInfo , "version" , "v" , false , "Show the version number and exit" )
777784 pflag .BoolVarP (& config .Quiet , "quiet" , "q" , false , "Suppress output" )
785+ pflag .BoolVarP (& config .ShallowClone , "shallow" , "s" , false , "Perform shallow clone with --depth=1" )
778786 pflag .IntVarP (& config .Workers , "workers" , "w" , getDefaultWorkers (), "Number of parallel workers for cloning" )
779787 pflag .Parse ()
780788
@@ -866,7 +874,7 @@ func processRepositoriesSequential(config *Config) *ProcessingResult {
866874 // Security: Use secure git clone with validated arguments
867875 ctx , cancel := context .WithTimeout (context .Background (), 10 * time .Minute )
868876 defer cancel ()
869- if err := config .Dependencies .GitClone .Clone (ctx , repository , filepath .Dir (projectDir ), config .Quiet ); err != nil {
877+ if err := config .Dependencies .GitClone .Clone (ctx , repository , filepath .Dir (projectDir ), config .Quiet , config . ShallowClone ); err != nil {
870878 prnt ("failed clone repository '%s': %s" , repository , err )
871879 result .FailedCount ++
872880 continue
@@ -979,6 +987,13 @@ func (uc *URLCache) Set(key, value string) {
979987 uc .cache [key ] = value
980988}
981989
990+ // Clear removes all entries from the URLCache for test isolation
991+ func (uc * URLCache ) Clear () {
992+ uc .mutex .Lock ()
993+ defer uc .mutex .Unlock ()
994+ uc .cache = make (map [string ]string )
995+ }
996+
982997// detectRegexType determines the best regex pattern for the given URL
983998func detectRegexType (repo string ) RegexType {
984999 if len (repo ) > 8 && (repo [:7 ] == "https://" || repo [:7 ] == "http://" ) {
@@ -1411,14 +1426,10 @@ func (dc *DirCache) evictLRU() {
14111426 entries = append (entries , entryWithKey {key : key , lastAccess : entry .lastAccess })
14121427 }
14131428
1414- // Sort by last access time (oldest first)
1415- for i := 0 ; i < len (entries )- 1 ; i ++ {
1416- for j := i + 1 ; j < len (entries ); j ++ {
1417- if entries [i ].lastAccess .After (entries [j ].lastAccess ) {
1418- entries [i ], entries [j ] = entries [j ], entries [i ]
1419- }
1420- }
1421- }
1429+ // Sort by last access time (oldest first) using efficient sort.Slice
1430+ sort .Slice (entries , func (i , j int ) bool {
1431+ return entries [i ].lastAccess .Before (entries [j ].lastAccess )
1432+ })
14221433
14231434 // Remove oldest entries
14241435 for i := 0 ; i < toEvict && i < len (entries ); i ++ {
@@ -1434,7 +1445,7 @@ func isDirectoryNotEmpty(name string) bool {
14341445
14351446// Usage prints the usage of the program.
14361447func usage () {
1437- fmt .Println ("usage: gclone [-h] [-v] [-q] [-w WORKERS] [REPOSITORY]" )
1448+ fmt .Println ("usage: gclone [-h] [-v] [-q] [-s] [- w WORKERS] [REPOSITORY]" )
14381449 fmt .Println ()
14391450 fmt .Println ("positional arguments:" )
14401451 fmt .Println (" REPOSITORY Repository URL" )
@@ -1443,6 +1454,7 @@ func usage() {
14431454 fmt .Println (" -h, --help Show this help message and exit" )
14441455 fmt .Println (" -v, --version Show the version number and exit" )
14451456 fmt .Println (" -q, --quiet Suppress output" )
1457+ fmt .Println (" -s, --shallow Perform shallow clone with --depth=1" )
14461458 fmt .Printf (" -w, --workers Number of parallel workers (default: %d)\n " , getDefaultWorkers ())
14471459 fmt .Println ()
14481460 fmt .Println ("environment variables:" )
0 commit comments