Closes #81.
Why
A web write committed an issue, then the redirect reloaded the whole issue index: every issue and comment blob, a git log --name-only walk of the whole branch history for numbers, and a delete-and-reinsert of every issue, label, assignee, and number row. The detail page also read every issue to render one.
What changed
Ensurenow applies the difference between the indexed commit and the tip when the indexed commit is an ancestor. It reloads only the issues whose files changed, appends only the entities the commit range created, and leaves untouched rows alone.- Numbers continue from the highest number already in the index. An entity created and deleted between refreshes still consumes its number, matching the full-history derivation.
- A first read, a rewritten branch, or a failed delta still falls back to the full rebuild, so a missing or stale index self-heals.
metadata.LoadIssueAtreads one issue and its comments;index.LoadIssueuses it, so issue pages and browser write handlers no longer load every issue.- Issue and issue-comment writes no longer re-run whole-tree
ValidateIssuesafter a write the writer already validated. External pushes still go through the pre-receive validation. git.DiffStatusPathslimits the changed-path scan to the subtrees the index owns.
Tests
- Adding one issue performs no deletes on the issue, label, assignee, or number tables.
- A comment and label edit updates one row and consumes no number.
- A creation that is deleted again inside the range keeps its spent number.
- The numbering call names the new commit range, not the whole tip.
- A cleared index rebuilds from the branch.
LoadIssueAtreads one issue, rejects a non-ULID, and reports a missing issue.
Ran go vet ./..., go test ./..., go test -race ./..., and ./vole workflow run verify; all pass.