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.
Sort the issue list by when it last moved
db4081ec934ddd9a7ce123b4d3cf6d6ea43a4537 · Keenan Nemetz · 5d ago · parent a32a859a
Sort the issue list by when it last moved An issue queue is read for what changed, not for the order things were filed. The list now shows the newest activity first and carries the columns that answer the question: labels have their own column, assignees render like authors do, and the creation time and author sit under the title. The last column is "updated": the newest commit over the issue's directory, which covers an edit, a comment, a label change, and a close or reopen. The metadata branch has no such field on purpose — the commit carries the time — so the index derives it from one `git log` per rebuild or one per refreshed range, and stores it. An incremental refresh also reads the commits' paths, so an edit reverted inside the range still counts as activity. Bumps the index schema version, which drops the derived rows and rebuilds them from the branch on the next read. Closes #192
forge/index/index.go +12−5
| @@ -135,10 +135,12 @@ CREATE TABLE IF NOT EXISTS issues ( | |||
| 135 | 135 | // Assignees are denormalised onto the row the way labels are: the list page | |
| 136 | 136 | // shows them without a join, and the filter table below is what queries use. | |
| 137 | 137 | _, _ = db.Exec(`ALTER TABLE issues ADD COLUMN assignees TEXT NOT NULL DEFAULT ''`) | |
| 138 | // The metadata format dropped its updated field (the commit carries the | ||
| 139 | // time), so the index has no column for it either. The index is derived: if | ||
| 140 | // this fails on some older file the table is rebuilt from the repos. | ||
| 141 | _, _ = db.Exec(`ALTER TABLE issues DROP COLUMN updated`) | ||
| 138 | // updated is when the metadata branch last touched the issue: an edit, a | ||
| 139 | // comment, or a state change. The branch keeps no such field — the commit | ||
| 140 | // carries the time — so the index derives it from `git log` and stores it, | ||
| 141 | // which is what lets the list sort by activity without walking history per | ||
| 142 | // page. The index is derived: a stale value is repaired by a rebuild. | ||
| 143 | _, _ = db.Exec(`ALTER TABLE issues ADD COLUMN updated INTEGER NOT NULL DEFAULT 0`) | ||
| 142 | 144 | if _, err := db.Exec(` | |
| 143 | 145 | CREATE TABLE IF NOT EXISTS issue_labels ( | |
| 144 | 146 | repo_id TEXT NOT NULL, | |
| @@ -328,7 +330,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS search USING fts5( | |||
| 328 | 330 | // and leaves every repo with an empty issues_sha, which makes the next read | |
| 329 | 331 | // rebuild that repo from git. A stale row is worse than a rebuild, and a rebuild is the | |
| 330 | 332 | // operation the index already has to survive. | |
| 331 | const schemaVersion = 12 | ||
| 333 | const schemaVersion = 13 | ||
| 332 | 334 | ||
| 333 | 335 | func (s *Store) migrate() error { | |
| 334 | 336 | var v int | |
| @@ -375,6 +377,11 @@ type IssueRow struct { | |||
| 375 | 377 | Labels []string | |
| 376 | 378 | Assignees []string | |
| 377 | 379 | Comments int | |
| 380 | // Updated is when the metadata branch last touched the issue: the newest | ||
| 381 | // commit over its directory, which covers an edit, a comment, a label change, | ||
| 382 | // and a close or reopen. It is at least Created, so a freshly filed issue | ||
| 383 | // reads as just updated rather than as never touched. | ||
| 384 | Updated time.Time | ||
| 378 | 385 | // Number is the `#42` alias from entity_numbers, or 0 when the branch has not | |
| 379 | 386 | // been indexed yet. It is a display alias; ID remains the identity. | |
| 380 | 387 | Number int | |