vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
Add repository code search
aef992843b95fd107327b103c12d135cd708c11e · Keenan Nemetz · 15d ago · parent 5cab7b18
Add repository code search The Code tab now searches file names and plain-text contents at the selected branch. Contents go through git grep with fixed strings, case-insensitive matching, binary skipping, and a capped result set; there is no second index to drift. Results group by file and link to line anchors in the blob view. Also highlights a targeted line and adds the remaining polish notes to the UI plan.
server/polish_test.go +36
| @@ -87,3 +87,39 @@ func TestIssueListPaginates(t *testing.T) { | |||
| 87 | 87 | t.Fatalf("page 2 has no previous link:\n%s", page2) | |
| 88 | 88 | } | |
| 89 | 89 | } | |
| 90 | |||
| 91 | func TestCodeSearchFindsNamesAndContents(t *testing.T) { | ||
| 92 | t.Parallel() | ||
| 93 | f := newFixture(t) | ||
| 94 | f.publish(f.repo) | ||
| 95 | work := workRepo(t, map[string]string{ | ||
| 96 | "main.go": "package main\n// HelloNeedle lives here\n", | ||
| 97 | "docs/guide.md": "# Guide\nneedle in docs\n", | ||
| 98 | "README.md": "nothing to find\n", | ||
| 99 | }) | ||
| 100 | if _, errs, err := f.git(work, "push", f.sshURL(f.repo), "main:refs/heads/main"); err != nil { | ||
| 101 | t.Fatalf("push: %s", errs) | ||
| 102 | } | ||
| 103 | |||
| 104 | code, body := f.get("/r/" + f.repo + "/search?q=needle&ref=main") | ||
| 105 | if code != 200 { | ||
| 106 | t.Fatalf("search page = %d", code) | ||
| 107 | } | ||
| 108 | for _, want := range []string{"main.go", "docs/guide.md", "HelloNeedle", "needle in docs"} { | ||
| 109 | if !strings.Contains(body, want) { | ||
| 110 | t.Fatalf("content search does not show %q:\n%s", want, body) | ||
| 111 | } | ||
| 112 | } | ||
| 113 | if !strings.Contains(body, "main.go#L2") { | ||
| 114 | t.Fatalf("content search has no line anchor:\n%s", body) | ||
| 115 | } | ||
| 116 | if _, body := f.get("/r/" + f.repo + "/search?q=guide&ref=main"); !strings.Contains(body, "docs/guide.md") { | ||
| 117 | t.Fatalf("file name search missed docs/guide.md:\n%s", body) | ||
| 118 | } | ||
| 119 | if _, body := f.get("/r/" + f.repo + "/search?q=nothingmatches&ref=main"); !strings.Contains(body, "No file names match") || !strings.Contains(body, "No file content matches") { | ||
| 120 | t.Fatalf("empty search state missing:\n%s", body) | ||
| 121 | } | ||
| 122 | if code, _ := f.get("/r/" + f.repo + "/search?q=needle&ref=nope"); code != 200 { | ||
| 123 | t.Fatalf("unknown ref = %d, want an empty search page", code) | ||
| 124 | } | ||
| 125 | } | ||