···11+package main
22+33+import (
44+ "fmt"
55+ "os"
66+77+ "github.com/go-git/go-git/v5"
88+ "github.com/go-git/go-git/v5/plumbing/serverinfo"
99+ "github.com/go-git/go-git/v5/storage/filesystem"
1010+)
1111+1212+// CmdUpdateServerInfo command updates the server info files in the repository.
1313+// This is used by git http transport (dumb) to generate a list of available
1414+// refs for the repository. See:
1515+// https://git-scm.com/docs/git-update-server-info
1616+type CmdUpdateServerInfo struct {
1717+ cmd
1818+}
1919+2020+// Usage returns the usage of the command.
2121+func (CmdUpdateServerInfo) Usage() string {
2222+ return fmt.Sprintf("within a git repository run: %s", os.Args[0])
2323+}
2424+2525+// Execute runs the command.
2626+func (c *CmdUpdateServerInfo) Execute(args []string) error {
2727+ r, err := git.PlainOpen(".")
2828+ if err != nil {
2929+ return err
3030+ }
3131+3232+ fs := r.Storer.(*filesystem.Storage).Filesystem()
3333+ return serverinfo.UpdateServerInfo(r.Storer, fs)
3434+}
+14
internal/reference/sort.go
···11+package reference
22+33+import (
44+ "sort"
55+66+ "github.com/go-git/go-git/v5/plumbing"
77+)
88+99+// Sort sorts the references by name to ensure a consistent order.
1010+func Sort(refs []*plumbing.Reference) {
1111+ sort.Slice(refs, func(i, j int) bool {
1212+ return refs[i].Name() < refs[j].Name()
1313+ })
1414+}
+94
plumbing/serverinfo/serverinfo.go
···11+package serverinfo
22+33+import (
44+ "fmt"
55+66+ "github.com/go-git/go-billy/v5"
77+ "github.com/go-git/go-git/v5"
88+ "github.com/go-git/go-git/v5/internal/reference"
99+ "github.com/go-git/go-git/v5/plumbing"
1010+ "github.com/go-git/go-git/v5/plumbing/object"
1111+ "github.com/go-git/go-git/v5/plumbing/storer"
1212+ "github.com/go-git/go-git/v5/storage"
1313+)
1414+1515+// UpdateServerInfo updates the server info files in the repository.
1616+//
1717+// It generates a list of available refs for the repository.
1818+// Used by git http transport (dumb), for more information refer to:
1919+// https://git-scm.com/book/id/v2/Git-Internals-Transfer-Protocols#_the_dumb_protocol
2020+func UpdateServerInfo(s storage.Storer, fs billy.Filesystem) error {
2121+ pos, ok := s.(storer.PackedObjectStorer)
2222+ if !ok {
2323+ return git.ErrPackedObjectsNotSupported
2424+ }
2525+2626+ infoRefs, err := fs.Create("info/refs")
2727+ if err != nil {
2828+ return err
2929+ }
3030+3131+ defer infoRefs.Close()
3232+3333+ refsIter, err := s.IterReferences()
3434+ if err != nil {
3535+ return err
3636+ }
3737+3838+ defer refsIter.Close()
3939+4040+ var refs []*plumbing.Reference
4141+ if err := refsIter.ForEach(func(ref *plumbing.Reference) error {
4242+ refs = append(refs, ref)
4343+ return nil
4444+ }); err != nil {
4545+ return err
4646+ }
4747+4848+ reference.Sort(refs)
4949+ for _, ref := range refs {
5050+ name := ref.Name()
5151+ hash := ref.Hash()
5252+ switch ref.Type() {
5353+ case plumbing.SymbolicReference:
5454+ if name == plumbing.HEAD {
5555+ continue
5656+ }
5757+ ref, err := s.Reference(ref.Target())
5858+ if err != nil {
5959+ return err
6060+ }
6161+6262+ hash = ref.Hash()
6363+ fallthrough
6464+ case plumbing.HashReference:
6565+ fmt.Fprintf(infoRefs, "%s\t%s\n", hash, name)
6666+ if name.IsTag() {
6767+ tag, err := object.GetTag(s, hash)
6868+ if err == nil {
6969+ fmt.Fprintf(infoRefs, "%s\t%s^{}\n", tag.Target, name)
7070+ }
7171+ }
7272+ }
7373+ }
7474+7575+ infoPacks, err := fs.Create("objects/info/packs")
7676+ if err != nil {
7777+ return err
7878+ }
7979+8080+ defer infoPacks.Close()
8181+8282+ packs, err := pos.ObjectPacks()
8383+ if err != nil {
8484+ return err
8585+ }
8686+8787+ for _, p := range packs {
8888+ fmt.Fprintf(infoPacks, "P pack-%s.pack\n", p)
8989+ }
9090+9191+ fmt.Fprintln(infoPacks)
9292+9393+ return nil
9494+}