aboutsummaryrefslogtreecommitdiff
path: root/refstore/packed/store.go
blob: 5ab9d6025202623cce1b39ac364d5a34a2c2213c (about) (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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Package packed provides a packed refs backend.
package packed

import (
	"fmt"
	"os"
	"path"

	"codeberg.org/lindenii/furgit/objectid"
	"codeberg.org/lindenii/furgit/ref"
	"codeberg.org/lindenii/furgit/refstore"
)

// Store reads references from a parsed packed-refs snapshot.
type Store struct {
	byName  map[string]ref.Detached
	ordered []ref.Detached
}

var _ refstore.Store = (*Store)(nil)

// New parses packed-refs from one repository root using the given object ID
// algorithm.
func New(root *os.Root, algo objectid.Algorithm) (*Store, error) {
	if algo.Size() == 0 {
		return nil, objectid.ErrInvalidAlgorithm
	}

	packedRefs, err := root.Open("packed-refs")
	if err != nil {
		return nil, fmt.Errorf("refstore/packed: open packed-refs: %w", err)
	}

	defer func() { _ = packedRefs.Close() }()

	byName, ordered, err := parsePackedRefs(packedRefs, algo)
	if err != nil {
		return nil, err
	}

	return &Store{
		byName:  byName,
		ordered: ordered,
	}, nil
}

// Resolve resolves a packed reference name to a detached ref.
func (store *Store) Resolve(name string) (ref.Ref, error) {
	detached, ok := store.byName[name]
	if !ok {
		return nil, refstore.ErrReferenceNotFound
	}

	return detached, nil
}

// ResolveFully resolves a packed reference name to a detached ref.
//
// Packed refs are detached-only, so ResolveFully is equivalent to Resolve.
func (store *Store) ResolveFully(name string) (ref.Detached, error) {
	detached, ok := store.byName[name]
	if !ok {
		return ref.Detached{}, refstore.ErrReferenceNotFound
	}

	return detached, nil
}

// List lists packed references matching pattern.
//
// Pattern uses path.Match syntax against full reference names.
// Empty pattern matches all references.
func (store *Store) List(pattern string) ([]ref.Ref, error) {
	matchAll := pattern == ""
	if !matchAll {
		_, err := path.Match(pattern, "refs/heads/main")
		if err != nil {
			return nil, err
		}
	}

	refs := make([]ref.Ref, 0, len(store.ordered))
	for _, entry := range store.ordered {
		if !matchAll {
			matched, err := path.Match(pattern, entry.Name())
			if err != nil {
				return nil, err
			}

			if !matched {
				continue
			}
		}

		refs = append(refs, entry)
	}

	return refs, nil
}

// Shorten returns the shortest unambiguous shorthand for a packed ref name.
func (store *Store) Shorten(name string) (string, error) {
	_, ok := store.byName[name]
	if !ok {
		return "", refstore.ErrReferenceNotFound
	}

	names := make([]string, 0, len(store.ordered))
	for _, entry := range store.ordered {
		names = append(names, entry.Name())
	}

	return refstore.ShortenName(name, names), nil
}

// Close releases resources associated with the backend.
func (store *Store) Close() error {
	return nil
}