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.
Apply a push's index delta as the push lands, and check the ref by reading it
dced06da1bfc4a8e542e5d86d67888362077ed39 · Keenan Nemetz · 10d ago · parent 26c9be33
Apply a push's index delta as the push lands, and check the ref by reading it A push's metadata delta used to be applied by the next reader, so the first page after a big push paid for the whole push. And search checked every visible repository before querying: a `symbolic-ref` to open it and a `rev-parse` to see whether the metadata branch moved, so N repositories cost 2N git processes per search. - Post-receive now opens the index (the SSH server hands it `VOLE_INDEX_DB` and the repository id) and runs Ensure after the refs have moved, which is also after a followed request's head has moved. A failure is reported and ignored: the refs have landed, and the next read still repairs it. - `git.Repo.RefCommit` resolves a branch ref by reading the loose ref file git writes for every branch update, and falls back to `rev-parse` for a packed ref, a symbolic ref, or a missing one. The index's Ensure and the web's metadata-head read use it. - `repo.Store.Meta` builds a repository handle without a HEAD lookup, and search uses it: the refresh needs the directory and the ACL, not the default branch. The per-repository check is now two file reads instead of two processes. Load harness, `search-find` at concurrency 4 over three repositories: 510.2 req/s and p50 7 ms before, 814.8 req/s and p50 4 ms after. A push that changes the `vole` branch leaves the rows current before the push returns; a test queries them with no Ensure in between.
forge/repo/repo.go +16−6
| @@ -142,6 +142,21 @@ func (s *Store) Public(name string) bool { | |||
| 142 | 142 | ||
| 143 | 143 | // Open loads a repo by name, returning an error if it is not a Vole repo. | |
| 144 | 144 | func (s *Store) Open(name string) (*Meta, error) { | |
| 145 | m, err := s.Meta(name) | ||
| 146 | if err != nil { | ||
| 147 | return nil, err | ||
| 148 | } | ||
| 149 | if br := git.Open(m.Dir).Symref(); br != "" { | ||
| 150 | m.DefaultBranch = br | ||
| 151 | } | ||
| 152 | return m, nil | ||
| 153 | } | ||
| 154 | |||
| 155 | // Meta loads a repo's instance-side facts without running git. The id, name, | ||
| 156 | // directory, description and creation time are files and a stat; the default | ||
| 157 | // branch is what Open adds, and that costs a git call. A caller that only needs | ||
| 158 | // the ACL or the git directory should use this. | ||
| 159 | func (s *Store) Meta(name string) (*Meta, error) { | ||
| 145 | 160 | if err := ValidateName(name); err != nil { | |
| 146 | 161 | return nil, err | |
| 147 | 162 | } | |
| @@ -155,15 +170,10 @@ func (s *Store) Open(name string) (*Meta, error) { | |||
| 155 | 170 | return nil, fmt.Errorf("repo %q is not a Vole repository (no %s)", name, idFile) | |
| 156 | 171 | } | |
| 157 | 172 | m := &Meta{ID: strings.TrimSpace(string(idb)), Name: name, Dir: dir, DefaultBranch: "main", ACLs: s.ACLs} | |
| 158 | if st, err := os.Stat(dir); err == nil { | ||
| 159 | m.Created = st.ModTime().UTC() | ||
| 160 | } | ||
| 173 | m.Created = st.ModTime().UTC() | ||
| 161 | 174 | if b, err := os.ReadFile(filepath.Join(dir, descFile)); err == nil { | |
| 162 | 175 | m.Description = strings.TrimSpace(string(b)) | |
| 163 | 176 | } | |
| 164 | if br := git.Open(dir).Symref(); br != "" { | ||
| 165 | m.DefaultBranch = br | ||
| 166 | } | ||
| 167 | 177 | return m, nil | |
| 168 | 178 | } | |
| 169 | 179 | ||