github.com/x-motemen/ghq@v1.6.1/cmd_list.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "path/filepath" 6 "sort" 7 "strings" 8 "sync" 9 10 "github.com/urfave/cli/v2" 11 ) 12 13 func doList(c *cli.Context) error { 14 var ( 15 w = c.App.Writer 16 query = c.Args().First() 17 exact = c.Bool("exact") 18 vcsBackend = c.String("vcs") 19 printFullPaths = c.Bool("full-path") 20 printUniquePaths = c.Bool("unique") 21 bare = c.Bool("bare") 22 ) 23 24 filterByQuery := func(_ *LocalRepository) bool { 25 return true 26 } 27 if query != "" { 28 if hasSchemePattern.MatchString(query) || scpLikeURLPattern.MatchString(query) { 29 if url, err := newURL(query, false, false); err == nil { 30 if repo, err := LocalRepositoryFromURL(url, bare); err == nil { 31 query = filepath.ToSlash(repo.RelPath) 32 } 33 } 34 } 35 36 if exact { 37 filterByQuery = func(repo *LocalRepository) bool { 38 return repo.Matches(query) 39 } 40 } else { 41 var host string 42 paths := strings.Split(query, "/") 43 if len(paths) > 1 && looksLikeAuthorityPattern.MatchString(paths[0]) { 44 query = strings.Join(paths[1:], "/") 45 host = paths[0] 46 } 47 // Using smartcase searching 48 if strings.ToLower(query) == query { 49 filterByQuery = func(repo *LocalRepository) bool { 50 return strings.Contains(strings.ToLower(repo.NonHostPath()), query) && 51 (host == "" || repo.PathParts[0] == host) 52 } 53 } else { 54 filterByQuery = func(repo *LocalRepository) bool { 55 return strings.Contains(repo.NonHostPath(), query) && 56 (host == "" || repo.PathParts[0] == host) 57 } 58 } 59 } 60 } 61 62 var ( 63 repos []*LocalRepository 64 mu sync.Mutex 65 ) 66 if err := walkLocalRepositories(vcsBackend, func(repo *LocalRepository) { 67 if !filterByQuery(repo) { 68 return 69 } 70 mu.Lock() 71 defer mu.Unlock() 72 repos = append(repos, repo) 73 }); err != nil { 74 return fmt.Errorf("failed to filter repos while walkLocalRepositories(repo): %w", err) 75 } 76 77 repoList := make([]string, 0, len(repos)) 78 if printUniquePaths { 79 subpathCount := map[string]int{} // Count duplicated subpaths (ex. foo/dotfiles and bar/dotfiles) 80 reposCount := map[string]int{} // Check duplicated repositories among roots 81 82 // Primary first 83 for _, repo := range repos { 84 if reposCount[repo.RelPath] == 0 { 85 for _, p := range repo.Subpaths() { 86 subpathCount[p] = subpathCount[p] + 1 87 } 88 } 89 90 reposCount[repo.RelPath] = reposCount[repo.RelPath] + 1 91 } 92 93 for _, repo := range repos { 94 if reposCount[repo.RelPath] > 1 && !repo.IsUnderPrimaryRoot() { 95 continue 96 } 97 98 for _, p := range repo.Subpaths() { 99 if subpathCount[p] == 1 { 100 repoList = append(repoList, p) 101 break 102 } 103 } 104 } 105 } else { 106 for _, repo := range repos { 107 if printFullPaths { 108 repoList = append(repoList, repo.FullPath) 109 } else { 110 repoList = append(repoList, repo.RelPath) 111 } 112 } 113 } 114 sort.Strings(repoList) 115 for _, r := range repoList { 116 fmt.Fprintln(w, r) 117 } 118 return nil 119 }