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/commit_test.go +17−12

@@ -127,20 +127,22 @@ func TestCommitPageOpensOneFileAtATime(t *testing.T) {
127 127 }
128 128 }
129 129
130 // TestCommitPageCutsOneFilesPatch: the cap is what stops a generated diff from
131 // being rendered in full, and it applies to the file that is open rather than to
132 // the whole commit.
133 func TestCommitPageCutsOneFilesPatch(t *testing.T) {
130 // TestCommitPageShowsTheWholeOfOneFilesPatch: the page renders one file's patch,
131 // not the whole commit's, so the file that is open is shown whole. A byte cut
132 // would hide the end of a large file and, worse, every file named after it.
133 func TestCommitPageShowsTheWholeOfOneFilesPatch(t *testing.T) {
134 134 t.Parallel()
135 135 f := newFixture(t)
136 136 f.publish(f.repo)
137 137
138 // One line longer than the 512 KiB cap, so the file that is open is cut.
138 // The file is larger than the 512 KiB that used to bound one file's patch,
139 // and it ends with a marker the cut would have dropped.
140 big := strings.Repeat("x", 513<<10) + "END-OF-BIG-FILE"
139 141 work := workRepo(t, map[string]string{"small.txt": "one\n"})
140 142 if _, errs, err := f.git(work, "push", f.sshURL(f.repo), "main:refs/heads/main"); err != nil {
141 143 t.Fatalf("initial push: %s", errs)
142 144 }
143 if err := os.WriteFile(filepath.Join(work, "big.txt"), []byte(strings.Repeat("x", 513<<10)+"\n"), 0o644); err != nil {
145 if err := os.WriteFile(filepath.Join(work, "big.txt"), []byte(big+"\n"), 0o644); err != nil {
144 146 t.Fatal(err)
145 147 }
146 148 if err := os.WriteFile(filepath.Join(work, "small.txt"), []byte("one\ntwo\n"), 0o644); err != nil {
@@ -159,15 +161,18 @@ func TestCommitPageCutsOneFilesPatch(t *testing.T) {
159 161 commitURL := "/r/" + f.repo + "/commit/" + sha
160 162
161 163 _, page := f.get(commitURL + "/big.txt")
162 if !strings.Contains(page, "showing the first 512 KiB") {
163 t.Fatalf("the cut file is not labelled as cut:\n%.2000s", page)
164 if !strings.Contains(page, "END-OF-BIG-FILE") {
165 t.Fatalf("the open file's patch is not whole:\n%.2000s", page)
164 166 }
165 // The small file is whole, and cutting the big one did not hide its row.
166 _, page = f.get(commitURL + "/small.txt")
167 167 if strings.Contains(page, "showing the first 512 KiB") {
168 t.Fatalf("the small file was reported as cut:\n%s", page)
168 t.Fatalf("the file was reported as cut:\n%.2000s", page)
169 }
170 // The small file is whole too, and the big one did not hide its row.
171 _, page = f.get(commitURL + "/small.txt")
172 if !strings.Contains(page, `data-highlight-side="new">two<`) {
173 t.Fatalf("the small file has no patch:\n%.2000s", page)
169 174 }
170 175 if !strings.Contains(page, commitURL+"/big.txt") {
171 t.Fatalf("the cut file is missing from the tree:\n%s", page)
176 t.Fatalf("the big file is missing from the tree:\n%.2000s", page)
172 177 }
173 178 }