diff options
| author | 2026-03-04 08:26:56 +0800 | |
|---|---|---|
| committer | 2026-03-04 08:59:53 +0800 | |
| commit | ab7501be34032fb9e5c48726a68ae90a917af9eb (patch) | |
| tree | 20d005647569befea8133e953c3270e8fd2a2a5b /refstore/loose | |
| parent | *: gofumpt (diff) | |
| signature | No signature | |
*: Lint
Diffstat (limited to 'refstore/loose')
| -rw-r--r-- | refstore/loose/list.go | 26 | ||||
| -rw-r--r-- | refstore/loose/loose_test.go | 43 | ||||
| -rw-r--r-- | refstore/loose/resolve.go | 11 | ||||
| -rw-r--r-- | refstore/loose/shorten.go | 6 | ||||
| -rw-r--r-- | refstore/loose/store.go | 1 |
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, |
