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.
core/git/git.go +37
| @@ -488,6 +488,43 @@ func (r *Repo) ListRefs(prefix string) (map[string]string, error) { | |||
| 488 | 488 | return m, nil | |
| 489 | 489 | } | |
| 490 | 490 | ||
| 491 | // RefEntry is one ref and the creation time git reports for it. A branch's time | ||
| 492 | // is the committer date of its tip, a tag's is its tagger or commit date, which | ||
| 493 | // is what makes one listing enough to sort both. | ||
| 494 | type RefEntry struct { | ||
| 495 | Name string | ||
| 496 | When int64 | ||
| 497 | } | ||
| 498 | |||
| 499 | // Refs lists the refs under every given prefix in one call. The ref picker shows | ||
| 500 | // branches and tags together, and asking for them separately is a second process | ||
| 501 | // per page for no reason. Like ListRefs, the listing stops at MaxRefsListed; the | ||
| 502 | // cap is shared by the prefixes, which is the same page-render bound the callers | ||
| 503 | // applied one list at a time. | ||
| 504 | func (r *Repo) Refs(prefixes ...string) ([]RefEntry, error) { | ||
| 505 | if len(prefixes) == 0 { | ||
| 506 | return nil, nil | ||
| 507 | } | ||
| 508 | args := make([]string, 0, len(prefixes)+3) | ||
| 509 | args = append(args, "for-each-ref", "--count="+strconv.Itoa(MaxRefsListed), | ||
| 510 | "--format=%(refname)%1f%(creatordate:unix)") | ||
| 511 | args = append(args, prefixes...) | ||
| 512 | out, err := r.Run(nil, args...) | ||
| 513 | if err != nil { | ||
| 514 | return nil, err | ||
| 515 | } | ||
| 516 | var refs []RefEntry | ||
| 517 | for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") { | ||
| 518 | name, when, ok := strings.Cut(line, "\x1f") | ||
| 519 | if !ok || name == "" { | ||
| 520 | continue | ||
| 521 | } | ||
| 522 | n, _ := strconv.ParseInt(when, 10, 64) | ||
| 523 | refs = append(refs, RefEntry{Name: name, When: n}) | ||
| 524 | } | ||
| 525 | return refs, nil | ||
| 526 | } | ||
| 527 | |||
| 491 | 528 | // BlobAt returns the contents of a blob at commit:path. | |
| 492 | 529 | func (r *Repo) BlobAt(commit, path string) ([]byte, error) { | |
| 493 | 530 | out, errs, err := r.run(nil, "cat-file", "blob", commit+":"+path) | |