Skip to content

Commit ca48ef6

Browse files
authored
Support serving bare repositories (#639)
This allows bare repos in addition to worktree style git repos in detection.
1 parent 888e699 commit ca48ef6

2 files changed

Lines changed: 72 additions & 16 deletions

File tree

internal/servegit/serve.go

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,33 @@ func (s *Serve) handler() http.Handler {
130130
})
131131
}
132132

133+
// Checks if git thinks the given path is a valid .git folder for a repository
134+
func isBareRepo(path string) bool {
135+
c := exec.Command("git", "--git-dir", path, "rev-parse", "--is-bare-repository")
136+
c.Dir = path
137+
out, err := c.CombinedOutput()
138+
139+
if err != nil {
140+
return false
141+
}
142+
143+
return string(out) != "false\n"
144+
}
145+
146+
// Check if git thinks the given path is a proper git checkout
147+
func isGitRepo(path string) bool {
148+
// Executing git rev-parse --git-dir in the root of a worktree returns .git
149+
c := exec.Command("git", "rev-parse", "--git-dir")
150+
c.Dir = path
151+
out, err := c.CombinedOutput()
152+
153+
if err != nil {
154+
return false
155+
}
156+
157+
return string(out) == ".git\n"
158+
}
159+
133160
// Repos returns a slice of all the git repositories it finds.
134161
func (s *Serve) Repos() ([]Repo, error) {
135162
var repos []Repo
@@ -158,10 +185,11 @@ func (s *Serve) Repos() ([]Repo, error) {
158185

159186
// Check whether a particular directory is a repository or not.
160187
//
161-
// A directory which also is a repository (have .git folder inside it)
162-
// will contain nil error. If it does, proceed to configure.
163-
gitdir := filepath.Join(path, ".git")
164-
if fi, err := os.Stat(gitdir); err != nil || !fi.IsDir() {
188+
// Valid paths are either bare repositories or git worktrees.
189+
isBare := isBareRepo(path)
190+
isGit := isGitRepo(path)
191+
192+
if !isGit && !isBare {
165193
s.Debug.Printf("not a repository root: %s", path)
166194
return nil
167195
}
@@ -182,14 +210,9 @@ func (s *Serve) Repos() ([]Repo, error) {
182210

183211
// Check whether a repository is a bare repository or not.
184212
//
185-
// If it yields false, which means it is a non-bare repository,
186-
// skip the directory so that it will not recurse to the subdirectories.
187-
// If it is a bare repository, proceed to recurse.
188-
c := exec.Command("git", "rev-parse", "--is-bare-repository")
189-
c.Dir = gitdir
190-
out, _ := c.CombinedOutput()
191-
192-
if string(out) == "false\n" {
213+
// Bare repositories shouldn't have any further child
214+
// repositories. Only regular git worktrees can have children.
215+
if isBare {
193216
return filepath.SkipDir
194217
}
195218

internal/servegit/serve_test.go

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func TestReposHandler(t *testing.T) {
3232
repos: []string{"project1", "project2"},
3333
}, {
3434
name: "nested",
35-
repos: []string{"project1", "project1/subproject", "project2", "dir/project3"},
35+
repos: []string{"project1", "project1/subproject", "project2", "dir/project3", "dir/project4.bare"},
3636
}}
3737
for _, tc := range cases {
3838
t.Run(tc.name, func(t *testing.T) {
@@ -171,6 +171,12 @@ func testReposHandler(t *testing.T, h http.Handler, repos []Repo) {
171171
}
172172
}
173173

174+
func gitInitBare(t *testing.T, path string) {
175+
if err := exec.Command("git", "init", "--bare", path).Run(); err != nil {
176+
t.Fatal(err)
177+
}
178+
}
179+
174180
func gitInitRepos(t *testing.T, names ...string) string {
175181
root, err := os.MkdirTemp("", "")
176182
if err != nil {
@@ -184,10 +190,11 @@ func gitInitRepos(t *testing.T, names ...string) string {
184190
if err := os.MkdirAll(p, 0755); err != nil {
185191
t.Fatal(err)
186192
}
187-
p = filepath.Join(p, ".git")
188-
if err := exec.Command("git", "init", "--bare", p).Run(); err != nil {
189-
t.Fatal(err)
193+
194+
if !strings.HasSuffix(p, ".bare") {
195+
p = filepath.Join(p, ".git")
190196
}
197+
gitInitBare(t, p)
191198
}
192199

193200
return root
@@ -221,6 +228,32 @@ func TestIgnoreGitSubmodules(t *testing.T) {
221228
}
222229
}
223230

231+
func TestIsBareRepo(t *testing.T) {
232+
dir, err := os.MkdirTemp("", "")
233+
if err != nil {
234+
t.Fatal(err)
235+
}
236+
t.Cleanup(func() { os.RemoveAll(dir) })
237+
238+
gitInitBare(t, dir)
239+
240+
if !isBareRepo(dir) {
241+
t.Errorf("Path %s it not a bare repository", dir)
242+
}
243+
}
244+
245+
func TestEmptyDirIsNotBareRepo(t *testing.T) {
246+
dir, err := os.MkdirTemp("", "")
247+
if err != nil {
248+
t.Fatal(err)
249+
}
250+
t.Cleanup(func() { os.RemoveAll(dir) })
251+
252+
if isBareRepo(dir) {
253+
t.Errorf("Path %s it falsey detected as a bare repository", dir)
254+
}
255+
}
256+
224257
func testLogger(t *testing.T) *log.Logger {
225258
return log.New(testWriter{t}, "testLogger ", log.LstdFlags)
226259
}

0 commit comments

Comments
 (0)