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.

Read a change's file list without its patch, and never cut a diff

f64c77e4950c1759f336d3bc1849ead86e8b8ebf · Keenan Nemetz · 8d ago · parent 11720250

Read a change's file list without its patch, and never cut a diff

The review tree, the file count and the landing summary were all parsed out of
one `git diff` capped at 512 KiB. When a change carries a large file the cut
lands inside it, and every file named after the cut is missing from the tree:
reviewing #143, a 546 KB deletion hid the 296 files that came after it while
the tab still said "6 changed". The cap existed to stop a page rendering every
hunk, but the pages it guarded already render one file at a time.

The inventory and the patch are separate reads now. DiffInventory runs
`git diff --raw --numstat -z`, which names every file with its status and line
counts in one walk of the trees and generates no hunks; the changed-file tree,
the counts and the landing summary read that. The Files tab then asks for the
one file it shows with DiffPaths, and the commit page keeps its per-file
ShowPatchPath. Nothing is capped by bytes: a page is as large as the file it
shows, and the inventory is the whole change however large it is.

Compare still renders every file of a range at once, and is uncapped too, so a
large comparison is one large page. Making that per-file is the next step if it
turns out to matter.

TestDiffInventoryCountsEveryFile covers the statuses, counts, renames and binary
files the inventory has to report; TestRequestFilesListsEveryChangedFile is the
regression for the tree that lost its files, and the commit page test now
asserts the end of a large patch instead of its cut.

server/web/commit.go +15−19

@@ -12,11 +12,6 @@ import (
12 12 "github.com/rodent-software/vole/core/git"
13 13 )
14 14
15 // diffCap bounds what one file's view renders. A patch is parsed in memory and
16 // shown in full; a generated 50 MiB diff is not a review, so it is cut and
17 // labelled as cut.
18 const diffCap = 512 << 10
19
20 15 // commitView is the commit page: the metadata, the changed-file tree, and the
21 16 // pane the selected file's diff renders in.
22 17 type commitView struct {
@@ -34,11 +29,10 @@ type commitView struct {
34 29 // commitMainView is the diff pane, both as the page's main column and as the
35 30 // HTMX fragment a tree click swaps in.
36 31 type commitMainView struct {
37 Path string
38 Files []diffFile
39 Added int
40 Removed int
41 Truncated bool
32 Path string
33 Files []diffFile
34 Added int
35 Removed int
42 36 // A commit page is read-only, so a diff line has nowhere to attach a review
43 37 // comment; the diff component reads this and draws no comment control.
44 38 LineCommentURL string
@@ -122,11 +116,10 @@ func commitDiff(g *git.Repo, sha, selected string, files []diffFile, chosen int)
122 116 return main, nil
123 117 }
124 118 f := files[chosen]
125 patch, truncated, err := g.ShowPatchPath(sha, f.pathspec(), diffCap)
119 patch, err := g.ShowPatchPath(sha, f.pathspec())
126 120 if err != nil {
127 121 return main, err
128 122 }
129 main.Truncated = truncated
130 123 for _, p := range parseDiff(patch) {
131 124 if diffFilePath(p) == selected {
132 125 main.Files = []diffFile{p}
@@ -134,9 +127,9 @@ func commitDiff(g *git.Repo, sha, selected string, files []diffFile, chosen int)
134 127 }
135 128 }
136 129 if main.Files == nil {
137 // A patch git printed nothing for (a mode change, or a cut that stopped
138 // before this file) still belongs in the pane: the tree lists it, so the
139 // pane can never pretend it is not there.
130 // A patch git printed nothing for (a mode change, for instance) still
131 // belongs in the pane: the tree lists it, so the pane can never pretend
132 // it is not there.
140 133 f.Language = prismLanguage(f.Label())
141 134 main.Files = []diffFile{f}
142 135 }
@@ -144,13 +137,16 @@ func commitDiff(g *git.Repo, sha, selected string, files []diffFile, chosen int)
144 137 return main, nil
145 138 }
146 139
147 // changedFiles turns a commit's name-status lines into the rows the tree and the
148 // pane need: the same shape a parsed diff produces, so the status mark and the
149 // file's label are derived the same way whichever read built them.
140 // changedFiles turns a name-status list, or a change's inventory, into the rows a
141 // changed-file tree and its pane need: the same shape a parsed diff produces, so
142 // the status mark, the label and the line counts are derived the same way
143 // whichever read built them.
150 144 func changedFiles(changes []git.PathChange) []diffFile {
151 145 files := make([]diffFile, 0, len(changes))
152 146 for _, c := range changes {
153 files = append(files, diffFile{OldPath: c.Old, NewPath: c.New})
147 f := diffFile{OldPath: c.Old, NewPath: c.New, Added: c.Added, Removed: c.Removed, Binary: c.Binary}
148 f.Language = prismLanguage(f.Label())
149 files = append(files, f)
154 150 }
155 151 return files
156 152 }