aboutsummaryrefslogtreecommitdiff
path: root/refs_test.go
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-02-20 19:06:13 +0800
committerGravatar Runxi Yu2026-02-20 19:07:14 +0800
commitaa513c069c1418734aea894dc944e27c6a78a3bb (patch)
tree687f0a11bb550fa088fd82a98ceb8979bbc35f69 /refs_test.go
parentComment on prior reverts removing the pack writing API (diff)
signatureNo signature
Delete everything, I'm redesigning this.
I'll stop using a flat package and make things much more modular. And also experiment with streaming APIs so large blobs don't OOM us.
Diffstat (limited to 'refs_test.go')
-rw-r--r--refs_test.go520
1 files changed, 0 insertions, 520 deletions
diff --git a/refs_test.go b/refs_test.go
deleted file mode 100644
index 2d4a1532..00000000
--- a/refs_test.go
+++ /dev/null
@@ -1,520 +0,0 @@
-package furgit
-
-import (
- "os"
- "path/filepath"
- "strings"
- "testing"
-)
-
-func TestResolveRef(t *testing.T) {
- repoPath, cleanup := setupTestRepo(t)
- defer cleanup()
-
- workDir, cleanupWork := setupWorkDir(t)
- defer cleanupWork()
-
- err := os.WriteFile(filepath.Join(workDir, "test.txt"), []byte("content"), 0o644)
- if err != nil {
- t.Fatalf("Failed to write test.txt: %v", err)
- }
- gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".")
- gitCmd(t, repoPath, "--work-tree="+workDir, "commit", "-m", "test")
- commitHash := gitCmd(t, repoPath, "rev-parse", "HEAD")
- gitCmd(t, repoPath, "update-ref", "refs/heads/main", commitHash)
-
- repo, err := OpenRepository(repoPath)
- if err != nil {
- t.Fatalf("OpenRepository failed: %v", err)
- }
- defer func() { _ = repo.Close() }()
-
- hashObj, _ := repo.ParseHash(commitHash)
- resolved, err := repo.ResolveRef("refs/heads/main")
- if err != nil {
- t.Fatalf("ResolveRef failed: %v", err)
- }
-
- if resolved.Kind != RefKindDetached {
- t.Fatalf("expected detached ref, got %v", resolved.Kind)
- }
- if resolved.Hash != hashObj {
- t.Errorf("resolved hash: got %s, want %s", resolved.Hash, hashObj)
- }
-
- gitRevParse := gitCmd(t, repoPath, "rev-parse", "refs/heads/main")
- if resolved.Hash.String() != gitRevParse {
- t.Errorf("furgit resolved %s, git resolved %s", resolved.Hash, gitRevParse)
- }
-
- _, err = repo.ResolveRef("refs/heads/nonexistent")
- if err == nil {
- t.Error("expected error for nonexistent ref")
- }
-}
-
-func TestResolveHEAD(t *testing.T) {
- repoPath, cleanup := setupTestRepo(t)
- defer cleanup()
-
- workDir, cleanupWork := setupWorkDir(t)
- defer cleanupWork()
-
- err := os.WriteFile(filepath.Join(workDir, "test.txt"), []byte("content"), 0o644)
- if err != nil {
- t.Fatalf("failed to write test.txt: %v", err)
- }
- gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".")
- gitCmd(t, repoPath, "--work-tree="+workDir, "commit", "-m", "test")
- commitHash := gitCmd(t, repoPath, "rev-parse", "HEAD")
- gitCmd(t, repoPath, "update-ref", "refs/heads/main", commitHash)
- gitCmd(t, repoPath, "symbolic-ref", "HEAD", "refs/heads/main")
-
- repo, err := OpenRepository(repoPath)
- if err != nil {
- t.Fatalf("OpenRepository failed: %v", err)
- }
- defer func() { _ = repo.Close() }()
-
- ref, err := repo.ResolveRef("HEAD")
- if err != nil {
- t.Fatalf("ResolveRef(HEAD) failed: %v", err)
- }
-
- if ref.Kind != RefKindSymbolic {
- t.Fatalf("HEAD kind: got %v, want %v", ref.Kind, RefKindSymbolic)
- }
-
- if ref.Ref != "refs/heads/main" {
- t.Errorf("HEAD symbolic ref: got %q, want %q", ref.Ref, "refs/heads/main")
- }
-
- gitSymRef := gitCmd(t, repoPath, "symbolic-ref", "HEAD")
- if ref.Ref != gitSymRef {
- t.Errorf("furgit resolved %v, git resolved %s", ref.Ref, gitSymRef)
- }
-}
-
-func TestPackedRefs(t *testing.T) {
- repoPath, cleanup := setupTestRepo(t)
- defer cleanup()
-
- workDir, cleanupWork := setupWorkDir(t)
- defer cleanupWork()
-
- err := os.WriteFile(filepath.Join(workDir, "test.txt"), []byte("content1"), 0o644)
- if err != nil {
- t.Fatalf("failed to write test.txt: %v", err)
- }
- gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".")
- gitCmd(t, repoPath, "--work-tree="+workDir, "commit", "-m", "commit1")
- commit1Hash := gitCmd(t, repoPath, "rev-parse", "HEAD")
-
- err = os.WriteFile(filepath.Join(workDir, "test2.txt"), []byte("content2"), 0o644)
- if err != nil {
- t.Fatalf("failed to write test2.txt: %v", err)
- }
- gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".")
- gitCmd(t, repoPath, "--work-tree="+workDir, "commit", "-m", "commit2")
- commit2Hash := gitCmd(t, repoPath, "rev-parse", "HEAD")
-
- gitCmd(t, repoPath, "update-ref", "refs/heads/branch1", commit1Hash)
- gitCmd(t, repoPath, "update-ref", "refs/heads/branch2", commit2Hash)
- gitCmd(t, repoPath, "update-ref", "refs/tags/v1.0", commit1Hash)
-
- gitCmd(t, repoPath, "pack-refs", "--all")
-
- repo, err := OpenRepository(repoPath)
- if err != nil {
- t.Fatalf("OpenRepository failed: %v", err)
- }
- defer func() { _ = repo.Close() }()
-
- hash1, _ := repo.ParseHash(commit1Hash)
- hash2, _ := repo.ParseHash(commit2Hash)
-
- resolved1, err := repo.ResolveRef("refs/heads/branch1")
- if err != nil {
- t.Fatalf("ResolveRef branch1 failed: %v", err)
- }
- if resolved1.Kind != RefKindDetached || resolved1.Hash != hash1 {
- t.Errorf("branch1: got %s, want %s", resolved1.Hash, hash1)
- }
-
- gitResolved1 := gitCmd(t, repoPath, "rev-parse", "refs/heads/branch1")
- if resolved1.Hash.String() != gitResolved1 {
- t.Errorf("furgit resolved %s, git resolved %s", resolved1.Hash, gitResolved1)
- }
-
- resolved2, err := repo.ResolveRef("refs/heads/branch2")
- if err != nil {
- t.Fatalf("ResolveRef branch2 failed: %v", err)
- }
- if resolved2.Kind != RefKindDetached || resolved2.Hash != hash2 {
- t.Errorf("branch2: got %s, want %s", resolved2.Hash, hash2)
- }
-
- resolvedTag, err := repo.ResolveRef("refs/tags/v1.0")
- if err != nil {
- t.Fatalf("ResolveRef tag failed: %v", err)
- }
- if resolvedTag.Kind != RefKindDetached || resolvedTag.Hash != hash1 {
- t.Errorf("tag: got %s, want %s", resolvedTag.Hash, hash1)
- }
-}
-
-func TestResolveRefFully(t *testing.T) {
- repoPath, cleanup := setupTestRepo(t)
- defer cleanup()
-
- workDir, cleanupWork := setupWorkDir(t)
- defer cleanupWork()
-
- // Create an initial commit
- err := os.WriteFile(filepath.Join(workDir, "file.txt"), []byte("content"), 0o644)
- if err != nil {
- t.Fatalf("failed to write file.txt: %v", err)
- }
- gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".")
- gitCmd(t, repoPath, "--work-tree="+workDir, "commit", "-m", "init")
- commit := gitCmd(t, repoPath, "rev-parse", "HEAD")
-
- // Create two layers of symbolic refs
- gitCmd(t, repoPath, "symbolic-ref", "refs/heads/level1", "refs/heads/level2")
- gitCmd(t, repoPath, "symbolic-ref", "refs/heads/level2", "refs/heads/main")
- gitCmd(t, repoPath, "update-ref", "refs/heads/main", commit)
-
- repo, err := OpenRepository(repoPath)
- if err != nil {
- t.Fatalf("OpenRepository failed: %v", err)
- }
- defer func() { _ = repo.Close() }()
-
- commitHash, err := repo.ParseHash(commit)
- if err != nil {
- t.Fatalf("ParseHash failed: %v", err)
- }
-
- resolved, err := repo.ResolveRefFully("refs/heads/level1")
- if err != nil {
- t.Fatalf("ResolveRefFully failed: %v", err)
- }
-
- if resolved.Hash != commitHash {
- t.Errorf("ResolveRefFully: got hash %s, want %s", resolved.Hash, commitHash)
- }
-}
-
-func TestResolveRefFullySymbolicCycle(t *testing.T) {
- repoPath, cleanup := setupTestRepo(t)
- defer cleanup()
-
- repo, err := OpenRepository(repoPath)
- if err != nil {
- t.Fatalf("OpenRepository failed: %v", err)
- }
- defer func() { _ = repo.Close() }()
-
- gitCmd(t, repoPath, "symbolic-ref", "refs/heads/A", "refs/heads/B")
- gitCmd(t, repoPath, "symbolic-ref", "refs/heads/B", "refs/heads/A")
-
- _, err = repo.ResolveRefFully("refs/heads/A")
- if err == nil {
- t.Fatalf("ResolveRefFully should fail on a symbolic cycle")
- }
-
- if !strings.Contains(err.Error(), "cycle") {
- t.Fatalf("unexpected error for symbolic cycle: %v", err)
- }
-}
-
-func TestResolveRefHashInput(t *testing.T) {
- repoPath, cleanup := setupTestRepo(t)
- defer cleanup()
-
- workDir, cleanupWork := setupWorkDir(t)
- defer cleanupWork()
-
- err := os.WriteFile(filepath.Join(workDir, "file.txt"), []byte("content"), 0o644)
- if err != nil {
- t.Fatalf("failed to write file.txt: %v", err)
- }
- gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".")
- gitCmd(t, repoPath, "--work-tree="+workDir, "commit", "-m", "init")
-
- commitHash := gitCmd(t, repoPath, "rev-parse", "HEAD")
-
- repo, err := OpenRepository(repoPath)
- if err != nil {
- t.Fatalf("OpenRepository failed: %v", err)
- }
- defer func() { _ = repo.Close() }()
-
- hashObj, err := repo.ParseHash(commitHash)
- if err != nil {
- t.Fatalf("ParseHash failed: %v", err)
- }
-
- ref, err := repo.ResolveRef(commitHash)
- if err != nil {
- t.Fatalf("ResolveRef(hash) failed: %v", err)
- }
- if ref.Kind != RefKindDetached {
- t.Fatalf("expected RefKindDetached, got %v", ref.Kind)
- }
- if ref.Hash != hashObj {
- t.Fatalf("hash mismatch: got %s, want %s", ref.Hash, hashObj)
- }
-
- hashRef, err := repo.ResolveRefFully(commitHash)
- if err != nil {
- t.Fatalf("ResolveRefFully(hash) failed: %v", err)
- }
- if hashRef.Hash != hashObj {
- t.Fatalf("hash mismatch: got %s, want %s", hashRef.Hash, hashObj)
- }
-
- _, err = repo.ResolveRef("this_is_not_a_hash")
- if err == nil {
- t.Fatalf("expected error for invalid hash input")
- }
-}
-
-func TestListRefsLooseOverridesPacked(t *testing.T) {
- repoPath, cleanup := setupTestRepo(t)
- defer cleanup()
-
- workDir, cleanupWork := setupWorkDir(t)
- defer cleanupWork()
-
- gitCmd(t, repoPath, "symbolic-ref", "HEAD", "refs/heads/main")
-
- err := os.WriteFile(filepath.Join(workDir, "file.txt"), []byte("one"), 0o644)
- if err != nil {
- t.Fatalf("failed to write file.txt: %v", err)
- }
- gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".")
- gitCmd(t, repoPath, "--work-tree="+workDir, "commit", "-m", "c1")
- commit1 := gitCmd(t, repoPath, "rev-parse", "HEAD")
-
- gitCmd(t, repoPath, "update-ref", "refs/heads/main", commit1)
- gitCmd(t, repoPath, "update-ref", "refs/heads/feature", commit1)
- gitCmd(t, repoPath, "pack-refs", "--all", "--prune")
-
- err = os.WriteFile(filepath.Join(workDir, "file.txt"), []byte("two"), 0o644)
- if err != nil {
- t.Fatalf("failed to write file.txt: %v", err)
- }
- gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".")
- gitCmd(t, repoPath, "--work-tree="+workDir, "commit", "-m", "c2")
- commit2 := gitCmd(t, repoPath, "rev-parse", "HEAD")
- gitCmd(t, repoPath, "update-ref", "refs/heads/main", commit2)
-
- repo, err := OpenRepository(repoPath)
- if err != nil {
- t.Fatalf("OpenRepository failed: %v", err)
- }
- defer func() { _ = repo.Close() }()
-
- hash1, _ := repo.ParseHash(commit1)
- hash2, _ := repo.ParseHash(commit2)
-
- refs, err := repo.ListRefs("refs/heads/*")
- if err != nil {
- t.Fatalf("ListRefs failed: %v", err)
- }
-
- if len(refs) != 2 {
- t.Fatalf("expected 2 refs, got %d", len(refs))
- }
-
- got := make(map[string]Ref, len(refs))
- for _, r := range refs {
- if _, exists := got[r.Name]; exists {
- t.Fatalf("duplicate ref %q in results", r.Name)
- }
- got[r.Name] = r
- }
-
- mainRef, ok := got["refs/heads/main"]
- if !ok {
- t.Fatalf("missing refs/heads/main in results")
- }
- if mainRef.Kind != RefKindDetached || mainRef.Hash != hash2 {
- t.Fatalf("refs/heads/main hash: got %s (kind %v), want %s", mainRef.Hash, mainRef.Kind, hash2)
- }
-
- featureRef, ok := got["refs/heads/feature"]
- if !ok {
- t.Fatalf("missing refs/heads/feature in results")
- }
- if featureRef.Kind != RefKindDetached || featureRef.Hash != hash1 {
- t.Fatalf("refs/heads/feature hash: got %s (kind %v), want %s", featureRef.Hash, featureRef.Kind, hash1)
- }
-}
-
-func TestListRefsPatternFiltering(t *testing.T) {
- repoPath, cleanup := setupTestRepo(t)
- defer cleanup()
-
- workDir, cleanupWork := setupWorkDir(t)
- defer cleanupWork()
-
- gitCmd(t, repoPath, "symbolic-ref", "HEAD", "refs/heads/main")
-
- err := os.WriteFile(filepath.Join(workDir, "file.txt"), []byte("one"), 0o644)
- if err != nil {
- t.Fatalf("failed to write file.txt: %v", err)
- }
- gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".")
- gitCmd(t, repoPath, "--work-tree="+workDir, "commit", "-m", "c1")
- commit1 := gitCmd(t, repoPath, "rev-parse", "HEAD")
-
- gitCmd(t, repoPath, "update-ref", "refs/heads/main", commit1)
- gitCmd(t, repoPath, "update-ref", "refs/heads/feature", commit1)
- gitCmd(t, repoPath, "pack-refs", "--all", "--prune")
-
- repo, err := OpenRepository(repoPath)
- if err != nil {
- t.Fatalf("OpenRepository failed: %v", err)
- }
- defer func() { _ = repo.Close() }()
-
- hash1, _ := repo.ParseHash(commit1)
-
- refs, err := repo.ListRefs("refs/heads/fea*")
- if err != nil {
- t.Fatalf("ListRefs failed: %v", err)
- }
- if len(refs) != 1 {
- t.Fatalf("expected 1 ref, got %d", len(refs))
- }
- if refs[0].Name != "refs/heads/feature" {
- t.Fatalf("unexpected ref name: got %q, want %q", refs[0].Name, "refs/heads/feature")
- }
- if refs[0].Kind != RefKindDetached || refs[0].Hash != hash1 {
- t.Fatalf("refs/heads/feature hash: got %s (kind %v), want %s", refs[0].Hash, refs[0].Kind, hash1)
- }
-}
-
-func TestListRefsPackedPatterns(t *testing.T) {
- repoPath, cleanup := setupTestRepo(t)
- defer cleanup()
-
- workDir, cleanupWork := setupWorkDir(t)
- defer cleanupWork()
-
- gitCmd(t, repoPath, "symbolic-ref", "HEAD", "refs/heads/main")
-
- err := os.WriteFile(filepath.Join(workDir, "file.txt"), []byte("one"), 0o644)
- if err != nil {
- t.Fatalf("failed to write file.txt: %v", err)
- }
- gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".")
- gitCmd(t, repoPath, "--work-tree="+workDir, "commit", "-m", "c1")
- commit := gitCmd(t, repoPath, "rev-parse", "HEAD")
-
- gitCmd(t, repoPath, "update-ref", "refs/heads/main", commit)
- gitCmd(t, repoPath, "update-ref", "refs/heads/feature/one", commit)
- gitCmd(t, repoPath, "update-ref", "refs/notes/review", commit)
- gitCmd(t, repoPath, "update-ref", "refs/tags/v1", commit)
- gitCmd(t, repoPath, "pack-refs", "--all", "--prune")
-
- repo, err := OpenRepository(repoPath)
- if err != nil {
- t.Fatalf("OpenRepository failed: %v", err)
- }
- defer func() { _ = repo.Close() }()
-
- tests := []struct {
- pattern string
- want []string
- }{
- {
- pattern: "refs/heads/*",
- want: []string{"refs/heads/main"},
- },
- {
- pattern: "refs/heads/*/*",
- want: []string{"refs/heads/feature/one"},
- },
- {
- pattern: "refs/*/feature/one",
- want: []string{"refs/heads/feature/one"},
- },
- {
- pattern: "refs/heads/feat?re/one",
- want: []string{"refs/heads/feature/one"},
- },
- {
- pattern: "refs/tags/v[0-9]",
- want: []string{"refs/tags/v1"},
- },
- {
- pattern: "refs/*/*",
- want: []string{"refs/heads/main", "refs/notes/review", "refs/tags/v1"},
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.pattern, func(t *testing.T) {
- refs, err := repo.ListRefs(tt.pattern)
- if err != nil {
- t.Fatalf("ListRefs(%q) failed: %v", tt.pattern, err)
- }
-
- got := make(map[string]struct{}, len(refs))
- for _, r := range refs {
- got[r.Name] = struct{}{}
- }
-
- want := make(map[string]struct{}, len(tt.want))
- for _, w := range tt.want {
- want[w] = struct{}{}
- }
-
- if len(got) != len(want) {
- t.Fatalf("ListRefs(%q) returned %d refs, want %d", tt.pattern, len(got), len(want))
- }
- for name := range got {
- if _, ok := want[name]; !ok {
- t.Fatalf("ListRefs(%q) unexpected ref %q", tt.pattern, name)
- }
- }
- })
- }
-}
-
-func TestRefShort(t *testing.T) {
- t.Run("unambiguous", func(t *testing.T) {
- ref := Ref{Name: "refs/heads/main"}
- short := ref.Short([]Ref{ref}, false)
- if short != "main" {
- t.Fatalf("expected short name %q, got %q", "main", short)
- }
- })
-
- t.Run("ambiguous", func(t *testing.T) {
- ref := Ref{Name: "refs/heads/main"}
- tags := Ref{Name: "refs/tags/main"}
- short := ref.Short([]Ref{ref, tags}, false)
- if short != "heads/main" {
- t.Fatalf("expected ambiguous ref to shorten to %q, got %q", "heads/main", short)
- }
- })
-
- t.Run("strict", func(t *testing.T) {
- ref := Ref{Name: "refs/heads/main"}
- remoteHead := Ref{Name: "refs/remotes/main/HEAD"}
-
- shortNonStrict := ref.Short([]Ref{ref, remoteHead}, false)
- if shortNonStrict != "main" {
- t.Fatalf("expected non-strict short name %q, got %q", "main", shortNonStrict)
- }
-
- shortStrict := ref.Short([]Ref{ref, remoteHead}, true)
- if shortStrict != "heads/main" {
- t.Fatalf("expected strict ambiguity to shorten to %q, got %q", "heads/main", shortStrict)
- }
- })
-}