aboutsummaryrefslogtreecommitdiffstats
path: root/plumbing/serverinfo/serverinfo.go
blob: d7ea7ef06020f1d7781ad84c1c1a7363f11a2a2b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package serverinfo

import (
	"fmt"

	"github.com/go-git/go-billy/v5"
	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/internal/reference"
	"github.com/go-git/go-git/v5/plumbing"
	"github.com/go-git/go-git/v5/plumbing/object"
	"github.com/go-git/go-git/v5/plumbing/storer"
	"github.com/go-git/go-git/v5/storage"
)

// UpdateServerInfo updates the server info files in the repository.
//
// It generates a list of available refs for the repository.
// Used by git http transport (dumb), for more information refer to:
// https://git-scm.com/book/id/v2/Git-Internals-Transfer-Protocols#_the_dumb_protocol
func UpdateServerInfo(s storage.Storer, fs billy.Filesystem) error {
	pos, ok := s.(storer.PackedObjectStorer)
	if !ok {
		return git.ErrPackedObjectsNotSupported
	}

	infoRefs, err := fs.Create("info/refs")
	if err != nil {
		return err
	}

	defer infoRefs.Close()

	refsIter, err := s.IterReferences()
	if err != nil {
		return err
	}

	defer refsIter.Close()

	var refs []*plumbing.Reference
	if err := refsIter.ForEach(func(ref *plumbing.Reference) error {
		refs = append(refs, ref)
		return nil
	}); err != nil {
		return err
	}

	reference.Sort(refs)
	for _, ref := range refs {
		name := ref.Name()
		hash := ref.Hash()
		switch ref.Type() {
		case plumbing.SymbolicReference:
			if name == plumbing.HEAD {
				continue
			}
			ref, err := s.Reference(ref.Target())
			if err != nil {
				return err
			}

			hash = ref.Hash()
			fallthrough
		case plumbing.HashReference:
			fmt.Fprintf(infoRefs, "%s\t%s\n", hash, name)
			if name.IsTag() {
				tag, err := object.GetTag(s, hash)
				if err == nil {
					fmt.Fprintf(infoRefs, "%s\t%s^{}\n", tag.Target, name)
				}
			}
		}
	}

	infoPacks, err := fs.Create("objects/info/packs")
	if err != nil {
		return err
	}

	defer infoPacks.Close()

	packs, err := pos.ObjectPacks()
	if err != nil {
		return err
	}

	for _, p := range packs {
		fmt.Fprintf(infoPacks, "P pack-%s.pack\n", p)
	}

	fmt.Fprintln(infoPacks)

	return nil
}