vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
Read one request, or one commit, instead of resolving twice
c9202f7f0ba5904af798e7f55bb91079aa774bf5 · Keenan Nemetz · 10d ago · parent 73ec800b
Read one request, or one commit, instead of resolving twice The index and the change page both paid for reads they did not need: - GetRequest read every request on the branch to return one. It now reads that request's files. - The index resolves the metadata commit once and then handed the ref to loaders that resolved it again. LoadIssuesAt, LoadNumbersAt and LoadRequestAt take the commit they were given. - Release artifacts get a batched loader: one tree listing and one cat-file for the whole set, instead of one process per file per tag. These are the loaders the index needs to derive its rows from one commit at a time.
metadata/releases.go +46
| @@ -157,3 +157,49 @@ func WriteReleasePointers(g *git.Repo, ref, tag string, arts []Artifact, message | |||
| 157 | 157 | }, message, author) | |
| 158 | 158 | return err | |
| 159 | 159 | } | |
| 160 | |||
| 161 | // LoadArtifactsAt reads every release artifact on the metadata branch at commit, | ||
| 162 | // keyed by tag. It is one tree listing and one blob read for the whole set: a | ||
| 163 | // release with fifty files is not fifty processes, and the index reads the set to | ||
| 164 | // answer the artifact list without touching git. | ||
| 165 | func LoadArtifactsAt(g *git.Repo, commit string) (map[string][]Artifact, error) { | ||
| 166 | out := map[string][]Artifact{} | ||
| 167 | if commit == "" { | ||
| 168 | return out, nil | ||
| 169 | } | ||
| 170 | entries, err := g.LsTreeR(commit) | ||
| 171 | if err != nil { | ||
| 172 | return nil, err | ||
| 173 | } | ||
| 174 | var paths, shas []string | ||
| 175 | for _, e := range entries { | ||
| 176 | if e.Type != "blob" || !strings.HasPrefix(e.Name, ReleasesDir+"/") { | ||
| 177 | continue | ||
| 178 | } | ||
| 179 | // releases/<tag>/<name>; anything deeper is not an artifact. | ||
| 180 | if parts := strings.Split(e.Name, "/"); len(parts) != 3 { | ||
| 181 | continue | ||
| 182 | } | ||
| 183 | paths = append(paths, e.Name) | ||
| 184 | shas = append(shas, e.SHA) | ||
| 185 | } | ||
| 186 | files, err := readBlobs(g, paths, shas) | ||
| 187 | if err != nil { | ||
| 188 | return nil, err | ||
| 189 | } | ||
| 190 | for _, path := range paths { | ||
| 191 | parts := strings.Split(path, "/") | ||
| 192 | p, ok := lfs.ParsePointer([]byte(files[path])) | ||
| 193 | if !ok { | ||
| 194 | // A file that is not a pointer is not an artifact; the release | ||
| 195 | // validator refuses those at push time, and a reader skips one a | ||
| 196 | // human put there by hand. | ||
| 197 | continue | ||
| 198 | } | ||
| 199 | out[parts[1]] = append(out[parts[1]], Artifact{Name: parts[2], OID: p.OID, Size: p.Size}) | ||
| 200 | } | ||
| 201 | for _, list := range out { | ||
| 202 | sort.Slice(list, func(i, j int) bool { return list[i].Name < list[j].Name }) | ||
| 203 | } | ||
| 204 | return out, nil | ||
| 205 | } | ||