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.
Phase 3: a review comment points at a line, and stays glued to it
9cc2f08757a36a49c7baa3ce502f5934341e7a5b · Keenan Nemetz · 17d ago · parent 7c6e52e7
Phase 3: a review comment points at a line, and stays glued to it A review thread can now say where it applies. A comment may carry an anchor — path, side, line and blob — on the same file it already lives in, written by the same reader and writer as before (four extra frontmatter lines, refused as a half-written set if any is missing). The interesting part is what it anchors on. A line number is not an identity: the file moves and the number points at different code. A commit id is too coarse: any unrelated change to the branch would invalidate every anchor. The **blob** of the content reviewed is content-addressed, so a comment is glued to exactly what was read: it stays current for as long as that file is unchanged — the same file at a later head has the same blob id — and becomes stale the moment it changes, rather than sliding to a line that now says something else. That is what "survive a rewrite" has to mean, and the page enforces it by resolving the blob at the head (or at the merge base, for `side: old`) and comparing. Which is also why nothing is verified at the push boundary. An anchor is a display hint, not an authorization: an anchor that lies can only make itself stale, and a rule in pre-receive would have to re-check every old anchor against a head that has since moved — rejecting pushes for comments nobody touched. `vole request comment <id> -m … --path f --line n [--side old]` computes the blob from the clone, refuses a line past the end of the file, and says `git fetch` rather than writing an anchor it cannot compute. On the page, a current anchor puts a marker under the line that links to the comment, and the card says `path:line (side)`; a stale one keeps its place in the thread with "the file has changed since this comment" and loses the marker. `side: old` covers reviewing a deletion, since that line exists only at the merge base. Verified: TestAnchorRoundTrips and TestAnchorMustBeWhole (blob is full-length, side is new or old, line is 1 or more, path stays inside the repository, and half an anchor is a refusal naming the file); and end to end through the binary, TestReviewCommentAnchorsALine — a new-side comment whose blob matches `rev-parse <head>:code.txt` on the server, the marker and the labelled card on the page, an old-side comment whose blob matches the merge base, a line past the end refused with the branch unmoved, and then a new head that changes the file, after which the new-side comment is labelled stale and loses its marker while the old-side one does not. scripts/ci.sh green (gofmt, vet, build, markdown links, go test -race ./...).
README.md +14
| @@ -264,6 +264,20 @@ A comment belongs to whoever wrote it: only that account can change or delete it | |||
| 264 | 264 | and an admin override is audited. The thread shows on the request page with an | |
| 265 | 265 | anchor per comment (`#c-<ulid>`), so a remark can be linked. | |
| 266 | 266 | ||
| 267 | A comment can also point at a line, on either side of the diff: | ||
| 268 | |||
| 269 | ```sh | ||
| 270 | vole request comment <id> -m "this drops the check" --path internal/auth.go --line 88 | ||
| 271 | vole request comment <id> -m "and this was the old line" --path internal/auth.go --line 88 --side old | ||
| 272 | ``` | ||
| 273 | |||
| 274 | The anchor records the *content* that was reviewed (the blob id), not the line | ||
| 275 | number, so the comment stays attached to what it was written about: the request page | ||
| 276 | marks the line while the file is unchanged, and marks the comment "the file has | ||
| 277 | changed since this comment" once it moves. The CLI needs the commits in the clone — | ||
| 278 | it prints `git fetch` rather than writing an anchor it cannot compute, and it | ||
| 279 | refuses a line past the end of the file. | ||
| 280 | |||
| 267 | 281 | ```sh | |
| 268 | 282 | vole acl protect demo refs/heads/main # protect a branch | |
| 269 | 283 | vole acl approvals demo 2 # require two approvals (default 1) | |