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.
Fold the reply count into the issue's metadata line
c67173ca550b5e3f969a23cdf1a8c96b9d6b8b3d · Keenan Nemetz · 4d ago · parent db4081ec
Fold the reply count into the issue's metadata line A whole column for a small number pushed the columns that matter apart. The count now sits under the title beside the number and the opening, and it is left out when there are none, so most rows are quieter than before. The row reads "1 comment" rather than "1 comment(s)", matching the issue page, which calls the discussion Comments.
server/issues_changes_test.go +7−4
| @@ -1431,8 +1431,9 @@ func TestIssueListShowsActivityColumnsAndSortsByUpdated(t *testing.T) { | |||
| 1431 | 1431 | const olderID = "01M5JR2JK29KKMAA1T9MDNTPC1" | |
| 1432 | 1432 | const newerID = "01M5JR2JK29KKMAA1T9MDNTPC2" | |
| 1433 | 1433 | files := map[string]string{ | |
| 1434 | "issues/" + olderID + "/issue.md": issueFile("Older issue", "keenan", "open", "2026-10-20T10:00:00Z", []string{"bug"}, "body\n", "wilbur"), | ||
| 1435 | "issues/" + newerID + "/issue.md": issueFile("Newer issue", "keenan", "open", "2026-10-22T10:00:00Z", nil, "body\n"), | ||
| 1434 | "issues/" + olderID + "/issue.md": issueFile("Older issue", "keenan", "open", "2026-10-20T10:00:00Z", []string{"bug"}, "body\n", "wilbur"), | ||
| 1435 | "issues/" + olderID + "/comments/01M5JR2JK29KKMAA1T9MDNTPK1.md": commentFile("keenan", "2026-10-21T10:00:00Z", "a note\n"), | ||
| 1436 | "issues/" + newerID + "/issue.md": issueFile("Newer issue", "keenan", "open", "2026-10-22T10:00:00Z", nil, "body\n"), | ||
| 1436 | 1437 | } | |
| 1437 | 1438 | work := f.issuesRepo(t, files) | |
| 1438 | 1439 | if _, errs, err := f.git(work, "push", f.sshURL(f.repo), "HEAD:refs/heads/vole"); err != nil { | |
| @@ -1462,7 +1463,8 @@ func TestIssueListShowsActivityColumnsAndSortsByUpdated(t *testing.T) { | |||
| 1462 | 1463 | t.Fatalf("issue list: %d\n%s", code, body) | |
| 1463 | 1464 | } | |
| 1464 | 1465 | for _, want := range []string{ | |
| 1465 | "<th>Status</th><th>Issue</th><th>Labels</th><th>Assignees</th><th>Replies</th><th>Updated</th>", | ||
| 1466 | "<th>Status</th><th>Issue</th><th>Labels</th><th>Assignees</th><th>Updated</th>", | ||
| 1467 | `<span>1 comment</span>`, | ||
| 1466 | 1468 | `<span class="label">bug</span>`, | |
| 1467 | 1469 | `opened by <a class="user" href="/u/keenan">keenan</a>`, | |
| 1468 | 1470 | `<a class="user" href="/u/wilbur">wilbur</a>`, | |
| @@ -1471,7 +1473,8 @@ func TestIssueListShowsActivityColumnsAndSortsByUpdated(t *testing.T) { | |||
| 1471 | 1473 | t.Fatalf("issue list is missing %q:\n%s", want, body) | |
| 1472 | 1474 | } | |
| 1473 | 1475 | } | |
| 1474 | if strings.Contains(body, "<th>Age</th>") || strings.Contains(body, "<th>Author</th>") { | ||
| 1476 | if strings.Contains(body, "<th>Age</th>") || strings.Contains(body, "<th>Author</th>") || | ||
| 1477 | strings.Contains(body, "<th>Replies</th>") { | ||
| 1475 | 1478 | t.Fatalf("issue list still has the replaced columns:\n%s", body) | |
| 1476 | 1479 | } | |
| 1477 | 1480 | editedAt, otherAt := strings.Index(body, "Older issue, edited"), strings.Index(body, "Newer issue") | |