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.
List branches and tags in one for-each-ref
34cbc99f43af6f8f49d76f9221cb7afebe40db0d · Keenan Nemetz · 10d ago · parent 5cecc618
List branches and tags in one for-each-ref The code browser and the compare page asked git for branch names and tag names separately. They are the same ref listing under two prefixes, so one `for-each-ref` answers both. The listing carries each ref's creation time, which is what lets the tag picker stay newest-first after the lists are merged: branches are sorted by name, tags by time with the name breaking a tie. The code page drops from 45 git invocations to 44 on a repository with a handful of refs, and one process per page on every repository.
server/web/codebrowser.go +26−19
| @@ -120,30 +120,37 @@ func (w *Web) codeShell(r *http.Request, m *repo.Meta, ref, commit, active strin | |||
| 120 | 120 | v.Count, _ = g.CountCommits(commit) | |
| 121 | 121 | v.Tree, v.TreeTruncated = buildFileTree(g, commit, active) | |
| 122 | 122 | } | |
| 123 | branches, _ := g.ListRefs("refs/heads/") | ||
| 124 | names := make([]string, 0, len(branches)) | ||
| 125 | for k := range branches { | ||
| 126 | names = append(names, strings.TrimPrefix(k, "refs/heads/")) | ||
| 127 | } | ||
| 128 | sort.Strings(names) | ||
| 129 | v.Branches = names | ||
| 130 | for _, name := range names { | ||
| 131 | if name == ref { | ||
| 132 | v.OnBranch = true | ||
| 133 | break | ||
| 134 | } | ||
| 135 | } | ||
| 136 | 123 | // Tags share the ref picker's target: a tag names a commit, so browsing a | |
| 137 | // tag is the same page as browsing a branch. | ||
| 138 | if tags, err := g.Tags(); err == nil { | ||
| 139 | v.Tags = make([]string, 0, len(tags)) | ||
| 140 | for _, tag := range tags { | ||
| 141 | v.Tags = append(v.Tags, tag.Name) | ||
| 142 | if tag.Name == ref { | ||
| 124 | // tag is the same page as browsing a branch. One listing answers both pickers; | ||
| 125 | // branches stay alphabetical and tags stay newest first. | ||
| 126 | refs, _ := g.Refs("refs/heads/", "refs/tags/") | ||
| 127 | var tags []git.RefEntry | ||
| 128 | for _, rf := range refs { | ||
| 129 | switch { | ||
| 130 | case strings.HasPrefix(rf.Name, "refs/heads/"): | ||
| 131 | name := strings.TrimPrefix(rf.Name, "refs/heads/") | ||
| 132 | v.Branches = append(v.Branches, name) | ||
| 133 | if name == ref { | ||
| 134 | v.OnBranch = true | ||
| 135 | } | ||
| 136 | case strings.HasPrefix(rf.Name, "refs/tags/"): | ||
| 137 | tags = append(tags, rf) | ||
| 138 | if strings.TrimPrefix(rf.Name, "refs/tags/") == ref { | ||
| 143 | 139 | v.OnTag = true | |
| 144 | 140 | } | |
| 145 | 141 | } | |
| 146 | 142 | } | |
| 143 | sort.Strings(v.Branches) | ||
| 144 | sort.SliceStable(tags, func(i, j int) bool { | ||
| 145 | if tags[i].When != tags[j].When { | ||
| 146 | return tags[i].When > tags[j].When | ||
| 147 | } | ||
| 148 | return tags[i].Name < tags[j].Name | ||
| 149 | }) | ||
| 150 | v.Tags = make([]string, 0, len(tags)) | ||
| 151 | for _, tag := range tags { | ||
| 152 | v.Tags = append(v.Tags, strings.TrimPrefix(tag.Name, "refs/tags/")) | ||
| 153 | } | ||
| 147 | 154 | return v | |
| 148 | 155 | } | |
| 149 | 156 | ||