fork of https://github.com/sourcegraph/zoekt
1// Copyright 2016 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package gitindex
16
17import (
18 "net/url"
19 "os"
20 "path/filepath"
21 "strings"
22 "sync"
23
24 git "github.com/go-git/go-git/v5"
25)
26
27// RepoCache is a set of repositories on the file system, named and
28// stored by URL.
29type RepoCache struct {
30 baseDir string
31
32 reposMu sync.Mutex
33 repos map[string]*git.Repository
34}
35
36// NewRepoCache creates a new RepoCache rooted at the given directory.
37func NewRepoCache(dir string) *RepoCache {
38 return &RepoCache{
39 baseDir: dir,
40 repos: make(map[string]*git.Repository),
41 }
42}
43
44func repoKeyStr(key string) string {
45 if !strings.HasSuffix(key, ".git") {
46 key += ".git"
47 }
48 return key
49}
50
51func repoKey(u *url.URL) string {
52 return repoKeyStr(filepath.Join(u.Host, u.Path))
53}
54
55// Path returns the absolute path of the bare repository.
56func Path(baseDir string, name string) string {
57 key := repoKeyStr(name)
58 return filepath.Join(baseDir, key)
59}
60
61func (rc *RepoCache) Path(u *url.URL) string {
62 key := repoKey(u)
63 return filepath.Join(rc.baseDir, key)
64}
65
66// Open opens a git repository. The cache retains a pointer to the
67// repository.
68func (rc *RepoCache) Open(u *url.URL) (*git.Repository, error) {
69 dir := rc.Path(u)
70 rc.reposMu.Lock()
71 defer rc.reposMu.Unlock()
72
73 key := repoKey(u)
74 r := rc.repos[key]
75 if r != nil {
76 return r, nil
77 }
78
79 repo, err := git.PlainOpen(dir)
80 if err == nil {
81 rc.repos[key] = repo
82 }
83 return repo, err
84}
85
86// ListRepos returns paths to repos on disk that start with the given
87// URL prefix. The paths are relative to baseDir, and typically
88// include a ".git" suffix.
89func ListRepos(baseDir string, u *url.URL) ([]string, error) {
90 key := filepath.Join(u.Host, u.Path)
91
92 var paths []string
93 walk := func(path string, info os.FileInfo, err error) error {
94 if err != nil {
95 return err
96 }
97 if !info.IsDir() {
98 return nil
99 }
100 if strings.HasSuffix(path, ".git") && !strings.HasSuffix(path, "/.git") {
101 _, err := git.PlainOpen(path)
102 if err == nil {
103 p, err := filepath.Rel(baseDir, path)
104 if err == nil {
105 paths = append(paths, p)
106 }
107 }
108 return filepath.SkipDir
109 }
110 return nil
111 }
112
113 if err := filepath.Walk(filepath.Join(baseDir, key), walk); err != nil {
114 return nil, err
115 }
116 return paths, nil
117}