github.com/motemen/ghq@v1.0.3/getter.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "os" 6 "path" 7 "path/filepath" 8 "strings" 9 "sync" 10 11 "github.com/motemen/ghq/logger" 12 ) 13 14 var ( 15 seen = make(map[string]bool) 16 mu = &sync.Mutex{} 17 ) 18 19 func getRepoLock(localRepoRoot string) bool { 20 mu.Lock() 21 defer func() { 22 seen[localRepoRoot] = true 23 mu.Unlock() 24 }() 25 return !seen[localRepoRoot] 26 } 27 28 type getter struct { 29 update, shallow, silent, ssh, recursive bool 30 vcs, branch string 31 } 32 33 func (g *getter) get(argURL string) error { 34 u, err := newURL(argURL, g.ssh, false) 35 if err != nil { 36 return fmt.Errorf("Could not parse URL %q: %w", argURL, err) 37 } 38 39 remote, err := NewRemoteRepository(u) 40 if err != nil { 41 return err 42 } 43 44 return g.getRemoteRepository(remote) 45 } 46 47 // getRemoteRepository clones or updates a remote repository remote. 48 // If doUpdate is true, updates the locally cloned repository. Otherwise does nothing. 49 // If isShallow is true, does shallow cloning. (no effect if already cloned or the VCS is Mercurial and git-svn) 50 func (g *getter) getRemoteRepository(remote RemoteRepository) error { 51 remoteURL := remote.URL() 52 local, err := LocalRepositoryFromURL(remoteURL) 53 if err != nil { 54 return err 55 } 56 57 var ( 58 fpath = local.FullPath 59 newPath = false 60 ) 61 62 _, err = os.Stat(fpath) 63 if err != nil { 64 if os.IsNotExist(err) { 65 newPath = true 66 err = nil 67 } 68 if err != nil { 69 return err 70 } 71 } 72 73 switch { 74 case newPath: 75 logger.Log("clone", fmt.Sprintf("%s -> %s", remoteURL, fpath)) 76 var ( 77 localRepoRoot = fpath 78 repoURL = remoteURL 79 ) 80 vcs, ok := vcsRegistry[g.vcs] 81 if !ok { 82 vcs, repoURL = remote.VCS() 83 if vcs == nil { 84 return fmt.Errorf("Could not find version control system: %s", remoteURL) 85 } 86 } 87 l := detectLocalRepoRoot( 88 strings.TrimSuffix(remoteURL.Path, ".git"), 89 strings.TrimSuffix(repoURL.Path, ".git")) 90 if l != "" { 91 localRepoRoot = filepath.Join(local.RootPath, remoteURL.Hostname(), l) 92 } 93 94 if getRepoLock(localRepoRoot) { 95 return vcs.Clone(&vcsGetOption{ 96 url: repoURL, 97 dir: localRepoRoot, 98 shallow: g.shallow, 99 silent: g.silent, 100 branch: g.branch, 101 recursive: g.recursive, 102 }) 103 } 104 return nil 105 case g.update: 106 logger.Log("update", fpath) 107 vcs, localRepoRoot := local.VCS() 108 if vcs == nil { 109 return fmt.Errorf("failed to detect VCS for %q", fpath) 110 } 111 if getRepoLock(localRepoRoot) { 112 return vcs.Update(&vcsGetOption{ 113 dir: localRepoRoot, 114 silent: g.silent, 115 recursive: g.recursive, 116 }) 117 } 118 return nil 119 } 120 logger.Log("exists", fpath) 121 return nil 122 } 123 124 func detectLocalRepoRoot(remotePath, repoPath string) string { 125 pathParts := strings.Split(repoPath, "/") 126 pathParts = pathParts[1:] 127 for i := 0; i < len(pathParts); i++ { 128 subPath := "/" + path.Join(pathParts[i:]...) 129 if subIdx := strings.Index(remotePath, subPath); subIdx >= 0 { 130 return remotePath[0:subIdx] + subPath 131 } 132 } 133 return "" 134 }