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).
metadata/limits_test.go +4−4
| @@ -70,7 +70,7 @@ func TestLoadRequestsRefusesAnOversizedFile(t *testing.T) { | |||
| 70 | 70 | t.Parallel() | |
| 71 | 71 | g := limitsRepo(t) | |
| 72 | 72 | huge := strings.Repeat("x", MaxFileBytes+1) | |
| 73 | putIssueFile(t, g, RequestsDir+"/01M5JR2JK29KKMAA1T9MDNTPBZ/request.md", | ||
| 73 | putIssueFile(t, g, ChangesDir+"/01M5JR2JK29KKMAA1T9MDNTPBZ/change.md", | ||
| 74 | 74 | []byte("---\nid: 01M5JR2JK29KKMAA1T9MDNTPBZ\ntitle: huge\nauthor: keenan\nbase: main\nhead: "+ | |
| 75 | 75 | strings.Repeat("a", 40)+"\ncreated: 2026-10-27T12:00:00Z\n---\n\n"+huge)) | |
| 76 | 76 | ||
| @@ -148,14 +148,14 @@ func TestLoadRequestsReadsEveryFile(t *testing.T) { | |||
| 148 | 148 | } | |
| 149 | 149 | files := map[string][]byte{} | |
| 150 | 150 | for i := 0; i < n; i++ { | |
| 151 | // Odd and even requests share a body, so their request.md files are the | ||
| 151 | // Odd and even requests share a body, so their change.md files are the | ||
| 152 | 152 | // same object under different paths. | |
| 153 | 153 | body := requestBody(headSHA) | |
| 154 | 154 | if i%2 == 1 { | |
| 155 | 155 | body = requestBody(sideSHA) | |
| 156 | 156 | } | |
| 157 | files[RequestsDir+"/"+id(i)+"/request.md"] = []byte(body) | ||
| 158 | files[RequestsDir+"/"+id(i)+"/comments/"+id(i+n)+".md"] = []byte( | ||
| 157 | files[ChangesDir+"/"+id(i)+"/change.md"] = []byte(body) | ||
| 158 | files[ChangesDir+"/"+id(i)+"/comments/"+id(i+n)+".md"] = []byte( | ||
| 159 | 159 | "---\nauthor: keenan\ncreated: 2026-10-22T22:00:00Z\n---\n\ncomment " + string('a'+rune(i)) + "\n") | |
| 160 | 160 | } | |
| 161 | 161 | if _, err := git.CommitFiles(g, Ref, func(f map[string][]byte) error { | |