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/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.

Refresh the issue index from what changed

34a404b394dd9abcce0a67e32e99342c69595413 · Keenan Nemetz · 10d ago · parent fdf2340f

Refresh the issue index from what changed

A page read after a write used to reload every issue and renumber the repository from the whole branch history. Ensure now applies the difference between the indexed commit and the tip: it reloads only changed issues, adds only the numbers the range created, and leaves every other row alone. A rewritten branch or a missing index still falls back to the full rebuild.

The detail page and the browser write handlers now read one issue instead of the whole issue tree. Issue writes no longer re-validate the whole metadata tree after a write the writer already validated; the pre-receive hook still owns that check for pushes.

metadata/issues.go +42

@@ -149,6 +149,37 @@ func LoadIssues(g *git.Repo, ref string) ([]*Issue, error) {
149 149 return ParseIssues(contents)
150 150 }
151 151
152 // LoadIssueAt reads one issue and its comments at ref, without reading the rest
153 // of the tree. The bool separates "no issue with that id" from a read error, so
154 // an incremental refresh can delete the rows for an issue that is gone.
155 func LoadIssueAt(g *git.Repo, ref, id string) (*Issue, bool, error) {
156 if !voleid.Valid(id) {
157 return nil, false, nil
158 }
159 commit, err := g.RevParse(ref)
160 if err != nil {
161 return nil, false, err
162 }
163 if commit == "" {
164 return nil, false, nil
165 }
166 contents, err := readMetadata(g, commit, IssuesDir+"/"+id)
167 if err != nil {
168 return nil, false, err
169 }
170 if len(contents) == 0 {
171 return nil, false, nil
172 }
173 list, err := ParseIssues(contents)
174 if err != nil {
175 return nil, false, err
176 }
177 if len(list) == 0 {
178 return nil, false, nil
179 }
180 return list[0], true, nil
181 }
182
152 183 // Parse turns a path -> content map into issues, validating the tree.
153 184 func ParseIssues(contents map[string]string) ([]*Issue, error) {
154 185 var problems []string
@@ -482,6 +513,17 @@ func IssueCommentPath(issueID, commentID string) string {
482 513 return fmt.Sprintf("%s/%s/%s/%s.md", IssuesDir, issueID, commentsDir, commentID)
483 514 }
484 515
516 // IssueIDFromPath returns the issue a file under issues/ belongs to. It matches
517 // only the files the format defines, so a stray path is not mistaken for an
518 // issue an incremental refresh should reload.
519 func IssueIDFromPath(path string) (string, bool) {
520 m := issuePathRE.FindStringSubmatch(path)
521 if m == nil {
522 return "", false
523 }
524 return m[1], true
525 }
526
485 527 // IsCommentPath reports whether path is a comment file rather than an issue file.
486 528 // The hook uses it to apply comment authorship to comments and not to the issue
487 529 // file, which carries shared state (state, labels, assignees).