aboutsummaryrefslogtreecommitdiff
path: root/refstore/loose
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-03-04 08:26:56 +0800
committerGravatar Runxi Yu2026-03-04 08:59:53 +0800
commitab7501be34032fb9e5c48726a68ae90a917af9eb (patch)
tree20d005647569befea8133e953c3270e8fd2a2a5b /refstore/loose
parent*: gofumpt (diff)
signatureNo signature
*: Lint
Diffstat (limited to 'refstore/loose')
-rw-r--r--refstore/loose/list.go26
-rw-r--r--refstore/loose/loose_test.go43
-rw-r--r--refstore/loose/resolve.go11
-rw-r--r--refstore/loose/shorten.go6
-rw-r--r--refstore/loose/store.go1
5 files changed, 77 insertions, 10 deletions
diff --git a/refstore/loose/list.go b/refstore/loose/list.go
index d28016da..1fa0adee 100644
--- a/refstore/loose/list.go
+++ b/refstore/loose/list.go
@@ -17,7 +17,8 @@ import (
func (store *Store) List(pattern string) ([]ref.Ref, error) {
matchAll := pattern == ""
if !matchAll {
- if _, err := path.Match(pattern, "HEAD"); err != nil {
+ _, err := path.Match(pattern, "HEAD")
+ if err != nil {
return nil, err
}
}
@@ -26,6 +27,7 @@ func (store *Store) List(pattern string) ([]ref.Ref, error) {
if err != nil {
return nil, err
}
+
slices.Sort(names)
refs := make([]ref.Ref, 0, len(names))
@@ -35,19 +37,24 @@ func (store *Store) List(pattern string) ([]ref.Ref, error) {
if err != nil {
return nil, err
}
+
if !matched {
continue
}
}
+
resolved, err := store.resolveOne(name)
if err != nil {
if errors.Is(err, refstore.ErrReferenceNotFound) {
continue
}
+
return nil, err
}
+
refs = append(refs, resolved)
}
+
return refs, nil
}
@@ -55,42 +62,53 @@ func (store *Store) List(pattern string) ([]ref.Ref, error) {
func (store *Store) collectLooseRefNames() ([]string, error) {
names := make([]string, 0, 16)
- if _, err := store.root.Stat("HEAD"); err == nil {
+ _, err := store.root.Stat("HEAD")
+ if err == nil {
names = append(names, "HEAD")
} else if !errors.Is(err, os.ErrNotExist) {
return nil, err
}
var walk func(string) error
+
walk = func(dir string) error {
file, err := store.root.Open(dir)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return nil
}
+
return err
}
+
defer func() { _ = file.Close() }()
entries, err := file.ReadDir(-1)
if err != nil {
return err
}
+
for _, entry := range entries {
name := path.Join(dir, entry.Name())
if entry.IsDir() {
- if err := walk(name); err != nil {
+ err := walk(name)
+ if err != nil {
return err
}
+
continue
}
+
names = append(names, name)
}
+
return nil
}
- if err := walk("refs"); err != nil {
+ err = walk("refs")
+ if err != nil {
return nil, err
}
+
return names, nil
}
diff --git a/refstore/loose/loose_test.go b/refstore/loose/loose_test.go
index 8c9d6f98..7b295bbb 100644
--- a/refstore/loose/loose_test.go
+++ b/refstore/loose/loose_test.go
@@ -16,16 +16,19 @@ import (
func openLooseStore(t *testing.T, repoPath string, algo objectid.Algorithm) *loose.Store {
t.Helper()
+
root, err := os.OpenRoot(repoPath)
if err != nil {
t.Fatalf("OpenRoot(%q): %v", repoPath, err)
}
+
t.Cleanup(func() { _ = root.Close() })
store, err := loose.New(root, algo)
if err != nil {
t.Fatalf("loose.New: %v", err)
}
+
return store
}
@@ -43,10 +46,12 @@ func TestLooseResolveAndResolveFully(t *testing.T) {
if err != nil {
t.Fatalf("Resolve(HEAD): %v", err)
}
+
headSym, ok := resolvedHead.(ref.Symbolic)
if !ok {
t.Fatalf("Resolve(HEAD) type = %T, want ref.Symbolic", resolvedHead)
}
+
if headSym.Target != "refs/heads/main" {
t.Fatalf("Resolve(HEAD) target = %q, want %q", headSym.Target, "refs/heads/main")
}
@@ -55,10 +60,12 @@ func TestLooseResolveAndResolveFully(t *testing.T) {
if err != nil {
t.Fatalf("Resolve(refs/heads/main): %v", err)
}
+
mainDet, ok := resolvedMain.(ref.Detached)
if !ok {
t.Fatalf("Resolve(main) type = %T, want ref.Detached", resolvedMain)
}
+
if mainDet.ID != commitID {
t.Fatalf("Resolve(main) id = %s, want %s", mainDet.ID, commitID)
}
@@ -67,11 +74,13 @@ func TestLooseResolveAndResolveFully(t *testing.T) {
if err != nil {
t.Fatalf("ResolveFully(HEAD): %v", err)
}
+
if fullHead.ID != commitID {
t.Fatalf("ResolveFully(HEAD) id = %s, want %s", fullHead.ID, commitID)
}
- if _, err := store.Resolve("refs/heads/does-not-exist"); !errors.Is(err, refstore.ErrReferenceNotFound) {
+ _, err = store.Resolve("refs/heads/does-not-exist")
+ if !errors.Is(err, refstore.ErrReferenceNotFound) {
t.Fatalf("Resolve(not-found) error = %v", err)
}
})
@@ -85,7 +94,9 @@ func TestLooseResolveFullyCycle(t *testing.T) {
testRepo.SymbolicRef(t, "refs/heads/b", "refs/heads/a")
store := openLooseStore(t, testRepo.Dir(), algo)
- if _, err := store.ResolveFully("refs/heads/a"); err == nil {
+
+ _, err := store.ResolveFully("refs/heads/a")
+ if err == nil {
t.Fatalf("ResolveFully(cycle) expected error")
}
})
@@ -107,11 +118,14 @@ func TestLooseListPattern(t *testing.T) {
if err != nil {
t.Fatalf("List(\"\"): %v", err)
}
+
allNames := make([]string, 0, len(allRefs))
for _, entry := range allRefs {
allNames = append(allNames, entry.Name())
}
+
slices.Sort(allNames)
+
wantAll := []string{"HEAD", "refs/heads/feature", "refs/heads/main", "refs/tags/v1.0.0"}
if !slices.Equal(allNames, wantAll) {
t.Fatalf("List(\"\") names = %v, want %v", allNames, wantAll)
@@ -121,11 +135,14 @@ func TestLooseListPattern(t *testing.T) {
if err != nil {
t.Fatalf("List(refs/heads/*): %v", err)
}
+
headNames := make([]string, 0, len(headRefs))
for _, entry := range headRefs {
headNames = append(headNames, entry.Name())
}
+
slices.Sort(headNames)
+
wantHeads := []string{"refs/heads/feature", "refs/heads/main"}
if !slices.Equal(headNames, wantHeads) {
t.Fatalf("List(refs/heads/*) names = %v, want %v", headNames, wantHeads)
@@ -182,13 +199,17 @@ func TestLooseListPatternMatrix(t *testing.T) {
if err != nil {
t.Fatalf("List(%q): %v", tt.pattern, err)
}
+
gotNames := make([]string, 0, len(got))
for _, entry := range got {
gotNames = append(gotNames, entry.Name())
}
+
slices.Sort(gotNames)
+
wantNames := append([]string(nil), tt.want...)
slices.Sort(wantNames)
+
if !slices.Equal(gotNames, wantNames) {
t.Fatalf("List(%q) names = %v, want %v", tt.pattern, gotNames, wantNames)
}
@@ -201,16 +222,23 @@ func TestLooseMalformedDetachedRef(t *testing.T) {
t.Parallel()
testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo, Bare: true})
+
refPath := filepath.Join(testRepo.Dir(), "refs", "heads", "bad")
- if err := os.MkdirAll(filepath.Dir(refPath), 0o755); err != nil {
+
+ err := os.MkdirAll(filepath.Dir(refPath), 0o755)
+ if err != nil {
t.Fatalf("MkdirAll: %v", err)
}
- if err := os.WriteFile(refPath, []byte("not-a-hash\n"), 0o644); err != nil {
+
+ err = os.WriteFile(refPath, []byte("not-a-hash\n"), 0o644)
+ if err != nil {
t.Fatalf("WriteFile: %v", err)
}
store := openLooseStore(t, testRepo.Dir(), algo)
- if _, err := store.Resolve("refs/heads/bad"); err == nil {
+
+ _, err = store.Resolve("refs/heads/bad")
+ if err == nil {
t.Fatalf("Resolve(malformed) expected error")
}
})
@@ -231,6 +259,7 @@ func TestLooseShorten(t *testing.T) {
if err != nil {
t.Fatalf("Shorten(head): %v", err)
}
+
if shortHead != "heads/main" {
t.Fatalf("Shorten(refs/heads/main) = %q, want %q", shortHead, "heads/main")
}
@@ -239,11 +268,13 @@ func TestLooseShorten(t *testing.T) {
if err != nil {
t.Fatalf("Shorten(remote): %v", err)
}
+
if shortRemote != "origin/main" {
t.Fatalf("Shorten(remote) = %q, want %q", shortRemote, "origin/main")
}
- if _, err := store.Shorten("refs/heads/does-not-exist"); !errors.Is(err, refstore.ErrReferenceNotFound) {
+ _, err = store.Shorten("refs/heads/does-not-exist")
+ if !errors.Is(err, refstore.ErrReferenceNotFound) {
t.Fatalf("Shorten(not-found) error = %v", err)
}
})
diff --git a/refstore/loose/resolve.go b/refstore/loose/resolve.go
index f54ab5a4..076c4098 100644
--- a/refstore/loose/resolve.go
+++ b/refstore/loose/resolve.go
@@ -16,10 +16,12 @@ func (store *Store) Resolve(name string) (ref.Ref, error) {
if name == "" {
return nil, refstore.ErrReferenceNotFound
}
+
resolved, err := store.resolveOne(name)
if err != nil {
return nil, err
}
+
return resolved, nil
}
@@ -30,17 +32,20 @@ func (store *Store) ResolveFully(name string) (ref.Detached, error) {
}
cur := name
+
seen := make(map[string]struct{})
for {
if _, ok := seen[cur]; ok {
return ref.Detached{}, fmt.Errorf("refstore/loose: symbolic reference cycle at %q", cur)
}
+
seen[cur] = struct{}{}
resolved, err := store.resolveOne(cur)
if err != nil {
return ref.Detached{}, err
}
+
switch resolved := resolved.(type) {
case ref.Detached:
return resolved, nil
@@ -49,6 +54,7 @@ func (store *Store) ResolveFully(name string) (ref.Detached, error) {
if target == "" {
return ref.Detached{}, fmt.Errorf("refstore/loose: symbolic reference %q has empty target", resolved.Name())
}
+
cur = target
default:
return ref.Detached{}, fmt.Errorf("refstore/loose: unsupported reference type %T", resolved)
@@ -63,23 +69,28 @@ func (store *Store) resolveOne(name string) (ref.Ref, error) {
if errors.Is(err, os.ErrNotExist) {
return nil, refstore.ErrReferenceNotFound
}
+
return nil, err
}
+
line := strings.TrimSpace(string(data))
if strings.HasPrefix(line, "ref: ") {
target := strings.TrimSpace(line[len("ref: "):])
if target == "" {
return nil, fmt.Errorf("refstore/loose: symbolic reference %q has empty target", name)
}
+
return ref.Symbolic{
RefName: name,
Target: target,
}, nil
}
+
id, err := objectid.ParseHex(store.algo, line)
if err != nil {
return nil, fmt.Errorf("refstore/loose: invalid detached reference %q: %w", name, err)
}
+
return ref.Detached{
RefName: name,
ID: id,
diff --git a/refstore/loose/shorten.go b/refstore/loose/shorten.go
index 17a60def..e863d783 100644
--- a/refstore/loose/shorten.go
+++ b/refstore/loose/shorten.go
@@ -10,20 +10,26 @@ func (store *Store) Shorten(name string) (string, error) {
if err != nil {
return "", err
}
+
names := make([]string, 0, len(refs))
found := false
+
for _, entry := range refs {
if entry == nil {
continue
}
+
full := entry.Name()
+
names = append(names, full)
if full == name {
found = true
}
}
+
if !found {
return "", refstore.ErrReferenceNotFound
}
+
return refstore.ShortenName(name, names), nil
}
diff --git a/refstore/loose/store.go b/refstore/loose/store.go
index e4dc3a34..ec814188 100644
--- a/refstore/loose/store.go
+++ b/refstore/loose/store.go
@@ -25,6 +25,7 @@ func New(root *os.Root, algo objectid.Algorithm) (*Store, error) {
if algo.Size() == 0 {
return nil, objectid.ErrInvalidAlgorithm
}
+
return &Store{
root: root,
algo: algo,