shithub: furgit

ref: 474b047cd065bb2cc45153636123ea0812507ef2
dir: /refstore/packed/store.go/

View raw version
// 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
}