Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

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/refresh.go +19−2

@@ -98,6 +98,12 @@ func (s *Store) IndexRepo(m *repo.Meta) error {
98 98 if err != nil {
99 99 return err
100 100 }
101 // One `git log` dates every issue's last activity. The branch has no updated
102 // field to read, and a process per issue would make a rebuild cost the queue.
103 touchTimes, err := issueTouchTimes(g, commit)
104 if err != nil {
105 return err
106 }
101 107
102 108 tx, err := s.db.Begin()
103 109 if err != nil {
@@ -121,7 +127,7 @@ func (s *Store) IndexRepo(m *repo.Meta) error {
121 127 }
122 128 }
123 129 for _, iss := range issues {
124 if err := insertIssue(tx, m.ID, iss); err != nil {
130 if err := insertIssue(tx, m.ID, iss, touchTimes[iss.ID]); err != nil {
125 131 return err
126 132 }
127 133 if err := replaceComments(tx, m.ID, commentKindIssue, iss.ID, iss.Title, iss.Comments); err != nil {
@@ -214,6 +220,17 @@ func (s *Store) indexFrom(m *repo.Meta, from, to string) error {
214 220 }
215 221 }
216 222
223 // The commits also date each issue's last activity, which is what the list
224 // sorts by. They name every issue the range touched, including one edited and
225 // then edited back: the tree diff cannot see those, but the issue still moved.
226 touchTimes, err := issueTouchTimes(g, from+".."+to)
227 if err != nil {
228 return err
229 }
230 for id := range touchTimes {
231 issueIDs[id] = true
232 }
233
217 234 // Read every changed entity before opening the write transaction. `to` is a
218 235 // commit, so a concurrent push cannot make the rows describe two tree states.
219 236 issues := make(map[string]*metadata.Issue, len(issueIDs))
@@ -278,7 +295,7 @@ func (s *Store) indexFrom(m *repo.Meta, from, to string) error {
278 295 if err := deleteIssue(tx, m.ID, iss.ID); err != nil {
279 296 return err
280 297 }
281 if err := insertIssue(tx, m.ID, iss); err != nil {
298 if err := insertIssue(tx, m.ID, iss, touchTimes[iss.ID]); err != nil {
282 299 return err
283 300 }
284 301 if err := replaceComments(tx, m.ID, commentKindIssue, iss.ID, iss.Title, iss.Comments); err != nil {