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.

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)