vole
Clone
Read only, no credentials:
https://rodent.software/git/vole.git
Write over SSH with a registered key:
git clone ssh://git@0.0.0.0:2222/vole.git
A free and open-source forge that lives in your Git repo.
Store changes under changes/<ulid>/change.md
b492b557f5cc5633b3dcb21fdbb5e2ad98d0bc16 · Keenan Nemetz · 3d ago · parent 9bce3723
Store changes under changes/<ulid>/change.md The web, the CLI, and the docs call these items changes; only the metadata tree still said requests. Rename the directory and the record file so the format and the product use one word. The tree is a contract, so this is a format change rather than a display change: the top-level layout stays closed, and a request path no longer validates. An existing repository is migrated by rewriting its vole branch, which is an audited admin force-push; the index rebuilds from the new tip and the #N aliases keep their ULIDs because the rewrite preserves commit order. Refs #202 (01M6S3JC3Y2KA6EFJK6APX7A1T).
server/ui_test.go +6−6
| @@ -77,9 +77,9 @@ func TestRepoTabsCountOpenIssuesAndRequests(t *testing.T) { | |||
| 77 | 77 | head := strings.Repeat("a", 40) | |
| 78 | 78 | meta := f.newMetaRepo(t) | |
| 79 | 79 | if err := meta.apply(map[string]string{ | |
| 80 | "requests/01M5JR2JK29KKMAA1T9MDNTR01/request.md": requestFileTitled( | ||
| 80 | "changes/01M5JR2JK29KKMAA1T9MDNTR01/change.md": requestFileTitled( | ||
| 81 | 81 | "Still open", "main", head, "keenan", "open\n"), | |
| 82 | "requests/01M5JR2JK29KKMAA1T9MDNTR02/request.md": requestFileState( | ||
| 82 | "changes/01M5JR2JK29KKMAA1T9MDNTR02/change.md": requestFileState( | ||
| 83 | 83 | "Already closed", "main", head, "keenan", "closed", "closed\n"), | |
| 84 | 84 | }, "requests"); err != nil { | |
| 85 | 85 | t.Fatalf("requests push: %v", err) | |
| @@ -509,9 +509,9 @@ func TestOverviewShowsRecentOpenIssuesAndChanges(t *testing.T) { | |||
| 509 | 509 | head := strings.Repeat("a", 40) | |
| 510 | 510 | meta := f.newMetaRepo(t) | |
| 511 | 511 | if err := meta.apply(map[string]string{ | |
| 512 | "requests/" + openChangeID + "/request.md": requestFileState( | ||
| 512 | "changes/" + openChangeID + "/change.md": requestFileState( | ||
| 513 | 513 | "Overview open change", "main", head, "keenan", "open", "open\n"), | |
| 514 | "requests/" + closedChangeID + "/request.md": requestFileState( | ||
| 514 | "changes/" + closedChangeID + "/change.md": requestFileState( | ||
| 515 | 515 | "Overview closed change", "main", head, "keenan", "closed", "closed\n"), | |
| 516 | 516 | }, "requests"); err != nil { | |
| 517 | 517 | t.Fatalf("requests push: %v", err) | |
| @@ -568,10 +568,10 @@ func TestOverviewContributorsAreAccountsNotCommitAuthors(t *testing.T) { | |||
| 568 | 568 | "Reported by keenan", "keenan", "closed", "2026-10-22T21:00:00Z", nil, ""), | |
| 569 | 569 | "issues/" + wilburIssue + "/issue.md": issueFile( | |
| 570 | 570 | "Reported by wilbur", "wilbur", "closed", "2026-10-22T21:01:00Z", nil, ""), | |
| 571 | "requests/" + changeID + "/request.md": requestFileMerged( | ||
| 571 | "changes/" + changeID + "/change.md": requestFileMerged( | ||
| 572 | 572 | "Fix both reports", "main", head, "wilbur", "keenan", | |
| 573 | 573 | []string{keenanIssue, wilburIssue}), | |
| 574 | "requests/" + changeID + "/approvals/keenan.md": approvalFile("main", head), | ||
| 574 | "changes/" + changeID + "/approvals/keenan.md": approvalFile("main", head), | ||
| 575 | 575 | }, "merge wilbur's change"); err != nil { | |
| 576 | 576 | t.Fatalf("push merged change: %v", err) | |
| 577 | 577 | } | |